From 7c810855de6a7241ff0a05592b821ae789e28cf7 Mon Sep 17 00:00:00 2001 From: Lindsay Stewart Date: Sat, 28 Oct 2023 23:28:32 -0700 Subject: [PATCH] Put tests back --- tests/unit/s2n_3des_test.c | 115 + tests/unit/s2n_aead_aes_test.c | 447 ++++ tests/unit/s2n_aead_chacha20_poly1305_test.c | 246 ++ tests/unit/s2n_aes_sha_composite_test.c | 397 ++++ tests/unit/s2n_aes_test.c | 184 ++ tests/unit/s2n_alerts_protocol_test.c | 636 +++++ tests/unit/s2n_alerts_test.c | 380 +++ tests/unit/s2n_array_test.c | 204 ++ tests/unit/s2n_async_pkey_test.c | 725 ++++++ tests/unit/s2n_auth_selection_test.c | 464 ++++ tests/unit/s2n_blob_test.c | 163 ++ tests/unit/s2n_build_test.c | 101 + tests/unit/s2n_cert_chain_and_key_test.c | 200 ++ tests/unit/s2n_cert_status_extension_test.c | 392 +++ .../s2n_cert_status_response_extension_test.c | 99 + .../unit/s2n_cert_validation_callback_test.c | 450 ++++ tests/unit/s2n_certificate_extensions_test.c | 345 +++ tests/unit/s2n_certificate_test.c | 1059 +++++++++ tests/unit/s2n_change_cipher_spec_test.c | 192 ++ tests/unit/s2n_choose_supported_group_test.c | 326 +++ tests/unit/s2n_cipher_info_test.c | 53 + tests/unit/s2n_cipher_suite_match_test.c | 1418 +++++++++++ tests/unit/s2n_cipher_suites_test.c | 149 ++ tests/unit/s2n_cleanup_test.c | 97 + tests/unit/s2n_cleanup_with_no_init_test.c | 50 + tests/unit/s2n_client_alpn_extension_test.c | 126 + tests/unit/s2n_client_auth_handshake_test.c | 362 +++ .../s2n_client_cert_request_context_test.c | 114 + ...lient_cert_status_request_extension_test.c | 145 ++ tests/unit/s2n_client_cert_verify_test.c | 322 +++ .../s2n_client_early_data_indication_test.c | 511 ++++ tests/unit/s2n_client_empty_cert_test.c | 122 + tests/unit/s2n_client_extensions_test.c | 1491 ++++++++++++ tests/unit/s2n_client_finished_test.c | 96 + ...n_client_hello_get_supported_groups_test.c | 388 +++ tests/unit/s2n_client_hello_recv_test.c | 522 ++++ tests/unit/s2n_client_hello_request_test.c | 562 +++++ tests/unit/s2n_client_hello_retry_test.c | 1681 +++++++++++++ tests/unit/s2n_client_hello_test.c | 1689 +++++++++++++ .../s2n_client_key_share_extension_pq_test.c | 896 +++++++ .../s2n_client_key_share_extension_test.c | 1157 +++++++++ .../s2n_client_max_frag_len_extension_test.c | 159 ++ tests/unit/s2n_client_pq_kem_extension_test.c | 134 ++ tests/unit/s2n_client_psk_extension_test.c | 1657 +++++++++++++ tests/unit/s2n_client_record_version_test.c | 316 +++ ...client_renegotiation_info_extension_test.c | 477 ++++ .../unit/s2n_client_sct_list_extension_test.c | 88 + .../s2n_client_secure_renegotiation_test.c | 309 +++ .../s2n_client_server_name_extension_test.c | 196 ++ ...s2n_client_session_ticket_extension_test.c | 190 ++ ...ient_signature_algorithms_extension_test.c | 135 ++ ...n_client_supported_groups_extension_test.c | 536 +++++ ...client_supported_versions_extension_test.c | 450 ++++ tests/unit/s2n_config_test.c | 982 ++++++++ tests/unit/s2n_connection_context_test.c | 47 + tests/unit/s2n_connection_preferences_test.c | 296 +++ .../s2n_connection_protocol_versions_test.c | 390 +++ tests/unit/s2n_connection_size_test.c | 64 + tests/unit/s2n_connection_test.c | 884 +++++++ tests/unit/s2n_cookie_test.c | 378 +++ tests/unit/s2n_crl_test.c | 930 ++++++++ tests/unit/s2n_drain_alert_test.c | 140 ++ tests/unit/s2n_drbg_test.c | 456 ++++ tests/unit/s2n_early_data_io_api_test.c | 1096 +++++++++ tests/unit/s2n_early_data_io_test.c | 814 +++++++ tests/unit/s2n_early_data_test.c | 1432 +++++++++++ tests/unit/s2n_ecc_evp_test.c | 409 ++++ .../s2n_ecc_point_format_extension_test.c | 106 + tests/unit/s2n_ecc_preferences_test.c | 49 + tests/unit/s2n_ecdsa_test.c | 254 ++ tests/unit/s2n_ems_extension_test.c | 113 + tests/unit/s2n_encrypted_extensions_test.c | 257 ++ tests/unit/s2n_error_lookup_test.c | 116 + tests/unit/s2n_error_type_test.c | 55 + tests/unit/s2n_evp_signing_test.c | 288 +++ tests/unit/s2n_examples_test.c | 329 +++ tests/unit/s2n_extended_master_secret_test.c | 304 +++ tests/unit/s2n_extension_list_parse_test.c | 423 ++++ tests/unit/s2n_extension_list_process_test.c | 339 +++ tests/unit/s2n_extension_list_send_test.c | 129 + tests/unit/s2n_extension_type_lists_test.c | 51 + tests/unit/s2n_extension_type_test.c | 571 +++++ ..._extensions_server_key_share_select_test.c | 448 ++++ tests/unit/s2n_fingerprint_ja3_test.c | 1005 ++++++++ tests/unit/s2n_fork_generation_number_test.c | 374 +++ .../unit/s2n_fragmentation_coalescing_test.c | 646 +++++ tests/unit/s2n_handshake_errno_test.c | 87 + tests/unit/s2n_handshake_fragment_test.c | 373 +++ tests/unit/s2n_handshake_hashes_test.c | 154 ++ tests/unit/s2n_handshake_invariant_test.c | 89 + tests/unit/s2n_handshake_io_async_test.c | 117 + tests/unit/s2n_handshake_io_early_data_test.c | 129 + tests/unit/s2n_handshake_io_errors_test.c | 129 + tests/unit/s2n_handshake_io_test.c | 61 + tests/unit/s2n_handshake_misc_test.c | 52 + tests/unit/s2n_handshake_partial_test.c | 192 ++ tests/unit/s2n_handshake_test.c | 439 ++++ tests/unit/s2n_handshake_type_test.c | 275 +++ tests/unit/s2n_hash_all_algs_test.c | 159 ++ tests/unit/s2n_hash_test.c | 335 +++ tests/unit/s2n_hkdf_test.c | 478 ++++ tests/unit/s2n_hmac_test.c | 293 +++ tests/unit/s2n_init_test.c | 92 + tests/unit/s2n_io_test.c | 131 + tests/unit/s2n_kem_preferences_test.c | 69 + tests/unit/s2n_kem_test.c | 513 ++++ tests/unit/s2n_kex_test.c | 79 + tests/unit/s2n_kex_with_kem_test.c | 183 ++ tests/unit/s2n_key_share_extension_test.c | 74 + tests/unit/s2n_key_update_test.c | 593 +++++ tests/unit/s2n_key_update_threads_test.c | 264 +++ tests/unit/s2n_ktls_io_sendfile_test.c | 218 ++ tests/unit/s2n_ktls_io_test.c | 1189 ++++++++++ tests/unit/s2n_ktls_mode_test.c | 38 + tests/unit/s2n_ktls_test.c | 564 +++++ tests/unit/s2n_ktls_test_utils_test.c | 634 +++++ tests/unit/s2n_locking_test.c | 124 + tests/unit/s2n_malformed_handshake_test.c | 539 +++++ tests/unit/s2n_map_test.c | 177 ++ tests/unit/s2n_mem_allocator_test.c | 31 +- tests/unit/s2n_mem_test.c | 89 + tests/unit/s2n_mem_testlib_test.c | 198 ++ tests/unit/s2n_mem_usage_test.c | 287 +++ tests/unit/s2n_mutual_auth_test.c | 316 +++ tests/unit/s2n_next_protocol_test.c | 221 ++ tests/unit/s2n_npn_extension_test.c | 289 +++ .../unit/s2n_nst_early_data_indication_test.c | 98 + tests/unit/s2n_openssl_test.c | 41 + tests/unit/s2n_optional_client_auth_test.c | 472 ++++ tests/unit/s2n_override_openssl_random_test.c | 147 ++ tests/unit/s2n_pem_rsa_dhe_test.c | 205 ++ tests/unit/s2n_pem_test.c | 96 + tests/unit/s2n_pkey_test.c | 171 ++ tests/unit/s2n_post_handshake_recv_test.c | 516 ++++ tests/unit/s2n_post_handshake_send_test.c | 235 ++ tests/unit/s2n_post_handshake_test.c | 269 +++ tests/unit/s2n_pq_kem_kat_kyber_r3_test.c | 52 + tests/unit/s2n_pq_kem_test.c | 118 + tests/unit/s2n_prf_key_material_test.c | 235 ++ tests/unit/s2n_protocol_preferences_test.c | 284 +++ ...2n_psk_key_exchange_modes_extension_test.c | 291 +++ tests/unit/s2n_psk_offered_test.c | 632 +++++ tests/unit/s2n_psk_test.c | 1107 +++++++++ tests/unit/s2n_quic_support_io_test.c | 526 +++++ tests/unit/s2n_quic_support_test.c | 304 +++ ...s2n_quic_transport_params_extension_test.c | 236 ++ tests/unit/s2n_random_test.c | 842 +++++++ tests/unit/s2n_rc4_test.c | 137 ++ tests/unit/s2n_record_size_test.c | 522 ++++ tests/unit/s2n_record_test.c | 437 ++++ tests/unit/s2n_record_write_test.c | 84 + tests/unit/s2n_recv_test.c | 670 ++++++ .../unit/s2n_release_non_empty_buffers_test.c | 203 ++ tests/unit/s2n_renegotiate_io_test.c | 501 ++++ tests/unit/s2n_renegotiate_test.c | 585 +++++ tests/unit/s2n_resume_test.c | 1843 +++++++++++++++ tests/unit/s2n_rfc5952_test.c | 91 + tests/unit/s2n_rsa_pss_rsae_test.c | 321 +++ tests/unit/s2n_rsa_pss_test.c | 350 +++ tests/unit/s2n_safety_blinding_test.c | 183 ++ tests/unit/s2n_safety_macros_test.c | 1043 ++++++++ tests/unit/s2n_safety_test.c | 392 +++ tests/unit/s2n_security_policies_test.c | 1085 +++++++++ tests/unit/s2n_self_talk_alerts_test.c | 334 +++ tests/unit/s2n_self_talk_alpn_test.c | 426 ++++ tests/unit/s2n_self_talk_broken_pipe_test.c | 179 ++ .../unit/s2n_self_talk_client_hello_cb_test.c | 473 ++++ tests/unit/s2n_self_talk_custom_io_test.c | 217 ++ tests/unit/s2n_self_talk_io_mem_test.c | 298 +++ tests/unit/s2n_self_talk_key_log_test.c | 192 ++ tests/unit/s2n_self_talk_ktls_test.c | 600 +++++ .../s2n_self_talk_min_protocol_version_test.c | 130 + tests/unit/s2n_self_talk_nonblocking_test.c | 392 +++ tests/unit/s2n_self_talk_npn_test.c | 170 ++ .../unit/s2n_self_talk_offload_signing_test.c | 273 +++ tests/unit/s2n_self_talk_psk_test.c | 449 ++++ tests/unit/s2n_self_talk_quic_support_test.c | 131 + tests/unit/s2n_self_talk_session_id_test.c | 667 ++++++ .../s2n_self_talk_session_resumption_test.c | 1246 ++++++++++ tests/unit/s2n_self_talk_shutdown_test.c | 151 ++ tests/unit/s2n_self_talk_tls12_test.c | 206 ++ tests/unit/s2n_self_talk_tls13_test.c | 185 ++ tests/unit/s2n_send_key_update_test.c | 195 ++ tests/unit/s2n_send_multirecord_test.c | 583 +++++ tests/unit/s2n_send_test.c | 604 +++++ tests/unit/s2n_sequence_number_test.c | 108 + tests/unit/s2n_server_alpn_extension_test.c | 125 + tests/unit/s2n_server_cert_request_test.c | 110 + ...erver_cert_status_request_extension_test.c | 68 + .../s2n_server_early_data_indication_test.c | 307 +++ tests/unit/s2n_server_extensions_test.c | 682 ++++++ tests/unit/s2n_server_finished_test.c | 95 + tests/unit/s2n_server_hello_retry_test.c | 626 +++++ tests/unit/s2n_server_hello_test.c | 684 ++++++ .../s2n_server_key_share_extension_test.c | 1023 ++++++++ .../s2n_server_max_frag_len_extension_test.c | 186 ++ .../unit/s2n_server_new_session_ticket_test.c | 1337 +++++++++++ tests/unit/s2n_server_psk_extension_test.c | 303 +++ .../unit/s2n_server_renegotiation_info_test.c | 498 ++++ .../unit/s2n_server_sct_list_extension_test.c | 120 + .../s2n_server_server_name_extension_test.c | 81 + ...s2n_server_session_ticket_extension_test.c | 96 + ...rver_signature_algorithms_extension_test.c | 59 + ...server_supported_versions_extension_test.c | 158 ++ tests/unit/s2n_session_ticket_test.c | 1358 +++++++++++ tests/unit/s2n_set_test.c | 173 ++ tests/unit/s2n_shutdown_test.c | 843 +++++++ tests/unit/s2n_signature_algorithms_test.c | 1001 ++++++++ tests/unit/s2n_signature_scheme_test.c | 48 + tests/unit/s2n_ssl_prf_test.c | 104 + tests/unit/s2n_stacktrace_test.c | 54 + tests/unit/s2n_stream_cipher_null_test.c | 60 + tests/unit/s2n_stuffer_base64_test.c | 87 + tests/unit/s2n_stuffer_hex_test.c | 115 + tests/unit/s2n_stuffer_network_order_test.c | 297 +++ tests/unit/s2n_stuffer_test.c | 244 ++ tests/unit/s2n_stuffer_text_test.c | 167 ++ tests/unit/s2n_testlib_test.c | 47 + tests/unit/s2n_timer_test.c | 63 + tests/unit/s2n_tls12_handshake_test.c | 528 +++++ .../s2n_tls13_cert_request_extensions_test.c | 67 + tests/unit/s2n_tls13_cert_request_test.c | 99 + tests/unit/s2n_tls13_cert_verify_test.c | 389 +++ tests/unit/s2n_tls13_client_finished_test.c | 178 ++ .../s2n_tls13_compute_shared_secret_test.c | 161 ++ .../s2n_tls13_handshake_early_data_test.c | 356 +++ .../s2n_tls13_handshake_state_machine_test.c | 1012 ++++++++ tests/unit/s2n_tls13_handshake_test.c | 332 +++ .../s2n_tls13_hybrid_shared_secret_test.c | 651 +++++ .../s2n_tls13_key_schedule_rfc8448_test.c | 365 +++ tests/unit/s2n_tls13_key_schedule_test.c | 290 +++ tests/unit/s2n_tls13_keys_test.c | 70 + .../unit/s2n_tls13_new_session_ticket_test.c | 143 ++ tests/unit/s2n_tls13_parse_record_type_test.c | 256 ++ tests/unit/s2n_tls13_pq_handshake_test.c | 599 +++++ tests/unit/s2n_tls13_prf_test.c | 145 ++ tests/unit/s2n_tls13_record_aead_test.c | 413 ++++ tests/unit/s2n_tls13_secrets_rfc8448_test.c | 666 ++++++ tests/unit/s2n_tls13_secrets_test.c | 548 +++++ tests/unit/s2n_tls13_server_cert_test.c | 204 ++ tests/unit/s2n_tls13_server_finished_test.c | 178 ++ tests/unit/s2n_tls13_support_test.c | 187 ++ .../unit/s2n_tls13_zero_length_payload_test.c | 178 ++ tests/unit/s2n_tls_hybrid_prf_test.c | 124 + tests/unit/s2n_tls_prf_test.c | 388 +++ tests/unit/s2n_tls_record_stuffer_test.c | 101 + tests/unit/s2n_utils_test.c | 62 + tests/unit/s2n_wildcard_hostname_test.c | 73 + ...09_validator_certificate_signatures_test.c | 240 ++ tests/unit/s2n_x509_validator_test.c | 2099 +++++++++++++++++ ...2n_x509_validator_time_verification_test.c | 285 +++ 251 files changed, 94282 insertions(+), 29 deletions(-) create mode 100644 tests/unit/s2n_3des_test.c create mode 100644 tests/unit/s2n_aead_aes_test.c create mode 100644 tests/unit/s2n_aead_chacha20_poly1305_test.c create mode 100644 tests/unit/s2n_aes_sha_composite_test.c create mode 100644 tests/unit/s2n_aes_test.c create mode 100644 tests/unit/s2n_alerts_protocol_test.c create mode 100644 tests/unit/s2n_alerts_test.c create mode 100644 tests/unit/s2n_array_test.c create mode 100644 tests/unit/s2n_async_pkey_test.c create mode 100644 tests/unit/s2n_auth_selection_test.c create mode 100644 tests/unit/s2n_blob_test.c create mode 100644 tests/unit/s2n_build_test.c create mode 100644 tests/unit/s2n_cert_chain_and_key_test.c create mode 100644 tests/unit/s2n_cert_status_extension_test.c create mode 100644 tests/unit/s2n_cert_status_response_extension_test.c create mode 100644 tests/unit/s2n_cert_validation_callback_test.c create mode 100644 tests/unit/s2n_certificate_extensions_test.c create mode 100644 tests/unit/s2n_certificate_test.c create mode 100644 tests/unit/s2n_change_cipher_spec_test.c create mode 100644 tests/unit/s2n_choose_supported_group_test.c create mode 100644 tests/unit/s2n_cipher_info_test.c create mode 100644 tests/unit/s2n_cipher_suite_match_test.c create mode 100644 tests/unit/s2n_cipher_suites_test.c create mode 100644 tests/unit/s2n_cleanup_test.c create mode 100644 tests/unit/s2n_cleanup_with_no_init_test.c create mode 100644 tests/unit/s2n_client_alpn_extension_test.c create mode 100644 tests/unit/s2n_client_auth_handshake_test.c create mode 100644 tests/unit/s2n_client_cert_request_context_test.c create mode 100644 tests/unit/s2n_client_cert_status_request_extension_test.c create mode 100644 tests/unit/s2n_client_cert_verify_test.c create mode 100644 tests/unit/s2n_client_early_data_indication_test.c create mode 100644 tests/unit/s2n_client_empty_cert_test.c create mode 100644 tests/unit/s2n_client_extensions_test.c create mode 100644 tests/unit/s2n_client_finished_test.c create mode 100644 tests/unit/s2n_client_hello_get_supported_groups_test.c create mode 100644 tests/unit/s2n_client_hello_recv_test.c create mode 100644 tests/unit/s2n_client_hello_request_test.c create mode 100644 tests/unit/s2n_client_hello_retry_test.c create mode 100644 tests/unit/s2n_client_hello_test.c create mode 100644 tests/unit/s2n_client_key_share_extension_pq_test.c create mode 100644 tests/unit/s2n_client_key_share_extension_test.c create mode 100644 tests/unit/s2n_client_max_frag_len_extension_test.c create mode 100644 tests/unit/s2n_client_pq_kem_extension_test.c create mode 100644 tests/unit/s2n_client_psk_extension_test.c create mode 100644 tests/unit/s2n_client_record_version_test.c create mode 100644 tests/unit/s2n_client_renegotiation_info_extension_test.c create mode 100644 tests/unit/s2n_client_sct_list_extension_test.c create mode 100644 tests/unit/s2n_client_secure_renegotiation_test.c create mode 100644 tests/unit/s2n_client_server_name_extension_test.c create mode 100644 tests/unit/s2n_client_session_ticket_extension_test.c create mode 100644 tests/unit/s2n_client_signature_algorithms_extension_test.c create mode 100644 tests/unit/s2n_client_supported_groups_extension_test.c create mode 100644 tests/unit/s2n_client_supported_versions_extension_test.c create mode 100644 tests/unit/s2n_config_test.c create mode 100644 tests/unit/s2n_connection_context_test.c create mode 100644 tests/unit/s2n_connection_preferences_test.c create mode 100644 tests/unit/s2n_connection_protocol_versions_test.c create mode 100644 tests/unit/s2n_connection_size_test.c create mode 100644 tests/unit/s2n_connection_test.c create mode 100644 tests/unit/s2n_cookie_test.c create mode 100644 tests/unit/s2n_crl_test.c create mode 100644 tests/unit/s2n_drain_alert_test.c create mode 100644 tests/unit/s2n_drbg_test.c create mode 100644 tests/unit/s2n_early_data_io_api_test.c create mode 100644 tests/unit/s2n_early_data_io_test.c create mode 100644 tests/unit/s2n_early_data_test.c create mode 100644 tests/unit/s2n_ecc_evp_test.c create mode 100644 tests/unit/s2n_ecc_point_format_extension_test.c create mode 100644 tests/unit/s2n_ecc_preferences_test.c create mode 100644 tests/unit/s2n_ecdsa_test.c create mode 100644 tests/unit/s2n_ems_extension_test.c create mode 100644 tests/unit/s2n_encrypted_extensions_test.c create mode 100644 tests/unit/s2n_error_lookup_test.c create mode 100644 tests/unit/s2n_error_type_test.c create mode 100644 tests/unit/s2n_evp_signing_test.c create mode 100644 tests/unit/s2n_examples_test.c create mode 100644 tests/unit/s2n_extended_master_secret_test.c create mode 100644 tests/unit/s2n_extension_list_parse_test.c create mode 100644 tests/unit/s2n_extension_list_process_test.c create mode 100644 tests/unit/s2n_extension_list_send_test.c create mode 100644 tests/unit/s2n_extension_type_lists_test.c create mode 100644 tests/unit/s2n_extension_type_test.c create mode 100644 tests/unit/s2n_extensions_server_key_share_select_test.c create mode 100644 tests/unit/s2n_fingerprint_ja3_test.c create mode 100644 tests/unit/s2n_fork_generation_number_test.c create mode 100644 tests/unit/s2n_fragmentation_coalescing_test.c create mode 100644 tests/unit/s2n_handshake_errno_test.c create mode 100644 tests/unit/s2n_handshake_fragment_test.c create mode 100644 tests/unit/s2n_handshake_hashes_test.c create mode 100644 tests/unit/s2n_handshake_invariant_test.c create mode 100644 tests/unit/s2n_handshake_io_async_test.c create mode 100644 tests/unit/s2n_handshake_io_early_data_test.c create mode 100644 tests/unit/s2n_handshake_io_errors_test.c create mode 100644 tests/unit/s2n_handshake_io_test.c create mode 100644 tests/unit/s2n_handshake_misc_test.c create mode 100644 tests/unit/s2n_handshake_partial_test.c create mode 100644 tests/unit/s2n_handshake_test.c create mode 100644 tests/unit/s2n_handshake_type_test.c create mode 100644 tests/unit/s2n_hash_all_algs_test.c create mode 100644 tests/unit/s2n_hash_test.c create mode 100644 tests/unit/s2n_hkdf_test.c create mode 100644 tests/unit/s2n_hmac_test.c create mode 100644 tests/unit/s2n_init_test.c create mode 100644 tests/unit/s2n_io_test.c create mode 100644 tests/unit/s2n_kem_preferences_test.c create mode 100644 tests/unit/s2n_kem_test.c create mode 100644 tests/unit/s2n_kex_test.c create mode 100644 tests/unit/s2n_kex_with_kem_test.c create mode 100644 tests/unit/s2n_key_share_extension_test.c create mode 100644 tests/unit/s2n_key_update_test.c create mode 100644 tests/unit/s2n_key_update_threads_test.c create mode 100644 tests/unit/s2n_ktls_io_sendfile_test.c create mode 100644 tests/unit/s2n_ktls_io_test.c create mode 100644 tests/unit/s2n_ktls_mode_test.c create mode 100644 tests/unit/s2n_ktls_test.c create mode 100644 tests/unit/s2n_ktls_test_utils_test.c create mode 100644 tests/unit/s2n_locking_test.c create mode 100644 tests/unit/s2n_malformed_handshake_test.c create mode 100644 tests/unit/s2n_map_test.c create mode 100644 tests/unit/s2n_mem_test.c create mode 100644 tests/unit/s2n_mem_testlib_test.c create mode 100644 tests/unit/s2n_mem_usage_test.c create mode 100644 tests/unit/s2n_mutual_auth_test.c create mode 100644 tests/unit/s2n_next_protocol_test.c create mode 100644 tests/unit/s2n_npn_extension_test.c create mode 100644 tests/unit/s2n_nst_early_data_indication_test.c create mode 100644 tests/unit/s2n_openssl_test.c create mode 100644 tests/unit/s2n_optional_client_auth_test.c create mode 100644 tests/unit/s2n_override_openssl_random_test.c create mode 100644 tests/unit/s2n_pem_rsa_dhe_test.c create mode 100644 tests/unit/s2n_pem_test.c create mode 100644 tests/unit/s2n_pkey_test.c create mode 100644 tests/unit/s2n_post_handshake_recv_test.c create mode 100644 tests/unit/s2n_post_handshake_send_test.c create mode 100644 tests/unit/s2n_post_handshake_test.c create mode 100644 tests/unit/s2n_pq_kem_kat_kyber_r3_test.c create mode 100644 tests/unit/s2n_pq_kem_test.c create mode 100644 tests/unit/s2n_prf_key_material_test.c create mode 100644 tests/unit/s2n_protocol_preferences_test.c create mode 100644 tests/unit/s2n_psk_key_exchange_modes_extension_test.c create mode 100644 tests/unit/s2n_psk_offered_test.c create mode 100644 tests/unit/s2n_psk_test.c create mode 100644 tests/unit/s2n_quic_support_io_test.c create mode 100644 tests/unit/s2n_quic_support_test.c create mode 100644 tests/unit/s2n_quic_transport_params_extension_test.c create mode 100644 tests/unit/s2n_random_test.c create mode 100644 tests/unit/s2n_rc4_test.c create mode 100644 tests/unit/s2n_record_size_test.c create mode 100644 tests/unit/s2n_record_test.c create mode 100644 tests/unit/s2n_record_write_test.c create mode 100644 tests/unit/s2n_recv_test.c create mode 100644 tests/unit/s2n_release_non_empty_buffers_test.c create mode 100644 tests/unit/s2n_renegotiate_io_test.c create mode 100644 tests/unit/s2n_renegotiate_test.c create mode 100644 tests/unit/s2n_resume_test.c create mode 100644 tests/unit/s2n_rfc5952_test.c create mode 100644 tests/unit/s2n_rsa_pss_rsae_test.c create mode 100644 tests/unit/s2n_rsa_pss_test.c create mode 100644 tests/unit/s2n_safety_blinding_test.c create mode 100644 tests/unit/s2n_safety_macros_test.c create mode 100644 tests/unit/s2n_safety_test.c create mode 100644 tests/unit/s2n_security_policies_test.c create mode 100644 tests/unit/s2n_self_talk_alerts_test.c create mode 100644 tests/unit/s2n_self_talk_alpn_test.c create mode 100644 tests/unit/s2n_self_talk_broken_pipe_test.c create mode 100644 tests/unit/s2n_self_talk_client_hello_cb_test.c create mode 100644 tests/unit/s2n_self_talk_custom_io_test.c create mode 100644 tests/unit/s2n_self_talk_io_mem_test.c create mode 100644 tests/unit/s2n_self_talk_key_log_test.c create mode 100644 tests/unit/s2n_self_talk_ktls_test.c create mode 100644 tests/unit/s2n_self_talk_min_protocol_version_test.c create mode 100644 tests/unit/s2n_self_talk_nonblocking_test.c create mode 100644 tests/unit/s2n_self_talk_npn_test.c create mode 100644 tests/unit/s2n_self_talk_offload_signing_test.c create mode 100644 tests/unit/s2n_self_talk_psk_test.c create mode 100644 tests/unit/s2n_self_talk_quic_support_test.c create mode 100644 tests/unit/s2n_self_talk_session_id_test.c create mode 100644 tests/unit/s2n_self_talk_session_resumption_test.c create mode 100644 tests/unit/s2n_self_talk_shutdown_test.c create mode 100644 tests/unit/s2n_self_talk_tls12_test.c create mode 100644 tests/unit/s2n_self_talk_tls13_test.c create mode 100644 tests/unit/s2n_send_key_update_test.c create mode 100644 tests/unit/s2n_send_multirecord_test.c create mode 100644 tests/unit/s2n_send_test.c create mode 100644 tests/unit/s2n_sequence_number_test.c create mode 100644 tests/unit/s2n_server_alpn_extension_test.c create mode 100644 tests/unit/s2n_server_cert_request_test.c create mode 100644 tests/unit/s2n_server_cert_status_request_extension_test.c create mode 100644 tests/unit/s2n_server_early_data_indication_test.c create mode 100644 tests/unit/s2n_server_extensions_test.c create mode 100644 tests/unit/s2n_server_finished_test.c create mode 100644 tests/unit/s2n_server_hello_retry_test.c create mode 100644 tests/unit/s2n_server_hello_test.c create mode 100644 tests/unit/s2n_server_key_share_extension_test.c create mode 100644 tests/unit/s2n_server_max_frag_len_extension_test.c create mode 100644 tests/unit/s2n_server_new_session_ticket_test.c create mode 100644 tests/unit/s2n_server_psk_extension_test.c create mode 100644 tests/unit/s2n_server_renegotiation_info_test.c create mode 100644 tests/unit/s2n_server_sct_list_extension_test.c create mode 100644 tests/unit/s2n_server_server_name_extension_test.c create mode 100644 tests/unit/s2n_server_session_ticket_extension_test.c create mode 100644 tests/unit/s2n_server_signature_algorithms_extension_test.c create mode 100644 tests/unit/s2n_server_supported_versions_extension_test.c create mode 100644 tests/unit/s2n_session_ticket_test.c create mode 100644 tests/unit/s2n_set_test.c create mode 100644 tests/unit/s2n_shutdown_test.c create mode 100644 tests/unit/s2n_signature_algorithms_test.c create mode 100644 tests/unit/s2n_signature_scheme_test.c create mode 100644 tests/unit/s2n_ssl_prf_test.c create mode 100644 tests/unit/s2n_stacktrace_test.c create mode 100644 tests/unit/s2n_stream_cipher_null_test.c create mode 100644 tests/unit/s2n_stuffer_base64_test.c create mode 100644 tests/unit/s2n_stuffer_hex_test.c create mode 100644 tests/unit/s2n_stuffer_network_order_test.c create mode 100644 tests/unit/s2n_stuffer_test.c create mode 100644 tests/unit/s2n_stuffer_text_test.c create mode 100644 tests/unit/s2n_testlib_test.c create mode 100644 tests/unit/s2n_timer_test.c create mode 100644 tests/unit/s2n_tls12_handshake_test.c create mode 100644 tests/unit/s2n_tls13_cert_request_extensions_test.c create mode 100644 tests/unit/s2n_tls13_cert_request_test.c create mode 100644 tests/unit/s2n_tls13_cert_verify_test.c create mode 100644 tests/unit/s2n_tls13_client_finished_test.c create mode 100644 tests/unit/s2n_tls13_compute_shared_secret_test.c create mode 100644 tests/unit/s2n_tls13_handshake_early_data_test.c create mode 100644 tests/unit/s2n_tls13_handshake_state_machine_test.c create mode 100644 tests/unit/s2n_tls13_handshake_test.c create mode 100644 tests/unit/s2n_tls13_hybrid_shared_secret_test.c create mode 100644 tests/unit/s2n_tls13_key_schedule_rfc8448_test.c create mode 100644 tests/unit/s2n_tls13_key_schedule_test.c create mode 100644 tests/unit/s2n_tls13_keys_test.c create mode 100644 tests/unit/s2n_tls13_new_session_ticket_test.c create mode 100644 tests/unit/s2n_tls13_parse_record_type_test.c create mode 100644 tests/unit/s2n_tls13_pq_handshake_test.c create mode 100644 tests/unit/s2n_tls13_prf_test.c create mode 100644 tests/unit/s2n_tls13_record_aead_test.c create mode 100644 tests/unit/s2n_tls13_secrets_rfc8448_test.c create mode 100644 tests/unit/s2n_tls13_secrets_test.c create mode 100644 tests/unit/s2n_tls13_server_cert_test.c create mode 100644 tests/unit/s2n_tls13_server_finished_test.c create mode 100644 tests/unit/s2n_tls13_support_test.c create mode 100644 tests/unit/s2n_tls13_zero_length_payload_test.c create mode 100644 tests/unit/s2n_tls_hybrid_prf_test.c create mode 100644 tests/unit/s2n_tls_prf_test.c create mode 100644 tests/unit/s2n_tls_record_stuffer_test.c create mode 100644 tests/unit/s2n_utils_test.c create mode 100644 tests/unit/s2n_wildcard_hostname_test.c create mode 100644 tests/unit/s2n_x509_validator_certificate_signatures_test.c create mode 100644 tests/unit/s2n_x509_validator_test.c create mode 100644 tests/unit/s2n_x509_validator_time_verification_test.c diff --git a/tests/unit/s2n_3des_test.c b/tests/unit/s2n_3des_test.c new file mode 100644 index 00000000000..c28d2a786f7 --- /dev/null +++ b/tests/unit/s2n_3des_test.c @@ -0,0 +1,115 @@ +/* + * 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_cipher.h" +#include "crypto/s2n_hmac.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_prf.h" +#include "tls/s2n_record.h" +#include "utils/s2n_random.h" + +int main(int argc, char **argv) +{ + struct s2n_connection *conn; + uint8_t mac_key[] = "sample mac key"; + uint8_t des3_key[] = "12345678901234567890123"; + struct s2n_blob des3 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&des3, des3_key, sizeof(des3_key))); + uint8_t random_data[S2N_DEFAULT_FRAGMENT_LENGTH + 1]; + struct s2n_blob r = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&r, random_data, sizeof(random_data))); + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_OK(s2n_get_public_random_data(&r)); + + /* Peer and we are in sync */ + conn->server = conn->secure; + conn->client = conn->secure; + + /* test the 3des cipher with a SHA1 hash */ + conn->secure->cipher_suite->record_alg = &s2n_record_alg_3des_sha; + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->init(&conn->secure->server_key)); + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->init(&conn->secure->client_key)); + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->set_encryption_key(&conn->secure->server_key, &des3)); + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->set_decryption_key(&conn->secure->client_key, &des3)); + EXPECT_SUCCESS(s2n_hmac_init(&conn->secure->client_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); + EXPECT_SUCCESS(s2n_hmac_init(&conn->secure->server_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); + conn->actual_protocol_version = S2N_TLS11; + + for (int i = 0; i <= S2N_DEFAULT_FRAGMENT_LENGTH + 1; i++) { + struct s2n_blob in = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&in, random_data, i)); + int bytes_written; + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); + + s2n_result result = s2n_record_write(conn, TLS_APPLICATION_DATA, &in); + if (i <= S2N_DEFAULT_FRAGMENT_LENGTH) { + EXPECT_OK(result); + bytes_written = i; + } else { + EXPECT_ERROR_WITH_ERRNO(result, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); + bytes_written = S2N_DEFAULT_FRAGMENT_LENGTH; + } + + uint16_t predicted_length = bytes_written + 1 + 20 + 8; + if (predicted_length % 8) { + predicted_length += (8 - (predicted_length % 8)); + } + EXPECT_EQUAL(conn->out.blob.data[0], TLS_APPLICATION_DATA); + EXPECT_EQUAL(conn->out.blob.data[1], 3); + EXPECT_EQUAL(conn->out.blob.data[2], 2); + EXPECT_EQUAL(conn->out.blob.data[3], (predicted_length >> 8) & 0xff); + EXPECT_EQUAL(conn->out.blob.data[4], predicted_length & 0xff); + + /* The data should be encrypted */ + if (bytes_written > 10) { + EXPECT_NOT_EQUAL(memcmp(conn->out.blob.data + 5, random_data, bytes_written), 0); + } + + /* Copy the encrypted out data to the in data */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, 5)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + + /* Let's decrypt it */ + uint8_t content_type; + uint16_t fragment_length; + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + EXPECT_SUCCESS(s2n_record_parse(conn)); + EXPECT_EQUAL(content_type, TLS_APPLICATION_DATA); + EXPECT_EQUAL(fragment_length, predicted_length); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + } + + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->destroy_key(&conn->secure->server_key)); + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->destroy_key(&conn->secure->client_key)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + + END_TEST(); +} diff --git a/tests/unit/s2n_aead_aes_test.c b/tests/unit/s2n_aead_aes_test.c new file mode 100644 index 00000000000..84ffe8a5a7f --- /dev/null +++ b/tests/unit/s2n_aead_aes_test.c @@ -0,0 +1,447 @@ +/* + * 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_cipher.h" +#include "crypto/s2n_hmac.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_prf.h" +#include "tls/s2n_record.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" + +static int destroy_server_keys(struct s2n_connection *server_conn) +{ + POSIX_GUARD(server_conn->initial->cipher_suite->record_alg->cipher->destroy_key(&server_conn->initial->server_key)); + POSIX_GUARD(server_conn->initial->cipher_suite->record_alg->cipher->destroy_key(&server_conn->initial->client_key)); + return 0; +} + +static int setup_server_keys(struct s2n_connection *server_conn, struct s2n_blob *key) +{ + POSIX_GUARD(server_conn->initial->cipher_suite->record_alg->cipher->init(&server_conn->initial->server_key)); + POSIX_GUARD(server_conn->initial->cipher_suite->record_alg->cipher->init(&server_conn->initial->client_key)); + POSIX_GUARD(server_conn->initial->cipher_suite->record_alg->cipher->set_encryption_key(&server_conn->initial->server_key, key)); + POSIX_GUARD(server_conn->initial->cipher_suite->record_alg->cipher->set_decryption_key(&server_conn->initial->client_key, key)); + + return 0; +} + +int main(int argc, char **argv) +{ + struct s2n_connection *conn; + uint8_t random_data[S2N_SMALL_FRAGMENT_LENGTH + 1]; + uint8_t aes128_key[] = "123456789012345"; + uint8_t aes256_key[] = "1234567890123456789012345678901"; + struct s2n_blob aes128 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&aes128, aes128_key, sizeof(aes128_key))); + struct s2n_blob aes256 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&aes256, aes256_key, sizeof(aes256_key))); + struct s2n_blob r = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&r, random_data, sizeof(random_data))); + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_OK(s2n_get_public_random_data(&r)); + + /* Peer and we are in sync */ + conn->server = conn->initial; + conn->client = conn->initial; + + /* test the AES128 cipher */ + conn->initial->cipher_suite->record_alg = &s2n_record_alg_aes128_gcm; + EXPECT_SUCCESS(setup_server_keys(conn, &aes128)); + + int max_fragment = S2N_SMALL_FRAGMENT_LENGTH; + for (size_t i = 0; i <= max_fragment + 1; i++) { + struct s2n_blob in = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&in, random_data, i)); + int bytes_written; + + /* TLS packet on the wire using AES-GCM: + * https://tools.ietf.org/html/rfc5246#section-6.2.3.3 + * https://tools.ietf.org/html/rfc5288#section-3 + * ---------------------------------------------- + * |TLS header|explicit IV|encrypted payload|TAG| + * ---------------------------------------------- + * Length: + * S2N_TLS_RECORD_HEADER_LENGTH + S2N_TLS_GCM_EXPLICIT_IV_LEN + i + S2N_TLS_GCM_TAG_LEN + */ + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_SUCCESS(s2n_connection_prefer_low_latency(conn)); + conn->actual_protocol_version_established = 1; + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + conn->server = conn->initial; + conn->client = conn->initial; + conn->initial->cipher_suite->record_alg = &s2n_record_alg_aes128_gcm; + EXPECT_SUCCESS(destroy_server_keys(conn)); + EXPECT_SUCCESS(setup_server_keys(conn, &aes128)); + + s2n_result result = s2n_record_write(conn, TLS_APPLICATION_DATA, &in); + if (i <= max_fragment) { + EXPECT_OK(result); + bytes_written = i; + } else { + EXPECT_ERROR_WITH_ERRNO(result, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); + bytes_written = max_fragment; + } + + uint16_t predicted_length = bytes_written; + predicted_length += conn->initial->cipher_suite->record_alg->cipher->io.aead.record_iv_size; + predicted_length += conn->initial->cipher_suite->record_alg->cipher->io.aead.tag_size; + + const int overhead = S2N_TLS_GCM_EXPLICIT_IV_LEN /* Explicit IV */ + + S2N_TLS_GCM_TAG_LEN /* TAG */; + EXPECT_EQUAL(predicted_length, bytes_written + overhead); + + EXPECT_EQUAL(conn->out.blob.data[0], TLS_APPLICATION_DATA); + EXPECT_EQUAL(conn->out.blob.data[1], 3); + EXPECT_EQUAL(conn->out.blob.data[2], 3); + EXPECT_EQUAL(conn->out.blob.data[3], (predicted_length >> 8) & 0xff); + EXPECT_EQUAL(conn->out.blob.data[4], predicted_length & 0xff); + + /* The data should be encrypted */ + if (bytes_written > 10) { + EXPECT_NOT_EQUAL(memcmp(conn->out.blob.data + S2N_TLS_RECORD_HEADER_LENGTH, random_data, bytes_written), 0); + } + + /* Copy the encrypted out data to the in data */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, S2N_TLS_RECORD_HEADER_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + + /* Let's decrypt it */ + uint8_t content_type; + uint16_t fragment_length; + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + EXPECT_SUCCESS(s2n_record_parse(conn)); + EXPECT_EQUAL(content_type, TLS_APPLICATION_DATA); + EXPECT_EQUAL(fragment_length, predicted_length); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + + /* Start over */ + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + conn->actual_protocol_version_established = 1; + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + conn->initial->cipher_suite->record_alg = &s2n_record_alg_aes128_gcm; + EXPECT_SUCCESS(destroy_server_keys(conn)); + EXPECT_SUCCESS(setup_server_keys(conn, &aes128)); + EXPECT_OK(s2n_record_write(conn, TLS_APPLICATION_DATA, &in)); + + /* Now lets corrupt some data and ensure the tests pass */ + /* Copy the encrypted out data to the in data */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, S2N_TLS_RECORD_HEADER_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + + /* Tamper the protocol version in the header, and ensure decryption fails, as we use this in the AAD */ + EXPECT_EQUAL(conn->header_in.blob.data[0], TLS_APPLICATION_DATA); + conn->header_in.blob.data[0] ^= 1; /* Flip a bit in the content_type of the TLS Record Header */ + + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + EXPECT_EQUAL(content_type, TLS_APPLICATION_DATA ^ 1); + + /** + * We are trying to test the case when the Additional Authenticated Data in AEAD ciphers is tampered with. + * + * AEAD Ciphers authenticate several fields, including the TLS Record content_type, so this should fail since + * we flipped a bit. See s2n_aead_aad_init() for which fields are added to the additional authenticated data. + * + * We can't flip the TLS Protocol Version bits here because s2n_record_header_parse() will error before we + * attempt decryption with AES-GCM because the Protocol version doesn't match "conn->actual_protocol_version". + */ + EXPECT_FAILURE(s2n_record_parse(conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + + /* Tamper with the explicit IV and ensure decryption fails */ + for (size_t j = 0; j < S2N_TLS_GCM_EXPLICIT_IV_LEN; j++) { + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + conn->actual_protocol_version_established = 1; + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + conn->initial->cipher_suite->record_alg = &s2n_record_alg_aes128_gcm; + EXPECT_SUCCESS(destroy_server_keys(conn)); + EXPECT_SUCCESS(setup_server_keys(conn, &aes128)); + EXPECT_OK(s2n_record_write(conn, TLS_APPLICATION_DATA, &in)); + + /* Copy the encrypted out data to the in data */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, S2N_TLS_RECORD_HEADER_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + conn->in.blob.data[j]++; + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + EXPECT_FAILURE(s2n_record_parse(conn)); + EXPECT_EQUAL(content_type, TLS_APPLICATION_DATA); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + } + + /* Tamper with the TAG and ensure decryption fails */ + for (size_t j = 0; j < S2N_TLS_GCM_TAG_LEN; j++) { + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + conn->actual_protocol_version_established = 1; + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + conn->initial->cipher_suite->record_alg = &s2n_record_alg_aes128_gcm; + EXPECT_SUCCESS(destroy_server_keys(conn)); + EXPECT_SUCCESS(setup_server_keys(conn, &aes128)); + EXPECT_OK(s2n_record_write(conn, TLS_APPLICATION_DATA, &in)); + + /* Copy the encrypted out data to the in data */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, S2N_TLS_RECORD_HEADER_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + conn->in.blob.data[s2n_stuffer_data_available(&conn->in) - j - 1]++; + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + EXPECT_FAILURE(s2n_record_parse(conn)); + EXPECT_EQUAL(content_type, TLS_APPLICATION_DATA); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + } + + /* Tamper with the encrypted payload in the ciphertext and ensure decryption fails */ + for (size_t j = 0; j < i; j++) { + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + conn->actual_protocol_version_established = 1; + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + conn->initial->cipher_suite->record_alg = &s2n_record_alg_aes128_gcm; + EXPECT_SUCCESS(destroy_server_keys(conn)); + EXPECT_SUCCESS(setup_server_keys(conn, &aes128)); + EXPECT_OK(s2n_record_write(conn, TLS_APPLICATION_DATA, &in)); + + /* Copy the encrypted out data to the in data */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, S2N_TLS_RECORD_HEADER_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + conn->in.blob.data[S2N_TLS_GCM_EXPLICIT_IV_LEN + j]++; + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + EXPECT_FAILURE(s2n_record_parse(conn)); + EXPECT_EQUAL(content_type, TLS_APPLICATION_DATA); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + } + } + EXPECT_SUCCESS(destroy_server_keys(conn)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + + /* test the AES256 cipher */ + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->initial->cipher_suite->record_alg = &s2n_record_alg_aes256_gcm; + EXPECT_SUCCESS(setup_server_keys(conn, &aes256)); + conn->actual_protocol_version = S2N_TLS12; + + for (size_t i = 0; i <= max_fragment + 1; i++) { + struct s2n_blob in = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&in, random_data, i)); + int bytes_written; + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + /* Set prefer low latency for S2N_SMALL_FRAGMENT_LENGTH for */ + EXPECT_SUCCESS(s2n_connection_prefer_low_latency(conn)); + conn->actual_protocol_version_established = 1; + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + conn->initial->cipher_suite->record_alg = &s2n_record_alg_aes256_gcm; + EXPECT_SUCCESS(destroy_server_keys(conn)); + EXPECT_SUCCESS(setup_server_keys(conn, &aes256)); + conn->actual_protocol_version = S2N_TLS12; + + s2n_result result = s2n_record_write(conn, TLS_APPLICATION_DATA, &in); + if (i <= max_fragment) { + EXPECT_OK(result); + bytes_written = i; + } else { + EXPECT_ERROR_WITH_ERRNO(result, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); + bytes_written = max_fragment; + } + + uint16_t predicted_length = bytes_written; + predicted_length += conn->initial->cipher_suite->record_alg->cipher->io.aead.record_iv_size; + predicted_length += conn->initial->cipher_suite->record_alg->cipher->io.aead.tag_size; + + const int overhead = S2N_TLS_GCM_EXPLICIT_IV_LEN /* Explicit IV */ + + S2N_TLS_GCM_TAG_LEN /* TAG */; + EXPECT_EQUAL(predicted_length, bytes_written + overhead); + + EXPECT_EQUAL(conn->out.blob.data[0], TLS_APPLICATION_DATA); + EXPECT_EQUAL(conn->out.blob.data[1], 3); + EXPECT_EQUAL(conn->out.blob.data[2], 3); + EXPECT_EQUAL(conn->out.blob.data[3], (predicted_length >> 8) & 0xff); + EXPECT_EQUAL(conn->out.blob.data[4], predicted_length & 0xff); + + /* The data should be encrypted */ + if (bytes_written > 10) { + EXPECT_NOT_EQUAL(memcmp(conn->out.blob.data + S2N_TLS_RECORD_HEADER_LENGTH, random_data, bytes_written), 0); + } + + /* Copy the encrypted out data to the in data */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, S2N_TLS_RECORD_HEADER_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + + /* Let's decrypt it */ + uint8_t content_type; + uint16_t fragment_length; + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + EXPECT_SUCCESS(s2n_record_parse(conn)); + EXPECT_EQUAL(content_type, TLS_APPLICATION_DATA); + EXPECT_EQUAL(fragment_length, predicted_length); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + conn->initial->cipher_suite->record_alg = &s2n_record_alg_aes256_gcm; + EXPECT_SUCCESS(destroy_server_keys(conn)); + EXPECT_SUCCESS(setup_server_keys(conn, &aes256)); + conn->actual_protocol_version = S2N_TLS12; + EXPECT_OK(s2n_record_write(conn, TLS_APPLICATION_DATA, &in)); + + /* Now lets corrupt some data and ensure the tests pass */ + /* Copy the encrypted out data to the in data */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, S2N_TLS_RECORD_HEADER_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + + /* Tamper with the protocol version in the header, and ensure decryption fails, as we use this in the AAD */ + conn->in.blob.data[2] = 2; + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + EXPECT_FAILURE(s2n_record_parse(conn)); + EXPECT_EQUAL(content_type, TLS_APPLICATION_DATA); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + + /* Tamper with the IV and ensure decryption fails */ + for (size_t j = 0; j < S2N_TLS_GCM_EXPLICIT_IV_LEN; j++) { + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + conn->actual_protocol_version_established = 1; + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + conn->initial->cipher_suite->record_alg = &s2n_record_alg_aes256_gcm; + EXPECT_SUCCESS(destroy_server_keys(conn)); + EXPECT_SUCCESS(setup_server_keys(conn, &aes256)); + conn->actual_protocol_version = S2N_TLS12; + EXPECT_OK(s2n_record_write(conn, TLS_APPLICATION_DATA, &in)); + + /* Copy the encrypted out data to the in data */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, S2N_TLS_RECORD_HEADER_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + conn->in.blob.data[j]++; + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + EXPECT_FAILURE(s2n_record_parse(conn)); + EXPECT_EQUAL(content_type, TLS_APPLICATION_DATA); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + } + + /* Tamper with the TAG and ensure decryption fails */ + for (size_t j = 0; j < S2N_TLS_GCM_TAG_LEN; j++) { + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + conn->actual_protocol_version_established = 1; + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + conn->initial->cipher_suite->record_alg = &s2n_record_alg_aes256_gcm; + EXPECT_SUCCESS(destroy_server_keys(conn)); + EXPECT_SUCCESS(setup_server_keys(conn, &aes256)); + conn->actual_protocol_version = S2N_TLS12; + EXPECT_OK(s2n_record_write(conn, TLS_APPLICATION_DATA, &in)); + + /* Copy the encrypted out data to the in data */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, S2N_TLS_RECORD_HEADER_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + conn->in.blob.data[s2n_stuffer_data_available(&conn->in) - j - 1]++; + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + EXPECT_FAILURE(s2n_record_parse(conn)); + EXPECT_EQUAL(content_type, TLS_APPLICATION_DATA); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + } + + /* Tamper with the encrypted payload in the ciphertext and ensure decryption fails */ + for (size_t j = 0; j < i; j++) { + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + conn->actual_protocol_version_established = 1; + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + conn->initial->cipher_suite->record_alg = &s2n_record_alg_aes256_gcm; + EXPECT_SUCCESS(destroy_server_keys(conn)); + EXPECT_SUCCESS(setup_server_keys(conn, &aes256)); + conn->actual_protocol_version = S2N_TLS12; + EXPECT_OK(s2n_record_write(conn, TLS_APPLICATION_DATA, &in)); + + /* Copy the encrypted out data to the in data */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, S2N_TLS_RECORD_HEADER_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + conn->in.blob.data[S2N_TLS_GCM_EXPLICIT_IV_LEN + j]++; + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + EXPECT_FAILURE(s2n_record_parse(conn)); + EXPECT_EQUAL(content_type, TLS_APPLICATION_DATA); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + } + } + EXPECT_SUCCESS(destroy_server_keys(conn)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + + END_TEST(); +} diff --git a/tests/unit/s2n_aead_chacha20_poly1305_test.c b/tests/unit/s2n_aead_chacha20_poly1305_test.c new file mode 100644 index 00000000000..1043b814d29 --- /dev/null +++ b/tests/unit/s2n_aead_chacha20_poly1305_test.c @@ -0,0 +1,246 @@ +/* + * 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_cipher.h" +#include "crypto/s2n_hmac.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_crypto.h" +#include "tls/s2n_prf.h" +#include "tls/s2n_record.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" + +static int destroy_server_keys(struct s2n_connection *server_conn) +{ + POSIX_GUARD(server_conn->initial->cipher_suite->record_alg->cipher->destroy_key(&server_conn->initial->server_key)); + POSIX_GUARD(server_conn->initial->cipher_suite->record_alg->cipher->destroy_key(&server_conn->initial->client_key)); + return 0; +} + +static int setup_server_keys(struct s2n_connection *server_conn, struct s2n_blob *key) +{ + POSIX_GUARD(server_conn->initial->cipher_suite->record_alg->cipher->init(&server_conn->initial->server_key)); + POSIX_GUARD(server_conn->initial->cipher_suite->record_alg->cipher->init(&server_conn->initial->client_key)); + POSIX_GUARD(server_conn->initial->cipher_suite->record_alg->cipher->set_encryption_key(&server_conn->initial->server_key, key)); + POSIX_GUARD(server_conn->initial->cipher_suite->record_alg->cipher->set_decryption_key(&server_conn->initial->client_key, key)); + + return 0; +} + +int main(int argc, char **argv) +{ + struct s2n_connection *conn; + uint8_t random_data[S2N_SMALL_FRAGMENT_LENGTH + 1]; + uint8_t chacha20_poly1305_key_data[] = "1234567890123456789012345678901"; + struct s2n_blob chacha20_poly1305_key = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&chacha20_poly1305_key, chacha20_poly1305_key_data, sizeof(chacha20_poly1305_key_data))); + struct s2n_blob r = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&r, random_data, sizeof(random_data))); + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Skip test if librcrypto doesn't support the cipher */ + if (!s2n_chacha20_poly1305.is_available()) { + END_TEST(); + } + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_OK(s2n_get_public_random_data(&r)); + + /* Peer and we are in sync */ + conn->server = conn->initial; + conn->client = conn->initial; + + /* test the chacha20_poly1305 cipher */ + conn->initial->cipher_suite->record_alg = &s2n_record_alg_chacha20_poly1305; + POSIX_GUARD(setup_server_keys(conn, &chacha20_poly1305_key)); + + int max_fragment = S2N_SMALL_FRAGMENT_LENGTH; + for (size_t i = 0; i <= max_fragment + 1; i++) { + struct s2n_blob in = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&in, random_data, i)); + int bytes_written; + + /* TLS packet on the wire using ChaCha20-Poly1305: + * https://tools.ietf.org/html/rfc5246#section-6.2.3.3 + * https://tools.ietf.org/html/rfc7905#section-2 + * ---------------------------------- + * |TLS header|encrypted payload|TAG| + * ---------------------------------- + * Length: + * S2N_TLS_RECORD_HEADER_LENGTH + i + S2N_TLS_CHACHA20_POLY1305_TAG_LEN + */ + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_SUCCESS(s2n_connection_prefer_low_latency(conn)); + conn->actual_protocol_version_established = 1; + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(destroy_server_keys(conn)); + EXPECT_SUCCESS(setup_server_keys(conn, &chacha20_poly1305_key)); + + s2n_result result = s2n_record_write(conn, TLS_APPLICATION_DATA, &in); + if (i <= max_fragment) { + EXPECT_OK(result); + bytes_written = i; + } else { + EXPECT_ERROR_WITH_ERRNO(result, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); + bytes_written = max_fragment; + } + + static const int overhead = S2N_TLS_CHACHA20_POLY1305_EXPLICIT_IV_LEN /* Should be 0 */ + + S2N_TLS_CHACHA20_POLY1305_TAG_LEN; /* TAG */ + + uint16_t predicted_length = bytes_written; + predicted_length += conn->initial->cipher_suite->record_alg->cipher->io.aead.record_iv_size; + predicted_length += conn->initial->cipher_suite->record_alg->cipher->io.aead.tag_size; + EXPECT_EQUAL(predicted_length, bytes_written + overhead); + + EXPECT_EQUAL(conn->out.blob.data[0], TLS_APPLICATION_DATA); + EXPECT_EQUAL(conn->out.blob.data[1], 3); + EXPECT_EQUAL(conn->out.blob.data[2], 3); + EXPECT_EQUAL(conn->out.blob.data[3], (predicted_length >> 8) & 0xff); + EXPECT_EQUAL(conn->out.blob.data[4], predicted_length & 0xff); + + /* The data should be encrypted */ + if (bytes_written > 10) { + EXPECT_NOT_EQUAL(memcmp(conn->out.blob.data + 5, random_data, bytes_written), 0); + } + + /* Copy the encrypted out data to the in data */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, 5)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + + /* Let's decrypt it */ + uint8_t content_type; + uint16_t fragment_length; + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + EXPECT_SUCCESS(s2n_record_parse(conn)); + EXPECT_EQUAL(content_type, TLS_APPLICATION_DATA); + EXPECT_EQUAL(fragment_length, predicted_length); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + + /* Start over */ + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + conn->actual_protocol_version_established = 1; + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(destroy_server_keys(conn)); + EXPECT_SUCCESS(setup_server_keys(conn, &chacha20_poly1305_key)); + EXPECT_OK(s2n_record_write(conn, TLS_APPLICATION_DATA, &in)); + + /* Now lets corrupt some data and ensure the tests pass */ + /* Copy the encrypted out data to the in data */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, 5)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + + /* Tamper the protocol version in the header, and ensure decryption fails, as we use this in the AAD */ + EXPECT_EQUAL(conn->header_in.blob.data[0], TLS_APPLICATION_DATA); + conn->header_in.blob.data[0] ^= 1; /* Flip a bit in the content_type of the TLS Record Header */ + + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + EXPECT_EQUAL(content_type, TLS_APPLICATION_DATA ^ 1); + + /** + * We are trying to test the case when the Additional Authenticated Data in AEAD ciphers is tampered with. + * + * AEAD Ciphers authenticate several fields, including the TLS Record content_type, so this should fail since + * we flipped a bit. See s2n_aead_aad_init() for which fields are added to the additional authenticated data. + * + * We can't flip the TLS Protocol Version bits here because s2n_record_header_parse() will error before we + * attempt decryption with ChaCha because the Protocol version doesn't match "conn->actual_protocol_version". + */ + EXPECT_FAILURE(s2n_record_parse(conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + POSIX_GUARD(conn->initial->cipher_suite->record_alg->cipher->destroy_key(&conn->initial->server_key)); + POSIX_GUARD(conn->initial->cipher_suite->record_alg->cipher->destroy_key(&conn->initial->client_key)); + + /* Tamper with the TAG and ensure decryption fails */ + for (size_t j = 0; j < S2N_TLS_CHACHA20_POLY1305_TAG_LEN; j++) { + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + conn->actual_protocol_version_established = 1; + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(destroy_server_keys(conn)); + EXPECT_SUCCESS(setup_server_keys(conn, &chacha20_poly1305_key)); + EXPECT_OK(s2n_record_write(conn, TLS_APPLICATION_DATA, &in)); + + /* Copy the encrypted out data to the in data */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, 5)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + conn->in.blob.data[s2n_stuffer_data_available(&conn->in) - j - 1]++; + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + EXPECT_FAILURE(s2n_record_parse(conn)); + EXPECT_EQUAL(content_type, TLS_APPLICATION_DATA); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + POSIX_GUARD(conn->initial->cipher_suite->record_alg->cipher->destroy_key(&conn->initial->server_key)); + POSIX_GUARD(conn->initial->cipher_suite->record_alg->cipher->destroy_key(&conn->initial->client_key)); + } + + /* Tamper with the encrypted payload in the ciphertext and ensure decryption fails */ + for (size_t j = 0; j < i; j++) { + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + conn->actual_protocol_version_established = 1; + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(destroy_server_keys(conn)); + EXPECT_SUCCESS(setup_server_keys(conn, &chacha20_poly1305_key)); + EXPECT_OK(s2n_record_write(conn, TLS_APPLICATION_DATA, &in)); + + /* Copy the encrypted out data to the in data */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, 5)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + conn->in.blob.data[j]++; + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + EXPECT_FAILURE(s2n_record_parse(conn)); + EXPECT_EQUAL(content_type, TLS_APPLICATION_DATA); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + POSIX_GUARD(conn->initial->cipher_suite->record_alg->cipher->destroy_key(&conn->initial->server_key)); + POSIX_GUARD(conn->initial->cipher_suite->record_alg->cipher->destroy_key(&conn->initial->client_key)); + } + } + + EXPECT_SUCCESS(destroy_server_keys(conn)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + END_TEST(); +} diff --git a/tests/unit/s2n_aes_sha_composite_test.c b/tests/unit/s2n_aes_sha_composite_test.c new file mode 100644 index 00000000000..e6dd9e01358 --- /dev/null +++ b/tests/unit/s2n_aes_sha_composite_test.c @@ -0,0 +1,397 @@ +/* + * 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_cipher.h" +#include "crypto/s2n_hash.h" +#include "crypto/s2n_hmac.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_record.h" +#include "utils/s2n_random.h" + +/* Explicit IV starts after the TLS record header. */ +#define EXPLICIT_IV_OFFSET S2N_TLS_RECORD_HEADER_LENGTH + +/* IVs should never be repeated with the same session key. */ +static int ensure_explicit_iv_is_unique(uint8_t existing_explicit_ivs[S2N_DEFAULT_FRAGMENT_LENGTH][S2N_TLS_MAX_IV_LEN], + size_t num_existing_ivs, + const uint8_t *candidate_iv, + uint16_t iv_len) +{ + for (size_t i = 0; i < num_existing_ivs; i++) { + if (memcmp(existing_explicit_ivs[i], candidate_iv, iv_len) == 0) { + return S2N_FAILURE; + } + } + + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + struct s2n_connection *conn; + uint8_t random_data[S2N_DEFAULT_FRAGMENT_LENGTH + 1]; + uint8_t mac_key_sha[20] = "server key shaserve"; + uint8_t mac_key_sha256[32] = "server key sha256server key sha"; + uint8_t aes128_key[] = "123456789012345"; + uint8_t aes256_key[] = "1234567890123456789012345678901"; + struct s2n_blob aes128 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&aes128, aes128_key, sizeof(aes128_key))); + struct s2n_blob aes256 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&aes256, aes256_key, sizeof(aes256_key))); + struct s2n_blob r = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&r, random_data, sizeof(random_data))); + /* Stores explicit IVs used in each test case to validate uniqueness. */ + uint8_t existing_explicit_ivs[S2N_DEFAULT_FRAGMENT_LENGTH + 2][S2N_TLS_MAX_IV_LEN]; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Skip test if we can't use the ciphers */ + if (!s2n_aes128_sha.is_available() + || !s2n_aes256_sha.is_available() + || !s2n_aes128_sha256.is_available() + || !s2n_aes256_sha256.is_available()) { + END_TEST(); + } + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_OK(s2n_get_public_random_data(&r)); + + /* Peer and we are in sync */ + conn->server = conn->initial; + conn->client = conn->initial; + + const int max_aligned_fragment = S2N_DEFAULT_FRAGMENT_LENGTH; + const uint8_t proto_versions[3] = { S2N_TLS10, S2N_TLS11, S2N_TLS12 }; + + /* test the composite AES128_SHA1 cipher */ + conn->initial->cipher_suite->record_alg = &s2n_record_alg_aes128_sha_composite; + + /* It's important to verify all TLS versions for the composite implementation. + * There are a few gotchas with respect to explicit IV length and payload length + */ + for (int j = 0; j < 3; j++) { + for (size_t i = 0; i <= max_aligned_fragment + 1; i++) { + struct s2n_blob in = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&in, random_data, i)); + int bytes_written; + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + EXPECT_SUCCESS(conn->initial->cipher_suite->record_alg->cipher->set_encryption_key(&conn->initial->server_key, &aes128)); + EXPECT_SUCCESS(conn->initial->cipher_suite->record_alg->cipher->set_decryption_key(&conn->initial->client_key, &aes128)); + EXPECT_SUCCESS(conn->initial->cipher_suite->record_alg->cipher->io.comp.set_mac_write_key(&conn->initial->server_key, mac_key_sha, sizeof(mac_key_sha))); + EXPECT_SUCCESS(conn->initial->cipher_suite->record_alg->cipher->io.comp.set_mac_write_key(&conn->initial->client_key, mac_key_sha, sizeof(mac_key_sha))); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); + conn->actual_protocol_version = proto_versions[j]; + + int explicit_iv_len; + if (conn->actual_protocol_version > S2N_TLS10) { + explicit_iv_len = 16; + } else { + explicit_iv_len = 0; + } + + s2n_result result = s2n_record_write(conn, TLS_APPLICATION_DATA, &in); + if (i <= max_aligned_fragment) { + EXPECT_OK(result); + bytes_written = i; + } else { + EXPECT_ERROR_WITH_ERRNO(result, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); + bytes_written = max_aligned_fragment; + } + + uint16_t predicted_length = bytes_written + 1 + SHA_DIGEST_LENGTH + explicit_iv_len; + if (predicted_length % 16) { + predicted_length += (16 - (predicted_length % 16)); + } + EXPECT_EQUAL(conn->out.blob.data[0], TLS_APPLICATION_DATA); + uint8_t record_version = conn->out.blob.data[1] * 10 + conn->out.blob.data[2]; + EXPECT_EQUAL(record_version, conn->actual_protocol_version); + EXPECT_EQUAL(conn->out.blob.data[3], (predicted_length >> 8) & 0xff); + EXPECT_EQUAL(conn->out.blob.data[4], predicted_length & 0xff); + + /* The data should be encrypted */ + if (bytes_written > 10) { + EXPECT_NOT_EQUAL(memcmp(conn->out.blob.data + EXPLICIT_IV_OFFSET + explicit_iv_len, random_data, bytes_written), 0); + } + + if (explicit_iv_len > 0) { + /* The explicit IV for every record written should be random */ + uint8_t *explicit_iv = conn->out.blob.data + EXPLICIT_IV_OFFSET; + EXPECT_SUCCESS(ensure_explicit_iv_is_unique(existing_explicit_ivs, i, explicit_iv, explicit_iv_len)); + /* Record this IV */ + EXPECT_MEMCPY_SUCCESS(existing_explicit_ivs[i], explicit_iv, explicit_iv_len); + } + + /* Copy the encrypted out data to the in data */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, S2N_TLS_RECORD_HEADER_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + + /* Let's decrypt it */ + uint8_t content_type; + uint16_t fragment_length; + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + EXPECT_SUCCESS(s2n_record_parse(conn)); + EXPECT_EQUAL(content_type, TLS_APPLICATION_DATA); + EXPECT_EQUAL(fragment_length, predicted_length); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + } + } + + /* test the composite AES256_SHA1 cipher */ + conn->initial->cipher_suite->record_alg = &s2n_record_alg_aes256_sha_composite; + for (int j = 0; j < 3; j++) { + for (int i = 0; i <= max_aligned_fragment + 1; i++) { + struct s2n_blob in = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&in, random_data, i)); + int bytes_written; + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + EXPECT_SUCCESS(conn->initial->cipher_suite->record_alg->cipher->set_encryption_key(&conn->initial->server_key, &aes256)); + EXPECT_SUCCESS(conn->initial->cipher_suite->record_alg->cipher->set_decryption_key(&conn->initial->client_key, &aes256)); + EXPECT_SUCCESS(conn->initial->cipher_suite->record_alg->cipher->io.comp.set_mac_write_key(&conn->initial->server_key, mac_key_sha, sizeof(mac_key_sha))); + EXPECT_SUCCESS(conn->initial->cipher_suite->record_alg->cipher->io.comp.set_mac_write_key(&conn->initial->client_key, mac_key_sha, sizeof(mac_key_sha))); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); + conn->actual_protocol_version = proto_versions[j]; + + int explicit_iv_len; + if (conn->actual_protocol_version > S2N_TLS10) { + explicit_iv_len = 16; + } else { + explicit_iv_len = 0; + } + + s2n_result result = s2n_record_write(conn, TLS_APPLICATION_DATA, &in); + if (i <= max_aligned_fragment) { + EXPECT_OK(result); + bytes_written = i; + } else { + EXPECT_ERROR_WITH_ERRNO(result, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); + bytes_written = max_aligned_fragment; + } + + uint16_t predicted_length = bytes_written + 1 + SHA_DIGEST_LENGTH + explicit_iv_len; + if (predicted_length % 16) { + predicted_length += (16 - (predicted_length % 16)); + } + EXPECT_EQUAL(conn->out.blob.data[0], TLS_APPLICATION_DATA); + uint8_t record_version = conn->out.blob.data[1] * 10 + conn->out.blob.data[2]; + EXPECT_EQUAL(record_version, conn->actual_protocol_version); + EXPECT_EQUAL(conn->out.blob.data[3], (predicted_length >> 8) & 0xff); + EXPECT_EQUAL(conn->out.blob.data[4], predicted_length & 0xff); + + /* The data should be encrypted */ + if (bytes_written > 10) { + EXPECT_NOT_EQUAL(memcmp(conn->out.blob.data + EXPLICIT_IV_OFFSET + explicit_iv_len, random_data, bytes_written), 0); + } + + if (explicit_iv_len > 0) { + /* The explicit IV for every record written should be random */ + uint8_t *explicit_iv = conn->out.blob.data + EXPLICIT_IV_OFFSET; + EXPECT_SUCCESS(ensure_explicit_iv_is_unique(existing_explicit_ivs, i, explicit_iv, explicit_iv_len)); + /* Record this IV */ + EXPECT_MEMCPY_SUCCESS(existing_explicit_ivs[i], explicit_iv, explicit_iv_len); + } + + /* Copy the encrypted out data to the in data */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, S2N_TLS_RECORD_HEADER_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + + /* Let's decrypt it */ + uint8_t content_type; + uint16_t fragment_length; + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + EXPECT_SUCCESS(s2n_record_parse(conn)); + EXPECT_EQUAL(content_type, TLS_APPLICATION_DATA); + EXPECT_EQUAL(fragment_length, predicted_length); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + } + } + + /* test the composite AES128_SHA256 cipher */ + conn->initial->cipher_suite->record_alg = &s2n_record_alg_aes128_sha256_composite; + for (int j = 0; j < 3; j++) { + for (int i = 0; i < max_aligned_fragment + 1; i++) { + struct s2n_blob in = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&in, random_data, i)); + int bytes_written; + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + EXPECT_SUCCESS(conn->initial->cipher_suite->record_alg->cipher->set_encryption_key(&conn->initial->server_key, &aes128)); + EXPECT_SUCCESS(conn->initial->cipher_suite->record_alg->cipher->set_decryption_key(&conn->initial->client_key, &aes128)); + EXPECT_SUCCESS(conn->initial->cipher_suite->record_alg->cipher->io.comp.set_mac_write_key(&conn->initial->server_key, mac_key_sha256, sizeof(mac_key_sha256))); + EXPECT_SUCCESS(conn->initial->cipher_suite->record_alg->cipher->io.comp.set_mac_write_key(&conn->initial->client_key, mac_key_sha256, sizeof(mac_key_sha256))); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); + conn->actual_protocol_version = proto_versions[j]; + + int explicit_iv_len; + if (conn->actual_protocol_version > S2N_TLS10) { + explicit_iv_len = 16; + } else { + explicit_iv_len = 0; + } + + s2n_result result = s2n_record_write(conn, TLS_APPLICATION_DATA, &in); + if (i <= max_aligned_fragment) { + EXPECT_OK(result); + bytes_written = i; + } else { + EXPECT_ERROR_WITH_ERRNO(result, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); + bytes_written = max_aligned_fragment; + } + + uint16_t predicted_length = bytes_written + 1 + SHA256_DIGEST_LENGTH + explicit_iv_len; + if (predicted_length % 16) { + predicted_length += (16 - (predicted_length % 16)); + } + EXPECT_EQUAL(conn->out.blob.data[0], TLS_APPLICATION_DATA); + uint8_t record_version = conn->out.blob.data[1] * 10 + conn->out.blob.data[2]; + EXPECT_EQUAL(record_version, conn->actual_protocol_version); + EXPECT_EQUAL(conn->out.blob.data[3], (predicted_length >> 8) & 0xff); + EXPECT_EQUAL(conn->out.blob.data[4], predicted_length & 0xff); + + /* The data should be encrypted */ + if (bytes_written > 10) { + EXPECT_NOT_EQUAL(memcmp(conn->out.blob.data + EXPLICIT_IV_OFFSET + explicit_iv_len, random_data, bytes_written), 0); + } + + if (explicit_iv_len > 0) { + /* The explicit IV for every record written should be random */ + uint8_t *explicit_iv = conn->out.blob.data + EXPLICIT_IV_OFFSET; + EXPECT_SUCCESS(ensure_explicit_iv_is_unique(existing_explicit_ivs, i, explicit_iv, explicit_iv_len)); + /* Record this IV */ + EXPECT_MEMCPY_SUCCESS(existing_explicit_ivs[i], explicit_iv, explicit_iv_len); + } + + /* Copy the encrypted out data to the in data */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, S2N_TLS_RECORD_HEADER_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + + /* Let's decrypt it */ + uint8_t content_type; + uint16_t fragment_length; + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + EXPECT_SUCCESS(s2n_record_parse(conn)); + EXPECT_EQUAL(content_type, TLS_APPLICATION_DATA); + EXPECT_EQUAL(fragment_length, predicted_length); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + } + } + + /* test the composite AES256_SHA256 cipher */ + conn->initial->cipher_suite->record_alg = &s2n_record_alg_aes256_sha256_composite; + for (int j = 0; j < 3; j++) { + for (int i = 0; i <= max_aligned_fragment + 1; i++) { + struct s2n_blob in = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&in, random_data, i)); + int bytes_written; + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + EXPECT_SUCCESS(conn->initial->cipher_suite->record_alg->cipher->set_encryption_key(&conn->initial->server_key, &aes256)); + EXPECT_SUCCESS(conn->initial->cipher_suite->record_alg->cipher->set_decryption_key(&conn->initial->client_key, &aes256)); + EXPECT_SUCCESS(conn->initial->cipher_suite->record_alg->cipher->io.comp.set_mac_write_key(&conn->initial->server_key, mac_key_sha256, sizeof(mac_key_sha256))); + EXPECT_SUCCESS(conn->initial->cipher_suite->record_alg->cipher->io.comp.set_mac_write_key(&conn->initial->client_key, mac_key_sha256, sizeof(mac_key_sha256))); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); + conn->actual_protocol_version = proto_versions[j]; + + int explicit_iv_len; + if (conn->actual_protocol_version > S2N_TLS10) { + explicit_iv_len = 16; + } else { + explicit_iv_len = 0; + } + + s2n_result result = s2n_record_write(conn, TLS_APPLICATION_DATA, &in); + if (i <= max_aligned_fragment) { + EXPECT_OK(result); + bytes_written = i; + } else { + EXPECT_ERROR_WITH_ERRNO(result, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); + bytes_written = max_aligned_fragment; + } + + uint16_t predicted_length = bytes_written + 1 + SHA256_DIGEST_LENGTH + explicit_iv_len; + if (predicted_length % 16) { + predicted_length += (16 - (predicted_length % 16)); + } + EXPECT_EQUAL(conn->out.blob.data[0], TLS_APPLICATION_DATA); + uint8_t record_version = conn->out.blob.data[1] * 10 + conn->out.blob.data[2]; + EXPECT_EQUAL(record_version, conn->actual_protocol_version); + EXPECT_EQUAL(conn->out.blob.data[3], (predicted_length >> 8) & 0xff); + EXPECT_EQUAL(conn->out.blob.data[4], predicted_length & 0xff); + + /* The data should be encrypted */ + if (bytes_written > 10) { + EXPECT_NOT_EQUAL(memcmp(conn->out.blob.data + EXPLICIT_IV_OFFSET + explicit_iv_len, random_data, bytes_written), 0); + } + + if (explicit_iv_len > 0) { + /* The explicit IV for every record written should be random */ + uint8_t *explicit_iv = conn->out.blob.data + EXPLICIT_IV_OFFSET; + EXPECT_SUCCESS(ensure_explicit_iv_is_unique(existing_explicit_ivs, i, explicit_iv, explicit_iv_len)); + /* Record this IV */ + EXPECT_MEMCPY_SUCCESS(existing_explicit_ivs[i], explicit_iv, explicit_iv_len); + } + + /* Copy the encrypted out data to the in data */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, S2N_TLS_RECORD_HEADER_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + + /* Let's decrypt it */ + uint8_t content_type; + uint16_t fragment_length; + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + EXPECT_SUCCESS(s2n_record_parse(conn)); + EXPECT_EQUAL(content_type, TLS_APPLICATION_DATA); + EXPECT_EQUAL(fragment_length, predicted_length); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + + END_TEST(); +} diff --git a/tests/unit/s2n_aes_test.c b/tests/unit/s2n_aes_test.c new file mode 100644 index 00000000000..1005bbb3a7b --- /dev/null +++ b/tests/unit/s2n_aes_test.c @@ -0,0 +1,184 @@ +/* + * 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_cipher.h" +#include "crypto/s2n_hmac.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_prf.h" +#include "tls/s2n_record.h" +#include "utils/s2n_random.h" + +int main(int argc, char **argv) +{ + struct s2n_connection *conn; + uint8_t mac_key[] = "sample mac key"; + uint8_t aes128_key[] = "123456789012345"; + uint8_t aes256_key[] = "1234567890123456789012345678901"; + struct s2n_blob aes128 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&aes128, aes128_key, sizeof(aes128_key))); + struct s2n_blob aes256 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&aes256, aes256_key, sizeof(aes256_key))); + uint8_t random_data[S2N_DEFAULT_FRAGMENT_LENGTH + 1]; + struct s2n_blob r = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&r, random_data, sizeof(random_data))); + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_OK(s2n_get_public_random_data(&r)); + + /* Peer and we are in sync */ + conn->server = conn->secure; + conn->client = conn->secure; + + /* test the AES128 cipher with a SHA1 hash */ + conn->secure->cipher_suite->record_alg = &s2n_record_alg_aes128_sha; + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->init(&conn->secure->server_key)); + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->init(&conn->secure->client_key)); + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->set_encryption_key(&conn->secure->server_key, &aes128)); + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->set_decryption_key(&conn->secure->client_key, &aes128)); + EXPECT_SUCCESS(s2n_hmac_init(&conn->secure->client_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); + EXPECT_SUCCESS(s2n_hmac_init(&conn->secure->server_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); + conn->actual_protocol_version = S2N_TLS11; + + for (size_t i = 0; i <= S2N_DEFAULT_FRAGMENT_LENGTH + 1; i++) { + struct s2n_blob in = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&in, random_data, i)); + int bytes_written; + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); + + s2n_result result = s2n_record_write(conn, TLS_APPLICATION_DATA, &in); + if (i <= S2N_DEFAULT_FRAGMENT_LENGTH) { + EXPECT_OK(result); + bytes_written = i; + } else { + EXPECT_ERROR_WITH_ERRNO(result, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); + bytes_written = S2N_DEFAULT_FRAGMENT_LENGTH; + } + + uint16_t predicted_length = bytes_written + 1 + 20 + 16; + if (predicted_length % 16) { + predicted_length += (16 - (predicted_length % 16)); + } + EXPECT_EQUAL(conn->out.blob.data[0], TLS_APPLICATION_DATA); + EXPECT_EQUAL(conn->out.blob.data[1], 3); + EXPECT_EQUAL(conn->out.blob.data[2], 2); + EXPECT_EQUAL(conn->out.blob.data[3], (predicted_length >> 8) & 0xff); + EXPECT_EQUAL(conn->out.blob.data[4], predicted_length & 0xff); + + /* The data should be encrypted */ + if (bytes_written > 10) { + EXPECT_NOT_EQUAL(memcmp(conn->out.blob.data + 5, random_data, bytes_written), 0); + } + + /* Copy the encrypted out data to the in data */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, 5)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + + /* Let's decrypt it */ + uint8_t content_type; + uint16_t fragment_length; + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + EXPECT_SUCCESS(s2n_record_parse(conn)); + EXPECT_EQUAL(content_type, TLS_APPLICATION_DATA); + EXPECT_EQUAL(fragment_length, predicted_length); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + } + + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->destroy_key(&conn->secure->server_key)); + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->destroy_key(&conn->secure->client_key)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + + /* test the AES256 cipher with a SHA1 hash */ + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->server = conn->secure; + conn->client = conn->secure; + conn->secure->cipher_suite->record_alg = &s2n_record_alg_aes256_sha; + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->init(&conn->secure->server_key)); + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->init(&conn->secure->client_key)); + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->set_encryption_key(&conn->secure->server_key, &aes256)); + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->set_decryption_key(&conn->secure->client_key, &aes256)); + EXPECT_SUCCESS(s2n_hmac_init(&conn->secure->client_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); + EXPECT_SUCCESS(s2n_hmac_init(&conn->secure->server_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); + conn->actual_protocol_version = S2N_TLS11; + + for (size_t i = 0; i <= S2N_DEFAULT_FRAGMENT_LENGTH + 1; i++) { + struct s2n_blob in = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&in, random_data, i)); + int bytes_written; + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); + + s2n_result result = s2n_record_write(conn, TLS_APPLICATION_DATA, &in); + if (i <= S2N_DEFAULT_FRAGMENT_LENGTH) { + EXPECT_OK(result); + bytes_written = i; + } else { + EXPECT_ERROR_WITH_ERRNO(result, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); + bytes_written = S2N_DEFAULT_FRAGMENT_LENGTH; + } + + uint16_t predicted_length = bytes_written + 1 + 20 + 16; + if (predicted_length % 16) { + predicted_length += (16 - (predicted_length % 16)); + } + EXPECT_EQUAL(conn->out.blob.data[0], TLS_APPLICATION_DATA); + EXPECT_EQUAL(conn->out.blob.data[1], 3); + EXPECT_EQUAL(conn->out.blob.data[2], 2); + EXPECT_EQUAL(conn->out.blob.data[3], (predicted_length >> 8) & 0xff); + EXPECT_EQUAL(conn->out.blob.data[4], predicted_length & 0xff); + + /* The data should be encrypted */ + if (bytes_written > 10) { + EXPECT_NOT_EQUAL(memcmp(conn->out.blob.data + 5, random_data, bytes_written), 0); + } + + /* Copy the encrypted out data to the in data */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, 5)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + + /* Let's decrypt it */ + uint8_t content_type; + uint16_t fragment_length; + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + EXPECT_SUCCESS(s2n_record_parse(conn)); + EXPECT_EQUAL(content_type, TLS_APPLICATION_DATA); + EXPECT_EQUAL(fragment_length, predicted_length); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + } + + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->destroy_key(&conn->secure->server_key)); + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->destroy_key(&conn->secure->client_key)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + + END_TEST(); +} diff --git a/tests/unit/s2n_alerts_protocol_test.c b/tests/unit/s2n_alerts_protocol_test.c new file mode 100644 index 00000000000..9f8204324e9 --- /dev/null +++ b/tests/unit/s2n_alerts_protocol_test.c @@ -0,0 +1,636 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_alerts.h" +#include "tls/s2n_tls.h" + +#define ERROR_ALERTS_COUNT (UINT16_MAX) +#define END_OF_DATA 0 +#define MODE_COUNT 2 + +int s2n_test_ch_cb(struct s2n_connection *conn, void *context) +{ + return S2N_FAILURE; +} + +typedef enum { + S2N_TEST_DURING_HANDSHAKE, + S2N_TEST_AFTER_HANDSHAKE, + S2N_TEST_TYPE_COUNT +} s2n_test_type; + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + return 0; + } + + uint8_t alert_header[] = { + /* type */ + TLS_ALERT, + /* legacy_record_version */ + 0x03, 0x03, + /* length */ + 0x00, 0x02 + }; + uint8_t close_notify[] = { 1, S2N_TLS_ALERT_CLOSE_NOTIFY }; + uint8_t data[] = "hello"; + + /** + *= https://tools.ietf.org/rfc/rfc8446#section-6 + *= type=test + *# Unknown Alert types MUST be treated as error alerts. + */ + uint8_t test_alert_levels[] = { 0, 1, 2, 3, 10, UINT8_MAX }; + + /** + *= https://tools.ietf.org/rfc/rfc8446#section-6 + *= type=test + *# All the alerts listed in Section 6.2 MUST be sent with + *# AlertLevel=fatal and MUST be treated as error alerts when received + *# regardless of the AlertLevel in the message. + */ + uint8_t error_alerts[ERROR_ALERTS_COUNT][2] = { 0 }; + size_t error_alerts_count = 0; + for (size_t level_i = 0; level_i < s2n_array_len(test_alert_levels); level_i++) { + for (size_t alert_code = 0; alert_code <= UINT8_MAX; alert_code++) { + /* Skip closure alerts */ + if (alert_code == S2N_TLS_ALERT_CLOSE_NOTIFY) { + continue; + } + if (alert_code == S2N_TLS_ALERT_USER_CANCELED) { + continue; + } + + /* To speed up the test, let's exclude a chunk of the unassigned values. */ + if (alert_code > 120 && alert_code < 250) { + continue; + } + + EXPECT_TRUE(error_alerts_count < ERROR_ALERTS_COUNT); + error_alerts[error_alerts_count][0] = test_alert_levels[level_i]; + error_alerts[error_alerts_count][1] = alert_code; + error_alerts_count++; + } + } + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + DEFER_CLEANUP(struct s2n_config *ecdsa_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(ecdsa_config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(ecdsa_config, ecdsa_chain_and_key)); + + /* Test: Receiving an error alert */ + for (size_t i = 0; i < error_alerts_count; i++) { + for (s2n_test_type type = 0; type < S2N_TEST_TYPE_COUNT; type++) { + for (uint8_t mode = 0; mode < MODE_COUNT; mode++) { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); + + if (type == S2N_TEST_DURING_HANDSHAKE) { + /* Partially perform handshake */ + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_CERT)); + } else { + /* Complete handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + } + EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS13); + + struct s2n_connection *receiver = server; + struct s2n_connection *sender = client; + if (mode == S2N_CLIENT) { + receiver = client; + sender = server; + } + + /* Send alert */ + struct s2n_blob alert = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_SUCCESS(s2n_blob_init(&alert, error_alerts[i], sizeof(error_alerts[0]))); + EXPECT_OK(s2n_record_write(sender, TLS_ALERT, &alert)); + EXPECT_SUCCESS(s2n_flush(sender, &blocked)); + + /** + *= https://tools.ietf.org/rfc/rfc8446#section-6 + *= type=test + *# Upon receiving an error alert, the TLS implementation + *# SHOULD indicate an error to the application and MUST NOT allow any + *# further data to be sent or received on the connection. + */ + if (type == S2N_TEST_DURING_HANDSHAKE) { + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(receiver, &blocked), S2N_ERR_ALERT); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(receiver, &blocked), S2N_ERR_CLOSED); + } else { + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(receiver, data, sizeof(data), &blocked), S2N_ERR_ALERT); + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(receiver, data, sizeof(data), &blocked), S2N_ERR_CLOSED); + EXPECT_FAILURE_WITH_ERRNO(s2n_send(receiver, data, sizeof(data), &blocked), S2N_ERR_CLOSED); + } + + /** + *= https://tools.ietf.org/rfc/rfc8446#section-6.2 + *= type=test + *# Upon transmission or + *# receipt of a fatal alert message, both parties MUST immediately close + *# the connection. + */ + EXPECT_TRUE(s2n_connection_check_io_status(receiver, S2N_IO_CLOSED)); + + /** + *= https://tools.ietf.org/rfc/rfc8446#section-6.2 + *= type=test + *# The implementation SHOULD provide a way to facilitate logging the sending + *# and receiving of alerts. + */ + EXPECT_EQUAL(error_alerts[i][1], s2n_connection_get_alert(receiver)); + } + } + }; + + /* Test: Sending an error alert */ + { + /* Testing a variety of alerts is more difficult sending than receiving. + * We can't trigger every possible fatal error, so just choose some common ones. + */ + int test_errors[] = { + /* handshake errors without blinding */ + S2N_ERR_CANCELLED, + S2N_ERR_CIPHER_NOT_SUPPORTED, + /* handshake errors with blinding */ + S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED, + S2N_ERR_CERT_UNTRUSTED, + /* application data error */ + S2N_ERR_DECRYPT, + }; + + DEFER_CLEANUP(struct s2n_config *bad_cb_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(bad_cb_config, s2n_test_ch_cb, NULL)); + + DEFER_CLEANUP(struct s2n_config *untrusted_config = s2n_config_new(), + s2n_config_ptr_free); + + for (size_t i = 0; i < s2n_array_len(test_errors); i++) { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + uint8_t expected_alert = S2N_TLS_ALERT_CLOSE_NOTIFY; + struct s2n_connection *failed_conn = server; + struct s2n_connection *closed_conn = client; + + switch (test_errors[i]) { + case S2N_ERR_CANCELLED: + /* Error triggered by callback failure during handshake */ + EXPECT_SUCCESS(s2n_connection_set_config(server, bad_cb_config)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server, client), + S2N_ERR_CANCELLED); + + expected_alert = S2N_TLS_ALERT_HANDSHAKE_FAILURE; + break; + case S2N_ERR_CIPHER_NOT_SUPPORTED: + /* Error triggered if no valid cipher suites. + * Use a security policy that only supports RSA certs for the client, + * and only set an EC cert for the server. + */ + EXPECT_SUCCESS(s2n_connection_set_config(server, ecdsa_config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client, "20170210")); + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server, client), + S2N_ERR_CIPHER_NOT_SUPPORTED); + break; + case S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED: + /* Remove TLS1.3 support before the ClientHello by setting + * a security policy that doesn't support TLS1.3, then add + * TLS1.3 support back after the ClientHello. + */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client, "20170210")); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(client, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server, &blocked), S2N_ERR_IO_BLOCKED); + client->client_protocol_version = S2N_TLS13; + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server, client), + S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED); + + failed_conn = client; + closed_conn = server; + break; + case S2N_ERR_CERT_UNTRUSTED: + EXPECT_SUCCESS(s2n_connection_set_config(client, untrusted_config)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server, client), + S2N_ERR_CERT_UNTRUSTED); + + failed_conn = client; + closed_conn = server; + break; + case S2N_ERR_DECRYPT: + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + /* The server will expect the original sequence number, + * not our updated sequence number, so fail to decrypt. + */ + client->secure->client_sequence_number[0] = 0xFF; + EXPECT_EQUAL(s2n_send(client, data, sizeof(data), &blocked), sizeof(data)); + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server, data, sizeof(data), &blocked), + S2N_ERR_DECRYPT); + break; + default: + FAIL_MSG("Fatal error not implemented for test"); + } + + /* Remove any blinding so that we can immediately call shutdown */ + failed_conn->delay = 0; + + /* The alert will not be sent until we attempt to shutdown */ + EXPECT_SUCCESS(s2n_shutdown_send(failed_conn, &blocked)); + EXPECT_TRUE(failed_conn->alert_sent); + + /** + *= https://tools.ietf.org/rfc/rfc8446#section-6.2 + *= type=test + *# Upon transmission or + *# receipt of a fatal alert message, both parties MUST immediately close + *# the connection. + */ + EXPECT_TRUE(s2n_connection_check_io_status(failed_conn, S2N_IO_CLOSED)); + + /** + *= https://tools.ietf.org/rfc/rfc8446#section-6.2 + *= type=test + *# Whenever an implementation encounters a fatal error condition, it + *# SHOULD send an appropriate fatal alert + */ + if (expected_alert == S2N_TLS_ALERT_CLOSE_NOTIFY) { + EXPECT_EQUAL(s2n_recv(closed_conn, data, sizeof(data), &blocked), END_OF_DATA); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + } else { + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(closed_conn, data, sizeof(data), &blocked), S2N_ERR_ALERT); + } + EXPECT_EQUAL(expected_alert, s2n_connection_get_alert(closed_conn)); + /** + *= https://tools.ietf.org/rfc/rfc8446#section-6.2 + *= type=test + *# and MUST close the connection + *# without sending or receiving any additional data. + */ + EXPECT_FAILURE_WITH_ERRNO(s2n_send(failed_conn, data, sizeof(data), &blocked), + S2N_ERR_CLOSED); + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(failed_conn, data, sizeof(data), &blocked), S2N_ERR_CLOSED); + } + }; + + /* Test: Receiving a closure alert + * + *= https://tools.ietf.org/rfc/rfc8446#section-6.1 + *= type=test + *# Either party MAY initiate a close of its write side of the connection + *# by sending a "close_notify" alert. Any data received after a closure + *# alert has been received MUST be ignored. + */ + for (s2n_test_type type = 0; type < S2N_TEST_TYPE_COUNT; type++) { + for (uint8_t mode = 0; mode < MODE_COUNT; mode++) { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); + + if (type == S2N_TEST_DURING_HANDSHAKE) { + /* Partially perform handshake */ + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_CERT)); + } else { + /* Complete handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + } + + struct s2n_connection *receiver = server; + struct s2n_stuffer *input = &io_pair.server_in; + if (mode == S2N_CLIENT) { + receiver = client; + input = &io_pair.client_in; + } + + /* Send close_notify */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(input, alert_header, sizeof(alert_header))); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(input, close_notify, sizeof(close_notify))); + + /* Receive close_notify */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + if (type == S2N_TEST_DURING_HANDSHAKE) { + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(receiver, &blocked), S2N_ERR_CLOSED); + } else { + EXPECT_EQUAL(s2n_recv(receiver, data, sizeof(data), &blocked), END_OF_DATA); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + } + EXPECT_FALSE(s2n_connection_check_io_status(receiver, S2N_IO_READABLE)); + + /* + *= https://tools.ietf.org/rfc/rfc8446#section-6.1 + *= type=test + *# Any data received after a closure alert has been received MUST be ignored. + */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(input, data, sizeof(data))); + if (type == S2N_TEST_DURING_HANDSHAKE) { + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(receiver, &blocked), S2N_ERR_CLOSED); + } else { + EXPECT_EQUAL(s2n_recv(receiver, data, sizeof(data), &blocked), END_OF_DATA); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + } + }; + }; + + /* Test: Sending a closure alert + * + *= https://tools.ietf.org/rfc/rfc8446#section-6.1 + *= type=test + *# Each party MUST send a "close_notify" alert before closing its write + *# side of the connection, unless it has already sent some error alert. + *# This does not have any effect on its read side of the connection. + */ + for (uint8_t mode = 0; mode < MODE_COUNT; mode++) { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); + + /* Perform handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS13); + + struct s2n_connection *sender = server; + struct s2n_connection *receiver = client; + if (mode == S2N_CLIENT) { + sender = client; + receiver = server; + } + + /* Send close_notify */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_SUCCESS(s2n_shutdown_send(sender, &blocked)); + EXPECT_FALSE(s2n_connection_check_io_status(sender, S2N_IO_WRITABLE)); + + /* Receive close_notify + * + *= https://tools.ietf.org/rfc/rfc8446#section-6 + *= type=test + *# The "close_notify" alert is used to indicate orderly closure of one + *# direction of the connection. Upon receiving such an alert, the TLS + *# implementation SHOULD indicate end-of-data to the application. + */ + EXPECT_EQUAL(s2n_recv(receiver, data, sizeof(data), &blocked), END_OF_DATA); + EXPECT_FALSE(s2n_connection_check_io_status(receiver, S2N_IO_READABLE)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + /* Read side is NOT affected. + * The receiver of the close_notify can continue to send, and the sender + * should continue to read. + */ + EXPECT_EQUAL(s2n_send(receiver, data, sizeof(data), &blocked), sizeof(data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(s2n_recv(sender, data, sizeof(data), &blocked), sizeof(data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + /* Respond with close_notify */ + EXPECT_SUCCESS(s2n_shutdown(receiver, &blocked)); + EXPECT_SUCCESS(s2n_shutdown(sender, &blocked)); + }; + + /* Test: Closure alerts in TLS1.2 + * + *= https://tools.ietf.org/rfc/rfc8446#section-6.1 + *= type=test + *# Note that this is a change from versions of TLS prior to TLS 1.3 in + *# which implementations were required to react to a "close_notify" by + *# discarding pending writes and sending an immediate "close_notify" + *# alert of their own. + */ + for (uint8_t mode = 0; mode < MODE_COUNT; mode++) { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server, "default")); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client, "default")); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client, server, &io_pair)); + + /* Perform handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + EXPECT_EQUAL(server->actual_protocol_version, S2N_TLS12); + + struct s2n_connection *sender = server; + struct s2n_connection *receiver = client; + if (mode == S2N_CLIENT) { + sender = client; + receiver = server; + } + + /* Send close_notify */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_SUCCESS(s2n_shutdown_send(sender, &blocked)); + EXPECT_TRUE(s2n_connection_check_io_status(sender, S2N_IO_CLOSED)); + + /* Receive close_notify */ + EXPECT_EQUAL(s2n_recv(receiver, data, sizeof(data), &blocked), END_OF_DATA); + EXPECT_TRUE(s2n_connection_check_io_status(receiver, S2N_IO_CLOSED)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + /* Read side is affected. + * The receiver should discard writes and just send a close_notify. + */ + EXPECT_FAILURE_WITH_ERRNO(s2n_send(receiver, data, sizeof(data), &blocked), + S2N_ERR_CLOSED); + + /* Respond with close_notify */ + EXPECT_SUCCESS(s2n_shutdown(receiver, &blocked)); + EXPECT_SUCCESS(s2n_shutdown(sender, &blocked)); + }; + + /* Test: End-of-Data + * + *= https://tools.ietf.org/rfc/rfc8446#6.1 + *= type=test + *# If a transport-level close + *# is received prior to a "close_notify", the receiver cannot know that + *# all the data that was sent has been received. + * + *= https://tools.ietf.org/rfc/rfc8446#6.1 + *= type=test + *# If the application protocol using TLS provides that any data may be + *# carried over the underlying transport after the TLS connection is + *# closed, the TLS implementation MUST receive a "close_notify" alert + *# before indicating end-of-data to the application layer. + */ + for (uint8_t mode = 0; mode < MODE_COUNT; mode++) { + /* Test: Without partial read */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + /* Perform handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + /* Close one end of pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, mode)); + + struct s2n_connection *receiver = client; + if (mode == S2N_CLIENT) { + receiver = server; + } + + /* Subsequent reads should NOT report END_OF_DATA, but instead an error */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + for (size_t i = 0; i < 5; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(receiver, data, sizeof(data), &blocked), + S2N_ERR_CLOSED); + EXPECT_FALSE(s2n_connection_check_io_status(receiver, S2N_IO_READABLE)); + } + }; + + /* Test: With partial read */ + { + /* In order to trigger a partial read, we need to encounter end-of-data + * when some data has already been successfully read. For that to be true, + * we need to read multiple records (one successfully, then end-of-data). + */ + struct s2n_config partial_write_config_copy = *config; + partial_write_config_copy.recv_multi_record = true; + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server, &partial_write_config_copy)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client, &partial_write_config_copy)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + /* Perform handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + + struct s2n_connection *sender = server; + struct s2n_connection *receiver = client; + if (mode == S2N_CLIENT) { + sender = client; + receiver = server; + } + + /* Send some data */ + const uint8_t partial_write = sizeof(data) / 2; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_EQUAL(s2n_send(sender, data, partial_write, &blocked), partial_write); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + /* Close one end of pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, mode)); + + /* First read should report a partial read, but also close the connection */ + EXPECT_EQUAL(s2n_recv(receiver, data, sizeof(data), &blocked), partial_write); + EXPECT_FALSE(s2n_connection_check_io_status(receiver, S2N_IO_READABLE)); + + /* Since we read at least one byte, the blocked status should be S2N_NOT_BLOCKED */ + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + /* Subsequent reads should NOT report END_OF_DATA, but instead an error */ + for (size_t i = 0; i < 5; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(receiver, data, sizeof(data), &blocked), + S2N_ERR_CLOSED); + } + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_alerts_test.c b/tests/unit/s2n_alerts_test.c new file mode 100644 index 00000000000..9131ee57a1c --- /dev/null +++ b/tests/unit/s2n_alerts_test.c @@ -0,0 +1,380 @@ +/* + * 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 "tls/s2n_alerts.h" + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_quic_support.h" + +#define ALERT_LEN (sizeof(uint16_t)) + +int s2n_flush(struct s2n_connection *conn, s2n_blocked_status *blocked); + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Test s2n_error_get_alert */ + { + uint8_t alert = 0; + + /* Test S2N_ERR_T_OK */ + EXPECT_FAILURE_WITH_ERRNO(s2n_error_get_alert(S2N_ERR_OK, &alert), S2N_ERR_NO_ALERT); + + /* Test S2N_ERR_T_CLOSED */ + EXPECT_FAILURE_WITH_ERRNO(s2n_error_get_alert(S2N_ERR_CLOSED, &alert), S2N_ERR_NO_ALERT); + + /* Test S2N_ERR_T_ALERT */ + EXPECT_FAILURE_WITH_ERRNO(s2n_error_get_alert(S2N_ERR_ALERT, &alert), S2N_ERR_NO_ALERT); + + /* Test S2N_ERR_T_BLOCKED */ + for (size_t i = S2N_ERR_T_BLOCKED_START; i < S2N_ERR_T_BLOCKED_END; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_error_get_alert(i, &alert), S2N_ERR_NO_ALERT); + } + + /* Test S2N_ERR_T_USAGE */ + for (size_t i = S2N_ERR_T_USAGE_START; i < S2N_ERR_T_USAGE_END; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_error_get_alert(i, &alert), S2N_ERR_NO_ALERT); + } + + /* Test S2N_ERR_T_PROTO */ + { + /* Test all protocol errors are handled */ + int ret_val; + for (size_t i = S2N_ERR_T_PROTO_START; i < S2N_ERR_T_PROTO_END; i++) { + ret_val = s2n_error_get_alert(i, &alert); + if (ret_val != S2N_SUCCESS && s2n_errno == S2N_ERR_UNIMPLEMENTED) { + fprintf(stdout, "\n\nNo alert mapping for protocol error %s\n\n", s2n_strerror_name(i)); + FAIL_MSG("Missing alert mapping for protocol error."); + } + } + + /* Test some known mappings */ + { + EXPECT_SUCCESS(s2n_error_get_alert(S2N_ERR_MISSING_EXTENSION, &alert)); + EXPECT_EQUAL(S2N_TLS_ALERT_MISSING_EXTENSION, alert); + + EXPECT_SUCCESS(s2n_error_get_alert(S2N_ERR_BAD_MESSAGE, &alert)); + EXPECT_EQUAL(S2N_TLS_ALERT_UNEXPECTED_MESSAGE, alert); + } + + /* Test unknown mapping */ + EXPECT_FAILURE_WITH_ERRNO(s2n_error_get_alert(S2N_ERR_EARLY_DATA_TRIAL_DECRYPT, &alert), S2N_ERR_NO_ALERT); + } + + /* Test S2N_ERR_T_IO */ + { + EXPECT_SUCCESS(s2n_error_get_alert(S2N_ERR_IO, &alert)); + EXPECT_EQUAL(alert, S2N_TLS_ALERT_INTERNAL_ERROR); + } + + /* Test S2N_ERR_T_INTERNAL */ + for (size_t i = S2N_ERR_T_INTERNAL_START; i < S2N_ERR_T_INTERNAL_END; i++) { + EXPECT_SUCCESS(s2n_error_get_alert(i, &alert)); + EXPECT_EQUAL(alert, S2N_TLS_ALERT_INTERNAL_ERROR); + } + } + + /* Test S2N_TLS_ALERT_CLOSE_NOTIFY and close_notify_received */ + { + const uint8_t close_notify_alert[] = { + 2 /* AlertLevel = fatal */, + 0 /* AlertDescription = close_notify */ + }; + + const uint8_t not_close_notify_alert[] = { + 2 /* AlertLevel = fatal */, + 10 /* AlertDescription = unexpected_msg */ + }; + + /* Don't mark close_notify_received = true if we receive an alert other than close_notify alert */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + /* Verify state prior to alert */ + EXPECT_FALSE(s2n_atomic_flag_test(&conn->close_notify_received)); + + /* Write and process the alert */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->in, not_close_notify_alert, sizeof(not_close_notify_alert))); + + /* This fails due to the alert. This is ok since we are only testing that close_notify_received was set */ + EXPECT_FAILURE_WITH_ERRNO(s2n_process_alert_fragment(conn), S2N_ERR_ALERT); + + /* Verify state after alert */ + EXPECT_FALSE(s2n_atomic_flag_test(&conn->close_notify_received)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Mark close_notify_received = true if we receive a close_notify alert */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + /* Verify state prior to alert */ + EXPECT_FALSE(s2n_atomic_flag_test(&conn->close_notify_received)); + + /* Write and process the alert */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->in, close_notify_alert, sizeof(close_notify_alert))); + EXPECT_SUCCESS(s2n_process_alert_fragment(conn)); + + /* Verify state after alert */ + EXPECT_TRUE(s2n_atomic_flag_test(&conn->close_notify_received)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + } + + /* Test s2n_process_alert_fragment */ + { + /* Safety check */ + EXPECT_FAILURE_WITH_ERRNO(s2n_process_alert_fragment(NULL), S2N_ERR_NULL); + + /* Fails if alerts not supported */ + if (s2n_is_tls13_fully_supported()) { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Succeeds by default */ + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&conn->in, 0)); + EXPECT_SUCCESS(s2n_process_alert_fragment(conn)); + + /* Wipe error */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->alert_in)); + + /* Fails when alerts not supported (when QUIC mode enabled) */ + EXPECT_SUCCESS(s2n_config_enable_quic(config)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&conn->in, 0)); + EXPECT_FAILURE_WITH_ERRNO(s2n_process_alert_fragment(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + } + + /* Test warning behavior */ + { + const uint8_t warning_alert[] = { + 1 /* AlertLevel = warning */, + 70 /* AlertDescription = protocol_version (arbitrary value) */ + }; + + const uint8_t user_canceled_alert[] = { + 1 /* AlertLevel = warning */, + 90 /* AlertDescription = user_canceled */ + }; + + /* Warnings treated as errors by default in TLS1.2 */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_EQUAL(conn->config->alert_behavior, S2N_ALERT_FAIL_ON_WARNINGS); + conn->actual_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->in, warning_alert, sizeof(warning_alert))); + + EXPECT_FAILURE_WITH_ERRNO(s2n_process_alert_fragment(conn), S2N_ERR_ALERT); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Warnings treated as errors by default in TLS1.3 */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_EQUAL(conn->config->alert_behavior, S2N_ALERT_FAIL_ON_WARNINGS); + conn->actual_protocol_version = S2N_TLS13; + + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->in, warning_alert, sizeof(warning_alert))); + + EXPECT_FAILURE_WITH_ERRNO(s2n_process_alert_fragment(conn), S2N_ERR_ALERT); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Warnings ignored in TLS1.2 if alert_behavior == S2N_ALERT_IGNORE_WARNINGS */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_alert_behavior(config, S2N_ALERT_IGNORE_WARNINGS)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->actual_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->in, warning_alert, sizeof(warning_alert))); + + EXPECT_SUCCESS(s2n_process_alert_fragment(conn)); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_FULL_DUPLEX)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + } + + /* Warnings treated as errors in TLS1.3 if alert_behavior == S2N_ALERT_IGNORE_WARNINGS */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_alert_behavior(config, S2N_ALERT_IGNORE_WARNINGS)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->actual_protocol_version = S2N_TLS13; + + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->in, warning_alert, sizeof(warning_alert))); + + EXPECT_FAILURE_WITH_ERRNO(s2n_process_alert_fragment(conn), S2N_ERR_ALERT); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + } + + /* user_canceled ignored in TLS1.3 by default */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->actual_protocol_version = S2N_TLS13; + + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->in, user_canceled_alert, sizeof(user_canceled_alert))); + + EXPECT_SUCCESS(s2n_process_alert_fragment(conn)); + + /* Expect no close */ + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_FULL_DUPLEX)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + } + } + } + + /* Test s2n_queue_reader_alert */ + { + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_queue_reader_unsupported_protocol_version_alert(NULL), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_queue_reader_handshake_failure_alert(NULL), + S2N_ERR_NULL); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + /* Alert queued */ + EXPECT_SUCCESS(s2n_queue_reader_unsupported_protocol_version_alert(conn)); + EXPECT_EQUAL(conn->reader_alert_out, S2N_TLS_ALERT_PROTOCOL_VERSION); + + /* New alert not queued if alert already set */ + EXPECT_SUCCESS(s2n_queue_reader_handshake_failure_alert(conn)); + EXPECT_EQUAL(conn->reader_alert_out, S2N_TLS_ALERT_PROTOCOL_VERSION); + + /* New alert queued if old alert cleared */ + conn->reader_alert_out = 0; + EXPECT_SUCCESS(s2n_queue_reader_handshake_failure_alert(conn)); + EXPECT_EQUAL(conn->reader_alert_out, S2N_TLS_ALERT_HANDSHAKE_FAILURE); + }; + + /* Test s2n_alerts_write_error_or_close_notify */ + { + const uint8_t expected_alert = S2N_TLS_ALERT_INTERNAL_ERROR; + const uint8_t wrong_alert = S2N_TLS_ALERT_CERTIFICATE_UNKNOWN; + + /* Test: if no alerts set, close_notify sent */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + EXPECT_OK(s2n_alerts_write_error_or_close_notify(conn)); + + /* Verify record written */ + uint8_t level = 0, code = 0; + EXPECT_SUCCESS(s2n_stuffer_skip_read(&conn->out, S2N_TLS_RECORD_HEADER_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&conn->out, &level)); + EXPECT_EQUAL(level, S2N_TLS_ALERT_LEVEL_WARNING); + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&conn->out, &code)); + EXPECT_EQUAL(code, S2N_TLS_ALERT_CLOSE_NOTIFY); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->out), 0); + }; + + /* Test: if only reader alert set, reader alert sent */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->reader_alert_out = expected_alert; + + EXPECT_OK(s2n_alerts_write_error_or_close_notify(conn)); + + /* Verify record written */ + uint8_t level = 0, code = 0; + EXPECT_SUCCESS(s2n_stuffer_skip_read(&conn->out, S2N_TLS_RECORD_HEADER_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&conn->out, &level)); + EXPECT_EQUAL(level, S2N_TLS_ALERT_LEVEL_FATAL); + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&conn->out, &code)); + EXPECT_EQUAL(code, expected_alert); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->out), 0); + }; + + /* Test: if both alerts set, writer alert sent */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->writer_alert_out = expected_alert; + conn->reader_alert_out = wrong_alert; + + EXPECT_OK(s2n_alerts_write_error_or_close_notify(conn)); + + /* Verify record written */ + uint8_t level = 0, code = 0; + EXPECT_SUCCESS(s2n_stuffer_skip_read(&conn->out, S2N_TLS_RECORD_HEADER_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&conn->out, &level)); + EXPECT_EQUAL(level, S2N_TLS_ALERT_LEVEL_FATAL); + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&conn->out, &code)); + EXPECT_EQUAL(code, expected_alert); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->out), 0); + }; + + /* If alerts not supported, no alerts sent */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->quic_enabled = true; + conn->writer_alert_out = expected_alert; + conn->reader_alert_out = wrong_alert; + + EXPECT_OK(s2n_alerts_write_error_or_close_notify(conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->out), 0); + }; + } + + END_TEST(); +} diff --git a/tests/unit/s2n_array_test.c b/tests/unit/s2n_array_test.c new file mode 100644 index 00000000000..2005aeb84c9 --- /dev/null +++ b/tests/unit/s2n_array_test.c @@ -0,0 +1,204 @@ +/* + * 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 "utils/s2n_array.h" + +#include "s2n_test.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +struct array_element { + int first; + char second; +}; + +#define NUM_OF_ELEMENTS 17 + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + size_t element_size = sizeof(struct array_element); + + struct array_element elements[NUM_OF_ELEMENTS] = { 0 }; + for (size_t i = 0; i < NUM_OF_ELEMENTS; i++) { + elements[i].first = i; + elements[i].second = 'a' + i; + } + + { + struct s2n_array *array = { 0 }; + uint32_t len = 0; + uint32_t capacity = 0; + + /* Verify add and get elements with null array */ + EXPECT_ERROR(s2n_array_pushback(NULL, NULL)); + EXPECT_ERROR(s2n_array_get(NULL, 0, NULL)); + + /* Verify freeing null array */ + EXPECT_ERROR(s2n_array_free(NULL)); + + EXPECT_NOT_NULL(array = s2n_array_new(element_size)); + + /* Validate array parameters */ + EXPECT_OK(s2n_array_capacity(array, &capacity)); + EXPECT_EQUAL(capacity, 16); + EXPECT_OK(s2n_array_num_elements(array, &len)); + EXPECT_EQUAL(len, 0); + EXPECT_EQUAL(array->element_size, element_size); + + /* Add an element */ + struct array_element *element = NULL; + EXPECT_OK(s2n_array_pushback(array, (void **) &element)); + EXPECT_NOT_NULL(element); + element->first = elements[0].first; + element->second = elements[0].second; + + /* Validate array parameters */ + EXPECT_OK(s2n_array_capacity(array, &capacity)); + EXPECT_EQUAL(capacity, 16); + EXPECT_OK(s2n_array_num_elements(array, &len)); + EXPECT_EQUAL(len, 1); + + /* Get first element */ + struct array_element *first_element = NULL; + EXPECT_OK(s2n_array_get(array, 0, (void **) &first_element)); + EXPECT_NOT_NULL(first_element); + EXPECT_EQUAL(first_element->first, elements[0].first); + EXPECT_EQUAL(first_element->second, elements[0].second); + + /* Get second element */ + struct array_element *second_element = NULL; + EXPECT_ERROR(s2n_array_get(array, 1, (void **) &second_element)); + EXPECT_NULL(second_element); + + /* Add more than 16 elements */ + for (int i = 1; i < NUM_OF_ELEMENTS; i++) { + struct array_element *elem = NULL; + EXPECT_OK(s2n_array_pushback(array, (void **) &elem)); + EXPECT_NOT_NULL(elem); + elem->first = elements[i].first; + elem->second = elements[i].second; + } + + /* Validate array parameters again */ + EXPECT_OK(s2n_array_capacity(array, &capacity)); + EXPECT_EQUAL(capacity, 32); + EXPECT_OK(s2n_array_num_elements(array, &len)); + EXPECT_EQUAL(len, 17); + EXPECT_EQUAL(array->element_size, element_size); + EXPECT_SUCCESS(memcmp(array->mem.data, elements, NUM_OF_ELEMENTS * element_size)); + + /* Insert element at given index */ + struct array_element *insert_element = NULL; + EXPECT_OK(s2n_array_insert(array, 16, (void **) &insert_element)); + EXPECT_NOT_NULL(insert_element); + insert_element->first = 20; + insert_element->second = 'a' + 20; + + /* Validate array parameters */ + EXPECT_OK(s2n_array_capacity(array, &capacity)); + EXPECT_EQUAL(capacity, 32); + EXPECT_OK(s2n_array_num_elements(array, &len)); + EXPECT_EQUAL(len, 18); + + /* Get the inserted element */ + struct array_element *inserted_element = NULL; + EXPECT_OK(s2n_array_get(array, 16, (void **) &inserted_element)); + EXPECT_NOT_NULL(inserted_element); + EXPECT_EQUAL(inserted_element->first, insert_element->first); + EXPECT_EQUAL(inserted_element->second, insert_element->second); + + /* Get the element after the inserted element */ + struct array_element *after_inserted_element = NULL; + EXPECT_OK(s2n_array_get(array, 17, (void **) &after_inserted_element)); + EXPECT_NOT_NULL(after_inserted_element); + EXPECT_EQUAL(after_inserted_element->first, elements[16].first); + EXPECT_EQUAL(after_inserted_element->second, elements[16].second); + + /* Delete element from given index */ + EXPECT_OK(s2n_array_remove(array, 0)); + + /* Validate array parameters */ + EXPECT_OK(s2n_array_capacity(array, &capacity)); + EXPECT_EQUAL(capacity, 32); + EXPECT_OK(s2n_array_num_elements(array, &len)); + EXPECT_EQUAL(len, 17); + + /* Get the current element at the deleted index */ + struct array_element *after_removed_element = NULL; + EXPECT_OK(s2n_array_get(array, 0, (void **) &after_removed_element)); + EXPECT_EQUAL(after_removed_element->first, elements[1].first); + EXPECT_EQUAL(after_removed_element->second, elements[1].second); + + /* Done with the array, make sure it can be freed */ + EXPECT_OK(s2n_array_free(array)); + + /* Check what happens if there is an integer overflow */ + /* 0xF00000F0 * 16 = 3840 (in 32 bit arithmatic) */ + EXPECT_NULL(array = s2n_array_new(0xF00000F0)); + EXPECT_NOT_NULL(array = s2n_array_new(240)); + EXPECT_OK(s2n_array_free(array)); + } + + /* Arrays initialize with default capacity */ + { + DEFER_CLEANUP(struct s2n_array *default_array = s2n_array_new(element_size), s2n_array_free_p); + + uint32_t capacity = 0; + EXPECT_OK(s2n_array_capacity(default_array, &capacity)); + EXPECT_EQUAL(capacity, S2N_INITIAL_ARRAY_SIZE); + } + + /* Test creating arrays with different initial capacities */ + for (int i = 0; i < 10; i++) { + uint32_t capacity_set = i * i; + DEFER_CLEANUP(struct s2n_array *array = s2n_array_new_with_capacity(element_size, capacity_set), + s2n_array_free_p); + + uint32_t actual_capacity = 0; + EXPECT_OK(s2n_array_capacity(array, &actual_capacity)); + EXPECT_EQUAL(capacity_set, actual_capacity); + + /* Array doesn't grow before capacity is reached */ + for (int j = 0; j < capacity_set; j++) { + struct array_element *element = NULL; + EXPECT_OK(s2n_array_pushback(array, (void **) &element)); + EXPECT_NOT_NULL(element); + + EXPECT_OK(s2n_array_capacity(array, &actual_capacity)); + EXPECT_EQUAL(capacity_set, actual_capacity); + + uint32_t len = 0; + EXPECT_OK(s2n_array_num_elements(array, &len)); + EXPECT_EQUAL(len, j + 1); + } + + /* Array grows only after capacity is reached */ + struct array_element *element = NULL; + EXPECT_OK(s2n_array_pushback(array, (void **) &element)); + EXPECT_NOT_NULL(element); + + EXPECT_OK(s2n_array_capacity(array, &actual_capacity)); + EXPECT_NOT_EQUAL(capacity_set, actual_capacity); + + uint32_t len = 0; + EXPECT_OK(s2n_array_num_elements(array, &len)); + EXPECT_EQUAL(len, capacity_set + 1); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_async_pkey_test.c b/tests/unit/s2n_async_pkey_test.c new file mode 100644 index 00000000000..2b8cc041ac6 --- /dev/null +++ b/tests/unit/s2n_async_pkey_test.c @@ -0,0 +1,725 @@ +/* + * 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 "tls/s2n_async_pkey.h" + +#include "api/s2n.h" +#include "error/s2n_errno.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_security_policies.h" +#include "utils/s2n_safety.h" + +struct s2n_async_pkey_op *pkey_op = NULL; + +uint8_t test_digest_data[] = "I hashed this"; +const uint32_t test_digest_size = sizeof(test_digest_data); +const uint8_t test_signature_data[] = "I signed this"; +const uint32_t test_signature_size = sizeof(test_signature_data); +uint8_t test_encrypted_data[] = "I encrypted this"; +const uint32_t test_encrypted_size = sizeof(test_encrypted_data); +uint8_t test_decrypted_data[] = "I decrypted this"; +const uint32_t test_decrypted_size = sizeof(test_decrypted_data); + +uint8_t offload_callback_count = 0; + +typedef int(async_handler)(struct s2n_connection *conn); + +/* Declaring a flag to check if sign operation is called at least once for all cipher_suites + * while performing handshake through handler (async_handler_sign_with_different_pkey_and_apply) */ +static bool async_handler_sign_operation_called = false; + +static int async_handler_fail(struct s2n_connection *conn) +{ + FAIL_MSG("async_handler_fail should never get invoked"); + return S2N_FAILURE; +} + +static int async_handler_wipe_connection_and_apply(struct s2n_connection *conn) +{ + /* Check that we have pkey_op */ + EXPECT_NOT_NULL(pkey_op); + + /* Extract pkey */ + struct s2n_cert_chain_and_key *chain_and_key = s2n_connection_get_selected_cert(conn); + EXPECT_NOT_NULL(chain_and_key); + + s2n_cert_private_key *pkey = s2n_cert_chain_and_key_get_private_key(chain_and_key); + EXPECT_NOT_NULL(pkey); + + /* Wipe connection */ + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + /* Test that we can perform pkey operation, even if original connection was wiped */ + EXPECT_SUCCESS(s2n_async_pkey_op_perform(pkey_op, pkey)); + + /* Test that pkey op can't be applied to wiped connection */ + EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_apply(pkey_op, conn), S2N_ERR_ASYNC_WRONG_CONNECTION); + + /* Free the pkey op */ + EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); + pkey_op = NULL; + + return S2N_FAILURE; +} + +static int async_handler_sign_with_different_pkey_and_apply(struct s2n_connection *conn) +{ + /* Check that we have pkey_op */ + EXPECT_NOT_NULL(pkey_op); + + /* Extract pkey */ + struct s2n_cert_chain_and_key *chain_and_key = s2n_connection_get_selected_cert(conn); + EXPECT_NOT_NULL(chain_and_key); + s2n_cert_private_key *pkey = s2n_cert_chain_and_key_get_private_key(chain_and_key); + EXPECT_NOT_NULL(pkey); + + /* Test that we can perform pkey operation */ + EXPECT_SUCCESS(s2n_async_pkey_op_perform(pkey_op, pkey)); + + /* Get type for pkey_op */ + s2n_async_pkey_op_type type = { 0 }; + EXPECT_SUCCESS(s2n_async_pkey_op_get_op_type(pkey_op, &type)); + + /* Test apply with different certificate chain only for sign operation */ + if (type == S2N_ASYNC_SIGN) { + /* Create new chain and key, and modify current server conn */ + struct s2n_cert_chain_and_key *chain_and_key_2 = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key_2, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + /* Change server conn cert data */ + EXPECT_NOT_NULL(conn->handshake_params.our_chain_and_key); + conn->handshake_params.our_chain_and_key = chain_and_key_2; + + /* Test that async sign operation will fail as signature was performed over different private key */ + EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_apply(pkey_op, conn), S2N_ERR_VERIFY_SIGNATURE); + + /* Set pkey_op's validation mode to S2N_ASYNC_PKEY_VALIDATION_FAST and test that async sign apply will pass now */ + EXPECT_SUCCESS(s2n_async_pkey_op_set_validation_mode(pkey_op, S2N_ASYNC_PKEY_VALIDATION_FAST)); + EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, conn)); + + /* Set chain_and_key back to original value and free new chain_and_key */ + conn->handshake_params.our_chain_and_key = chain_and_key; + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key_2)); + + /* Update async_handler_sign_operation_called flag to true */ + async_handler_sign_operation_called = true; + } else { + /* Test decrypt operation passes */ + EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, conn)); + } + + /* Free the pkey op */ + EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); + pkey_op = NULL; + + return S2N_SUCCESS; +} + +static int async_handler_free_pkey_op(struct s2n_connection *conn) +{ + static int function_entered = 0; + + /* Return failure on the second entrance into function so that we drop from try_handshake */ + if (function_entered++ % 2 == 1) { + return S2N_FAILURE; + } + + /* Check that we have pkey_op */ + EXPECT_NOT_NULL(pkey_op); + + /* Free the pkey op */ + EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); + pkey_op = NULL; + + /* Return success so that try_handshake calls s2n_negotiate again */ + return S2N_SUCCESS; +} + +static int try_handshake(struct s2n_connection *server_conn, struct s2n_connection *client_conn, async_handler handler) +{ + s2n_blocked_status server_blocked; + s2n_blocked_status client_blocked; + + int tries = 0; + do { + int client_rc = s2n_negotiate(client_conn, &client_blocked); + if (!(client_rc == 0 || (client_blocked && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED))) { + return S2N_FAILURE; + } + + int server_rc = s2n_negotiate(server_conn, &server_blocked); + if (!(server_rc == 0 || (server_blocked && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED))) { + return S2N_FAILURE; + } + + if (server_blocked == S2N_BLOCKED_ON_APPLICATION_INPUT) { + POSIX_GUARD(handler(server_conn)); + } + + EXPECT_NOT_EQUAL(++tries, 5); + } while (client_blocked || server_blocked); + + POSIX_GUARD(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + return S2N_SUCCESS; +} + +int async_pkey_apply_in_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + /* Check that we have op */ + EXPECT_NOT_NULL(op); + + /* Extract pkey */ + struct s2n_cert_chain_and_key *chain_and_key = s2n_connection_get_selected_cert(conn); + EXPECT_NOT_NULL(chain_and_key); + + s2n_cert_private_key *pkey = s2n_cert_chain_and_key_get_private_key(chain_and_key); + EXPECT_NOT_NULL(pkey); + + /* Perform the op */ + EXPECT_SUCCESS(s2n_async_pkey_op_perform(op, pkey)); + + /* Test that op can be applied inside the callback */ + EXPECT_SUCCESS(s2n_async_pkey_op_apply(op, conn)); + + /* Free the op */ + EXPECT_SUCCESS(s2n_async_pkey_op_free(op)); + + return S2N_SUCCESS; +} + +int async_pkey_store_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + pkey_op = op; + return S2N_SUCCESS; +} + +int async_pkey_signature_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + pkey_op = op; + + s2n_async_pkey_op_type type = { 0 }; + EXPECT_SUCCESS(s2n_async_pkey_op_get_op_type(op, &type)); + EXPECT_EQUAL(type, S2N_ASYNC_SIGN); + + uint8_t expected_size = 0; + EXPECT_SUCCESS(s2n_hash_digest_size(S2N_HASH_SHA256, &expected_size)); + + uint32_t input_size = 0; + EXPECT_SUCCESS(s2n_async_pkey_op_get_input_size(op, &input_size)); + EXPECT_EQUAL(input_size, expected_size); + + struct s2n_blob input1 = { 0 }; + EXPECT_SUCCESS(s2n_alloc(&input1, input_size)); + + struct s2n_blob input2 = { 0 }; + EXPECT_SUCCESS(s2n_alloc(&input2, input_size)); + + struct s2n_blob expected_digest = { 0 }; + EXPECT_SUCCESS(s2n_alloc(&expected_digest, expected_size)); + + struct s2n_hash_state digest = { 0 }; + EXPECT_SUCCESS(s2n_hash_new(&digest)); + EXPECT_SUCCESS(s2n_hash_init(&digest, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&digest, test_digest_data, test_digest_size)); + EXPECT_SUCCESS(s2n_hash_digest(&digest, expected_digest.data, expected_digest.size)); + EXPECT_SUCCESS(s2n_hash_free(&digest)); + + /* Make sure that s2n_async_pkey_op_get_input can be called multiple times, and the returned values are the same. */ + EXPECT_SUCCESS(s2n_async_pkey_op_get_input(op, input1.data, input1.size)); + EXPECT_SUCCESS(s2n_async_pkey_op_get_input(op, input2.data, input2.size)); + + EXPECT_EQUAL(input1.size, input2.size); + EXPECT_BYTEARRAY_EQUAL(input1.data, input2.data, input1.size); + EXPECT_BYTEARRAY_EQUAL(input1.data, expected_digest.data, expected_size); + + EXPECT_SUCCESS(s2n_free(&input1)); + EXPECT_SUCCESS(s2n_free(&input2)); + EXPECT_SUCCESS(s2n_free(&expected_digest)); + + EXPECT_SUCCESS(s2n_async_pkey_op_set_output(op, test_signature_data, test_signature_size)); + offload_callback_count++; + + return S2N_SUCCESS; +} + +int async_pkey_decrypt_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + pkey_op = op; + + s2n_async_pkey_op_type type = { 0 }; + EXPECT_SUCCESS(s2n_async_pkey_op_get_op_type(op, &type)); + EXPECT_EQUAL(type, S2N_ASYNC_DECRYPT); + + uint32_t input_size = 0; + EXPECT_SUCCESS(s2n_async_pkey_op_get_input_size(op, &input_size)); + EXPECT_EQUAL(input_size, test_encrypted_size); + + struct s2n_blob input_buffer1 = { 0 }; + EXPECT_SUCCESS(s2n_alloc(&input_buffer1, input_size)); + + struct s2n_blob input_buffer2 = { 0 }; + EXPECT_SUCCESS(s2n_alloc(&input_buffer2, input_size)); + + /* Make sure that s2n_async_pkey_op_get_input can be called multiple times, and the returned values are the same. */ + EXPECT_SUCCESS(s2n_async_pkey_op_get_input(op, input_buffer1.data, input_buffer1.size)); + EXPECT_BYTEARRAY_EQUAL(input_buffer1.data, test_encrypted_data, test_encrypted_size); + + EXPECT_SUCCESS(s2n_async_pkey_op_get_input(op, input_buffer2.data, input_buffer2.size)); + EXPECT_BYTEARRAY_EQUAL(input_buffer2.data, test_encrypted_data, test_encrypted_size); + + EXPECT_EQUAL(input_buffer1.size, input_buffer2.size); + EXPECT_EQUAL(input_buffer1.size, test_encrypted_size); + EXPECT_BYTEARRAY_EQUAL(input_buffer1.data, input_buffer2.data, test_encrypted_size); + + EXPECT_SUCCESS(s2n_free(&input_buffer1)); + EXPECT_SUCCESS(s2n_free(&input_buffer2)); + + EXPECT_SUCCESS(s2n_async_pkey_op_set_output(op, test_decrypted_data, test_decrypted_size)); + offload_callback_count++; + + return S2N_SUCCESS; +} + +int s2n_async_sign_complete(struct s2n_connection *conn, struct s2n_blob *signature) +{ + EXPECT_NOT_NULL(conn); + EXPECT_NOT_NULL(signature); + + EXPECT_EQUAL(signature->size, test_signature_size); + EXPECT_BYTEARRAY_EQUAL(signature->data, test_signature_data, test_signature_size); + offload_callback_count++; + + return S2N_SUCCESS; +} + +int s2n_async_decrypt_complete(struct s2n_connection *conn, bool rsa_failed, struct s2n_blob *decrypted) +{ + EXPECT_NOT_NULL(conn); + EXPECT_NOT_NULL(decrypted); + EXPECT_FALSE(rsa_failed); + + EXPECT_EQUAL(decrypted->size, test_decrypted_size); + EXPECT_BYTEARRAY_EQUAL(decrypted->data, test_decrypted_data, test_decrypted_size); + offload_callback_count++; + + return S2N_SUCCESS; +} + +int async_pkey_invalid_input_callback(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + pkey_op = op; + + EXPECT_FAILURE(s2n_async_pkey_op_get_op_type(op, NULL)); + EXPECT_FAILURE(s2n_async_pkey_op_get_input_size(op, NULL)); + EXPECT_FAILURE(s2n_async_pkey_op_get_input(op, NULL, 0)); + + uint32_t input_size = 0; + EXPECT_SUCCESS(s2n_async_pkey_op_get_input_size(op, &input_size)); + + uint8_t placeholder_buffer[] = { 0x0, 0x0, 0x0, 0x0 }; + + /* Buffer too small to contain data. */ + EXPECT_FAILURE(s2n_async_pkey_op_get_input(op, placeholder_buffer, input_size - 1)); + + EXPECT_FAILURE(s2n_async_pkey_op_set_output(op, NULL, test_signature_size)); + offload_callback_count++; + + return S2N_FAILURE; +} + +int async_pkey_invalid_complete(struct s2n_connection *conn, struct s2n_blob *signature) +{ + FAIL_MSG("Invalid async pkey callback was invoked. The callback should never be invoked if there was an earlier" + " failure in the async_pkey_op."); + return S2N_FAILURE; +} + +static int s2n_test_bad_sign(const struct s2n_pkey *pub_key, s2n_signature_algorithm sig_alg, + struct s2n_hash_state *digest, struct s2n_blob *signature) +{ + /* Just write all zeroes. + * This could accidentally be the correct signature, but it's very unlikely. + */ + POSIX_GUARD(s2n_blob_zero(signature)); + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + char dhparams_pem[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + /* Run all tests for 2 cipher suites to test both sign and decrypt operations */ + struct s2n_cipher_suite *test_cipher_suites[] = { + &s2n_rsa_with_aes_128_gcm_sha256, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256, + }; + + for (size_t i = 0; i < s2n_array_len(test_cipher_suites); i++) { + struct s2n_cipher_preferences server_cipher_preferences = { + .count = 1, + .suites = &test_cipher_suites[i], + }; + + struct s2n_security_policy server_security_policy = { + .minimum_protocol_version = S2N_TLS12, + .cipher_preferences = &server_cipher_preferences, + .kem_preferences = &kem_preferences_null, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20200310, + }; + + EXPECT_TRUE(test_cipher_suites[i]->available); + + TEST_DEBUG_PRINT("Testing %s\n", test_cipher_suites[i]->name); + + /* Test: apply while invoking callback */ + { + struct s2n_config *server_config, *client_config; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_apply_in_callback)); + server_config->security_policy = &server_security_policy; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + /* Security policy must support all cipher suites in test_cipher_suites above */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* Create connection */ + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_SUCCESS(try_handshake(server_conn, client_conn, async_handler_fail)); + + /* Free the data */ + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Test: wipe connection and then perform and apply pkey op */ + { + struct s2n_config *server_config, *client_config; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_store_callback)); + server_config->security_policy = &server_security_policy; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + /* Security policy must support all cipher suites in test_cipher_suites above */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* Create connection */ + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_EQUAL(try_handshake(server_conn, client_conn, async_handler_wipe_connection_and_apply), S2N_FAILURE); + + /* Free the data */ + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Test: free the pkey op and try s2n_negotiate again */ + { + struct s2n_config *server_config, *client_config; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_store_callback)); + server_config->security_policy = &server_security_policy; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + /* Security policy must support all cipher suites in test_cipher_suites above */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* Create connection */ + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_FAILURE_WITH_ERRNO( + try_handshake(server_conn, client_conn, async_handler_free_pkey_op), S2N_ERR_ASYNC_BLOCKED); + + /* Free the data */ + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Test: Apply invalid signature */ + { + struct s2n_config *server_config, *client_config; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_store_callback)); + server_config->security_policy = &server_security_policy; + /* Enable signature validation for async sign call */ + EXPECT_SUCCESS(s2n_config_set_async_pkey_validation_mode(server_config, S2N_ASYNC_PKEY_VALIDATION_STRICT)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + /* Security policy must support all cipher suites in test_cipher_suites above */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* Create connection */ + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_SUCCESS(try_handshake(server_conn, client_conn, async_handler_sign_with_different_pkey_and_apply)); + + /* Free the data */ + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Test: Apply invalid signature, when signature validation is enabled for all sync / async signatures */ + { + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_store_callback)); + server_config->security_policy = &server_security_policy; + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + /* Security policy must support all cipher suites in test_cipher_suites above */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* Create connection */ + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + /* Enable signature validation */ + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, S2N_VERIFY_AFTER_SIGN_ENABLED)); + EXPECT_SUCCESS(try_handshake(server_conn, client_conn, async_handler_sign_with_different_pkey_and_apply)); + } + } + + /* Test if sign operation was called at least once for 'Test: Apply invalid signature', + * the flag holds the value after executing handshakes for all cipher_suites */ + EXPECT_EQUAL(async_handler_sign_operation_called, true); + + DEFER_CLEANUP(struct s2n_hash_state digest = { 0 }, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&digest)); + EXPECT_SUCCESS(s2n_hash_init(&digest, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&digest, test_digest_data, test_digest_size)); + + /* Test: signature offload. */ + { + EXPECT_EQUAL(0, offload_callback_count); + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + conn->config->async_pkey_cb = async_pkey_signature_callback; + + EXPECT_FALSE(s2n_result_is_ok(s2n_async_pkey_sign(conn, S2N_SIGNATURE_ECDSA, &digest, s2n_async_sign_complete))); + EXPECT_TRUE(s2n_errno == S2N_ERR_ASYNC_BLOCKED); + EXPECT_EQUAL(1, offload_callback_count); + + EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, conn)); + EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); + EXPECT_EQUAL(2, offload_callback_count); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Test: decrypt offload. */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + conn->config->async_pkey_cb = async_pkey_decrypt_callback; + + struct s2n_blob encrypted_data = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&encrypted_data, test_encrypted_data, test_encrypted_size)); + + struct s2n_blob decrypted_data = { 0 }; + /* Re-use the encrypted data buffer to make sure that the data was actually transformed in the callback. + * If we filled this with the decrypted data, we would not know if the decryption happened in the callback. */ + EXPECT_SUCCESS(s2n_blob_init(&decrypted_data, test_encrypted_data, test_encrypted_size)); + + EXPECT_FALSE(s2n_result_is_ok(s2n_async_pkey_decrypt(conn, &encrypted_data, &decrypted_data, s2n_async_decrypt_complete))); + EXPECT_TRUE(s2n_errno == S2N_ERR_ASYNC_BLOCKED); + EXPECT_EQUAL(3, offload_callback_count); + + EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, conn)); + EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); + EXPECT_EQUAL(4, offload_callback_count); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Test: errors in callback. */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + conn->config->async_pkey_cb = async_pkey_invalid_input_callback; + + EXPECT_FALSE(s2n_result_is_ok(s2n_async_pkey_sign(conn, S2N_SIGNATURE_ECDSA, &digest, async_pkey_invalid_complete))); + EXPECT_TRUE(s2n_errno == S2N_ERR_ASYNC_CALLBACK_FAILED); + EXPECT_EQUAL(5, offload_callback_count); + + EXPECT_FAILURE(s2n_async_pkey_op_apply(pkey_op, conn)); + EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); + EXPECT_EQUAL(5, offload_callback_count); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + EXPECT_SUCCESS(s2n_reset_tls13_in_test()); + + /* Test: Apply invalid signature to sync operation */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + /* Start the handshake. + * We need to perform enough of the handshake to choose a certificate / private key. + */ + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(config, S2N_VERIFY_AFTER_SIGN_ENABLED)); + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, SERVER_CERT)); + + /* Setup the pkey verify operation to fail for the chosen private key */ + EXPECT_NOT_NULL(server_conn->handshake_params.our_chain_and_key); + EXPECT_NOT_NULL(server_conn->handshake_params.our_chain_and_key->private_key); + struct s2n_pkey *original_pkey = server_conn->handshake_params.our_chain_and_key->private_key; + struct s2n_pkey bad_pkey = *original_pkey; + bad_pkey.sign = s2n_test_bad_sign; + server_conn->handshake_params.our_chain_and_key->private_key = &bad_pkey; + + /* Verify after sign should fail */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(config, S2N_VERIFY_AFTER_SIGN_ENABLED)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &blocked), S2N_ERR_VERIFY_SIGNATURE); + + /* Reset pkey for cleanup */ + server_conn->handshake_params.our_chain_and_key->private_key = original_pkey; + } + + END_TEST(); +} diff --git a/tests/unit/s2n_auth_selection_test.c b/tests/unit/s2n_auth_selection_test.c new file mode 100644 index 00000000000..96263b754d5 --- /dev/null +++ b/tests/unit/s2n_auth_selection_test.c @@ -0,0 +1,464 @@ +/* + * 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 "tls/s2n_auth_selection.h" + +#include "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "crypto/s2n_rsa_pss.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_preferences.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_signature_scheme.h" + +#define RSA_AUTH_CIPHER_SUITE &s2n_dhe_rsa_with_3des_ede_cbc_sha +#define ECDSA_AUTH_CIPHER_SUITE &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha +#define NO_AUTH_CIPHER_SUITE &s2n_tls13_aes_128_gcm_sha256 + +#define RSA_PKCS1_SIG_SCHEME &s2n_rsa_pkcs1_md5_sha1 +#define RSA_PSS_PSS_SIG_SCHEME &s2n_rsa_pss_pss_sha256 +#define RSA_PSS_RSAE_SIG_SCHEME &s2n_rsa_pss_rsae_sha256 +#define ECDSA_SIG_SCHEME &s2n_ecdsa_secp384r1_sha384 +#define ECDSA_SIG_SCHEME_OTHER_CURVE &s2n_ecdsa_secp256r1_sha256 + +#define EXPECT_SUCCESS_IF_RSA_PSS_CERTS_SUPPORTED(x) \ + if (s2n_is_rsa_pss_certs_supported()) { \ + EXPECT_SUCCESS(x); \ + } else { \ + EXPECT_FAILURE(x); \ + } + +static int s2n_test_auth_combo(struct s2n_connection *conn, + struct s2n_cipher_suite *cipher_suite, const struct s2n_signature_scheme *sig_scheme, + struct s2n_cert_chain_and_key *expected_cert_chain) +{ + struct s2n_cert_chain_and_key *actual_cert_chain; + + POSIX_GUARD(s2n_is_cipher_suite_valid_for_auth(conn, cipher_suite)); + conn->secure->cipher_suite = cipher_suite; + + POSIX_GUARD(s2n_is_sig_scheme_valid_for_auth(conn, sig_scheme)); + conn->handshake_params.server_cert_sig_scheme = sig_scheme; + + POSIX_GUARD(s2n_select_certs_for_server_auth(conn, &actual_cert_chain)); + POSIX_ENSURE_EQ(actual_cert_chain, expected_cert_chain); + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + struct s2n_cert_chain_and_key *rsa_cert_chain; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&rsa_cert_chain, + S2N_RSA_2048_PKCS1_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY)); + + struct s2n_cert_chain_and_key *ecdsa_cert_chain; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_cert_chain, + S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); + + struct s2n_config *no_certs_config = s2n_config_new(); + + struct s2n_config *rsa_cert_config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(rsa_cert_config, rsa_cert_chain)); + + struct s2n_config *ecdsa_cert_config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(ecdsa_cert_config, ecdsa_cert_chain)); + + struct s2n_config *all_certs_config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(all_certs_config, rsa_cert_chain)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(all_certs_config, ecdsa_cert_chain)); + + struct s2n_cert_chain_and_key *rsa_pss_cert_chain = NULL; + struct s2n_config *rsa_pss_cert_config = s2n_config_new(); + +#if RSA_PSS_CERTS_SUPPORTED + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&rsa_pss_cert_chain, + S2N_RSA_PSS_2048_SHA256_LEAF_CERT, S2N_RSA_PSS_2048_SHA256_LEAF_KEY)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(rsa_pss_cert_config, rsa_pss_cert_chain)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(all_certs_config, rsa_pss_cert_chain)); +#endif + + /* s2n_is_cipher_suite_valid_for_auth */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + + /* Test: not valid if certs not available */ + { + /* No certs exist */ + s2n_connection_set_config(conn, no_certs_config); + EXPECT_FAILURE(s2n_is_cipher_suite_valid_for_auth(conn, RSA_AUTH_CIPHER_SUITE)); + EXPECT_FAILURE(s2n_is_cipher_suite_valid_for_auth(conn, ECDSA_AUTH_CIPHER_SUITE)); + EXPECT_SUCCESS(s2n_is_cipher_suite_valid_for_auth(conn, NO_AUTH_CIPHER_SUITE)); + + /* RSA certs exist */ + s2n_connection_set_config(conn, rsa_cert_config); + EXPECT_SUCCESS(s2n_is_cipher_suite_valid_for_auth(conn, RSA_AUTH_CIPHER_SUITE)); + EXPECT_FAILURE(s2n_is_cipher_suite_valid_for_auth(conn, ECDSA_AUTH_CIPHER_SUITE)); + EXPECT_SUCCESS(s2n_is_cipher_suite_valid_for_auth(conn, NO_AUTH_CIPHER_SUITE)); + + /* RSA-PSS certs exist */ + s2n_connection_set_config(conn, rsa_pss_cert_config); + EXPECT_SUCCESS_IF_RSA_PSS_CERTS_SUPPORTED(s2n_is_cipher_suite_valid_for_auth(conn, RSA_AUTH_CIPHER_SUITE)); + EXPECT_FAILURE(s2n_is_cipher_suite_valid_for_auth(conn, ECDSA_AUTH_CIPHER_SUITE)); + EXPECT_SUCCESS(s2n_is_cipher_suite_valid_for_auth(conn, NO_AUTH_CIPHER_SUITE)); + + /* ECDSA certs exist */ + s2n_connection_set_config(conn, ecdsa_cert_config); + EXPECT_FAILURE(s2n_is_cipher_suite_valid_for_auth(conn, RSA_AUTH_CIPHER_SUITE)); + EXPECT_SUCCESS(s2n_is_cipher_suite_valid_for_auth(conn, ECDSA_AUTH_CIPHER_SUITE)); + EXPECT_SUCCESS(s2n_is_cipher_suite_valid_for_auth(conn, NO_AUTH_CIPHER_SUITE)); + + /* All certs exist */ + s2n_connection_set_config(conn, all_certs_config); + EXPECT_SUCCESS(s2n_is_cipher_suite_valid_for_auth(conn, RSA_AUTH_CIPHER_SUITE)); + EXPECT_SUCCESS(s2n_is_cipher_suite_valid_for_auth(conn, ECDSA_AUTH_CIPHER_SUITE)); + EXPECT_SUCCESS(s2n_is_cipher_suite_valid_for_auth(conn, NO_AUTH_CIPHER_SUITE)); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* s2n_is_sig_scheme_valid_for_auth */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + + /* Test: not valid if certs not available */ + { + conn->secure->cipher_suite = NO_AUTH_CIPHER_SUITE; + + /* No certs exist */ + s2n_connection_set_config(conn, no_certs_config); + EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PKCS1_SIG_SCHEME)); + EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_PSS_SIG_SCHEME)); + EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_RSAE_SIG_SCHEME)); + EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, ECDSA_SIG_SCHEME)); + + /* RSA certs exist */ + s2n_connection_set_config(conn, rsa_cert_config); + EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PKCS1_SIG_SCHEME)); + EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_PSS_SIG_SCHEME)); + EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_RSAE_SIG_SCHEME)); + EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, ECDSA_SIG_SCHEME)); + + /* RSA-PSS certs exist */ + s2n_connection_set_config(conn, rsa_pss_cert_config); + EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PKCS1_SIG_SCHEME)); + EXPECT_SUCCESS_IF_RSA_PSS_CERTS_SUPPORTED(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_PSS_SIG_SCHEME)); + EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_RSAE_SIG_SCHEME)); + EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, ECDSA_SIG_SCHEME)); + + /* ECDSA certs exist */ + s2n_connection_set_config(conn, ecdsa_cert_config); + EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PKCS1_SIG_SCHEME)); + EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_PSS_SIG_SCHEME)); + EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_RSAE_SIG_SCHEME)); + EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(conn, ECDSA_SIG_SCHEME)); + + /* All certs exist */ + s2n_connection_set_config(conn, all_certs_config); + EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PKCS1_SIG_SCHEME)); + EXPECT_SUCCESS_IF_RSA_PSS_CERTS_SUPPORTED(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_PSS_SIG_SCHEME)); + EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_RSAE_SIG_SCHEME)); + EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(conn, ECDSA_SIG_SCHEME)); + } + + /* Test: If signature algorithm specifies curve, must match cert curve */ + { + struct s2n_cert_chain_and_key *ecdsa_cert_chain_for_other_curve; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_cert_chain_for_other_curve, + S2N_ECDSA_P256_PKCS1_CERT_CHAIN, S2N_ECDSA_P256_PKCS1_KEY)); + + struct s2n_config *ecdsa_cert_config_for_other_curve = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store( + ecdsa_cert_config_for_other_curve, ecdsa_cert_chain_for_other_curve)); + + conn->secure->cipher_suite = NO_AUTH_CIPHER_SUITE; + + s2n_connection_set_config(conn, ecdsa_cert_config); + EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(conn, ECDSA_SIG_SCHEME)); + EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, ECDSA_SIG_SCHEME_OTHER_CURVE)); + + s2n_connection_set_config(conn, ecdsa_cert_config_for_other_curve); + EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, ECDSA_SIG_SCHEME)); + EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(conn, ECDSA_SIG_SCHEME_OTHER_CURVE)); + + EXPECT_SUCCESS(s2n_config_free(ecdsa_cert_config_for_other_curve)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert_chain_for_other_curve)); + } + + /* Test: If cipher suite specifies auth type, auth type must be valid for sig alg on server */ + { + s2n_connection_set_config(conn, all_certs_config); + + /* RSA auth type */ + conn->secure->cipher_suite = RSA_AUTH_CIPHER_SUITE; + EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PKCS1_SIG_SCHEME)); + EXPECT_SUCCESS_IF_RSA_PSS_CERTS_SUPPORTED(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_PSS_SIG_SCHEME)); + EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_RSAE_SIG_SCHEME)); + EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, ECDSA_SIG_SCHEME)); + + /* ECDSA auth type */ + conn->secure->cipher_suite = ECDSA_AUTH_CIPHER_SUITE; + EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PKCS1_SIG_SCHEME)); + EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_PSS_SIG_SCHEME)); + EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_RSAE_SIG_SCHEME)); + EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(conn, ECDSA_SIG_SCHEME)); + } + + /* Test: If cipher suite specifies auth type, auth type does not need to be valid for sig alg on client */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + + /* RSA auth type */ + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, rsa_cert_config)); + client_conn->secure->cipher_suite = ECDSA_AUTH_CIPHER_SUITE; + EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(client_conn, RSA_PKCS1_SIG_SCHEME)); + EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(client_conn, RSA_PSS_RSAE_SIG_SCHEME)); + + /* ECDSA auth type */ + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, ecdsa_cert_config)); + client_conn->secure->cipher_suite = RSA_AUTH_CIPHER_SUITE; + EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(client_conn, ECDSA_SIG_SCHEME)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + } + + /* Test: RSA-PSS requires a non-ephemeral kex */ + { + s2n_connection_set_config(conn, all_certs_config); + + /* ephemeral key */ + conn->secure->cipher_suite = &s2n_dhe_rsa_with_3des_ede_cbc_sha; /* kex = (dhe) */ + EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PKCS1_SIG_SCHEME)); + EXPECT_SUCCESS_IF_RSA_PSS_CERTS_SUPPORTED(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_PSS_SIG_SCHEME)); + EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_RSAE_SIG_SCHEME)); + + /* non-ephemeral key */ + conn->secure->cipher_suite = &s2n_rsa_with_aes_128_cbc_sha; /* kex = (rsa) */ + EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PKCS1_SIG_SCHEME)); + EXPECT_FAILURE(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_PSS_SIG_SCHEME)); + EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_RSAE_SIG_SCHEME)); + + /* no kex at all */ + conn->secure->cipher_suite = NO_AUTH_CIPHER_SUITE; /* kex = NULL */ + EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PKCS1_SIG_SCHEME)); + EXPECT_SUCCESS_IF_RSA_PSS_CERTS_SUPPORTED(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_PSS_SIG_SCHEME)); + EXPECT_SUCCESS(s2n_is_sig_scheme_valid_for_auth(conn, RSA_PSS_RSAE_SIG_SCHEME)); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* s2n_is_cert_type_valid_for_auth */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + + /* RSA auth type */ + conn->secure->cipher_suite = RSA_AUTH_CIPHER_SUITE; + EXPECT_SUCCESS(s2n_is_cert_type_valid_for_auth(conn, S2N_PKEY_TYPE_RSA)); + EXPECT_SUCCESS(s2n_is_cert_type_valid_for_auth(conn, S2N_PKEY_TYPE_RSA_PSS)); + EXPECT_FAILURE(s2n_is_cert_type_valid_for_auth(conn, S2N_PKEY_TYPE_ECDSA)); + EXPECT_FAILURE(s2n_is_cert_type_valid_for_auth(conn, S2N_PKEY_TYPE_UNKNOWN)); + + /* ECDSA auth type */ + conn->secure->cipher_suite = ECDSA_AUTH_CIPHER_SUITE; + EXPECT_FAILURE(s2n_is_cert_type_valid_for_auth(conn, S2N_PKEY_TYPE_RSA)); + EXPECT_FAILURE(s2n_is_cert_type_valid_for_auth(conn, S2N_PKEY_TYPE_RSA_PSS)); + EXPECT_SUCCESS(s2n_is_cert_type_valid_for_auth(conn, S2N_PKEY_TYPE_ECDSA)); + EXPECT_FAILURE(s2n_is_cert_type_valid_for_auth(conn, S2N_PKEY_TYPE_UNKNOWN)); + + /* No auth type */ + conn->secure->cipher_suite = NO_AUTH_CIPHER_SUITE; + EXPECT_SUCCESS(s2n_is_cert_type_valid_for_auth(conn, S2N_PKEY_TYPE_RSA)); + EXPECT_SUCCESS(s2n_is_cert_type_valid_for_auth(conn, S2N_PKEY_TYPE_RSA_PSS)); + EXPECT_SUCCESS(s2n_is_cert_type_valid_for_auth(conn, S2N_PKEY_TYPE_ECDSA)); + EXPECT_FAILURE(s2n_is_cert_type_valid_for_auth(conn, S2N_PKEY_TYPE_UNKNOWN)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* s2n_select_certs_for_server_auth */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + struct s2n_cert_chain_and_key *chosen_certs; + + /* Requested cert chain exists */ + s2n_connection_set_config(conn, all_certs_config); + + struct s2n_signature_scheme test_sig_scheme = { 0 }; + conn->handshake_params.server_cert_sig_scheme = &test_sig_scheme; + + test_sig_scheme.sig_alg = S2N_SIGNATURE_RSA; + EXPECT_SUCCESS(s2n_select_certs_for_server_auth(conn, &chosen_certs)); + EXPECT_EQUAL(chosen_certs, rsa_cert_chain); + + /* cppcheck-suppress redundantAssignment */ + test_sig_scheme.sig_alg = S2N_SIGNATURE_RSA_PSS_PSS; + EXPECT_SUCCESS_IF_RSA_PSS_CERTS_SUPPORTED(s2n_select_certs_for_server_auth(conn, &chosen_certs)); + EXPECT_EQUAL(chosen_certs, rsa_pss_cert_chain); + + /* cppcheck-suppress redundantAssignment */ + test_sig_scheme.sig_alg = S2N_SIGNATURE_RSA_PSS_RSAE; + EXPECT_SUCCESS(s2n_select_certs_for_server_auth(conn, &chosen_certs)); + EXPECT_EQUAL(chosen_certs, rsa_cert_chain); + + /* cppcheck-suppress redundantAssignment */ + test_sig_scheme.sig_alg = S2N_SIGNATURE_ECDSA; + EXPECT_SUCCESS(s2n_select_certs_for_server_auth(conn, &chosen_certs)); + EXPECT_EQUAL(chosen_certs, ecdsa_cert_chain); + + /* Requested cert chain does NOT exist */ + s2n_connection_set_config(conn, no_certs_config); + + /* cppcheck-suppress redundantAssignment */ + test_sig_scheme.sig_alg = S2N_SIGNATURE_RSA; + EXPECT_FAILURE(s2n_select_certs_for_server_auth(conn, &chosen_certs)); + EXPECT_NULL(chosen_certs); + + /* cppcheck-suppress redundantAssignment */ + test_sig_scheme.sig_alg = S2N_SIGNATURE_RSA_PSS_PSS; + EXPECT_FAILURE(s2n_select_certs_for_server_auth(conn, &chosen_certs)); + EXPECT_NULL(chosen_certs); + + /* cppcheck-suppress redundantAssignment */ + test_sig_scheme.sig_alg = S2N_SIGNATURE_RSA_PSS_RSAE; + EXPECT_FAILURE(s2n_select_certs_for_server_auth(conn, &chosen_certs)); + EXPECT_NULL(chosen_certs); + + /* cppcheck-suppress redundantAssignment */ + test_sig_scheme.sig_alg = S2N_SIGNATURE_ECDSA; + EXPECT_FAILURE(s2n_select_certs_for_server_auth(conn, &chosen_certs)); + EXPECT_NULL(chosen_certs); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Test all possible combos */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + s2n_connection_set_config(conn, all_certs_config); + + /* No certs exist */ + s2n_connection_set_config(conn, no_certs_config); + + EXPECT_FAILURE(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, RSA_PKCS1_SIG_SCHEME, NULL)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, RSA_PSS_PSS_SIG_SCHEME, NULL)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, RSA_PSS_RSAE_SIG_SCHEME, NULL)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, ECDSA_SIG_SCHEME, NULL)); + + EXPECT_FAILURE(s2n_test_auth_combo(conn, ECDSA_AUTH_CIPHER_SUITE, RSA_PKCS1_SIG_SCHEME, NULL)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, ECDSA_AUTH_CIPHER_SUITE, RSA_PSS_PSS_SIG_SCHEME, NULL)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, ECDSA_AUTH_CIPHER_SUITE, RSA_PSS_RSAE_SIG_SCHEME, NULL)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, ECDSA_AUTH_CIPHER_SUITE, ECDSA_SIG_SCHEME, NULL)); + + EXPECT_FAILURE(s2n_test_auth_combo(conn, NO_AUTH_CIPHER_SUITE, RSA_PKCS1_SIG_SCHEME, NULL)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, NO_AUTH_CIPHER_SUITE, RSA_PSS_PSS_SIG_SCHEME, NULL)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, NO_AUTH_CIPHER_SUITE, RSA_PSS_RSAE_SIG_SCHEME, NULL)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, NO_AUTH_CIPHER_SUITE, ECDSA_SIG_SCHEME, NULL)); + + /* RSA certs exist */ + s2n_connection_set_config(conn, rsa_cert_config); + + EXPECT_SUCCESS(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, RSA_PKCS1_SIG_SCHEME, rsa_cert_chain)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, RSA_PSS_PSS_SIG_SCHEME, NULL)); + EXPECT_SUCCESS(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, RSA_PSS_RSAE_SIG_SCHEME, rsa_cert_chain)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, ECDSA_SIG_SCHEME, NULL)); + + EXPECT_FAILURE(s2n_test_auth_combo(conn, ECDSA_AUTH_CIPHER_SUITE, RSA_PKCS1_SIG_SCHEME, NULL)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, ECDSA_AUTH_CIPHER_SUITE, RSA_PSS_PSS_SIG_SCHEME, NULL)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, ECDSA_AUTH_CIPHER_SUITE, RSA_PSS_RSAE_SIG_SCHEME, NULL)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, ECDSA_AUTH_CIPHER_SUITE, ECDSA_SIG_SCHEME, NULL)); + + EXPECT_SUCCESS(s2n_test_auth_combo(conn, NO_AUTH_CIPHER_SUITE, RSA_PKCS1_SIG_SCHEME, rsa_cert_chain)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, NO_AUTH_CIPHER_SUITE, RSA_PSS_PSS_SIG_SCHEME, NULL)); + EXPECT_SUCCESS(s2n_test_auth_combo(conn, NO_AUTH_CIPHER_SUITE, RSA_PSS_RSAE_SIG_SCHEME, rsa_cert_chain)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, NO_AUTH_CIPHER_SUITE, ECDSA_SIG_SCHEME, NULL)); + + /* RSA_PSS certs exist */ + s2n_connection_set_config(conn, rsa_pss_cert_config); + + EXPECT_FAILURE(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, RSA_PKCS1_SIG_SCHEME, NULL)); + EXPECT_SUCCESS_IF_RSA_PSS_CERTS_SUPPORTED(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, RSA_PSS_PSS_SIG_SCHEME, rsa_pss_cert_chain)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, RSA_PSS_RSAE_SIG_SCHEME, NULL)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, ECDSA_SIG_SCHEME, NULL)); + + EXPECT_FAILURE(s2n_test_auth_combo(conn, ECDSA_AUTH_CIPHER_SUITE, RSA_PKCS1_SIG_SCHEME, NULL)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, ECDSA_AUTH_CIPHER_SUITE, RSA_PSS_PSS_SIG_SCHEME, NULL)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, ECDSA_AUTH_CIPHER_SUITE, RSA_PSS_RSAE_SIG_SCHEME, NULL)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, ECDSA_AUTH_CIPHER_SUITE, ECDSA_SIG_SCHEME, NULL)); + + EXPECT_FAILURE(s2n_test_auth_combo(conn, NO_AUTH_CIPHER_SUITE, RSA_PKCS1_SIG_SCHEME, NULL)); + EXPECT_SUCCESS_IF_RSA_PSS_CERTS_SUPPORTED(s2n_test_auth_combo(conn, NO_AUTH_CIPHER_SUITE, RSA_PSS_PSS_SIG_SCHEME, rsa_pss_cert_chain)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, NO_AUTH_CIPHER_SUITE, RSA_PSS_RSAE_SIG_SCHEME, NULL)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, NO_AUTH_CIPHER_SUITE, ECDSA_SIG_SCHEME, NULL)); + + /* ECDSA certs exist */ + s2n_connection_set_config(conn, ecdsa_cert_config); + + EXPECT_FAILURE(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, RSA_PKCS1_SIG_SCHEME, NULL)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, RSA_PSS_PSS_SIG_SCHEME, NULL)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, RSA_PSS_RSAE_SIG_SCHEME, NULL)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, ECDSA_SIG_SCHEME, NULL)); + + EXPECT_FAILURE(s2n_test_auth_combo(conn, ECDSA_AUTH_CIPHER_SUITE, RSA_PKCS1_SIG_SCHEME, NULL)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, ECDSA_AUTH_CIPHER_SUITE, RSA_PSS_PSS_SIG_SCHEME, NULL)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, ECDSA_AUTH_CIPHER_SUITE, RSA_PSS_RSAE_SIG_SCHEME, NULL)); + EXPECT_SUCCESS(s2n_test_auth_combo(conn, ECDSA_AUTH_CIPHER_SUITE, ECDSA_SIG_SCHEME, ecdsa_cert_chain)); + + EXPECT_FAILURE(s2n_test_auth_combo(conn, NO_AUTH_CIPHER_SUITE, RSA_PKCS1_SIG_SCHEME, NULL)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, NO_AUTH_CIPHER_SUITE, RSA_PSS_PSS_SIG_SCHEME, NULL)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, NO_AUTH_CIPHER_SUITE, RSA_PSS_RSAE_SIG_SCHEME, NULL)); + EXPECT_SUCCESS(s2n_test_auth_combo(conn, NO_AUTH_CIPHER_SUITE, ECDSA_SIG_SCHEME, ecdsa_cert_chain)); + + /* All certs exist */ + s2n_connection_set_config(conn, all_certs_config); + + EXPECT_SUCCESS(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, RSA_PKCS1_SIG_SCHEME, rsa_cert_chain)); + EXPECT_SUCCESS_IF_RSA_PSS_CERTS_SUPPORTED(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, RSA_PSS_PSS_SIG_SCHEME, rsa_pss_cert_chain)); + EXPECT_SUCCESS(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, RSA_PSS_RSAE_SIG_SCHEME, rsa_cert_chain)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, RSA_AUTH_CIPHER_SUITE, ECDSA_SIG_SCHEME, NULL)); + + EXPECT_FAILURE(s2n_test_auth_combo(conn, ECDSA_AUTH_CIPHER_SUITE, RSA_PKCS1_SIG_SCHEME, NULL)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, ECDSA_AUTH_CIPHER_SUITE, RSA_PSS_PSS_SIG_SCHEME, NULL)); + EXPECT_FAILURE(s2n_test_auth_combo(conn, ECDSA_AUTH_CIPHER_SUITE, RSA_PSS_RSAE_SIG_SCHEME, NULL)); + EXPECT_SUCCESS(s2n_test_auth_combo(conn, ECDSA_AUTH_CIPHER_SUITE, ECDSA_SIG_SCHEME, ecdsa_cert_chain)); + + EXPECT_SUCCESS(s2n_test_auth_combo(conn, NO_AUTH_CIPHER_SUITE, RSA_PKCS1_SIG_SCHEME, rsa_cert_chain)); + EXPECT_SUCCESS_IF_RSA_PSS_CERTS_SUPPORTED(s2n_test_auth_combo(conn, NO_AUTH_CIPHER_SUITE, RSA_PSS_PSS_SIG_SCHEME, rsa_pss_cert_chain)); + EXPECT_SUCCESS(s2n_test_auth_combo(conn, NO_AUTH_CIPHER_SUITE, RSA_PSS_RSAE_SIG_SCHEME, rsa_cert_chain)); + EXPECT_SUCCESS(s2n_test_auth_combo(conn, NO_AUTH_CIPHER_SUITE, ECDSA_SIG_SCHEME, ecdsa_cert_chain)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + EXPECT_SUCCESS(s2n_config_free(rsa_cert_config)); + EXPECT_SUCCESS(s2n_config_free(rsa_pss_cert_config)); + EXPECT_SUCCESS(s2n_config_free(ecdsa_cert_config)); + EXPECT_SUCCESS(s2n_config_free(all_certs_config)); + EXPECT_SUCCESS(s2n_config_free(no_certs_config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(rsa_cert_chain)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(rsa_pss_cert_chain)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert_chain)); + + END_TEST(); + + return 0; +} diff --git a/tests/unit/s2n_blob_test.c b/tests/unit/s2n_blob_test.c new file mode 100644 index 00000000000..48aed4ea74f --- /dev/null +++ b/tests/unit/s2n_blob_test.c @@ -0,0 +1,163 @@ +/* + * 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 "utils/s2n_blob.h" + +#include "api/s2n.h" +#include "s2n_test.h" +#include "utils/s2n_mem.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Null blob is not valid */ + EXPECT_ERROR(s2n_blob_validate(NULL)); + +#ifndef NDEBUG + /* Invalid blob is not valid */ + struct s2n_blob b1 = { .data = 0, .size = 101 }; + EXPECT_ERROR(s2n_blob_validate(&b1)); +#endif + + /* Size of 0 is OK if data is null */ + struct s2n_blob b2 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&b2, 0, 0)); + EXPECT_OK(s2n_blob_validate(&b2)); + + /* Valid blob is valid */ + uint8_t array[12]; + struct s2n_blob b3 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&b3, array, sizeof(array))); + EXPECT_OK(s2n_blob_validate(&b3)); + + /* Null blob is not growable */ + EXPECT_FALSE(s2n_blob_is_growable(NULL)); + EXPECT_FAILURE(s2n_realloc(NULL, 24)); + EXPECT_FAILURE(s2n_free(NULL)); + + /* Static blob is not growable or freeable */ + struct s2n_blob g1 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&g1, array, 12)); + EXPECT_FALSE(s2n_blob_is_growable(&g1)); + EXPECT_FAILURE(s2n_realloc(&g1, 24)); + EXPECT_FAILURE(s2n_free(&g1)); + + /* Empty blob is freeable */ + struct s2n_blob g2 = { 0 }; + EXPECT_TRUE(s2n_blob_is_growable(&g2)); + EXPECT_SUCCESS(s2n_free(&g2)); + + /* Empty blob is growable */ + struct s2n_blob g3 = { 0 }; + EXPECT_TRUE(s2n_blob_is_growable(&g3)); + EXPECT_SUCCESS(s2n_realloc(&g3, 24)); + EXPECT_SUCCESS(s2n_free(&g3)); + + /* Alloced blob can be freed */ + struct s2n_blob g4 = { 0 }; + EXPECT_SUCCESS(s2n_alloc(&g4, 12)); + EXPECT_TRUE(s2n_blob_is_growable(&g4)); + EXPECT_SUCCESS(s2n_free(&g4)); + + /* Alloced blob can be realloced and data preserved */ + struct s2n_blob g5 = { 0 }; + uint8_t hello_world[] = "HELLO WORLD"; + EXPECT_SUCCESS(s2n_alloc(&g5, 12)); + EXPECT_TRUE(s2n_blob_is_growable(&g5)); + EXPECT_MEMCPY_SUCCESS(g5.data, hello_world, sizeof(hello_world)); + EXPECT_SUCCESS(s2n_realloc(&g5, 24)); + EXPECT_EQUAL(memcmp(g5.data, hello_world, sizeof(hello_world)), 0); + EXPECT_SUCCESS(s2n_free(&g5)); + + /* Alloced blob can be reallocated without leaking memory */ + struct s2n_blob g6 = { 0 }; + EXPECT_SUCCESS(s2n_alloc(&g6, 12)); + g6.size = 0; + EXPECT_SUCCESS(s2n_realloc(&g6, g6.allocated + 12)); + EXPECT_SUCCESS(s2n_free(&g6)); + + /* Down-casing works */ + struct s2n_blob g7 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&g7, hello_world, sizeof(hello_world))); + EXPECT_SUCCESS(s2n_blob_char_to_lower(&g7)); + EXPECT_SUCCESS(memcmp(g7.data, "hello world", sizeof(hello_world))); + + /* Slicing works */ + struct s2n_blob g8 = { 0 }; + uint8_t hello[] = "hello "; + uint8_t world[] = "world"; + EXPECT_SUCCESS(s2n_blob_slice(&g7, &g8, strlen((char *) hello), sizeof(world))); + EXPECT_EQUAL(memcmp(g8.data, world, sizeof(world)), 0); + EXPECT_EQUAL(g8.size, sizeof(world)); + + /* Test s2n_hex_string_to_bytes */ + { + uint8_t test_mem[10] = { 0 }; + + /* Test with output buffer too small */ + { + const uint8_t long_input_str[] = "abcdef123456"; + struct s2n_blob output_blob = { 0 }; + + /* Succeeds with output blob of the right size */ + EXPECT_SUCCESS(s2n_blob_init(&output_blob, test_mem, sizeof(long_input_str) / 2)); + EXPECT_SUCCESS(s2n_hex_string_to_bytes(long_input_str, &output_blob)); + + /* Fails with output blob that's too small */ + EXPECT_SUCCESS(s2n_blob_init(&output_blob, test_mem, 1)); + EXPECT_FAILURE_WITH_ERRNO(s2n_hex_string_to_bytes(long_input_str, &output_blob), + S2N_ERR_INVALID_HEX); + }; + + /* Test with invalid characters */ + { + struct s2n_blob output_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&output_blob, test_mem, sizeof(test_mem))); + + EXPECT_SUCCESS(s2n_hex_string_to_bytes((const uint8_t *) "12", &output_blob)); + EXPECT_FAILURE_WITH_ERRNO(s2n_hex_string_to_bytes((const uint8_t *) "#2", &output_blob), + S2N_ERR_INVALID_HEX); + EXPECT_FAILURE_WITH_ERRNO(s2n_hex_string_to_bytes((const uint8_t *) "1#", &output_blob), + S2N_ERR_INVALID_HEX); + }; + + struct { + const char *input; + size_t expected_output_size; + uint8_t expected_output[sizeof(test_mem)]; + } test_cases[] = { + { .input = "abcd", .expected_output = { 171, 205 }, .expected_output_size = 2 }, + { .input = "ab cd", .expected_output = { 171, 205 }, .expected_output_size = 2 }, + { .input = " abcd", .expected_output = { 171, 205 }, .expected_output_size = 2 }, + { .input = "abcd ", .expected_output = { 171, 205 }, .expected_output_size = 2 }, + { .input = " ab cd ", .expected_output = { 171, 205 }, .expected_output_size = 2 }, + { .input = "", .expected_output = { 0 }, .expected_output_size = 0 }, + { .input = " ", .expected_output = { 0 }, .expected_output_size = 0 }, + { .input = "12 34 56 78 90", .expected_output = { 18, 52, 86, 120, 144 }, .expected_output_size = 5 }, + { .input = "1234567890", .expected_output = { 18, 52, 86, 120, 144 }, .expected_output_size = 5 }, + }; + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + struct s2n_blob actual_output = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&actual_output, test_mem, sizeof(test_mem))); + + EXPECT_SUCCESS(s2n_hex_string_to_bytes((const uint8_t *) test_cases[i].input, &actual_output)); + EXPECT_BYTEARRAY_EQUAL(actual_output.data, test_cases[i].expected_output, test_cases[i].expected_output_size); + } + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_build_test.c b/tests/unit/s2n_build_test.c new file mode 100644 index 00000000000..7e707954df4 --- /dev/null +++ b/tests/unit/s2n_build_test.c @@ -0,0 +1,101 @@ +/* +* 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. +*/ +#define _GNU_SOURCE +#include +#include +#include +#include +#include +#include + +#include "crypto/s2n_openssl.h" +#include "s2n_test.h" + +int tokenize_s2n_libcrypto(char *s2n_libcrypto, char **name, char **version) +{ + if (name == NULL || version == NULL || s2n_libcrypto == NULL) { + return S2N_FAILURE; + } + + *name = strtok(s2n_libcrypto, "-"); + if (*name == NULL) { + return S2N_FAILURE; + } + + char *remaining = strtok(NULL, ""); + + *version = NULL; + if (remaining != NULL && isdigit(remaining[0])) { + *version = strtok(remaining, "_-"); + } + + return S2N_SUCCESS; +} + +int main() +{ + BEGIN_TEST(); + + const char *s2n_libcrypto = getenv("S2N_LIBCRYPTO"); + + /* S2N_LIBCRYPTO and S2N_BUILD_PRESET should be consistent, but only + if S2N_BUILD_PRESET is set. */ + { + const char *s2n_build_preset = getenv("S2N_BUILD_PRESET"); + if (s2n_build_preset != NULL) { + EXPECT_NOT_NULL(s2n_libcrypto); + EXPECT_NOT_NULL(strstr(s2n_build_preset, s2n_libcrypto)); + } + }; + + if (s2n_libcrypto == NULL || s2n_libcrypto[0] == '\0') { + END_TEST(); + return 0; + } + + if (strcmp(s2n_libcrypto, "default") == 0) { + END_TEST(); + } + + char s2n_libcrypto_copy[100] = { 0 }; + strncpy(s2n_libcrypto_copy, s2n_libcrypto, 99); + char *name = NULL; + char *version = NULL; + EXPECT_SUCCESS(tokenize_s2n_libcrypto(s2n_libcrypto_copy, &name, &version)); + + /* Check libcrypto name matches the intent of the CI. */ + { + if (strstr(name, "awslc") != NULL) { + /* Early versions of awslc's SSLeay_version return an inaccurate value left over + * after its fork from BoringSSL. */ + EXPECT_TRUE(s2n_libcrypto_is_awslc()); + } else { + /* Any other library should have the name of the library (modulo case) in its version string. */ + const char *ssleay_version_text = SSLeay_version(SSLEAY_VERSION); + EXPECT_NOT_NULL(strcasestr(ssleay_version_text, name)); + } + }; + + /* Check libcrypto version matches the intent of the CI. */ + { + if (version != NULL) { + const char *ssleay_version_text = SSLeay_version(SSLEAY_VERSION); + EXPECT_NOT_NULL(strstr(ssleay_version_text, version)); + } + }; + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_cert_chain_and_key_test.c b/tests/unit/s2n_cert_chain_and_key_test.c new file mode 100644 index 00000000000..f299ebc7b95 --- /dev/null +++ b/tests/unit/s2n_cert_chain_and_key_test.c @@ -0,0 +1,200 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_safety.h" + +#define NUM_TIED_CERTS 100 + +struct s2n_connection *create_conn(s2n_mode mode, struct s2n_config *config) +{ + struct s2n_connection *conn = s2n_connection_new(mode); + PTR_GUARD_POSIX(s2n_connection_set_config(conn, config)); + return conn; +} + +static int num_times_cb_executed = 0; +static struct s2n_cert_chain_and_key *test_cert_tiebreak_cb(struct s2n_cert_chain_and_key *cert1, + struct s2n_cert_chain_and_key *cert2, + uint8_t *name, + uint32_t name_len) +{ + const int priority1 = *((int *) s2n_cert_chain_and_key_get_ctx(cert1)); + const int priority2 = *((int *) s2n_cert_chain_and_key_get_ctx(cert2)); + num_times_cb_executed++; + return (priority1 > priority2 ? cert1 : cert2); +} + +int main(int argc, char **argv) +{ + struct s2n_config *server_config; + struct s2n_config *client_config; + struct s2n_connection *server_conn; + struct s2n_connection *client_conn; + char *alligator_cert; + char *alligator_key; + char *cert_chain; + char *private_key; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(alligator_cert = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(alligator_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ALLIGATOR_SAN_CERT, alligator_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ALLIGATOR_SAN_KEY, alligator_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + + EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + /* Create config with s2n_config_add_cert_chain_and_key_to_store API with multiple certs */ + { + struct s2n_cert_chain_and_key *default_cert; + /* Associated data to attach to each certificate to use in the tiebreak callback. */ + int tiebreak_priorites[NUM_TIED_CERTS] = { 0 }; + /* Collection of certs with the same domain name that need to have ties resolved. */ + struct s2n_cert_chain_and_key *tied_certs[NUM_TIED_CERTS] = { NULL }; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cert_tiebreak_callback(server_config, test_cert_tiebreak_cb)); + + /* Need to add at least one cert with a different domain name to make cert lookup utilize hashmap */ + EXPECT_NOT_NULL(default_cert = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(default_cert, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, default_cert)); + + /* Add NUM_TIED_CERTS that are actually the same certificate(www.alligator.com) to trigger the tiebreak callback. */ + for (unsigned int i = 0; i < NUM_TIED_CERTS; i++) { + EXPECT_NOT_NULL(tied_certs[i] = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tied_certs[i], alligator_cert, alligator_key)); + tiebreak_priorites[i] = i; + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ctx(tied_certs[i], (void *) &tiebreak_priorites[i])); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tied_certs[i])); + } + + EXPECT_NOT_NULL(server_conn = create_conn(S2N_SERVER, server_config)); + EXPECT_NOT_NULL(client_conn = create_conn(S2N_CLIENT, client_config)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "www.alligator.com")); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_EQUAL(num_times_cb_executed, NUM_TIED_CERTS - 1); + struct s2n_cert_chain_and_key *selected_cert = s2n_connection_get_selected_cert(server_conn); + /* The last alligator certificate should have the highest priority */ + EXPECT_EQUAL(selected_cert, tied_certs[(NUM_TIED_CERTS - 1)]); + EXPECT_EQUAL(s2n_cert_chain_and_key_get_ctx(selected_cert), (void *) &tiebreak_priorites[(NUM_TIED_CERTS - 1)]); + EXPECT_EQUAL(*((int *) s2n_cert_chain_and_key_get_ctx(selected_cert)), NUM_TIED_CERTS - 1); + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + for (size_t i = 0; i < NUM_TIED_CERTS; i++) { + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tied_certs[i])); + } + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(default_cert)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Create config with deprecated s2n_config_add_cert_chain_and_key API */ + { + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); + + EXPECT_NOT_NULL(server_conn = create_conn(S2N_SERVER, server_config)); + EXPECT_NOT_NULL(client_conn = create_conn(S2N_CLIENT, client_config)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Do not allow configs to call both + * s2n_config_add_cert_chain_and_key and s2n_config_add_cert_chain_and_key_to_store */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* Config first uses s2n_config_add_cert_chain_and_key: library owns chain */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_EQUAL(config->cert_ownership, S2N_NOT_OWNED); + + /* Add first chain */ + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(config, cert_chain, private_key)); + EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); + + /* Try to add second chain of same type */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_cert_chain_and_key(config, cert_chain, private_key), + S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE); + EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); + + /* Try to add chain using other method */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_cert_chain_and_key_to_store(config, chain), + S2N_ERR_CERT_OWNERSHIP); + EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); + }; + + /* Config first uses s2n_config_add_cert_chain_and_key_to_store: application owns chain */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_EQUAL(config->cert_ownership, S2N_NOT_OWNED); + + /* Add first chain */ + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + + /* Add second chain */ + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + + /* Try to add chain using other method */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_cert_chain_and_key(config, cert_chain, private_key), + S2N_ERR_CERT_OWNERSHIP); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + }; + }; + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + + free(cert_chain); + free(private_key); + free(alligator_cert); + free(alligator_key); + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_cert_status_extension_test.c b/tests/unit/s2n_cert_status_extension_test.c new file mode 100644 index 00000000000..e49fc814abe --- /dev/null +++ b/tests/unit/s2n_cert_status_extension_test.c @@ -0,0 +1,392 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_cert_status.h" + +const uint8_t ocsp_data[] = "OCSP DATA"; + +int s2n_test_enable_sending_extension(struct s2n_connection *conn, struct s2n_cert_chain_and_key *chain_and_key) +{ + conn->status_type = S2N_STATUS_REQUEST_OCSP; + conn->handshake_params.our_chain_and_key = chain_and_key; + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ocsp_data(chain_and_key, ocsp_data, s2n_array_len(ocsp_data))); + conn->x509_validator.state = VALIDATED; + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* should_send */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Don't send by default */ + EXPECT_FALSE(s2n_cert_status_extension.should_send(conn)); + + /* Send if all prerequisites met */ + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + EXPECT_TRUE(s2n_cert_status_extension.should_send(conn)); + + /* Send if client */ + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + conn->mode = S2N_CLIENT; + EXPECT_TRUE(s2n_cert_status_extension.should_send(conn)); + + /* Send if server */ + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + conn->mode = S2N_SERVER; + EXPECT_TRUE(s2n_cert_status_extension.should_send(conn)); + + /* Don't send if no certificate set */ + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + conn->handshake_params.our_chain_and_key = NULL; + EXPECT_FALSE(s2n_cert_status_extension.should_send(conn)); + + /* Don't send if no ocsp data */ + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + EXPECT_SUCCESS(s2n_free(&conn->handshake_params.our_chain_and_key->ocsp_status)); + EXPECT_FALSE(s2n_cert_status_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test send */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_cert_status_extension.send(conn, &stuffer)); + + uint8_t request_type; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&stuffer, &request_type)); + EXPECT_EQUAL(request_type, S2N_STATUS_REQUEST_OCSP); + + uint32_t ocsp_size; + EXPECT_SUCCESS(s2n_stuffer_read_uint24(&stuffer, &ocsp_size)); + EXPECT_EQUAL(ocsp_size, s2n_stuffer_data_available(&stuffer)); + EXPECT_EQUAL(ocsp_size, s2n_array_len(ocsp_data)); + + uint8_t *actual_ocsp_data; + EXPECT_NOT_NULL(actual_ocsp_data = s2n_stuffer_raw_read(&stuffer, ocsp_size)); + EXPECT_BYTEARRAY_EQUAL(actual_ocsp_data, ocsp_data, ocsp_size); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test recv */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_cert_status_extension.send(conn, &stuffer)); + + EXPECT_EQUAL(conn->status_response.size, 0); + EXPECT_SUCCESS(s2n_cert_status_extension.recv(conn, &stuffer)); + EXPECT_BYTEARRAY_EQUAL(conn->status_response.data, ocsp_data, s2n_array_len(ocsp_data)); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test recv - not ocsp */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, S2N_STATUS_REQUEST_NONE)); + + EXPECT_EQUAL(conn->status_response.size, 0); + EXPECT_SUCCESS(s2n_cert_status_extension.recv(conn, &stuffer)); + EXPECT_EQUAL(conn->status_response.size, 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test recv - bad ocsp data */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_cert_status_extension.send(conn, &stuffer)); + + if (s2n_x509_ocsp_stapling_supported()) { + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_status_extension.recv(conn, &stuffer), + S2N_ERR_INVALID_OCSP_RESPONSE); + } else { + /* s2n_x509_validator_validate_cert_stapled_ocsp_response returns untrusted error if ocsp is not supported */ + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_status_extension.recv(conn, &stuffer), + S2N_ERR_CERT_UNTRUSTED); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Self-talk tests */ + if (s2n_x509_ocsp_stapling_supported() && s2n_is_tls13_fully_supported()) { + uint8_t ocsp_response[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint32_t ocsp_response_len = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_OCSP_RESPONSE_DER, ocsp_response, &ocsp_response_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_TRUE(ocsp_response_len > 0); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *ocsp_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ocsp_chain_and_key, S2N_OCSP_SERVER_CERT, S2N_OCSP_SERVER_KEY)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ocsp_data(ocsp_chain_and_key, ocsp_response, ocsp_response_len)); + + /* Client requests OCSP staple, and server sends OCSP response */ + { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_OCSP_CA_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ocsp_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "s2n Test Cert")); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + + uint32_t client_received_ocsp_response_len = 0; + const uint8_t *client_received_ocsp_response = s2n_connection_get_ocsp_response(client_conn, + &client_received_ocsp_response_len); + EXPECT_NOT_NULL(client_received_ocsp_response); + + uint32_t server_received_ocsp_response_len = 0; + const uint8_t *server_received_ocsp_response = s2n_connection_get_ocsp_response(server_conn, + &server_received_ocsp_response_len); + /* Only the client requested a response, the server should not have received one. */ + EXPECT_NULL(server_received_ocsp_response); + + /* The server sent an OCSP response, and the client received an OCSP response */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 1); + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 1); + } + + /* Server requests OCSP staple, and client sends OCSP response */ + { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_OCSP_CA_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_NONE)); + + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, ocsp_chain_and_key)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ocsp_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_status_request_type(server_config, S2N_STATUS_REQUEST_OCSP)); + + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_OCSP_CA_CERT, NULL)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "s2n Test Cert")); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + + uint32_t client_received_ocsp_response_len = 0; + const uint8_t *client_received_ocsp_response = s2n_connection_get_ocsp_response(client_conn, + &client_received_ocsp_response_len); + /* Only the server requested a response, the client should not have received one. */ + EXPECT_NULL(client_received_ocsp_response); + + uint32_t server_received_ocsp_response_len = 0; + const uint8_t *server_received_ocsp_response = s2n_connection_get_ocsp_response(server_conn, + &server_received_ocsp_response_len); + EXPECT_NOT_NULL(server_received_ocsp_response); + + /* The server did not send an OCSP response, and the client did not receive an OCSP response */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 0); + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 0); + } + + /* Client and server both request OCSP staples, and client and server both send responses */ + { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_OCSP_CA_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); + + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, ocsp_chain_and_key)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ocsp_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_status_request_type(server_config, S2N_STATUS_REQUEST_OCSP)); + + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_OCSP_CA_CERT, NULL)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "s2n Test Cert")); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + + uint32_t client_received_ocsp_response_len = 0; + const uint8_t *client_received_ocsp_response = s2n_connection_get_ocsp_response(client_conn, + &client_received_ocsp_response_len); + EXPECT_NOT_NULL(client_received_ocsp_response); + + uint32_t server_received_ocsp_response_len = 0; + const uint8_t *server_received_ocsp_response = s2n_connection_get_ocsp_response(server_conn, + &server_received_ocsp_response_len); + EXPECT_NOT_NULL(server_received_ocsp_response); + + /* The server sent an OCSP response, and the client received an OCSP response */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 1); + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 1); + } + + /* Server sets an OCSP response but client does not request OCSP stapling */ + { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_OCSP_CA_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_NONE)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ocsp_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "s2n Test Cert")); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + + uint32_t client_received_ocsp_response_len = 0; + const uint8_t *client_received_ocsp_response = s2n_connection_get_ocsp_response(client_conn, + &client_received_ocsp_response_len); + + uint32_t server_received_ocsp_response_len = 0; + const uint8_t *server_received_ocsp_response = s2n_connection_get_ocsp_response(server_conn, + &server_received_ocsp_response_len); + + /* Both the server and client did not request OCSP responses, so neither should have received them. */ + EXPECT_NULL(client_received_ocsp_response); + EXPECT_NULL(server_received_ocsp_response); + + /* The server did not send an OCSP response, and the client did not receive an OCSP response */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 0); + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 0); + } + } + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_cert_status_response_extension_test.c b/tests/unit/s2n_cert_status_response_extension_test.c new file mode 100644 index 00000000000..160fa7f5f24 --- /dev/null +++ b/tests/unit/s2n_cert_status_response_extension_test.c @@ -0,0 +1,99 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_cert_status_response.h" + +const uint8_t ocsp_data[] = "OCSP DATA"; + +int s2n_test_enable_sending_extension(struct s2n_connection *conn, struct s2n_cert_chain_and_key *chain_and_key) +{ + conn->mode = S2N_SERVER; + conn->status_type = S2N_STATUS_REQUEST_OCSP; + conn->handshake_params.our_chain_and_key = chain_and_key; + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ocsp_data(chain_and_key, ocsp_data, s2n_array_len(ocsp_data))); + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* should_send */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Don't send by default */ + EXPECT_FALSE(s2n_cert_status_response_extension.should_send(conn)); + + /* Send if all prerequisites met */ + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + EXPECT_TRUE(s2n_cert_status_response_extension.should_send(conn)); + + /* Don't send if client */ + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + conn->mode = S2N_CLIENT; + EXPECT_FALSE(s2n_cert_status_response_extension.should_send(conn)); + + /* Don't send if no status request configured */ + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + conn->status_type = S2N_STATUS_REQUEST_NONE; + EXPECT_FALSE(s2n_cert_status_response_extension.should_send(conn)); + + /* Don't send if no certificate set */ + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + conn->handshake_params.our_chain_and_key = NULL; + EXPECT_FALSE(s2n_cert_status_response_extension.should_send(conn)); + + /* Don't send if no ocsp data */ + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + EXPECT_SUCCESS(s2n_free(&conn->handshake_params.our_chain_and_key->ocsp_status)); + EXPECT_FALSE(s2n_cert_status_response_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Set oscp data */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ocsp_data(chain_and_key, ocsp_data, s2n_array_len(ocsp_data))); + + /* Test send and recv */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_SUCCESS(s2n_cert_status_response_extension.send(conn, NULL)); + + EXPECT_EQUAL(conn->status_type, S2N_STATUS_REQUEST_NONE); + EXPECT_SUCCESS(s2n_cert_status_response_extension.recv(conn, NULL)); + EXPECT_EQUAL(conn->status_type, S2N_STATUS_REQUEST_OCSP); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_cert_validation_callback_test.c b/tests/unit/s2n_cert_validation_callback_test.c new file mode 100644 index 00000000000..bd85a6c92d0 --- /dev/null +++ b/tests/unit/s2n_cert_validation_callback_test.c @@ -0,0 +1,450 @@ +/* +* 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_x509_validator.h" + +struct s2n_cert_validation_data { + unsigned call_accept_or_reject : 1; + unsigned accept : 1; + unsigned return_success : 1; + + int invoked_count; +}; + +static int s2n_test_cert_validation_callback(struct s2n_connection *conn, struct s2n_cert_validation_info *info, void *ctx) +{ + struct s2n_cert_validation_data *data = (struct s2n_cert_validation_data *) ctx; + + data->invoked_count += 1; + + int ret = S2N_FAILURE; + if (data->return_success) { + ret = S2N_SUCCESS; + } + + if (!data->call_accept_or_reject) { + return ret; + } + + if (data->accept) { + EXPECT_SUCCESS(s2n_cert_validation_accept(info)); + } else { + EXPECT_SUCCESS(s2n_cert_validation_reject(info)); + } + + return ret; +} + +static int s2n_test_cert_validation_callback_self_talk(struct s2n_connection *conn, + struct s2n_cert_validation_info *info, void *ctx) +{ + DEFER_CLEANUP(struct s2n_cert_chain_and_key *peer_cert_chain = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + EXPECT_NOT_NULL(peer_cert_chain); + + /* Ensure that the peer's certificate chain can be retrieved at the time the callback is invoked */ + EXPECT_SUCCESS(s2n_connection_get_peer_cert_chain(conn, peer_cert_chain)); + uint32_t peer_cert_chain_len = 0; + EXPECT_SUCCESS(s2n_cert_chain_get_length(peer_cert_chain, &peer_cert_chain_len)); + EXPECT_TRUE(peer_cert_chain_len > 0); + + return s2n_test_cert_validation_callback(conn, info, ctx); +} + +static int s2n_test_cert_validation_callback_self_talk_server(struct s2n_connection *conn, + struct s2n_cert_validation_info *info, void *ctx) +{ + /* Ensure that the callback was invoked on the server connection */ + EXPECT_EQUAL(conn->mode, S2N_SERVER); + + /* Ensure that the client's certificate chain can be retrieved at the time the callback was invoked */ + uint8_t *der_cert_chain = 0; + uint32_t cert_chain_len = 0; + EXPECT_SUCCESS(s2n_connection_get_client_cert_chain(conn, &der_cert_chain, &cert_chain_len)); + EXPECT_TRUE(cert_chain_len > 0); + + return s2n_test_cert_validation_callback_self_talk(conn, info, ctx); +} + +static int s2n_test_cert_validation_callback_self_talk_ocsp(struct s2n_connection *conn, + struct s2n_cert_validation_info *info, void *ctx) +{ + /* Ensure that the OCSP response was received prior to invoking the callback */ + uint32_t ocsp_response_length = 0; + const uint8_t *ocsp_response = s2n_connection_get_ocsp_response(conn, &ocsp_response_length); + EXPECT_NOT_NULL(ocsp_response); + EXPECT_TRUE(ocsp_response_length > 0); + + return s2n_test_cert_validation_callback_self_talk(conn, info, ctx); +} + +int main(int argc, char *argv[]) +{ + BEGIN_TEST(); + + /* Accept/reject tests */ + { + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_accept(NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_reject(NULL), S2N_ERR_NULL); + + /* Accept sets the proper state */ + { + struct s2n_cert_validation_info info = { 0 }; + + EXPECT_SUCCESS(s2n_cert_validation_accept(&info)); + + EXPECT_EQUAL(info.finished, true); + EXPECT_EQUAL(info.accepted, true); + } + + /* Reject sets the proper state */ + { + struct s2n_cert_validation_info info = { 0 }; + + EXPECT_SUCCESS(s2n_cert_validation_reject(&info)); + + EXPECT_EQUAL(info.finished, true); + EXPECT_EQUAL(info.accepted, false); + } + + /* Calls to accept/reject fail if accept has already been called */ + { + struct s2n_cert_validation_info info = { 0 }; + + EXPECT_SUCCESS(s2n_cert_validation_accept(&info)); + + for (int i = 0; i < 10; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_accept(&info), S2N_ERR_INVALID_STATE); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_reject(&info), S2N_ERR_INVALID_STATE); + } + + /* State was updated from the successful call */ + EXPECT_EQUAL(info.finished, true); + EXPECT_EQUAL(info.accepted, true); + } + + /* Calls to accept/reject fail if reject has already been called */ + { + struct s2n_cert_validation_info info = { 0 }; + + EXPECT_SUCCESS(s2n_cert_validation_reject(&info)); + + for (int i = 0; i < 10; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_reject(&info), S2N_ERR_INVALID_STATE); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_validation_accept(&info), S2N_ERR_INVALID_STATE); + } + + /* State was updated from the successful call */ + EXPECT_EQUAL(info.finished, true); + EXPECT_EQUAL(info.accepted, false); + } + } + + /* Test s2n_cert_validation_callback */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* clang-format off */ + struct { + const struct s2n_cert_validation_data data; + s2n_error expected_error; + } test_cases[] = { + /* No error when accept is called from the callback */ + { + .data = { .call_accept_or_reject = true, .accept = true, .return_success = true }, + .expected_error = S2N_ERR_OK + }, + + /* Error if reject was called from the callback */ + { + .data = { .call_accept_or_reject = true, .accept = false, .return_success = true }, + .expected_error = S2N_ERR_CERT_REJECTED + }, + + /* Error if the callback doesn't return successfully */ + { + .data = { .call_accept_or_reject = true, .accept = true, .return_success = false }, + .expected_error = S2N_ERR_CANCELLED + }, + { + .data = { .call_accept_or_reject = true, .accept = false, .return_success = false }, + .expected_error = S2N_ERR_CANCELLED + }, + { + .data = { .call_accept_or_reject = false, .return_success = false }, + .expected_error = S2N_ERR_CANCELLED + }, + + /* Error if accept or reject wasn't called from the callback */ + { + .data = { .call_accept_or_reject = false, .return_success = true }, + .expected_error = S2N_ERR_INVALID_STATE + }, + }; + /* clang-format on */ + + /* s2n_x509_validator test */ + for (int i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, cert_chain)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + + struct s2n_cert_validation_data data = test_cases[i].data; + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_set_server_name(conn, "s2nTestServer")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(conn, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + s2n_error expected_error = test_cases[i].expected_error; + if (expected_error == S2N_ERR_OK) { + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, + &pkey_type, &public_key_out)); + } else { + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, + chain_len, &pkey_type, &public_key_out), + expected_error); + } + + EXPECT_EQUAL(data.invoked_count, 1); + } + + /* The callback is invoked even if cert verification is disabled */ + for (int i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, cert_chain)); + + /* Initialize the x509_validator with skip_cert_validation enabled */ + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init_no_x509_validation(&validator)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + + struct s2n_cert_validation_data data = test_cases[i].data; + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(conn, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + s2n_error expected_error = test_cases[i].expected_error; + if (expected_error == S2N_ERR_OK) { + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, + &pkey_type, &public_key_out)); + } else { + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, + chain_len, &pkey_type, &public_key_out), + expected_error); + } + + EXPECT_EQUAL(data.invoked_count, 1); + } + + /* Self-talk: callback is invoked on the client after receiving the server's certificate */ + for (int i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + + struct s2n_cert_validation_data data = test_cases[i].data; + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_callback_self_talk, &data)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "s2nTestServer")); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + s2n_error expected_error = test_cases[i].expected_error; + if (expected_error == S2N_ERR_OK) { + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } else { + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + expected_error); + } + + EXPECT_EQUAL(data.invoked_count, 1); + } + + /* Self-talk: callback is invoked on the server after receiving the client's certificate */ + for (int i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + + struct s2n_cert_validation_data data = test_cases[i].data; + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(server_config, + s2n_test_cert_validation_callback_self_talk_server, &data)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "s2nTestServer")); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + s2n_error expected_error = test_cases[i].expected_error; + if (expected_error == S2N_ERR_OK) { + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } else { + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + expected_error); + } + + EXPECT_EQUAL(data.invoked_count, 1); + } + + /* Self-talk: callback is invoked after an OCSP response is received in TLS 1.3 + * + * Currently, the cert validation callback is invoked after validating the certificate + * chain and after processing the Certificate message extensions. In TLS 1.3, the OCSP + * response is sent in a Certificate message extension, and should be accessible to the + * cert validation callback. + * + * In TLS 1.2, the OCSP response is sent in a separate CertificateStatus message which is + * received after the cert validation callback is invoked. So, OCSP information won't be + * accessible from the callback in TLS 1.2. + */ + for (int i = 0; i < s2n_array_len(test_cases); i++) { + if (!s2n_x509_ocsp_stapling_supported() || !s2n_is_tls13_fully_supported()) { + break; + } + + uint8_t ocsp_response[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint32_t ocsp_response_len = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_OCSP_RESPONSE_DER, ocsp_response, &ocsp_response_len, + S2N_MAX_TEST_PEM_SIZE)); + EXPECT_TRUE(ocsp_response_len > 0); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *ocsp_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ocsp_chain_and_key, + S2N_OCSP_SERVER_CERT, S2N_OCSP_SERVER_KEY)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ocsp_data(ocsp_chain_and_key, ocsp_response, ocsp_response_len)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ocsp_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_OCSP_CA_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); + + struct s2n_cert_validation_data data = test_cases[i].data; + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(client_config, + s2n_test_cert_validation_callback_self_talk_ocsp, &data)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "s2n Test Cert")); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + s2n_error expected_error = test_cases[i].expected_error; + if (expected_error == S2N_ERR_OK) { + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } else { + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + expected_error); + } + + EXPECT_EQUAL(data.invoked_count, 1); + } + } + + END_TEST(); +} diff --git a/tests/unit/s2n_certificate_extensions_test.c b/tests/unit/s2n_certificate_extensions_test.c new file mode 100644 index 00000000000..ef8de874779 --- /dev/null +++ b/tests/unit/s2n_certificate_extensions_test.c @@ -0,0 +1,345 @@ +/* + * 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 "error/s2n_errno.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_extension_list.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +s2n_cert_public_key public_key; +s2n_pkey_type actual_cert_pkey_type; + +static int s2n_skip_cert_chain_size(struct s2n_stuffer *stuffer) +{ + uint32_t cert_chain_size; + POSIX_GUARD(s2n_stuffer_read_uint24(stuffer, &cert_chain_size)); + POSIX_ENSURE_EQ(cert_chain_size, s2n_stuffer_data_available(stuffer)); + return S2N_SUCCESS; +} + +static int s2n_skip_cert(struct s2n_stuffer *stuffer) +{ + uint32_t cert_size; + POSIX_GUARD(s2n_stuffer_read_uint24(stuffer, &cert_size)); + POSIX_GUARD(s2n_stuffer_skip_read(stuffer, cert_size)); + return S2N_SUCCESS; +} + +static int s2n_x509_validator_validate_cert_chain_test(struct s2n_connection *conn, struct s2n_stuffer *stuffer) +{ + POSIX_GUARD(s2n_skip_cert_chain_size(stuffer)); + uint32_t cert_chain_size = s2n_stuffer_data_available(stuffer); + + uint8_t *cert_chain_data; + POSIX_ENSURE_REF(cert_chain_data = s2n_stuffer_raw_read(stuffer, cert_chain_size)); + + POSIX_GUARD_RESULT(s2n_x509_validator_validate_cert_chain(&conn->x509_validator, conn, + cert_chain_data, cert_chain_size, &actual_cert_pkey_type, &public_key)); + + POSIX_GUARD(s2n_pkey_free(&public_key)); + return S2N_SUCCESS; +} + +static int s2n_write_test_cert(struct s2n_stuffer *stuffer, struct s2n_cert_chain_and_key *chain_and_key) +{ + struct s2n_blob *cert = &chain_and_key->cert_chain->head->raw; + POSIX_GUARD(s2n_stuffer_write_uint24(stuffer, cert->size)); + POSIX_GUARD(s2n_stuffer_write_bytes(stuffer, cert->data, cert->size)); + return S2N_SUCCESS; +} + +static int s2n_setup_connection_for_ocsp_validate_test(struct s2n_connection **conn, struct s2n_cert_chain_and_key *chain_and_key) +{ + struct s2n_connection *nconn; + + POSIX_ENSURE_REF(nconn = s2n_connection_new(S2N_SERVER)); + nconn->actual_protocol_version = S2N_TLS13; + nconn->handshake_params.our_chain_and_key = chain_and_key; + + POSIX_GUARD(s2n_connection_allow_all_response_extensions(nconn)); + nconn->status_type = S2N_STATUS_REQUEST_OCSP; + + *conn = nconn; + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key)); + + /* Initialize cert chain */ + struct s2n_cert_chain_and_key *chain_and_key; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + /* Initialize cert extension data */ + uint8_t data[] = "extension data"; + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ocsp_data(chain_and_key, data, s2n_array_len(data))); + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_sct_list(chain_and_key, data, s2n_array_len(data))); + + /* Test: s2n_send_cert_chain sends extensions */ + { + /* Test: extensions only sent for >= TLS1.3 */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->handshake_params.our_chain_and_key = chain_and_key; + + EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(conn)); + conn->status_type = S2N_STATUS_REQUEST_OCSP; + + /* TLS1.2 does NOT send extensions */ + { + DEFER_CLEANUP(struct s2n_stuffer stuffer, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_send_cert_chain(conn, &stuffer, chain_and_key)); + + s2n_parsed_extensions_list extensions; + EXPECT_SUCCESS(s2n_skip_cert_chain_size(&stuffer)); + EXPECT_SUCCESS(s2n_skip_cert(&stuffer)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_extension_list_parse(&stuffer, &extensions), + S2N_ERR_BAD_MESSAGE); + }; + + /* TLS1.3 DOES send extensions */ + { + DEFER_CLEANUP(struct s2n_stuffer stuffer, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_send_cert_chain(conn, &stuffer, chain_and_key)); + + s2n_parsed_extensions_list extensions; + EXPECT_SUCCESS(s2n_skip_cert_chain_size(&stuffer)); + EXPECT_SUCCESS(s2n_skip_cert(&stuffer)); + + EXPECT_SUCCESS(s2n_extension_list_parse(&stuffer, &extensions)); + EXPECT_PARSED_EXTENSION_LIST_NOT_EMPTY(extensions); + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: extensions only sent on first certificate */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->handshake_params.our_chain_and_key = chain_and_key; + + DEFER_CLEANUP(struct s2n_stuffer stuffer, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(conn)); + conn->status_type = S2N_STATUS_REQUEST_OCSP; + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_send_cert_chain(conn, &stuffer, chain_and_key)); + + s2n_parsed_extensions_list extensions; + EXPECT_SUCCESS(s2n_skip_cert_chain_size(&stuffer)); + + /* First cert includes extensions */ + EXPECT_SUCCESS(s2n_skip_cert(&stuffer)); + EXPECT_SUCCESS(s2n_extension_list_parse(&stuffer, &extensions)); + EXPECT_PARSED_EXTENSION_LIST_NOT_EMPTY(extensions); + + /* Other certs do not include extensions */ + do { + EXPECT_SUCCESS(s2n_skip_cert(&stuffer)); + EXPECT_SUCCESS(s2n_extension_list_parse(&stuffer, &extensions)); + EXPECT_PARSED_EXTENSION_LIST_EMPTY(extensions); + } while (s2n_stuffer_data_available(&stuffer)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test: s2n_x509_validator_validate_cert_chain handles the output of s2n_send_cert_chain */ + { + /* Test: with no extensions */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS13; + conn->handshake_params.our_chain_and_key = chain_and_key; + + DEFER_CLEANUP(struct s2n_stuffer stuffer, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_send_cert_chain(conn, &stuffer, chain_and_key)); + EXPECT_SUCCESS(s2n_x509_validator_validate_cert_chain_test(conn, &stuffer)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: with extensions */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS13; + conn->handshake_params.our_chain_and_key = chain_and_key; + + DEFER_CLEANUP(struct s2n_stuffer stuffer, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(conn)); + conn->status_type = S2N_STATUS_REQUEST_OCSP; + conn->ct_level_requested = S2N_CT_SUPPORT_REQUEST; + + EXPECT_SUCCESS(s2n_send_cert_chain(conn, &stuffer, chain_and_key)); + EXPECT_SUCCESS(s2n_x509_validator_validate_cert_chain_test(conn, &stuffer)); + + /* OCSP extension processed */ + EXPECT_EQUAL(conn->status_response.size, s2n_array_len(data)); + EXPECT_BYTEARRAY_EQUAL(conn->status_response.data, data, s2n_array_len(data)); + + /* SCT extension processed */ + EXPECT_EQUAL(conn->ct_response.size, s2n_array_len(data)); + EXPECT_BYTEARRAY_EQUAL(conn->ct_response.data, data, s2n_array_len(data)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test: s2n_x509_validator_validate_cert_chain receives extensions */ + { + /* Test: extensions only processed for >= TLS1.3 */ + { + struct s2n_connection *setup_conn; + POSIX_GUARD(s2n_setup_connection_for_ocsp_validate_test(&setup_conn, chain_and_key)); + + DEFER_CLEANUP(struct s2n_stuffer stuffer, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + struct s2n_stuffer_reservation size = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint24(&stuffer, &size)); + EXPECT_SUCCESS(s2n_write_test_cert(&stuffer, chain_and_key)); + EXPECT_SUCCESS(s2n_extension_list_send(S2N_EXTENSION_LIST_CERTIFICATE, setup_conn, &stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&size)); + + /* TLS1.2 does NOT process extensions */ + { + struct s2n_connection *conn; + POSIX_GUARD(s2n_setup_connection_for_ocsp_validate_test(&conn, chain_and_key)); + + EXPECT_SUCCESS(s2n_stuffer_reread(&stuffer)); + conn->actual_protocol_version = S2N_TLS12; + + EXPECT_FAILURE(s2n_x509_validator_validate_cert_chain_test(conn, &stuffer)); + + EXPECT_EQUAL(conn->status_response.size, 0); + EXPECT_EQUAL(conn->status_response.data, NULL); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS1.3 DOES process extensions */ + { + struct s2n_connection *conn; + POSIX_GUARD(s2n_setup_connection_for_ocsp_validate_test(&conn, chain_and_key)); + + EXPECT_SUCCESS(s2n_stuffer_reread(&stuffer)); + conn->actual_protocol_version = S2N_TLS13; + + EXPECT_SUCCESS(s2n_x509_validator_validate_cert_chain_test(conn, &stuffer)); + + EXPECT_EQUAL(conn->status_response.size, s2n_array_len(data)); + EXPECT_BYTEARRAY_EQUAL(conn->status_response.data, data, s2n_array_len(data)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + EXPECT_SUCCESS(s2n_connection_free(setup_conn)); + }; + + /* Test: extensions only processed on first certificate */ + { + struct s2n_stuffer_reservation size = { 0 }; + + /* Extensions on second cert ignored */ + { + struct s2n_connection *conn; + POSIX_GUARD(s2n_setup_connection_for_ocsp_validate_test(&conn, chain_and_key)); + + DEFER_CLEANUP(struct s2n_stuffer stuffer, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_stuffer_reserve_uint24(&stuffer, &size)); + EXPECT_SUCCESS(s2n_write_test_cert(&stuffer, chain_and_key)); + EXPECT_SUCCESS(s2n_extension_list_send(S2N_EXTENSION_LIST_EMPTY, conn, &stuffer)); + EXPECT_SUCCESS(s2n_write_test_cert(&stuffer, chain_and_key)); + EXPECT_SUCCESS(s2n_extension_list_send(S2N_EXTENSION_LIST_CERTIFICATE, conn, &stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&size)); + + EXPECT_SUCCESS(s2n_x509_validator_validate_cert_chain_test(conn, &stuffer)); + + EXPECT_EQUAL(conn->status_response.size, 0); + EXPECT_EQUAL(conn->status_response.data, NULL); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Extensions on first cert processed */ + { + struct s2n_connection *conn; + POSIX_GUARD(s2n_setup_connection_for_ocsp_validate_test(&conn, chain_and_key)); + + DEFER_CLEANUP(struct s2n_stuffer stuffer, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_stuffer_reserve_uint24(&stuffer, &size)); + EXPECT_SUCCESS(s2n_write_test_cert(&stuffer, chain_and_key)); + EXPECT_SUCCESS(s2n_extension_list_send(S2N_EXTENSION_LIST_CERTIFICATE, conn, &stuffer)); + EXPECT_SUCCESS(s2n_write_test_cert(&stuffer, chain_and_key)); + EXPECT_SUCCESS(s2n_extension_list_send(S2N_EXTENSION_LIST_EMPTY, conn, &stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&size)); + + EXPECT_SUCCESS(s2n_x509_validator_validate_cert_chain_test(conn, &stuffer)); + + EXPECT_EQUAL(conn->status_response.size, s2n_array_len(data)); + EXPECT_BYTEARRAY_EQUAL(conn->status_response.data, data, s2n_array_len(data)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + }; + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(config)); + + END_TEST(); + + return 0; +} diff --git a/tests/unit/s2n_certificate_test.c b/tests/unit/s2n_certificate_test.c new file mode 100644 index 00000000000..22fd45fc2e0 --- /dev/null +++ b/tests/unit/s2n_certificate_test.c @@ -0,0 +1,1059 @@ +/* + * 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 "api/s2n.h" +#include "crypto/s2n_crypto.h" +#include "crypto/s2n_openssl_x509.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_safety.h" + +#define S2N_DEFAULT_TEST_CERT_CHAIN_LENGTH 3 +#define S2N_CERT_DER_SIZE 2048 + +#define S2N_RSA_2048_SHA256_INTERMEDIATE_CA_KEY "../pems/rsa_2048_sha256_intermediate_ca_key.pem" +#define S2N_RSA_2048_SHA256_INTERMEDIATE_CERT_CUSTOM_OID "../pems/rsa_2048_sha256_intermediate_cert_custom_oid.pem" + +#define ext_value_MAX_LEN UINT16_MAX +#define OFFSET_INSUFFICIENT_MEM_SIZE 3 + +DEFINE_POINTER_CLEANUP_FUNC(X509_NAME *, X509_NAME_free); + +struct host_verify_data { + bool callback_invoked; + bool allow; +}; + +static uint8_t verify_host_fn(const char *host_name, size_t host_name_len, void *data) +{ + struct host_verify_data *verify_data = (struct host_verify_data *) data; + verify_data->callback_invoked = 1; + return verify_data->allow; +} + +static uint8_t always_verify_host_fn(const char *host_name, size_t host_name_len, void *data) +{ + return true; +} + +static int s2n_noop_async_pkey_fn(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_compare_cert_chain(struct s2n_connection *conn, struct s2n_cert_chain_and_key *test_peer_chain) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(test_peer_chain); + uint32_t cert_chain_length = 0; + RESULT_GUARD_POSIX(s2n_cert_chain_get_length(test_peer_chain, &cert_chain_length)); + DEFER_CLEANUP(STACK_OF(X509) *cert_chain_validated = X509_STORE_CTX_get1_chain(conn->x509_validator.store_ctx), + s2n_openssl_x509_stack_pop_free); + RESULT_ENSURE_REF(cert_chain_validated); + RESULT_ENSURE_EQ(cert_chain_length, sk_X509_num(cert_chain_validated)); + struct s2n_cert *cur_cert = NULL; + + for (size_t cert_idx = 0; cert_idx < cert_chain_length; cert_idx++) { + X509 *cert = sk_X509_value(cert_chain_validated, cert_idx); + RESULT_ENSURE_REF(cert); + DEFER_CLEANUP(uint8_t *cert_data_from_validator = NULL, s2n_crypto_free); + int cert_size_from_validator = i2d_X509(cert, &cert_data_from_validator); + RESULT_ENSURE_REF(cert_data_from_validator); + RESULT_ENSURE_GT(cert_size_from_validator, 0); + + RESULT_GUARD_POSIX(s2n_cert_chain_get_cert(test_peer_chain, &cur_cert, cert_idx)); + RESULT_ENSURE_REF(cur_cert); + RESULT_ENSURE_EQ(cert_size_from_validator, cur_cert->raw.size); + RESULT_ENSURE_EQ(memcmp(cert_data_from_validator, cur_cert->raw.data, cur_cert->raw.size), 0); + } + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_compare_utf8_strings(struct s2n_blob *input_der, const char *expected_utf8_str, uint32_t utf8_len_in, uint32_t expected_utf8_len) +{ + RESULT_ENSURE_REF(input_der); + RESULT_ENSURE_REF(expected_utf8_str); + RESULT_ENSURE_GT(expected_utf8_len, 0); + + DEFER_CLEANUP(struct s2n_blob utf8_str = { 0 }, s2n_free); + RESULT_GUARD_POSIX(s2n_alloc(&utf8_str, utf8_len_in)); + + RESULT_GUARD_POSIX(s2n_cert_get_utf8_string_from_extension_data(input_der->data, input_der->size, utf8_str.data, &utf8_str.size)); + + RESULT_ENSURE_EQ(utf8_str.size, expected_utf8_len); + RESULT_ENSURE_EQ(memcmp(utf8_str.data, expected_utf8_str, utf8_str.size), 0); + + RESULT_GUARD_POSIX(s2n_free(&utf8_str)); + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + const uint8_t ocsp_data[] = "ocsp data"; + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + /* Test s2n_cert_chain_and_key_new */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + EXPECT_NOT_NULL(chain); + + /* Sanity check some fields */ + EXPECT_NOT_NULL(chain->cert_chain); + EXPECT_NOT_NULL(chain->private_key); + EXPECT_NOT_NULL(chain->cn_names); + EXPECT_NOT_NULL(chain->san_names); + EXPECT_EQUAL(chain->cert_chain->chain_size, 0); + EXPECT_NULL(chain->cert_chain->head); + EXPECT_NULL(chain->private_key->pkey); + EXPECT_NULL(chain->private_key->sign); + EXPECT_NULL(chain->context); + }; + + /* Test s2n_cert_chain_and_key_load_public_pem_bytes */ + { + uint32_t pem_len = 0; + uint8_t pem[S2N_CERT_DER_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, pem, &pem_len, sizeof(pem))); + + /* Load only a public certificate */ + struct s2n_cert_chain_and_key *cert_only_chain = s2n_cert_chain_and_key_new(); + EXPECT_NOT_NULL(cert_only_chain); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_public_pem_bytes(cert_only_chain, pem, pem_len)); + EXPECT_FAILURE(s2n_pkey_check_key_exists(cert_only_chain->private_key)); + + /* Add cert chain to config */ + struct s2n_config *config = s2n_config_new(); + EXPECT_FALSE(config->no_signing_key); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, cert_only_chain)); + EXPECT_TRUE(config->no_signing_key); + + /* Add config to connection */ + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_set_config(conn, config), S2N_ERR_NO_PRIVATE_KEY); + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(config, s2n_noop_async_pkey_fn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(cert_only_chain)); + }; + + /* Test s2n_cert_chain_get_length */ + { + uint32_t length = 0; + + /* Safety checks */ + { + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_get_length(NULL, &length), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_get_length(chain_and_key, NULL), S2N_ERR_NULL); + }; + + /* Test success case */ + EXPECT_SUCCESS(s2n_cert_chain_get_length(chain_and_key, &length)); + EXPECT_EQUAL(length, S2N_DEFAULT_TEST_CERT_CHAIN_LENGTH); + }; + + /* Test s2n_cert_chain_get_cert */ + { + struct s2n_cert *out_cert = NULL; + uint32_t cert_idx = 0; + + /* Safety checks */ + { + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_get_cert(NULL, &out_cert, cert_idx), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_get_cert(chain_and_key, NULL, cert_idx), S2N_ERR_NULL); + }; + + struct s2n_cert *cur_cert = chain_and_key->cert_chain->head; + + /* Test error case for invalid cert_idx, the valid range of cert_idx is 0 to cert_chain_length - 1 */ + cert_idx = S2N_DEFAULT_TEST_CERT_CHAIN_LENGTH; + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_get_cert(chain_and_key, &out_cert, cert_idx), S2N_ERR_NO_CERT_FOUND); + + /* Test success case */ + for (size_t i = 0; i < S2N_DEFAULT_TEST_CERT_CHAIN_LENGTH; i++) { + EXPECT_SUCCESS(s2n_cert_chain_get_cert(chain_and_key, &out_cert, i)); + EXPECT_NOT_NULL(cur_cert); + EXPECT_EQUAL(out_cert, cur_cert); + cur_cert = cur_cert->next; + } + }; + + /* Test s2n_cert_get_der */ + { + struct s2n_cert *cert = chain_and_key->cert_chain->head; + const uint8_t *out_cert_der = NULL; + uint32_t cert_len = 0; + + /* Safety checks */ + { + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_der(NULL, &out_cert_der, &cert_len), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_der(cert, NULL, &cert_len), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_der(cert, &out_cert_der, NULL), S2N_ERR_NULL); + }; + + EXPECT_SUCCESS(s2n_cert_get_der(cert, &out_cert_der, &cert_len)); + EXPECT_EQUAL(cert_len, cert->raw.size); + EXPECT_BYTEARRAY_EQUAL(out_cert_der, cert->raw.data, cert_len); + }; + + /* Test s2n_connection_get_peer_cert_chain */ + { + /* Setup connections */ + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + struct s2n_cert_chain_and_key *s2n_chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&s2n_chain_and_key, S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, + S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + struct s2n_config *config_skip_x509_verification = s2n_config_new(); + EXPECT_NOT_NULL(config_skip_x509_verification); + /* Skip x509 verification */ + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config_skip_x509_verification)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(config_skip_x509_verification, 0)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config_skip_x509_verification, "test_all")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(config_skip_x509_verification, S2N_CERT_AUTH_OPTIONAL)); + struct host_verify_data verify_data = { .allow = 1, .callback_invoked = 0 }; + EXPECT_SUCCESS(s2n_config_set_verify_host_callback(config_skip_x509_verification, verify_host_fn, &verify_data)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config_skip_x509_verification, S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config_skip_x509_verification, s2n_chain_and_key)); + + struct s2n_config *config_with_x509_verification = s2n_config_new(); + EXPECT_NOT_NULL(config_with_x509_verification); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config_with_x509_verification, "test_all")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(config_with_x509_verification, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_config_set_verify_host_callback(config_with_x509_verification, verify_host_fn, &verify_data)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config_with_x509_verification, S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config_with_x509_verification, s2n_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(config_with_x509_verification, false)); + + /* Test s2n_connection_get_peer_cert_chain failure cases with error codes */ + { + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_skip_x509_verification)); + EXPECT_EQUAL(client_conn->x509_validator.skip_cert_validation, 1); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_with_x509_verification)); + EXPECT_EQUAL(server_conn->x509_validator.skip_cert_validation, 0); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(server_conn->x509_validator.state, VALIDATED); + EXPECT_NOT_EQUAL(client_conn->x509_validator.state, VALIDATED); + + /* Safety checks */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + EXPECT_NOT_NULL(chain); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_peer_cert_chain(NULL, chain), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_peer_cert_chain(server_conn, NULL), S2N_ERR_NULL); + } + + /* Input certificate chain is not empty */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *input = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&input, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_peer_cert_chain(server_conn, input), + S2N_ERR_INVALID_ARGUMENT); + + /* Validate that the original cert chain was not modified */ + EXPECT_NOT_NULL(input->cert_chain); + EXPECT_NOT_NULL(input->cert_chain->head); + EXPECT_EQUAL(input->cert_chain->head->pkey_type, S2N_PKEY_TYPE_ECDSA); + } + + /* x509 verification is skipped on client side */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + EXPECT_NOT_NULL(chain); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_peer_cert_chain(client_conn, chain), + S2N_ERR_CERT_NOT_VALIDATED); + } + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + /* Clean-up */ + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + }; + + /* Test s2n_connection_get_peer_cert_chain success on the server side */ + { + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_skip_x509_verification)); + EXPECT_EQUAL(client_conn->x509_validator.skip_cert_validation, 1); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_with_x509_verification)); + EXPECT_EQUAL(server_conn->x509_validator.skip_cert_validation, 0); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(server_conn->x509_validator.state, VALIDATED); + + struct s2n_cert_chain_and_key *test_peer_chain = s2n_cert_chain_and_key_new(); + EXPECT_NOT_NULL(test_peer_chain); + + EXPECT_SUCCESS(s2n_connection_get_peer_cert_chain(server_conn, test_peer_chain)); + + EXPECT_OK(s2n_compare_cert_chain(server_conn, test_peer_chain)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + /* Clean-up */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(test_peer_chain)); + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + }; + + /* Test s2n_connection_get_peer_cert_chain success on the client side */ + { + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_skip_x509_verification)); + EXPECT_EQUAL(server_conn->x509_validator.skip_cert_validation, 1); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_x509_verification)); + EXPECT_EQUAL(client_conn->x509_validator.skip_cert_validation, 0); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(client_conn->x509_validator.state, VALIDATED); + + struct s2n_cert_chain_and_key *test_peer_chain = s2n_cert_chain_and_key_new(); + EXPECT_NOT_NULL(test_peer_chain); + + EXPECT_SUCCESS(s2n_connection_get_peer_cert_chain(client_conn, test_peer_chain)); + + EXPECT_OK(s2n_compare_cert_chain(client_conn, test_peer_chain)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + /* Clean-up */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(test_peer_chain)); + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + }; + + /* Test s2n_connection_get_peer_cert_chain with OCSP */ + if (s2n_x509_ocsp_stapling_supported()) { + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ocsp_data(s2n_chain_and_key, + ocsp_data, s2n_array_len(ocsp_data))); + EXPECT_SUCCESS(s2n_config_set_status_request_type(config_with_x509_verification, S2N_STATUS_REQUEST_OCSP)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_skip_x509_verification)); + EXPECT_EQUAL(server_conn->x509_validator.skip_cert_validation, 1); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_x509_verification)); + EXPECT_EQUAL(client_conn->x509_validator.skip_cert_validation, 0); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(client_conn->x509_validator.state, OCSP_VALIDATED); + + struct s2n_cert_chain_and_key *test_peer_chain = s2n_cert_chain_and_key_new(); + EXPECT_NOT_NULL(test_peer_chain); + + EXPECT_SUCCESS(s2n_connection_get_peer_cert_chain(client_conn, test_peer_chain)); + + EXPECT_OK(s2n_compare_cert_chain(client_conn, test_peer_chain)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + /* Clean-up */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(test_peer_chain)); + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + } + + /* Clean-up */ + EXPECT_SUCCESS(s2n_config_free(config_skip_x509_verification)); + EXPECT_SUCCESS(s2n_config_free(config_with_x509_verification)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(s2n_chain_and_key)); + }; + + /* Test X509 Extension helper functions */ + { + struct s2n_blob ext_value = { 0 }; + struct s2n_blob utf8_str = { 0 }; + bool critical = false; + size_t i = 0; + + struct s2n_cert_chain_and_key *custom_cert_chain = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&custom_cert_chain, + S2N_RSA_2048_SHA256_INTERMEDIATE_CERT_CUSTOM_OID, + S2N_RSA_2048_SHA256_INTERMEDIATE_CA_KEY)); + struct s2n_cert *cert = custom_cert_chain->cert_chain->head; + EXPECT_NOT_NULL(cert); + + S2N_BLOB_FROM_HEX(subject_key_id_blob, "04 14 F9 19 58 9D 9E 97 89 9C 27 67 5B 62 19 \ + 2A 1E 27 D6 4E 1E F6"); + S2N_BLOB_FROM_HEX(authority_key_id_blob, "30 16 80 14 56 9E 26 B6 09 4C 2E AC C8 4E 51 \ + E1 AD 7F E7 92 84 28 D4 3E"); + S2N_BLOB_FROM_HEX(basic_constraints_blob, "30 06 01 01 FF 02 01 00"); + S2N_BLOB_FROM_HEX(key_usage_blob, "03 02 01 86"); + S2N_BLOB_FROM_HEX(custom_oid_1_blob, "0C 41 6B 65 79 69 64 3A 33 \ + 36 3A 36 31 3A 33 46 3A 31 42 3A 30 32 3A 43 37 \ + 3A 31 32 3A 32 42 3A 35 33 3A 30 41 3A 32 32 3A \ + 42 41 3A 35 38 3A 42 36 3A 41 38 3A 38 30 3A 31 \ + 39 3A 45 45 3A 35 31 3A 38 35"); + S2N_BLOB_FROM_HEX(custom_oid_2_blob, "0C 18 49 50 20 41 64 64 72 65 73 73 3A 31 32 2E \ + 33 34 35 2E 36 37 2E 38 39 30"); + S2N_BLOB_FROM_HEX(custom_oid_3_blob, "0C 28 44 4E 53 3A 31 32 2E 33 34 35 2E 36 37 2E \ + 38 39 30 2E 61 75 74 6F 2E 70 64 78 2E 65 63 32 \ + 2E 73 75 62 73 74 72 61 74 65"); + + struct { + const char *oid; + uint32_t ext_value_len; + const char *expected_utf8; + uint32_t utf8_len; + struct s2n_blob expected_der; + struct s2n_blob returned_der; + bool critical; + } test_cases[] = { + { .oid = "X509v3 Subject Key Identifier", + .expected_der = subject_key_id_blob, + .critical = false }, + { .oid = "X509v3 Authority Key Identifier", + .expected_der = authority_key_id_blob, + .critical = false }, + { .oid = "X509v3 Basic Constraints", + .expected_der = basic_constraints_blob, + .critical = true }, + { .oid = "X509v3 Key Usage", + .expected_der = key_usage_blob, + .critical = true }, + { .oid = "1.2.3.4.5.6.7890.1.2.100.1", + .expected_utf8 = "keyid:36:61:3F:1B:02:C7:12:2B:53:0A:22:BA:58:B6:A8:80:19:EE:51:85", + .expected_der = custom_oid_1_blob, + .critical = false }, + { .oid = "1.2.3.4.5.6.7890.1.2.100.2", + .expected_utf8 = "IP Address:12.345.67.890", + .expected_der = custom_oid_2_blob, + .critical = false }, + { .oid = "1.2.3.4.5.6.7890.1.2.100.3", + .expected_utf8 = "DNS:12.345.67.890.auto.pdx.ec2.substrate", + .expected_der = custom_oid_3_blob, + .critical = false }, + { .oid = "1.2.3.4.5.6.7890.1.2.100", + .critical = false }, + }; + + /* Test s2n_cert_get_x509_extension_value_length */ + { + /* Safety checks */ + { + const uint8_t oid[] = "Example X509 extension OID"; + uint32_t ext_value_len = 0; + + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_x509_extension_value_length(NULL, oid, &ext_value_len), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_x509_extension_value_length(cert, NULL, &ext_value_len), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_x509_extension_value_length(cert, oid, NULL), S2N_ERR_NULL); + }; + + /* Test success cases */ + for (i = 0; i < s2n_array_len(test_cases) - 1; i++) { + EXPECT_SUCCESS(s2n_cert_get_x509_extension_value_length(cert, (const uint8_t *) test_cases[i].oid, &test_cases[i].ext_value_len)); + EXPECT_EQUAL(test_cases[i].ext_value_len, test_cases[i].expected_der.size); + } + + /* Test failure case for invalid X509 extension OID */ + { + size_t invalid_test_case = s2n_array_len(test_cases) - 1; + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_x509_extension_value_length(cert, (const uint8_t *) test_cases[invalid_test_case].oid, + &test_cases[i].ext_value_len), + S2N_ERR_X509_EXTENSION_VALUE_NOT_FOUND); + }; + }; + + /* Test s2n_cert_get_x509_extension_value */ + { + /* Safety checks */ + { + const uint8_t oid[] = "Example X509 extension OID"; + EXPECT_SUCCESS(s2n_alloc(&ext_value, ext_value_MAX_LEN)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_x509_extension_value(NULL, oid, + ext_value.data, &ext_value.size, &critical), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_x509_extension_value(cert, NULL, + ext_value.data, &ext_value.size, &critical), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_x509_extension_value(cert, oid, + NULL, &ext_value.size, &critical), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_x509_extension_value(cert, oid, + ext_value.data, NULL, &critical), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_x509_extension_value(cert, oid, + ext_value.data, &ext_value.size, NULL), + S2N_ERR_NULL); + + EXPECT_SUCCESS(s2n_free(&ext_value)); + }; + + /* Test success cases for s2n_cert_get_x509_extension_value */ + for (i = 0; i < s2n_array_len(test_cases) - 1; i++) { + EXPECT_SUCCESS(s2n_alloc(&test_cases[i].returned_der, test_cases[i].ext_value_len)); + EXPECT_SUCCESS(s2n_cert_get_x509_extension_value(cert, (const uint8_t *) test_cases[i].oid, + test_cases[i].returned_der.data, + &test_cases[i].returned_der.size, &critical)); + EXPECT_BYTEARRAY_EQUAL(test_cases[i].returned_der.data, test_cases[i].expected_der.data, test_cases[i].expected_der.size); + EXPECT_EQUAL(critical, test_cases[i].critical); + } + + /* Test failure case for insufficient amount of memory allocated */ + { + size_t insuf_test_case = 0; + EXPECT_SUCCESS(s2n_alloc(&ext_value, test_cases[insuf_test_case].returned_der.size - OFFSET_INSUFFICIENT_MEM_SIZE)); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_x509_extension_value(cert, (const uint8_t *) test_cases[insuf_test_case].oid, + ext_value.data, &ext_value.size, &critical), + S2N_ERR_INSUFFICIENT_MEM_SIZE); + EXPECT_SUCCESS(s2n_free(&ext_value)); + }; + + /* Test failure case for invalid X509 extension OID */ + { + size_t invalid_test_case = s2n_array_len(test_cases) - 1; + EXPECT_SUCCESS(s2n_alloc(&test_cases[invalid_test_case].returned_der, ext_value_MAX_LEN)); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_x509_extension_value(cert, (const uint8_t *) test_cases[invalid_test_case].oid, + test_cases[invalid_test_case].returned_der.data, + &test_cases[invalid_test_case].returned_der.size, &critical), + S2N_ERR_X509_EXTENSION_VALUE_NOT_FOUND); + }; + }; + + /* Test s2n_cert_get_utf8_string_from_extension_data_length */ + { + /* Safety checks */ + { + const uint8_t der_ext_value[] = "DER encoded X509 extension value"; + size_t der_ext_value_len = strlen((const char *) der_ext_value); + uint32_t utf8_len = 0; + + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_utf8_string_from_extension_data_length(NULL, der_ext_value_len, &utf8_len), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_utf8_string_from_extension_data_length(der_ext_value, 0, &utf8_len), S2N_ERR_SAFETY); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_utf8_string_from_extension_data_length(der_ext_value, der_ext_value_len, 0), S2N_ERR_NULL); + }; + + /* Test success and failure cases */ + for (i = 0; i < s2n_array_len(test_cases) - 1; i++) { + if (i > 3) { + EXPECT_SUCCESS(s2n_cert_get_utf8_string_from_extension_data_length(test_cases[i].returned_der.data, + test_cases[i].returned_der.size, + &test_cases[i].utf8_len)); + EXPECT_EQUAL(test_cases[i].utf8_len, strlen((const char *) test_cases[i].expected_utf8)); + } else { + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_utf8_string_from_extension_data_length( + test_cases[i].returned_der.data, + test_cases[i].returned_der.size, + &test_cases[i].utf8_len), + S2N_ERR_INVALID_X509_EXTENSION_TYPE); + } + } + + /* Test failure case for insufficient amount of memory allocated */ + { + size_t insuf_test_case = 4; + EXPECT_SUCCESS(s2n_alloc(&utf8_str, test_cases[insuf_test_case].returned_der.size - OFFSET_INSUFFICIENT_MEM_SIZE)); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_utf8_string_from_extension_data( + test_cases[insuf_test_case].returned_der.data, + test_cases[insuf_test_case].returned_der.size, + utf8_str.data, &utf8_str.size), + S2N_ERR_INSUFFICIENT_MEM_SIZE); + EXPECT_SUCCESS(s2n_free(&utf8_str)); + }; + }; + + /* Test s2n_cert_get_utf8_string_from_extension_data */ + { + /* Safety checks */ + { + const uint8_t der_ext_value[] = "DER encoded X509 extension value"; + size_t der_ext_value_len = strlen((const char *) der_ext_value); + EXPECT_SUCCESS(s2n_alloc(&utf8_str, ext_value_MAX_LEN)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_utf8_string_from_extension_data(NULL, der_ext_value_len, utf8_str.data, + &utf8_str.size), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_utf8_string_from_extension_data(der_ext_value, 0, utf8_str.data, + &utf8_str.size), + S2N_ERR_SAFETY); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_utf8_string_from_extension_data(der_ext_value, der_ext_value_len, NULL, + &utf8_str.size), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_utf8_string_from_extension_data(der_ext_value, der_ext_value_len, + utf8_str.data, NULL), + S2N_ERR_NULL); + + EXPECT_SUCCESS(s2n_free(&utf8_str)); + }; + + /* Test success and failure cases for s2n_cert_get_utf8_string_from_extension_data */ + for (i = 0; i < s2n_array_len(test_cases) - 1; i++) { + if (i > 3) { + EXPECT_OK(s2n_compare_utf8_strings(&test_cases[i].returned_der, test_cases[i].expected_utf8, + test_cases[i].utf8_len, strlen((const char *) test_cases[i].expected_utf8))); + } else { + EXPECT_ERROR_WITH_ERRNO(s2n_compare_utf8_strings( + &test_cases[i].returned_der, (const char *) test_cases[i].expected_der.data, ext_value_MAX_LEN, + test_cases[i].expected_der.size), + S2N_ERR_INVALID_X509_EXTENSION_TYPE); + } + } + + /* Test failure case for insufficient amount of memory allocated */ + { + size_t insuf_test_case = 4; + EXPECT_SUCCESS(s2n_alloc(&utf8_str, test_cases[insuf_test_case].returned_der.size - OFFSET_INSUFFICIENT_MEM_SIZE)); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_get_utf8_string_from_extension_data( + test_cases[insuf_test_case].returned_der.data, + test_cases[insuf_test_case].returned_der.size, + utf8_str.data, &utf8_str.size), + S2N_ERR_INSUFFICIENT_MEM_SIZE); + EXPECT_SUCCESS(s2n_free(&utf8_str)); + }; + }; + + /* Cleanup */ + for (i = 0; i < s2n_array_len(test_cases); i++) { + EXPECT_SUCCESS(s2n_free(&test_cases[i].returned_der)); + } + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(custom_cert_chain)); + }; + + /* Test s2n_connection_get_client_cert_chain */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_config_set_verify_host_callback(config, always_verify_host_fn, NULL)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, NULL)); + + DEFER_CLEANUP(struct s2n_connection *tls12_client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *tls12_server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *tls13_client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *tls13_server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + + /* Should error before the handshake is performed. + * This method is intended to be called after the handshake is complete and requires + * state set during the handshake. + */ + { + uint8_t *output = NULL; + uint32_t output_len = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_client_cert_chain(tls12_server_conn, &output, &output_len), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_client_cert_chain(tls13_server_conn, &output, &output_len), + S2N_ERR_NULL); + EXPECT_NULL(output); + EXPECT_EQUAL(output_len, 0); + }; + + /* Perform TLS1.2 handshake and verify cert chain is available */ + { + EXPECT_SUCCESS(s2n_connection_set_config(tls12_client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_config(tls12_server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(tls12_client_conn, "test_all_tls12")); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(tls12_server_conn, "test_all_tls12")); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(tls12_client_conn, tls12_server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(tls12_server_conn, tls12_client_conn)); + + EXPECT_EQUAL(tls12_server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_NOT_NULL(tls12_server_conn->handshake_params.client_cert_chain.data); + EXPECT_NOT_EQUAL(tls12_server_conn->handshake_params.client_cert_chain.size, 0); + }; + + /* Perform TLS1.3 handshake and verify cert chain is available. + * + * The TLS1.3 handshake is only possible if TLS1.3 is fully supported because of client auth: + * the server doesn't know whether the client will offer a RSA-PSS certificate or not. + */ + if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_connection_set_config(tls13_client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_config(tls13_server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(tls13_client_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(tls13_server_conn, "default_tls13")); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(tls13_client_conn, tls13_server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(tls13_server_conn, tls13_client_conn)); + + EXPECT_EQUAL(tls13_server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_NOT_NULL(tls13_server_conn->handshake_params.client_cert_chain.data); + EXPECT_NOT_EQUAL(tls13_server_conn->handshake_params.client_cert_chain.size, 0); + }; + + /* Should error if called by client */ + { + uint8_t *output = NULL; + uint32_t output_len = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_client_cert_chain(tls12_client_conn, &output, &output_len), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_client_cert_chain(tls13_client_conn, &output, &output_len), + S2N_ERR_NULL); + EXPECT_NULL(output); + EXPECT_EQUAL(output_len, 0); + }; + + /* Should produce same result for TLS1.2 and TLS1.3 + * (Both connections used the same certificate chain for the handshake) + */ + if (s2n_is_tls13_fully_supported()) { + uint8_t *tls12_output = NULL; + uint32_t tls12_output_len = 0; + EXPECT_SUCCESS(s2n_connection_get_client_cert_chain(tls12_server_conn, &tls12_output, &tls12_output_len)); + + uint8_t *tls13_output = NULL; + uint32_t tls13_output_len = 0; + EXPECT_SUCCESS(s2n_connection_get_client_cert_chain(tls13_server_conn, &tls13_output, &tls13_output_len)); + + EXPECT_EQUAL(tls12_output_len, tls13_output_len); + EXPECT_BYTEARRAY_EQUAL(tls12_output, tls13_output, tls13_output_len); + }; + + /* Test: Certificate that skips validation still available */ + { + DEFER_CLEANUP(struct s2n_config *unsafe_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(unsafe_config, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(unsafe_config, chain_and_key)); + + /* Disable certificate verification */ + EXPECT_SUCCESS(s2n_config_disable_x509_verification(unsafe_config)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, unsafe_config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, unsafe_config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + EXPECT_NOT_EQUAL(server->handshake_params.client_cert_chain.size, 0); + EXPECT_NOT_NULL(server->handshake_params.client_cert_chain.data); + }; + + /* Test: Certificate that fails validation still available */ + { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_config_set_verify_host_callback(client_config, always_verify_host_fn, NULL)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_config_set_verify_host_callback(server_config, always_verify_host_fn, NULL)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Disable client verification of the server cert. + * Do not disable server verification of the client cert, but also + * don't provide any trust store to successfully perform the verification. + */ + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server, client), + S2N_ERR_CERT_UNTRUSTED); + /* Both the client and server could produce S2N_ERR_CERT_UNTRUSTED. + * Verify that only the server encountered an error and therefore only + * the server closed the connection. + */ + EXPECT_TRUE(s2n_connection_check_io_status(server, S2N_IO_CLOSED)); + EXPECT_FALSE(s2n_connection_check_io_status(client, S2N_IO_CLOSED)); + + EXPECT_NOT_EQUAL(server->handshake_params.client_cert_chain.size, 0); + EXPECT_NOT_NULL(server->handshake_params.client_cert_chain.data); + }; + + /* Test: Certificate that fails parsing is still available in TLS1.2 */ + { + DEFER_CLEANUP(struct s2n_stuffer input, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + + /* Generate a completely malformed certificate chain, with no + * properly formed certificates. + * + * We don't parse pre-TLS1.3 chains before storing them, so don't + * care whether or not they're malformed. + */ + const uint32_t chain_size = 24; + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&input, chain_size)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&input, chain_size)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + server->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_stuffer_copy(&input, &server->handshake.io, + s2n_stuffer_data_available(&input))); + + EXPECT_FAILURE_WITH_ERRNO(s2n_client_cert_recv(server), S2N_ERR_CERT_INVALID); + EXPECT_NOT_EQUAL(server->handshake_params.client_cert_chain.size, 0); + EXPECT_NOT_NULL(server->handshake_params.client_cert_chain.data); + }; + + /* Test: Certificate that fails parsing is not available in TLS1.3 */ + { + DEFER_CLEANUP(struct s2n_stuffer input, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + + /* Since it's TLS1.3, we also need a zero-length request context */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&input, 0)); + + /* Generate a well-formed certificate chain. + * + * Unlike in TLS1.2, in TLS1.3 we must parse chains before storing + * them to remove the per-certificate TLS extension lists. Chains + * we cannot parse should not be stored. + * + * We could generate a completely invalid chain to test this case, + * like we do for TLS1.2. However, we want to ensure that a partially + * parsed chain is not partially stored. To do that, we need to + * generated a partially correct chain. + * + * We start with a completely parseable chain and then modify it + * to create a malformed chain. + */ + struct s2n_stuffer_reservation total_size = { 0 }; + const uint32_t cert_size = 24, extensions_size = 20; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint24(&input, &total_size)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&input, cert_size)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&input, cert_size)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&input, extensions_size)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&input, extensions_size)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&input, cert_size)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&input, cert_size)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&input, extensions_size)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&input, extensions_size)); + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&total_size)); + + /* Validate that the certificate chain we generated is parseable. + * The chain will still ultimately fail validation, but it will be + * parsed and stored on the connection. + */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + server->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_stuffer_copy(&input, &server->handshake.io, + s2n_stuffer_data_available(&input))); + + EXPECT_FAILURE_WITH_ERRNO(s2n_client_cert_recv(server), S2N_ERR_CERT_INVALID); + EXPECT_NOT_EQUAL(server->handshake_params.client_cert_chain.size, 0); + EXPECT_NOT_NULL(server->handshake_params.client_cert_chain.data); + } + + /* Modify the last certificate's TLS extension list to be malformed. + * + * We want the parsing error to occur as late as possible (on the last + * extension list) to test that the cert chain is not partially stored. + */ + EXPECT_SUCCESS(s2n_stuffer_reread(&input)); + EXPECT_SUCCESS(s2n_stuffer_wipe_n(&input, 1)); + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&total_size)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + server->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_stuffer_copy(&input, &server->handshake.io, + s2n_stuffer_data_available(&input))); + + /* Assert that no part of the certificate chain is stored */ + EXPECT_FAILURE_WITH_ERRNO(s2n_client_cert_recv(server), S2N_ERR_BAD_MESSAGE); + EXPECT_EQUAL(server->handshake_params.client_cert_chain.size, 0); + EXPECT_NULL(server->handshake_params.client_cert_chain.data); + }; + }; + + /* Test s2n_cert_chain_and_key_set_ocsp_data */ + { + uint8_t more_ocsp_data[] = "more ocsp data"; + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* Safety checks */ + EXPECT_FAILURE(s2n_cert_chain_and_key_set_ocsp_data(NULL, ocsp_data, sizeof(ocsp_data))); + + /* Set ocsp data */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ocsp_data(chain, ocsp_data, sizeof(ocsp_data))); + EXPECT_EQUAL(chain->ocsp_status.size, sizeof(ocsp_data)); + EXPECT_BYTEARRAY_EQUAL(chain->ocsp_status.data, ocsp_data, sizeof(ocsp_data)); + + /* Change oscp data */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ocsp_data(chain, more_ocsp_data, sizeof(more_ocsp_data))); + EXPECT_EQUAL(chain->ocsp_status.size, sizeof(more_ocsp_data)); + EXPECT_BYTEARRAY_EQUAL(chain->ocsp_status.data, more_ocsp_data, sizeof(more_ocsp_data)); + + /* Free ocsp data */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ocsp_data(chain, NULL, 0)); + EXPECT_EQUAL(chain->ocsp_status.size, 0); + + /* Set ocsp data again */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_ocsp_data(chain, ocsp_data, sizeof(ocsp_data))); + EXPECT_EQUAL(chain->ocsp_status.size, sizeof(ocsp_data)); + EXPECT_BYTEARRAY_EQUAL(chain->ocsp_status.data, ocsp_data, sizeof(ocsp_data)); + }; + + /* Test s2n_cert_chain_and_key_set_sct_list */ + { + uint8_t sct_list[] = "sct list"; + uint8_t other_sct_list[] = "other sct list"; + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* Safety checks */ + EXPECT_FAILURE(s2n_cert_chain_and_key_set_sct_list(NULL, sct_list, sizeof(sct_list))); + + /* Set sct list */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_sct_list(chain, sct_list, sizeof(sct_list))); + EXPECT_EQUAL(chain->sct_list.size, sizeof(sct_list)); + EXPECT_BYTEARRAY_EQUAL(chain->sct_list.data, sct_list, sizeof(sct_list)); + + /* Change sct list */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_sct_list(chain, other_sct_list, sizeof(other_sct_list))); + EXPECT_EQUAL(chain->sct_list.size, sizeof(other_sct_list)); + EXPECT_BYTEARRAY_EQUAL(chain->sct_list.data, other_sct_list, sizeof(other_sct_list)); + + /* Free sct list */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_sct_list(chain, NULL, 0)); + EXPECT_EQUAL(chain->sct_list.size, 0); + + /* Set sct list again */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_sct_list(chain, sct_list, sizeof(sct_list))); + EXPECT_EQUAL(chain->sct_list.size, sizeof(sct_list)); + EXPECT_BYTEARRAY_EQUAL(chain->sct_list.data, sct_list, sizeof(sct_list)); + }; + + /* Test s2n_cert_chain_and_key_load_cns */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, + S2N_DEFAULT_TEST_CERT_CHAIN, + S2N_DEFAULT_TEST_PRIVATE_KEY)); + POSIX_ENSURE_REF(chain->cert_chain); + + struct s2n_cert *head = chain->cert_chain->head; + POSIX_ENSURE_REF(head); + + struct s2n_blob *leaf_bytes = &head->raw; + const unsigned char *leaf_der = leaf_bytes->data; + EXPECT_NOT_NULL(leaf_der); + + DEFER_CLEANUP(X509 *cert = NULL, X509_free_pointer); + cert = d2i_X509(NULL, &leaf_der, leaf_bytes->size); + EXPECT_NOT_NULL(cert); + + DEFER_CLEANUP(X509_NAME *x509_name = NULL, X509_NAME_free_pointer); + x509_name = X509_NAME_new(); + EXPECT_NOT_NULL(x509_name); + + /* We start with one CN name already */ + uint32_t len = 0; + EXPECT_OK(s2n_array_num_elements(chain->cn_names, &len)); + EXPECT_EQUAL(len, 1); + + /* Try loading a zero length CN name */ + EXPECT_SUCCESS(X509_NAME_add_entry_by_NID(x509_name, NID_commonName, V_ASN1_IA5STRING, + (unsigned char *) (uintptr_t) "", -1, -1, 1)); + EXPECT_EQUAL(X509_set_subject_name(cert, x509_name), 1); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_cns(chain, cert)); + + /* No CN name has been added */ + EXPECT_OK(s2n_array_num_elements(chain->cn_names, &len)); + EXPECT_EQUAL(len, 1); + + /* Try loading an invalid CN name */ + EXPECT_SUCCESS(X509_NAME_add_entry_by_NID(x509_name, NID_commonName, 29, + (unsigned char *) (uintptr_t) "invalid", -1, -1, 1)); + EXPECT_EQUAL(X509_set_subject_name(cert, x509_name), 1); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_cns(chain, cert)); + + /* No CN name has been added */ + EXPECT_OK(s2n_array_num_elements(chain->cn_names, &len)); + EXPECT_EQUAL(len, 1); + + /* Add a valid CN name */ + EXPECT_SUCCESS(X509_NAME_add_entry_by_NID(x509_name, NID_commonName, V_ASN1_IA5STRING, + (unsigned char *) (uintptr_t) "valid", -1, -1, 1)); + EXPECT_EQUAL(X509_set_subject_name(cert, x509_name), 1); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_cns(chain, cert)); + + /* 1 more CN name has been added */ + EXPECT_OK(s2n_array_num_elements(chain->cn_names, &len)); + EXPECT_EQUAL(len, 2); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_change_cipher_spec_test.c b/tests/unit/s2n_change_cipher_spec_test.c new file mode 100644 index 00000000000..3d95e00b61e --- /dev/null +++ b/tests/unit/s2n_change_cipher_spec_test.c @@ -0,0 +1,192 @@ +/* + * 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 "s2n_test.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_crypto.h" +#include "tls/s2n_tls.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + uint8_t empty_finished_array[S2N_TLS_FINISHED_LEN] = { 0 }; + + /* Test s2n_ccs_send */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_ccs_send(conn)); + + uint8_t result; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&conn->handshake.io, &result)); + /* Always 0x01: https://tools.ietf.org/html/rfc5246#section-7.1 */ + EXPECT_EQUAL(result, 0x01); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test that s2n_basic_ccs_recv can parse the output of s2n_change_cipher_spec_send */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_ccs_send(conn)); + EXPECT_SUCCESS(s2n_basic_ccs_recv(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test that s2n_basic_ccs_recv errors on wrong change cipher spec types */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&conn->handshake.io, 0)); + EXPECT_FAILURE_WITH_ERRNO(s2n_basic_ccs_recv(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test that s2n_client_ccs_recv errors on wrong change cipher spec types */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&conn->handshake.io, 0)); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_ccs_recv(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test that s2n_server_ccs_recv errors on wrong change cipher spec types */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&conn->handshake.io, 0)); + EXPECT_FAILURE_WITH_ERRNO(s2n_server_ccs_recv(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_client_ccs_recv */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + /* Needed to not break prf */ + conn->secure->cipher_suite = &s2n_ecdhe_rsa_with_aes_256_gcm_sha384; + + /* Check preconditions */ + conn->secure->client_sequence_number[0] = 1; + EXPECT_BYTEARRAY_EQUAL(&conn->handshake.client_finished, &empty_finished_array, S2N_TLS_FINISHED_LEN); + EXPECT_EQUAL(conn->handshake.finished_len, 0); + EXPECT_NOT_EQUAL(conn->client, conn->secure); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&conn->alert_in, 1)); + + EXPECT_SUCCESS(s2n_ccs_send(conn)); + EXPECT_SUCCESS(s2n_client_ccs_recv(conn)); + + /* Check for expected updates */ + EXPECT_EQUAL(conn->secure->client_sequence_number[0], 0); + EXPECT_EQUAL(conn->client, conn->secure); + EXPECT_FALSE(s2n_stuffer_data_available(&conn->alert_in)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test SSLv3 s2n_client_ccs_recv */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->secure->cipher_suite = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; + conn->actual_protocol_version = S2N_SSLv3; + + /* Check preconditions */ + conn->secure->client_sequence_number[0] = 1; + EXPECT_BYTEARRAY_EQUAL(&conn->handshake.client_finished, &empty_finished_array, S2N_TLS_FINISHED_LEN); + EXPECT_EQUAL(conn->handshake.finished_len, 0); + EXPECT_NOT_EQUAL(conn->client, conn->secure); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&conn->alert_in, 1)); + + EXPECT_SUCCESS(s2n_ccs_send(conn)); + EXPECT_SUCCESS(s2n_client_ccs_recv(conn)); + + /* Check for expected updates */ + EXPECT_EQUAL(conn->secure->client_sequence_number[0], 0); + EXPECT_EQUAL(conn->client, conn->secure); + EXPECT_FALSE(s2n_stuffer_data_available(&conn->alert_in)); + }; + + /* Test s2n_server_ccs_recv */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + /* Needed to not break prf */ + conn->secure->cipher_suite = &s2n_ecdhe_rsa_with_aes_256_gcm_sha384; + + /* Check preconditions */ + conn->secure->server_sequence_number[0] = 1; + EXPECT_BYTEARRAY_EQUAL(&conn->handshake.server_finished, &empty_finished_array, S2N_TLS_FINISHED_LEN); + EXPECT_EQUAL(conn->handshake.finished_len, 0); + EXPECT_NOT_EQUAL(conn->server, conn->secure); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&conn->alert_in, 1)); + + EXPECT_SUCCESS(s2n_ccs_send(conn)); + EXPECT_SUCCESS(s2n_server_ccs_recv(conn)); + + /* Check for expected updates */ + EXPECT_EQUAL(conn->secure->server_sequence_number[0], 0); + EXPECT_BYTEARRAY_NOT_EQUAL(&conn->handshake.server_finished, &empty_finished_array, S2N_TLS_FINISHED_LEN); + EXPECT_EQUAL(conn->handshake.finished_len, S2N_TLS_FINISHED_LEN); + EXPECT_EQUAL(conn->server, conn->secure); + EXPECT_FALSE(s2n_stuffer_data_available(&conn->alert_in)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test SSLv3 s2n_server_ccs_recv */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->secure->cipher_suite = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; + conn->actual_protocol_version = S2N_SSLv3; + + /* Check preconditions */ + conn->secure->server_sequence_number[0] = 1; + EXPECT_BYTEARRAY_EQUAL(&conn->handshake.server_finished, &empty_finished_array, S2N_TLS_FINISHED_LEN); + EXPECT_EQUAL(conn->handshake.finished_len, 0); + EXPECT_NOT_EQUAL(conn->server, conn->secure); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&conn->alert_in, 1)); + + EXPECT_SUCCESS(s2n_ccs_send(conn)); + EXPECT_SUCCESS(s2n_server_ccs_recv(conn)); + + /* Check for expected updates */ + EXPECT_EQUAL(conn->secure->server_sequence_number[0], 0); + EXPECT_BYTEARRAY_NOT_EQUAL(&conn->handshake.server_finished, &empty_finished_array, S2N_TLS_FINISHED_LEN); + EXPECT_EQUAL(conn->handshake.finished_len, S2N_SSL_FINISHED_LEN); + EXPECT_EQUAL(conn->server, conn->secure); + EXPECT_FALSE(s2n_stuffer_data_available(&conn->alert_in)); + }; + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_choose_supported_group_test.c b/tests/unit/s2n_choose_supported_group_test.c new file mode 100644 index 00000000000..542e060aa3c --- /dev/null +++ b/tests/unit/s2n_choose_supported_group_test.c @@ -0,0 +1,326 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_client_supported_groups.h" +#include "tls/s2n_kem_preferences.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" + +/* Include the C file directly to allow testing of static functions */ +#include "tls/extensions/s2n_client_supported_groups.c" + +/* This test checks the logic in the function s2n_choose_supported_group, which should select the highest + * supported group or, if none are available, select NULL. */ +int main() +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Tests with default KEM preferences (kem_preferences_null) */ + { + /* If the lists of mutually supported groups are empty, chosen group should be set to null */ + { + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(server_conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + EXPECT_EQUAL(kem_pref, &kem_preferences_null); + + for (size_t i = 0; i < ecc_pref->count; i++) { + EXPECT_NULL(server_conn->kex_params.mutually_supported_curves[i]); + } + + for (size_t i = 0; i < kem_pref->tls13_kem_group_count; i++) { + EXPECT_NULL(server_conn->kex_params.mutually_supported_kem_groups[i]); + } + + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_params.kem); + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + + EXPECT_SUCCESS(s2n_choose_supported_group(server_conn)); + + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_params.kem); + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* If the lists of mutually supported groups have one ECC match, + * the chosen group should be set to the ECC match. */ + { + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(server_conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + EXPECT_EQUAL(kem_pref, &kem_preferences_null); + + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_params.kem); + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + + for (size_t i = 0; i < ecc_pref->count; i++) { + EXPECT_NULL(server_conn->kex_params.mutually_supported_curves[i]); + } + + for (size_t i = 0; i < kem_pref->tls13_kem_group_count; i++) { + EXPECT_NULL(server_conn->kex_params.mutually_supported_kem_groups[i]); + } + + server_conn->kex_params.mutually_supported_curves[1] = ecc_pref->ecc_curves[1]; + EXPECT_SUCCESS(s2n_choose_supported_group(server_conn)); + + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_params.kem); + EXPECT_EQUAL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve, ecc_pref->ecc_curves[1]); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* If the lists of mutually supported groups have several matches, the chosen group should be set to + * the highest supported ECC. */ + { + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(server_conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + EXPECT_EQUAL(kem_pref, &kem_preferences_null); + + for (size_t i = 0; i < ecc_pref->count; i++) { + server_conn->kex_params.mutually_supported_curves[i] = ecc_pref->ecc_curves[i]; + } + + for (size_t i = 0; i < kem_pref->tls13_kem_group_count; i++) { + EXPECT_NULL(server_conn->kex_params.mutually_supported_kem_groups[i]); + } + + EXPECT_SUCCESS(s2n_choose_supported_group(server_conn)); + + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_params.kem); + EXPECT_EQUAL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve, ecc_pref->ecc_curves[0]); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + }; + + /* Test for PQ */ + { + const struct s2n_kem_group *test_kem_groups[] = { + &s2n_secp256r1_kyber_512_r3, +#if EVP_APIS_SUPPORTED + &s2n_x25519_kyber_512_r3, +#endif +#if defined(S2N_LIBCRYPTO_SUPPORTS_KYBER) + &s2n_secp384r1_kyber_768_r3, + &s2n_secp521r1_kyber_1024_r3, +#endif + }; + + const struct s2n_kem_preferences test_kem_prefs = { + .kem_count = 0, + .kems = NULL, + .tls13_kem_group_count = s2n_array_len(test_kem_groups), + .tls13_kem_groups = test_kem_groups, + }; + + const struct s2n_security_policy test_pq_security_policy = { + .minimum_protocol_version = S2N_SSLv3, + .cipher_preferences = &cipher_preferences_test_all_tls13, + .kem_preferences = &test_kem_prefs, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20200310, + }; + + /* If the server supports PQ, but the client didn't send any PQ IDs, mutually_supported_kem_groups will + * not be populated, and the highest preference ECC should be chosen. */ + { + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->security_policy_override = &test_pq_security_policy; + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(server_conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + EXPECT_EQUAL(kem_pref, &test_kem_prefs); + + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_params.kem); + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + + for (size_t i = 0; i < ecc_pref->count; i++) { + server_conn->kex_params.mutually_supported_curves[i] = ecc_pref->ecc_curves[i]; + } + + for (size_t i = 0; i < kem_pref->tls13_kem_group_count; i++) { + EXPECT_NULL(server_conn->kex_params.mutually_supported_kem_groups[i]); + } + + EXPECT_SUCCESS(s2n_choose_supported_group(server_conn)); + + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_params.kem); + EXPECT_EQUAL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve, ecc_pref->ecc_curves[0]); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* If server has multiple mutually supported KEM groups and ECC curves, the highest preferred KEM group + * should be chosen. */ + { + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->security_policy_override = &test_pq_security_policy; + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(server_conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + EXPECT_EQUAL(kem_pref, &test_kem_prefs); + + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_params.kem); + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + + for (size_t i = 0; i < ecc_pref->count; i++) { + server_conn->kex_params.mutually_supported_curves[i] = ecc_pref->ecc_curves[i]; + } + + for (size_t i = 0; i < kem_pref->tls13_kem_group_count; i++) { + server_conn->kex_params.mutually_supported_kem_groups[i] = kem_pref->tls13_kem_groups[i]; + } + + EXPECT_SUCCESS(s2n_choose_supported_group(server_conn)); + + EXPECT_EQUAL(server_conn->kex_params.server_kem_group_params.kem_group, kem_pref->tls13_kem_groups[0]); + EXPECT_EQUAL(server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve, kem_pref->tls13_kem_groups[0]->curve); + EXPECT_EQUAL(server_conn->kex_params.server_kem_group_params.kem_params.kem, kem_pref->tls13_kem_groups[0]->kem); + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; +/* Need at least two KEM's to test fallback */ +#if (S2N_SUPPORTED_KEM_GROUPS_COUNT > 1) + /* If server has one mutually supported KEM group and multiple mutually supported ECC, the KEM + * group should be chosen. */ + { + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->security_policy_override = &test_pq_security_policy; + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(server_conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + EXPECT_EQUAL(kem_pref, &test_kem_prefs); + + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_params.kem); + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + + for (size_t i = 0; i < ecc_pref->count; i++) { + server_conn->kex_params.mutually_supported_curves[i] = ecc_pref->ecc_curves[i]; + } + + for (size_t i = 0; i < kem_pref->tls13_kem_group_count; i++) { + EXPECT_NULL(server_conn->kex_params.mutually_supported_kem_groups[i]); + } + + server_conn->kex_params.mutually_supported_kem_groups[1] = kem_pref->tls13_kem_groups[1]; + EXPECT_SUCCESS(s2n_choose_supported_group(server_conn)); + + EXPECT_EQUAL(server_conn->kex_params.server_kem_group_params.kem_group, kem_pref->tls13_kem_groups[1]); + EXPECT_EQUAL(server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve, kem_pref->tls13_kem_groups[1]->curve); + EXPECT_EQUAL(server_conn->kex_params.server_kem_group_params.kem_params.kem, kem_pref->tls13_kem_groups[1]->kem); + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + } +#endif + /* If there are no mutually supported KEM groups or ECC curves, chosen group should be set to null */ + { + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->security_policy_override = &test_pq_security_policy; + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(server_conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + EXPECT_EQUAL(kem_pref, &test_kem_prefs); + + for (size_t i = 0; i < ecc_pref->count; i++) { + EXPECT_NULL(server_conn->kex_params.mutually_supported_curves[i]); + } + + for (size_t i = 0; i < kem_pref->tls13_kem_group_count; i++) { + EXPECT_NULL(server_conn->kex_params.mutually_supported_kem_groups[i]); + } + + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_params.kem); + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + + EXPECT_SUCCESS(s2n_choose_supported_group(server_conn)); + + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_params.kem); + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + }; + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_cipher_info_test.c b/tests/unit/s2n_cipher_info_test.c new file mode 100644 index 00000000000..3fff8211f51 --- /dev/null +++ b/tests/unit/s2n_cipher_info_test.c @@ -0,0 +1,53 @@ +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + + uint8_t iana_value[2] = { 0 }; + + /* Make sure the call fails before the connection has negotiated the cipher suite */ + EXPECT_FAILURE(s2n_connection_get_cipher_iana_value(conn, &iana_value[0], &iana_value[1])); + + const struct s2n_security_policy *security_policy = conn->security_policy_override; + EXPECT_NOT_NULL(security_policy); + + const struct s2n_cipher_preferences *cipher_preferences = security_policy->cipher_preferences; + EXPECT_NOT_NULL(cipher_preferences); + + /* Verify the cipher info functions work for every cipher suite */ + for (size_t cipher_idx = 0; cipher_idx < cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_suite *expected_cipher = cipher_preferences->suites[cipher_idx]; + conn->secure->cipher_suite = expected_cipher; + + EXPECT_STRING_EQUAL(s2n_connection_get_cipher(conn), expected_cipher->name); + EXPECT_SUCCESS(s2n_connection_get_cipher_iana_value(conn, &iana_value[0], &iana_value[1])); + EXPECT_EQUAL(memcmp(expected_cipher->iana_value, iana_value, sizeof(iana_value)), 0); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + + END_TEST(); + return S2N_SUCCESS; +} diff --git a/tests/unit/s2n_cipher_suite_match_test.c b/tests/unit/s2n_cipher_suite_match_test.c new file mode 100644 index 00000000000..a2bfbef5e65 --- /dev/null +++ b/tests/unit/s2n_cipher_suite_match_test.c @@ -0,0 +1,1418 @@ +/* + * 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 "crypto/s2n_ecc_evp.h" +#include "pq-crypto/s2n_pq.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_security_policies.h" + +static s2n_result s2n_conn_set_chosen_psk(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + uint8_t psk_identity[] = "psk identity"; + RESULT_GUARD(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &conn->psk_params.chosen_psk)); + RESULT_ENSURE_REF(conn->psk_params.chosen_psk); + RESULT_GUARD(s2n_psk_init(conn->psk_params.chosen_psk, S2N_PSK_TYPE_EXTERNAL)); + RESULT_GUARD_POSIX(s2n_psk_set_identity(conn->psk_params.chosen_psk, psk_identity, sizeof(psk_identity))); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + char dhparams_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, + S2N_MAX_TEST_PEM_SIZE)); + + /* Test client cipher selection */ + { + /* Setup connections */ + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + /* Setup config */ + struct s2n_cert_chain_and_key *chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Test that the client allows the server to select ciphers that were offered in ClientHello */ + { + conn->client_protocol_version = S2N_TLS13; + conn->actual_protocol_version = S2N_TLS13; + conn->server_protocol_version = S2N_TLS13; + + /* The client will offer the default tls13 ciphersuites */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); + + /* The server will send a TLS13 cipher over the wire */ + uint8_t valid_wire_ciphers[] = { + TLS_AES_128_GCM_SHA256 + }; + + /* We expect to succeed because the cipher was offered by the client */ + EXPECT_SUCCESS(s2n_set_cipher_as_client(conn, valid_wire_ciphers)); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Test that the client rejects a cipher that was not originally offered in ClientHello */ + { + conn->client_protocol_version = S2N_TLS13; + conn->actual_protocol_version = S2N_TLS13; + conn->server_protocol_version = S2N_TLS13; + + /* The client will offer the default tls13 ciphersuites */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all_tls13")); + + /* The server will send a TLS12 cipher over the wire */ + uint8_t invalid_wire_ciphers[] = { + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + }; + + /* We expect to fail because the cipher was not offered by the client */ + EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_client(conn, invalid_wire_ciphers), S2N_ERR_CIPHER_NOT_SUPPORTED); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /** Clients MUST verify + *= https://tools.ietf.org/rfc/rfc8446#section-4.2.11 + *= type=test + *# that the server selected a cipher suite + *# indicating a Hash associated with the PSK + **/ + { + /* If chosen PSK is set, test error case for incorrect hash match */ + { + s2n_connection_set_cipher_preferences(conn, "default_tls13"); + + EXPECT_OK(s2n_conn_set_chosen_psk(conn)); + + uint8_t valid_tls13_wire_ciphers[] = { + TLS_AES_128_GCM_SHA256, + }; + + /* S2N_HMAC_SHA1 is not a matching hmac algorithm */ + conn->psk_params.chosen_psk->hmac_alg = S2N_HMAC_SHA1; + EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_client(conn, valid_tls13_wire_ciphers), + S2N_ERR_CIPHER_NOT_SUPPORTED); + EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_null_cipher_suite); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* If chosen PSK is set, test success case for matching hash algorithm */ + { + s2n_connection_set_cipher_preferences(conn, "default_tls13"); + + EXPECT_OK(s2n_conn_set_chosen_psk(conn)); + + uint8_t valid_tls13_wire_ciphers[] = { + TLS_AES_128_GCM_SHA256, + }; + + /* S2N_HMAC_SHA256 is a matching hmac algorithm for the cipher suite present in valid_tls13_wire_ciphers */ + conn->psk_params.chosen_psk->hmac_alg = S2N_HMAC_SHA256; + EXPECT_SUCCESS(s2n_set_cipher_as_client(conn, valid_tls13_wire_ciphers)); + EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_tls13_aes_128_gcm_sha256); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test server cipher selection and scsv detection */ + { + struct s2n_connection *conn; + struct s2n_config *server_config; + char *rsa_cert_chain_pem, *rsa_private_key_pem, *ecdsa_cert_chain_pem, *ecdsa_private_key_pem; + struct s2n_cert_chain_and_key *rsa_cert, *ecdsa_cert; + /* Allocate all of the objects and PEMs we'll need for this test. */ + EXPECT_NOT_NULL(rsa_cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(rsa_private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(ecdsa_cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(ecdsa_private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(rsa_cert = s2n_cert_chain_and_key_new()); + EXPECT_NOT_NULL(ecdsa_cert = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, rsa_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, rsa_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, ecdsa_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, ecdsa_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(rsa_cert, rsa_cert_chain_pem, rsa_private_key_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(ecdsa_cert, ecdsa_cert_chain_pem, ecdsa_private_key_pem)); + + uint8_t wire_ciphers[] = { + TLS_RSA_WITH_RC4_128_MD5, + TLS_RSA_WITH_RC4_128_SHA, + TLS_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA256, + TLS_RSA_WITH_AES_256_CBC_SHA256, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + TLS_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_KYBER_RSA_WITH_AES_256_GCM_SHA384, + }; + const uint8_t cipher_count = sizeof(wire_ciphers) / S2N_TLS_CIPHER_SUITE_LEN; + + uint8_t wire_ciphers_fallback[] = { + TLS_RSA_WITH_RC4_128_MD5, + TLS_RSA_WITH_RC4_128_SHA, + TLS_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA256, + TLS_RSA_WITH_AES_256_CBC_SHA256, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + TLS_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_FALLBACK_SCSV, /* At the end to verify it isn't missed */ + }; + const uint8_t cipher_count_fallback = sizeof(wire_ciphers_fallback) / S2N_TLS_CIPHER_SUITE_LEN; + + uint8_t wire_ciphers_renegotiation[] = { + TLS_RSA_WITH_RC4_128_MD5, + TLS_RSA_WITH_RC4_128_SHA, + TLS_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA, + TLS_RSA_WITH_AES_256_CBC_SHA, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA, + TLS_RSA_WITH_AES_128_CBC_SHA256, + TLS_RSA_WITH_AES_256_CBC_SHA256, + TLS_DHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_DHE_RSA_WITH_AES_256_CBC_SHA256, + TLS_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_EMPTY_RENEGOTIATION_INFO_SCSV, /* At the end to verify it isn't missed */ + }; + const uint8_t cipher_count_renegotiation = sizeof(wire_ciphers_renegotiation) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Only two ciphers for testing RSA vs ECDSA. */ + uint8_t wire_ciphers_with_ecdsa[] = { + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, + }; + const uint8_t cipher_count_ecdsa = sizeof(wire_ciphers_with_ecdsa) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Only ECDSA ciphers */ + uint8_t wire_ciphers_only_ecdsa[] = { + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + }; + const uint8_t cipher_count_only_ecdsa = sizeof(wire_ciphers_only_ecdsa) / S2N_TLS_CIPHER_SUITE_LEN; + + uint8_t wire_ciphers_rsa_fallback[] = { + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA, + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + TLS_RSA_WITH_AES_256_GCM_SHA384, + }; + const uint8_t cipher_count_rsa_fallback = sizeof(wire_ciphers_rsa_fallback) / S2N_TLS_CIPHER_SUITE_LEN; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_cert)); + /* Security policy must allow all test cipher suites */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + + /* TEST RSA */ + conn->actual_protocol_version = S2N_TLS10; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers, cipher_count)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(1, s2n_connection_is_valid_for_cipher_preferences(conn, "test_all")); + EXPECT_EQUAL(0, s2n_connection_is_valid_for_cipher_preferences(conn, "null")); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + /* TEST RENEGOTIATION + * + *= https://tools.ietf.org/rfc/rfc5746#3.6 + *= type=test + *# o When a ClientHello is received, the server MUST check if it + *# includes the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If it does, + *# set the secure_renegotiation flag to TRUE. + */ + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_renegotiation, cipher_count_renegotiation)); + EXPECT_EQUAL(conn->secure_renegotiation, 1); + EXPECT_EQUAL(1, s2n_connection_is_valid_for_cipher_preferences(conn, "test_all")); + EXPECT_EQUAL(-1, s2n_connection_is_valid_for_cipher_preferences(conn, "not_exist")); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + /* Simulate a TLSv11 client to trigger the fallback error */ + conn->actual_protocol_version = S2N_TLS11; + EXPECT_FAILURE(s2n_set_cipher_as_tls_server(conn, wire_ciphers_fallback, cipher_count_fallback)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(1, s2n_connection_is_valid_for_cipher_preferences(conn, "null")); + EXPECT_EQUAL(0, s2n_connection_is_valid_for_cipher_preferences(conn, "CloudFront-TLS-1-2-2018")); + EXPECT_EQUAL(0, s2n_connection_is_valid_for_cipher_preferences(conn, "CloudFront-TLS-1-2-2019")); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + /* TEST RSA cipher chosen when ECDSA cipher is at top */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + /* Assume default for negotiated curve. */ + /* Shouldn't be necessary unless the test fails, but we want the failure to be obvious. */ + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + const struct s2n_cipher_suite *expected_rsa_wire_choice = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_rsa_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + /* Test that PQ cipher suites are marked available/unavailable appropriately in s2n_cipher_suites_init() */ + { + const struct s2n_cipher_suite *pq_suites[] = { + &s2n_ecdhe_kyber_rsa_with_aes_256_gcm_sha384, + }; + + for (size_t i = 0; i < s2n_array_len(pq_suites); i++) { + if (s2n_pq_is_enabled()) { + EXPECT_EQUAL(pq_suites[i]->available, 1); + EXPECT_NOT_NULL(pq_suites[i]->record_alg); + } else { + EXPECT_EQUAL(pq_suites[i]->available, 0); + EXPECT_NULL(pq_suites[i]->record_alg); + } + } + }; + + /* Test that clients that support PQ ciphers can negotiate them. */ + { + uint8_t client_extensions_data[] = { + 0xFE, 0x01, /* PQ KEM extension ID */ + 0x00, 0x04, /* Total extension length in bytes */ + 0x00, 0x02, /* Length of the supported parameters list in bytes */ + 0x00, TLS_PQ_KEM_EXTENSION_ID_KYBER_512_R3 /* Kyber-512-Round3*/ + }; + int client_extensions_len = sizeof(client_extensions_data); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "PQ-TLS-1-0-2021-05-24")); + conn->actual_protocol_version = S2N_TLS12; + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->kex_params.client_pq_kem_extension.data = client_extensions_data; + conn->kex_params.client_pq_kem_extension.size = client_extensions_len; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers, cipher_count)); + const struct s2n_cipher_suite *kyber_cipher = &s2n_ecdhe_kyber_rsa_with_aes_256_gcm_sha384; + const struct s2n_cipher_suite *ecc_cipher = &s2n_ecdhe_rsa_with_aes_256_gcm_sha384; + if (s2n_pq_is_enabled()) { + EXPECT_EQUAL(conn->secure->cipher_suite, kyber_cipher); + } else { + EXPECT_EQUAL(conn->secure->cipher_suite, ecc_cipher); + } + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + /* Test cipher preferences that use PQ cipher suites that require TLS 1.2 fall back to classic ciphers if a client + * only supports TLS 1.1 or below, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA is the first cipher suite that supports + * TLS 1.1 in KMS-PQ-TLS-1-0-2019-06 */ + for (int i = S2N_TLS10; i <= S2N_TLS11; i++) { + const struct s2n_cipher_suite *expected_classic_wire_choice = &s2n_ecdhe_rsa_with_aes_256_cbc_sha; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "KMS-PQ-TLS-1-0-2019-06")); + conn->actual_protocol_version = i; + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->kex_params.client_pq_kem_extension.data = client_extensions_data; + conn->kex_params.client_pq_kem_extension.size = client_extensions_len; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers, cipher_count)); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_classic_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + } + }; + + /* Clean+free to setup for ECDSA tests */ + EXPECT_SUCCESS(s2n_config_free(server_config)); + + /* Set ECDSA CERT in s2n_config */ + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + /* TEST ECDSA */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all_ecdsa")); + const struct s2n_cipher_suite *expected_ecdsa_wire_choice = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256; + /* Assume default for negotiated curve. */ + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_ecdsa_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + /* TEST ECDSA cipher chosen when RSA cipher is at top */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + /* Assume default for negotiated curve. */ + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_ecdsa_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + + /* TEST two certificates. Use two certs with different key types(RSA, ECDSA) and add them to a single + * s2n_config. + */ + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_cert)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert)); + + /* Client sends RSA and ECDSA ciphers, server prioritizes ECDSA, ECDSA + RSA cert is configured */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Client sends RSA and ECDSA ciphers, server prioritizes RSA, ECDSA + RSA cert is configured */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Client sends both RSA and ECDSA ciphers, server only configures RSA ciphers, + * ECDSA + RSA cert is configured. + */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; + /* 20170328 only supports RSA ciphers */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "20170328")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Client sends both RSA and ECDSA ciphers, server only configures ECDSA ciphers, ECDSA + RSA cert is + * configured. + */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all_ecdsa")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Client only sends RSA ciphers, server prioritizes ECDSA ciphers, ECDSA + RSA cert is + * configured. + */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_rsa_with_rc4_128_md5; + if (!expected_wire_choice->available) { + expected_wire_choice = &s2n_rsa_with_3des_ede_cbc_sha; + } + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers, cipher_count)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Client only sends ECDSA ciphers, server prioritizes ECDSA ciphers, ECDSA + RSA cert is + * configured. + */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_ecdsa_with_aes_256_cbc_sha; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_only_ecdsa, cipher_count_only_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Client sends ECDHE-ECDSA, RSA, ECDHE-RSA ciphers. Server prioritizes ECDSA but also supports RSA. + * No mutually supported elliptic curves between client and server. ECDSA + RSA cert is configured. + */ + { + /* If there are no shared elliptic curves, we must fall through to a cipher that supports RSA kx. + * This is the first RSA kx cipher that CloudFront-Upstream supports. + */ + const struct s2n_cipher_suite *expected_wire_choice = &s2n_rsa_with_aes_256_gcm_sha384; + /* Selecting this preference list because it prioritizes ECDHE-ECDSA and ECDHE-RSA over plain RSA kx. */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "CloudFront-Upstream")); + /* No shared curve */ + conn->kex_params.server_ecc_evp_params.negotiated_curve = NULL; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_rsa_fallback, cipher_count_rsa_fallback)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + EXPECT_SUCCESS(s2n_config_free(server_config)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_cert)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert)); + /* Override auto-chosen defaults with only RSA cert default. ECDSA still loaded, but not default. */ + EXPECT_SUCCESS(s2n_config_set_cert_chain_and_key_defaults(server_config, &rsa_cert, 1)); + + /* Client sends RSA and ECDSA ciphers, server prioritizes ECDSA, ECDSA + RSA cert is configured, + * only RSA is default. Expect default RSA used instead of previous test that expects ECDSA for this case. */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Override auto-chosen defaults with only ECDSA cert default. RSA still loaded, but not default. */ + EXPECT_SUCCESS(s2n_config_set_cert_chain_and_key_defaults(server_config, &ecdsa_cert, 1)); + + /* Client sends RSA and ECDSA ciphers, server prioritizes RSA, ECDSA + RSA cert is configured, + * only ECDSA is default. Expect default ECDSA used instead of previous test that expects RSA for this case. */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Test override back to both RSA and ECDSA defaults. */ + struct s2n_cert_chain_and_key *certs_list[] = { rsa_cert, ecdsa_cert }; + EXPECT_SUCCESS(s2n_config_set_cert_chain_and_key_defaults(server_config, certs_list, 2)); + + /* Client sends RSA and ECDSA ciphers, server prioritizes ECDSA, ECDSA + RSA cert is configured */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_ecdsa_priority")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Test that defaults are not overriden after failures to set new default certificates */ + EXPECT_FAILURE_WITH_ERRNO_NO_RESET(s2n_config_set_cert_chain_and_key_defaults(server_config, NULL, 0), S2N_ERR_NULL); + EXPECT_EQUAL(strcmp(s2n_strerror_name(s2n_errno), "S2N_ERR_NULL"), 0); + EXPECT_FAILURE_WITH_ERRNO_NO_RESET(s2n_config_set_cert_chain_and_key_defaults(server_config, &rsa_cert, 0), + S2N_ERR_NUM_DEFAULT_CERTIFICATES); + EXPECT_EQUAL(strcmp(s2n_strerror_name(s2n_errno), "S2N_ERR_NUM_DEFAULT_CERTIFICATES"), 0); + struct s2n_cert_chain_and_key *rsa_certs_list[] = { rsa_cert, rsa_cert }; + EXPECT_FAILURE_WITH_ERRNO_NO_RESET(s2n_config_set_cert_chain_and_key_defaults(server_config, rsa_certs_list, 2), + S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE); + EXPECT_EQUAL(strcmp(s2n_strerror_name(s2n_errno), "S2N_ERR_MULTIPLE_DEFAULT_CERTIFICATES_PER_AUTH_TYPE"), 0); + + /* Client sends RSA and ECDSA ciphers, server prioritizes RSA, ECDSA + RSA cert is configured. + * RSA default certificate should be chosen. */ + { + const struct s2n_cipher_suite *expected_wire_choice = &s2n_ecdhe_rsa_with_aes_128_cbc_sha; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->actual_protocol_version = conn->server_protocol_version; + EXPECT_SUCCESS(s2n_connection_set_config(conn, server_config)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_ecdsa, cipher_count_ecdsa)); + EXPECT_EQUAL(conn->secure_renegotiation, 0); + EXPECT_EQUAL(conn->secure->cipher_suite, expected_wire_choice); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + struct s2n_cipher_suite *tls12_cipher_suite = cipher_preferences_20170210.suites[cipher_preferences_20170210.count - 1]; + uint8_t wire_ciphers_with_tls13[] = { + TLS_AES_128_GCM_SHA256, + TLS_AES_256_GCM_SHA384, + TLS_CHACHA20_POLY1305_SHA256, + tls12_cipher_suite->iana_value[0], tls12_cipher_suite->iana_value[1] + }; + const uint8_t cipher_count_tls13 = sizeof(wire_ciphers_with_tls13) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Client sends TLS1.3 cipher suites, but server does not support TLS1.3 */ + { + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->client_protocol_version = S2N_TLS13; + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_tls13, cipher_count_tls13)); + EXPECT_EQUAL(conn->secure->cipher_suite, tls12_cipher_suite); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Client sends TLS1.3 cipher suites, server selects correct TLS1.3 ciphersuite */ + if (s2n_is_tls13_fully_supported()) { + struct test_case { + const char *cipher_pref; + const struct s2n_cipher_suite *expected_cipher_wire; + }; + + struct test_case test_cases[] = { + { .cipher_pref = "default_tls13", .expected_cipher_wire = &s2n_tls13_aes_128_gcm_sha256 }, + { .cipher_pref = "test_all", .expected_cipher_wire = &s2n_tls13_aes_128_gcm_sha256 }, + { .cipher_pref = "test_all_tls13", .expected_cipher_wire = &s2n_tls13_aes_128_gcm_sha256 }, + }; + + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, test_cases[i].cipher_pref)); + conn->client_protocol_version = S2N_TLS13; + conn->actual_protocol_version = S2N_TLS13; + conn->server_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_tls13, cipher_count_tls13)); + EXPECT_EQUAL(conn->secure->cipher_suite, test_cases[i].expected_cipher_wire); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + } + } + + /* Check wire's cipher suites with preferred tls12 ordering does not affect tls13 selection */ + { + uint8_t wire_ciphers2[] = { + TLS_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA, + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* tls 1.2 */ + TLS_CHACHA20_POLY1305_SHA256, /* tls 1.3 */ + }; + + const uint8_t count = sizeof(wire_ciphers2) / S2N_TLS_CIPHER_SUITE_LEN; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->client_protocol_version = S2N_TLS13; + conn->actual_protocol_version = S2N_TLS13; + conn->server_protocol_version = S2N_TLS13; + + if (s2n_chacha20_poly1305.is_available()) { + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers2, count)); + EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_tls13_chacha20_poly1305_sha256); + } else { + EXPECT_FAILURE(s2n_set_cipher_as_tls_server(conn, wire_ciphers2, count)); + } + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Test cipher suite with a required version higher than what connection supports should not be selected */ + { + uint8_t test_wire_ciphers[] = { + TLS_AES_128_GCM_SHA256, /* tls 1.3 */ + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* tls 1.2 */ + }; + + const uint8_t count = sizeof(test_wire_ciphers) / S2N_TLS_CIPHER_SUITE_LEN; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->actual_protocol_version = S2N_TLS12; + conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, count)); + EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_128_gcm_sha256); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* We should skip cipher suites with a minimum protocol version unsupported by the connection. + * If no valid cipher suite is found, we should fall back to a cipher suite with a higher protocol version, + * but we should NEVER use a TLS1.3 suite on a pre-TLS1.3 connection or vice versa. */ + { + /* Skip but fall back to cipher suite with protocol version higher than connection */ + { + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + + uint8_t test_wire_ciphers[] = { + TLS_AES_128_GCM_SHA256, /* tls 1.3 */ + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* tls 1.2 */ + TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA, /* ssl v3 */ + }; + + conn->actual_protocol_version = S2N_TLS10; + + /* If a match exists, skip the invalid cipher and choose it */ + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 3)); + EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_128_cbc_sha); + + /* If a match does not exist, choose the invalid cipher */ + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 2)); + EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_128_gcm_sha256); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* Skip and do NOT fall back to a TLS1.3 cipher suite if using TLS1.2 */ + { + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + + uint8_t test_wire_ciphers[] = { + TLS_AES_128_GCM_SHA256, /* tls 1.3 */ + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* tls 1.2 */ + }; + + conn->actual_protocol_version = S2N_TLS12; + + /* If a match exists, skip the invalid cipher and choose it */ + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 2)); + EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_128_gcm_sha256); + + /* If a match does not exist, fail to negotiate a cipher suite. + * We cannot fall back to the TLS1.3 choice. */ + EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 1), + S2N_ERR_CIPHER_NOT_SUPPORTED); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + } + + /* Skip and do NOT fall back to a TLS1.2 cipher suite if using TLS1.3 */ + { + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + + uint8_t test_wire_ciphers[] = { + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, /* tls 1.2 */ + TLS_AES_128_GCM_SHA256, /* tls 1.3 */ + }; + + conn->actual_protocol_version = S2N_TLS13; + + /* If a match exists, skip the invalid cipher and choose it */ + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 2)); + EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_tls13_aes_128_gcm_sha256); + + /* If a match does not exist, fail to negotiate a cipher suite. + * We cannot fall back to the TLS1.2 choice. */ + EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_tls_server(conn, test_wire_ciphers, 1), + S2N_ERR_CIPHER_NOT_SUPPORTED); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + } + }; + + /* If a PSK is being used, then the cipher suite must match the PSK's HMAC algorithm. + * + *= https://tools.ietf.org/rfc/rfc8446#section-4.2.11 + *= type=test + *# The server MUST ensure that it selects a compatible PSK + *# (if any) and cipher suite. + **/ + { + /* If chosen PSK is set, a cipher suite with matching HMAC algorithm must be selected */ + { + s2n_connection_set_cipher_preferences(conn, "test_all"); + conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + conn->actual_protocol_version = S2N_TLS13; + + EXPECT_OK(s2n_conn_set_chosen_psk(conn)); + + conn->psk_params.chosen_psk->hmac_alg = S2N_HMAC_SHA256; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_tls13, cipher_count_tls13)); + EXPECT_EQUAL(conn->secure->cipher_suite->prf_alg, conn->psk_params.chosen_psk->hmac_alg); + + conn->psk_params.chosen_psk->hmac_alg = S2N_HMAC_SHA384; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_tls13, cipher_count_tls13)); + EXPECT_EQUAL(conn->secure->cipher_suite->prf_alg, conn->psk_params.chosen_psk->hmac_alg); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + + /* If chosen PSK is set but there is no matching cipher, the server MUST fail to set a cipher */ + { + s2n_connection_set_cipher_preferences(conn, "test_all"); + conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + conn->actual_protocol_version = S2N_TLS13; + + /* S2N_HMAC_SHA1 is not a matching HMAC algorithm for any TLS1.3 cipher */ + EXPECT_OK(s2n_conn_set_chosen_psk(conn)); + conn->psk_params.chosen_psk->hmac_alg = S2N_HMAC_SHA1; + + EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_tls13, cipher_count_tls13), + S2N_ERR_CIPHER_NOT_SUPPORTED); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + }; + }; + + /* Client sends cipher which is not in the configured suite */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + uint8_t invalid_cipher_pref[] = { + TLS_ECDHE_KYBER_RSA_WITH_AES_256_GCM_SHA384 + }; + + const uint8_t invalid_cipher_count = sizeof(invalid_cipher_pref) / S2N_TLS_CIPHER_SUITE_LEN; + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); + conn->client_protocol_version = S2N_TLS13; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_tls_server(conn, invalid_cipher_pref, invalid_cipher_count), S2N_ERR_CIPHER_NOT_SUPPORTED); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* Client sends cipher that requires DH params */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, rsa_cert)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + /* The client only offers one cipher suite, which requires dh kex */ + uint8_t wire[] = { TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 }; + + /* By default, the server does not accept cipher suites with dh kex. */ + EXPECT_FAILURE_WITH_ERRNO(s2n_set_cipher_as_tls_server(server, wire, 1), + S2N_ERR_CIPHER_NOT_SUPPORTED); + + /* With dh params configured, the server accepts cipher suites with dh kex. */ + EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(server, wire, 1)); + EXPECT_EQUAL(server->secure->cipher_suite, &s2n_dhe_rsa_with_aes_128_gcm_sha256); + }; + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(rsa_cert)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert)); + free(ecdsa_cert_chain_pem); + free(ecdsa_private_key_pem); + free(rsa_cert_chain_pem); + free(rsa_private_key_pem); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test chacha20 boosting behaviour */ + { + /* Setup cipher preferences + security policy */ + struct s2n_cipher_preferences cipher_preferences = { 0 }; + struct s2n_security_policy security_policy = { + .minimum_protocol_version = S2N_SSLv2, + .cipher_preferences = &cipher_preferences, + .kem_preferences = &kem_preferences_null, + .signature_preferences = &s2n_signature_preferences_20201021, + .ecc_preferences = &s2n_ecc_preferences_test_all, + }; + + /* Initialise config and relevant certs */ + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + struct s2n_cert_chain_and_key *rsa_cert = NULL; + struct s2n_cert_chain_and_key *ecdsa_cert = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&rsa_cert, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_cert, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + EXPECT_NOT_NULL(rsa_cert); + EXPECT_NOT_NULL(ecdsa_cert); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, rsa_cert)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); + + if (s2n_chacha20_poly1305.is_available()) { + /* Test chacha20 boosting when ciphersuites fail auth validation */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); + DEFER_CLEANUP(struct s2n_config *rsa_only_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(rsa_only_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(rsa_only_config, rsa_cert)); + /* Connection only supports rsa auth. */ + EXPECT_SUCCESS(s2n_connection_set_config(connection, rsa_only_config)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Not negotiated because invalid (ecdsa) */ + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + /* Only negotiated if chacha20 boosting is disabled */ + &s2n_ecdhe_rsa_with_aes_256_gcm_sha384, + /* First valid chacha20 cipher suite and is negotiated */ + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting; valid chacha20 ciphersuite and is negotiated */ + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Not negotiated because invalid (ecdsa) */ + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + /* Negotiated if chacha20 boosting is off */ + TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384, + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify chacha20 RSA ciphersuite chosen with chacha20 boosting enabled */ + cipher_preferences.allow_chacha20_boosting = true; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256); + + /* Sanity check: non-chacha20 RSA ciphersuite chosen without chacha20 boosting enabled */ + /* cppcheck-suppress redundantAssignment */ + cipher_preferences.allow_chacha20_boosting = false; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_256_gcm_sha384); + }; + + /* Server is able to negotiate its most preferred chacha20 ciphersuite */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); + EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Skipped because it is not a chacha20 ciphersuite. Is negotiated if chacha20 boosting is disabled. */ + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + /* First chacha20 ciphersuite and is negotiated */ + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + /* Second chacha20 ciphersuite and is not negotiated (not server's most preferred chacha20 ciphersuite) */ + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting: not negotiated because it's not server's most preferred chacha20 ciphersuite */ + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Negotiated if chacha20 boosting is on */ + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + /* Negotiated if chacha20 boosting is off */ + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify most preferred chacha20 ciphersuite is chosen with chacha20 boosting enabled */ + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256); + + /* Sanity check: server's most preferred cipehrsuite is chosen when chacha20 boosting is off */ + cipher_preferences.allow_chacha20_boosting = false; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_256_cbc_sha384); + }; + + /* Server's most preferred chacha20 is not offered by the client */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); + EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Server's most preferred chacha20 ciphersuite; not offered by the client */ + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + /* Negotiated if chacha20 boosting is off */ + &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256, + /* First valid chacha20 ciphersuite; negotiated if chacha20 boosting is on */ + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting; negotiated if chacha20 boosting is on */ + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Negotiated if chacha20 boosting is off */ + TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + /* Verify server selects its second most preferred chacha20 ciphersuite */ + cipher_preferences.allow_chacha20_boosting = true; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256); + + /* Sanity check: server's most preferred ciphersuite is chosen without chacha20 boosting enabled */ + /* cppcheck-suppress redundantAssignment */ + cipher_preferences.allow_chacha20_boosting = false; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256); + }; + + /* Server does not negotiate the client's most preferred chacha20 ciphersuite */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); + EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Skipped because not a chacha20 ciphersuite. If chacha20 boosting is off then this is negotiated.*/ + &s2n_ecdhe_rsa_with_aes_256_cbc_sha384, + /* First chacha20 ciphersuite and is negotiated (client's second preferred ciphersuite) */ + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + /* Second chacha20 ciphersuite and is the client's most preferred ciphersuite. Not negotiated. */ + &s2n_dhe_rsa_with_chacha20_poly1305_sha256, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting. This is not negotiated as it's not the server's most preferred chacha20 ciphersuite */ + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Negotiated if chacha20 boosting is on */ + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Negotiated if chacha20 boosting is off */ + TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384, + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify client's second preferred (ecdhe_rsa_chacha20) is negotiated when chacha20 boosting enabled */ + cipher_preferences.allow_chacha20_boosting = true; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256); + + /* Sanity check: server's most preferred ciphersuite is chosen without chacha20 boosting enabled */ + /* cppcheck-suppress redundantAssignment */ + cipher_preferences.allow_chacha20_boosting = false; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_aes_256_cbc_sha384); + }; + + /* Chacha20 boosting is disabled when client did not indicate chacha20 preference */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); + + EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Negotiated because client did not signal chacha20 boosting and it is present in client wire */ + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + /* Never considered; if client did signal chacha20 boosting we expect this to be negotiated */ + &s2n_ecdhe_ecdsa_with_chacha20_poly1305_sha256, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + uint8_t wire[] = { + /* Chacha20 boosting is off. This ciphersuite is negotiated. */ + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + /* Not negotiated */ + TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256, + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify that client negotiates non-chacha20 ciphersuite when chacha20 boosting is not signalled by client */ + cipher_preferences.allow_chacha20_boosting = true; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256); + }; + + /* Server negotiates its most preferred chacha20 ciphersuite for tls 1.3 */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS13)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Most preferred ciphersuite. If chacha20 boosting behaviour is on then this can't be negotiated. */ + &s2n_tls13_aes_128_gcm_sha256, + /* Second preferred ciphersuite. If chacha20 boosting behaviour is on then this can't be negotiated. */ + &s2n_tls13_aes_256_gcm_sha384, + /* Negotiated if chacha20 boosting behaviour is on. Otherwise, one of the two ciphersuites above is choosen. */ + &s2n_tls13_chacha20_poly1305_sha256, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + uint8_t test_wire_1[] = { + /* Client signalled chacha20 boosting. Negotiated. */ + TLS_CHACHA20_POLY1305_SHA256, + /* Not negotiated even when chacha20 boosting is off. Server prefers aes 128 gcm. */ + TLS_AES_256_GCM_SHA384, + /* Negotiated if chacha20 boosting is off. This is the server's most preferred ciphersuite. */ + TLS_AES_128_GCM_SHA256, + }; + uint8_t count = sizeof(test_wire_1) / S2N_TLS_CIPHER_SUITE_LEN; + + cipher_preferences.allow_chacha20_boosting = true; + /* Verify that client negotiates chacha20 when chacha20 boosting is on */ + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, test_wire_1, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_tls13_chacha20_poly1305_sha256); + + /* Sanity check: server's most preferred ciphersuite is chosen without chacha20 boosting enabled */ + /* cppcheck-suppress redundantAssignment */ + cipher_preferences.allow_chacha20_boosting = false; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, test_wire_1, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_tls13_aes_128_gcm_sha256); + + /* + * Client did not signal chacha20 boosting. + * TLS_AES_256_GCM_SHA384 > TLS_CHACHA20_POLY1305_SHA256 + */ + uint8_t test_wire_2[] = { + /* Client did not signal chacha20 boosting. Negotiated if chacha20 boosting is off. */ + TLS_AES_256_GCM_SHA384, + /* Not negotiated since the server prefers aes 256 gcm over chacha20 */ + TLS_CHACHA20_POLY1305_SHA256, + }; + count = sizeof(test_wire_2) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify that client negotiates server preferred non-chacha20 aes_256 when client did not signal */ + /* cppcheck-suppress redundantAssignment */ + cipher_preferences.allow_chacha20_boosting = true; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, test_wire_2, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_tls13_aes_256_gcm_sha384); + }; + + /* Server can negotiate chacha20 ciphersuite, even when boosting is disabled */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS13)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Negotiated (only available option) */ + &s2n_tls13_chacha20_poly1305_sha256, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = false, + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting. Negotiated as this is the only negotiable option. */ + TLS_CHACHA20_POLY1305_SHA256, + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify server can still negotiate chacha20 if it's the only option even when chacha20 boosting is off */ + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_tls13_chacha20_poly1305_sha256); + }; + + /* + * sslv2 server correctly negotiates ciphersuite when chacha20 boosting is enabled. + */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_SSLv2)); + EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* First 'valid' ciphersuite that is saved as a higher_vers_match and is negotiated */ + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + /* Not valid for connection (does not meet minimum version requirement and higher_vers_match already found) */ + &s2n_dhe_rsa_with_chacha20_poly1305_sha256, + /* Not valid for connection (does not meet minimum version requirement and higher_vers_match already found) */ + &s2n_dhe_rsa_with_aes_256_gcm_sha384 + }; + + /* cppcheck-suppress redundantAssignment */ + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting. Not negotiated as it's not offered by the server. */ + 0x00, + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Not saved as a higher_vers_match as ecdhe_ecdsa_aes_128 was already saved as one; not negotiated */ + 0x00, + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Saved by server as higher_vers_match; negotiated as a 'fall-back' ciphersuite */ + 0x00, + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA + }; + uint8_t count = sizeof(wire) / S2N_SSLv2_CIPHER_SUITE_LEN; + + /* Verify server negotiate its higher_vers_match ecdhe_ecdsa_aes_128_cbc instead of a chacha20 ciphersuite */ + cipher_preferences.allow_chacha20_boosting = true; + /* + * For an sslv2 connection, all of the ciphersuites in test_cipher_suite_list will not meet the minimum + * required tls version validation (they all require at least sslv3). This means that the server will save this + * cipher as a higher_vers_match and will use this ciphersuite as a fallback if no other ciphersuite can be identified. + * When this logic happens, we bypass chacha20 boosting altogether; therefore because the first ciphersuite + * ecdhe_ecdsa_with_aes_128_cbc is offered by both the client and server and only fails the minimum required version check, + * then we save this ciphersuite as a higher_vers_match and continue. + */ + EXPECT_SUCCESS(s2n_set_cipher_as_sslv2_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha); + }; + + /* Server is able to negotiate a ciphersuite even without chacha20 in its cipher pref */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS13)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Negotiated because it is the server's most preferred */ + &s2n_tls13_aes_128_gcm_sha256, + /* Not considered */ + &s2n_tls13_aes_256_gcm_sha384, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting; not negotiated as server does not have any chacha20 ciphersuites */ + TLS_CHACHA20_POLY1305_SHA256, + /* Not negotiated as the server prefers aes 128 over aes 256 */ + TLS_AES_256_GCM_SHA384, + /* Negotiated */ + TLS_AES_128_GCM_SHA256, + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify that server is able to negotiate aes_128 even without any chacha20 ciphersuites in its preferences */ + cipher_preferences.allow_chacha20_boosting = false; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_tls13_aes_128_gcm_sha256); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* Test chacha20 boosting when the most preferred ciphersuite fails version validation */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); + + EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Only negotiated if chacha20 boosting is off */ + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + /* Invalid; never use tls 1.3 ciphers on pre-tls 1.3 connections */ + &s2n_tls13_chacha20_poly1305_sha256, + /* Negotiated (if chacha20 boosting is on) */ + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting. Not negotiated by server since it is invalid for the connection. */ + TLS_CHACHA20_POLY1305_SHA256, + /* Negotiated if chacha20 boosting is off */ + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + /* Negotiated if chacha20 boosting is on */ + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify server negotiates second preferred chacha20 ciphersuite ecdhe_rsa_chacha20 */ + cipher_preferences.allow_chacha20_boosting = true; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256); + + /* Sanity check: server's most preferred ciphersuite is chosen without chacha20 boosting enabled */ + /* cppcheck-suppress redundantAssignment */ + cipher_preferences.allow_chacha20_boosting = false; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256); + }; + } + + if (!s2n_chacha20_poly1305.is_available()) { + /* Chacha20 can't be negotiated when it's not available in libcrypto */ + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + connection->security_policy_override = &security_policy; + connection->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(connection, S2N_TLS12)); + EXPECT_SUCCESS(s2n_connection_set_config(connection, server_config)); + + static struct s2n_cipher_suite *test_cipher_suite_list[] = { + /* Invalid (no libcrypto) */ + &s2n_dhe_rsa_with_chacha20_poly1305_sha256, + /* Negotiated (only valid option) */ + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256, + /* Not considered + invalid (no libcrypto) */ + &s2n_ecdhe_rsa_with_chacha20_poly1305_sha256, + }; + + cipher_preferences = (struct s2n_cipher_preferences){ + .count = s2n_array_len(test_cipher_suite_list), + .suites = test_cipher_suite_list, + .allow_chacha20_boosting = true, + }; + + uint8_t wire[] = { + /* Client signalled chacha20 boosting. Not negotiated because of missing libcrypto for chacha20 */ + TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Not negotiated because of missing libcrypto for chacha20 */ + TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256, + /* Negotiated whether chacha20 boosting is enabled or not by server */ + TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256, + }; + uint8_t count = sizeof(wire) / S2N_TLS_CIPHER_SUITE_LEN; + + /* Verify that server negotiated non-chacha20 ciphersuite ecdhe_ecdsa_aes_128 */ + cipher_preferences.allow_chacha20_boosting = true; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(connection, wire, count)); + EXPECT_EQUAL(connection->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha256); + } + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(rsa_cert)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_cipher_suites_test.c b/tests/unit/s2n_cipher_suites_test.c new file mode 100644 index 00000000000..0731fbf855b --- /dev/null +++ b/tests/unit/s2n_cipher_suites_test.c @@ -0,0 +1,149 @@ +/* + * 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 "tls/s2n_cipher_suites.h" + +#include "s2n_test.h" + +int main() +{ + BEGIN_TEST(); + + /* Test: s2n_all_cipher_suites */ + { + /* Test: S2N_CIPHER_SUITE_COUNT matches the number of cipher suites in s2n_all_cipher_suites */ + { + EXPECT_EQUAL(cipher_preferences_test_all.count, S2N_CIPHER_SUITE_COUNT); + }; + + /* Test: all cipher suites in s2n_all_cipher_suites are in IANA order */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + const uint8_t cipher_suite_count = cipher_preferences_test_all.count; + for (size_t i = 0; i < cipher_suite_count - 1; i++) { + int cipher_suite_order = memcmp(cipher_preferences_test_all.suites[i]->iana_value, + cipher_preferences_test_all.suites[i + 1]->iana_value, 2); + EXPECT_TRUE(cipher_suite_order < 0); + } + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: all possible cipher suites are in s2n_all_cipher_suites */ + { + const struct s2n_security_policy *security_policy = NULL; + const struct s2n_cipher_preferences *cipher_preferences = NULL; + for (size_t policy_index = 0; security_policy_selection[policy_index].version != NULL; policy_index++) { + security_policy = security_policy_selection[policy_index].security_policy; + cipher_preferences = security_policy->cipher_preferences; + for (size_t cipher_index = 0; cipher_index < cipher_preferences->count; cipher_index++) { + /* The null cipher suite is just a placeholder, and is not included */ + if (cipher_preferences->suites[cipher_index] == &s2n_null_cipher_suite) { + continue; + } + + const struct s2n_cipher_suite *match = NULL; + for (size_t all_index = 0; all_index < cipher_preferences_test_all.count; all_index++) { + if (memcmp(cipher_preferences->suites[cipher_index]->iana_value, + cipher_preferences_test_all.suites[all_index]->iana_value, + S2N_TLS_CIPHER_SUITE_LEN) + == 0) { + EXPECT_NULL(match); + match = cipher_preferences_test_all.suites[all_index]; + } + } + EXPECT_NOT_NULL(match); + EXPECT_EQUAL(match, cipher_preferences->suites[cipher_index]); + } + } + }; + }; + + /* Test s2n_cipher_suite_from_iana */ + { + /* Safety */ + { + uint8_t iana[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; + struct s2n_cipher_suite *cipher_suite = NULL; + EXPECT_ERROR_WITH_ERRNO(s2n_cipher_suite_from_iana(NULL, sizeof(iana), &cipher_suite), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_cipher_suite_from_iana(iana, sizeof(iana), NULL), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_cipher_suite_from_iana(iana, sizeof(iana) - 1, &cipher_suite), S2N_ERR_SAFETY); + EXPECT_ERROR_WITH_ERRNO(s2n_cipher_suite_from_iana(iana, sizeof(iana) + 1, &cipher_suite), S2N_ERR_SAFETY); + }; + + /* Known values */ + { + uint8_t null_iana[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; + struct s2n_cipher_suite *cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + EXPECT_ERROR_WITH_ERRNO(s2n_cipher_suite_from_iana(null_iana, sizeof(null_iana), &cipher_suite), + S2N_ERR_CIPHER_NOT_SUPPORTED); + EXPECT_EQUAL(cipher_suite, NULL); + + uint8_t tls12_iana[] = { TLS_RSA_WITH_AES_128_CBC_SHA }; + cipher_suite = NULL; + EXPECT_OK(s2n_cipher_suite_from_iana(tls12_iana, sizeof(tls12_iana), &cipher_suite)); + EXPECT_EQUAL(cipher_suite, &s2n_rsa_with_aes_128_cbc_sha); + + cipher_suite = NULL; + EXPECT_OK(s2n_cipher_suite_from_iana(s2n_tls13_aes_256_gcm_sha384.iana_value, + sizeof(s2n_tls13_aes_256_gcm_sha384.iana_value), &cipher_suite)); + EXPECT_EQUAL(cipher_suite, &s2n_tls13_aes_256_gcm_sha384); + }; + + /* Conversion is correct for all supported cipher suites */ + { + struct s2n_cipher_suite *actual_cipher_suite = NULL; + struct s2n_cipher_suite *expected_cipher_suite = NULL; + for (size_t i = 0; i < cipher_preferences_test_all.count; i++) { + expected_cipher_suite = cipher_preferences_test_all.suites[i]; + actual_cipher_suite = NULL; + + EXPECT_OK(s2n_cipher_suite_from_iana(expected_cipher_suite->iana_value, + sizeof(expected_cipher_suite->iana_value), &actual_cipher_suite)); + EXPECT_EQUAL(expected_cipher_suite, actual_cipher_suite); + } + }; + + /* Conversion is correct for all possible iana values */ + { + size_t supported_i = 0; + struct s2n_cipher_suite *actual_cipher_suite = NULL; + uint8_t iana_value[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; + for (size_t i0 = 0; i0 <= UINT8_MAX; i0++) { + iana_value[0] = i0; + for (size_t i1 = 0; i1 <= UINT8_MAX; i1++) { + iana_value[1] = i1; + + s2n_result r = s2n_cipher_suite_from_iana(iana_value, sizeof(iana_value), &actual_cipher_suite); + + bool is_supported = supported_i < cipher_preferences_test_all.count + && memcmp(iana_value, cipher_preferences_test_all.suites[supported_i]->iana_value, S2N_TLS_CIPHER_SUITE_LEN) == 0; + if (is_supported) { + EXPECT_OK(r); + EXPECT_EQUAL(actual_cipher_suite, cipher_preferences_test_all.suites[supported_i]); + supported_i++; + } else { + EXPECT_ERROR_WITH_ERRNO(r, S2N_ERR_CIPHER_NOT_SUPPORTED); + EXPECT_NULL(actual_cipher_suite); + } + } + } + EXPECT_EQUAL(supported_i, cipher_preferences_test_all.count); + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_cleanup_test.c b/tests/unit/s2n_cleanup_test.c new file mode 100644 index 00000000000..c4893610761 --- /dev/null +++ b/tests/unit/s2n_cleanup_test.c @@ -0,0 +1,97 @@ +/* + * 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 "s2n_test.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +#define S2N_UNUSED(x) \ + do { \ + (void) x; \ + } while (0) + +struct foo { + int x; + int y; +}; + +int foo_cleanup_calls; + +void foo_free(struct foo* p) +{ + foo_cleanup_calls++; +} + +DEFINE_POINTER_CLEANUP_FUNC(struct foo*, foo_free); + +int check_cleanup_obj_on_fn_exit() +{ + DEFER_CLEANUP(struct foo x = { 0 }, foo_free); + S2N_UNUSED(x); + return 0; +} + +int check_cleanup_pointer_on_fn_exit() +{ + struct foo thefoo = { 0 }; + DEFER_CLEANUP(struct foo* foop = &thefoo, foo_free_pointer); + S2N_UNUSED(thefoo); + S2N_UNUSED(foop); + return 0; +} + +/* check that our macros don't cleanup null objects */ +int check_dont_cleanup_null_on_fn_exit() +{ + DEFER_CLEANUP(struct foo* foop = NULL, foo_free_pointer); + S2N_UNUSED(foop); + return 0; +} + +/* This test checks that the compiler correctly implements deferred cleanup */ +int main() +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + int expected_cleanup_count = 0; + + /* check that the cleanup functions are called on each loop exit */ + for (int i = 0; i < 10; ++i) { + DEFER_CLEANUP(struct foo x = { i }, foo_free); + S2N_UNUSED(x); + EXPECT_EQUAL(foo_cleanup_calls, expected_cleanup_count); + expected_cleanup_count++; + } + + EXPECT_EQUAL(foo_cleanup_calls, expected_cleanup_count); + + EXPECT_SUCCESS(check_cleanup_obj_on_fn_exit()); + expected_cleanup_count++; + EXPECT_EQUAL(foo_cleanup_calls, expected_cleanup_count); + + EXPECT_SUCCESS(check_cleanup_pointer_on_fn_exit()); + expected_cleanup_count++; + EXPECT_EQUAL(foo_cleanup_calls, expected_cleanup_count); + + EXPECT_SUCCESS(check_dont_cleanup_null_on_fn_exit()); + /* don't increment expected_cleanup_count */ + EXPECT_EQUAL(foo_cleanup_calls, expected_cleanup_count); + + END_TEST(); +} diff --git a/tests/unit/s2n_cleanup_with_no_init_test.c b/tests/unit/s2n_cleanup_with_no_init_test.c new file mode 100644 index 00000000000..9ef2eb2db51 --- /dev/null +++ b/tests/unit/s2n_cleanup_with_no_init_test.c @@ -0,0 +1,50 @@ +/* + * 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 "s2n_test.h" + +static int my_global; + +static void my_destructor(void *arg) {} + +/* Checks that calling s2n_cleanup without s2n_init does not corrupt + * thread-local storage. */ + +int main(int argc, char **argv) +{ + BEGIN_TEST_NO_INIT(); + + pthread_key_t my_key; + + /* Init the pthread key */ + EXPECT_SUCCESS(pthread_key_create(&my_key, my_destructor)); + + /* Set it a value */ + EXPECT_SUCCESS(pthread_setspecific(my_key, &my_global)); + + /* Call s2n_cleanup with no init */ + EXPECT_SUCCESS(s2n_cleanup()); + + /* Check that the key is not corrupted */ + int *new_value = (int *) pthread_getspecific(my_key); + EXPECT_EQUAL(new_value, &my_global); + + /* Delete the key */ + EXPECT_SUCCESS(pthread_key_delete(my_key)); + + END_TEST_NO_INIT(); +} diff --git a/tests/unit/s2n_client_alpn_extension_test.c b/tests/unit/s2n_client_alpn_extension_test.c new file mode 100644 index 00000000000..0cb2cf0f9f8 --- /dev/null +++ b/tests/unit/s2n_client_alpn_extension_test.c @@ -0,0 +1,126 @@ +/* + * 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 "s2n_test.h" +#include "tls/extensions/s2n_client_alpn.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + const char *protocols[] = { "protocol1", "protocol2", "protocol3" }; + const uint8_t protocols_count = s2n_array_len(protocols); + + /* Test should_send */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_protocol_preferences(conn, NULL, 0)); + EXPECT_FALSE(s2n_client_alpn_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_set_protocol_preferences(conn, protocols, protocols_count)); + EXPECT_TRUE(s2n_client_alpn_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test send */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_protocol_preferences(conn, protocols, protocols_count)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_client_alpn_extension.send(conn, &stuffer)); + + /* Should have correct size */ + uint16_t actual_size; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &actual_size)); + EXPECT_EQUAL(actual_size, s2n_stuffer_data_available(&stuffer)); + EXPECT_EQUAL(actual_size, conn->application_protocols_overridden.size); + + /* Should have correct data */ + uint8_t actual_data[256]; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, actual_data, actual_size)); + EXPECT_BYTEARRAY_EQUAL(actual_data, conn->application_protocols_overridden.data, actual_size); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test receive can accept the output of send */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_protocol_preferences(conn, protocols, protocols_count)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_client_alpn_extension.send(conn, &stuffer)); + + EXPECT_NULL(s2n_get_application_protocol(conn)); + EXPECT_SUCCESS(s2n_client_alpn_extension.recv(conn, &stuffer)); + EXPECT_NOT_NULL(s2n_get_application_protocol(conn)); + EXPECT_STRING_EQUAL(s2n_get_application_protocol(conn), protocols[0]); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + /* Test receive does nothing if no protocol preferences configured */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_client_alpn_extension.send(conn, &stuffer)); + + EXPECT_NULL(s2n_get_application_protocol(conn)); + EXPECT_SUCCESS(s2n_client_alpn_extension.recv(conn, &stuffer)); + EXPECT_NULL(s2n_get_application_protocol(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + /* Test receive does nothing if extension malformed */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_client_alpn_extension.send(conn, &stuffer)); + EXPECT_SUCCESS(s2n_stuffer_wipe_n(&stuffer, 1)); + + EXPECT_NULL(s2n_get_application_protocol(conn)); + EXPECT_SUCCESS(s2n_client_alpn_extension.recv(conn, &stuffer)); + EXPECT_NULL(s2n_get_application_protocol(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_client_auth_handshake_test.c b/tests/unit/s2n_client_auth_handshake_test.c new file mode 100644 index 00000000000..1de7415d772 --- /dev/null +++ b/tests/unit/s2n_client_auth_handshake_test.c @@ -0,0 +1,362 @@ +/* + * 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 +#include +#include + +#include "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_preferences.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +/* To get access to the static functions / variables we need to test */ +#include "tls/s2n_handshake_io.c" +#include "tls/s2n_tls13_handshake.c" + +int s2n_test_client_auth_negotiation(struct s2n_config *server_config, struct s2n_config *client_config, struct s2n_cert_chain_and_key *ecdsa_cert, bool no_cert) +{ + /* Set up client and server connections */ + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->x509_validator.skip_cert_validation = 1; + client_conn->server_protocol_version = S2N_TLS13; + client_conn->client_protocol_version = S2N_TLS13; + client_conn->actual_protocol_version = S2N_TLS13; + client_conn->handshake_params.server_cert_sig_scheme = &s2n_ecdsa_secp256r1_sha256; + client_conn->handshake_params.client_cert_sig_scheme = &s2n_ecdsa_secp256r1_sha256; + client_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + if (!no_cert) { + client_conn->handshake_params.our_chain_and_key = ecdsa_cert; + } + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + server_conn->server_protocol_version = S2N_TLS13; + server_conn->client_protocol_version = S2N_TLS13; + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->handshake_params.server_cert_sig_scheme = &s2n_ecdsa_secp256r1_sha256; + server_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + if (no_cert) { + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_OPTIONAL)); + } else { + server_conn->x509_validator.skip_cert_validation = 1; + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); + } + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_TRUE(IS_CLIENT_AUTH_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_CLIENT_AUTH_HANDSHAKE(client_conn)); + EXPECT_EQUAL(IS_CLIENT_AUTH_NO_CERT(server_conn), no_cert); + EXPECT_EQUAL(IS_CLIENT_AUTH_NO_CERT(client_conn), no_cert); + + const char *app_data_str = "APPLICATION_DATA"; + EXPECT_EQUAL(strcmp(app_data_str, s2n_connection_get_last_message_name(client_conn)), 0); + + /* Clean up */ + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + + return 0; +} + +/* Test to verify the explicit ordering of client_auth handshake with and without a client + * certificate. This includes some pre and post condition checks that pertain to client + * authentication between messages. + */ +int s2n_test_client_auth_message_by_message(bool no_cert) +{ + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + + struct s2n_config *server_config, *client_config; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20190801")); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20190801")); + + char *cert_chain = NULL; + char *private_key = NULL; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + + struct s2n_cert_chain_and_key *default_cert; + EXPECT_NOT_NULL(default_cert = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(default_cert, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, default_cert)); + if (!no_cert) { + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, default_cert)); + } + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + 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_config(client_conn, client_config)); + if (no_cert) { + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_OPTIONAL)); + } else { + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); + client_conn->x509_validator.skip_cert_validation = 1; + server_conn->x509_validator.skip_cert_validation = 1; + } + + struct s2n_stuffer client_to_server = { 0 }; + struct s2n_stuffer server_to_client = { 0 }; + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); + + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + + /* Client sends ClientHello */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, 0); + + EXPECT_EQUAL(server_conn->handshake.handshake_type, INITIAL); + + /* Server reads ClientHello */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); /* Server is now on TLS13 */ + EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT); + + EXPECT_SUCCESS(s2n_conn_set_handshake_type(server_conn)); + + /* Server sends ServerHello */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_HELLO); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Server sends CCS */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CHANGE_CIPHER_SPEC); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Server sends EncryptedExtensions */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), ENCRYPTED_EXTENSIONS); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Server sends CertificateRequest */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CERT_REQ); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Server sends ServerCert */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CERT); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Server sends CertVerify */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CERT_VERIFY); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Server sends ServerFinished */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_FINISHED); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Client reads ServerHello */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client reads CCS + * The CCS message does not affect its place in the state machine. */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), ENCRYPTED_EXTENSIONS); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client reads EncryptedExtensions */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), ENCRYPTED_EXTENSIONS); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + if (no_cert) { + /* Client reads CertificateRequest but expects Cert */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT); + } else { + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT_REQ); + } + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + EXPECT_EQUAL(client_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT); + + /* Client reads ServerCert */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client reads CertVerify */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT_VERIFY); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client reads ServerFinished */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_FINISHED); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client sends CCS */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_CHANGE_CIPHER_SPEC); + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + + /* Client sends ClientCert */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_CERT); + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + + if (no_cert) { + EXPECT_EQUAL(client_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT); + } else { + EXPECT_EQUAL(client_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT); + + /* Client sends CertVerify */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_CERT_VERIFY); + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + } + + /* Client sends ClientFinished */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_FINISHED); + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + + /* Server reads CCS + * The CCS message does not affect its place in the state machine. */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_CERT); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + + /* Server reads ClientCert */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_CERT); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + + if (no_cert) { + EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | MIDDLEBOX_COMPAT); + } else { + EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT); + + /* Server reads CertVerify */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_CERT_VERIFY); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + } + + /* Server reads ClientFinished */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_FINISHED); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); + + /* Clean up */ + EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); + EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(default_cert)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + + free(private_key); + free(cert_chain); + + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* client_auth handshake negotiation */ + { + struct s2n_config *server_config, *client_config; + uint8_t *cert_chain_pem = NULL; + uint8_t *private_key_pem = NULL; + uint32_t cert_chain_len = 0; + uint32_t private_key_len = 0; + struct s2n_cert_chain_and_key *ecdsa_cert; + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20190801")); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20190801")); + + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, cert_chain_pem, &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_KEY, private_key_pem, &private_key_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(ecdsa_cert = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem_bytes(ecdsa_cert, cert_chain_pem, cert_chain_len, private_key_pem, private_key_len)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert)); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_ECDSA_P384_PKCS1_CERT_CHAIN, NULL)); + + /* client_auth with no cert */ + EXPECT_SUCCESS(s2n_test_client_auth_negotiation(server_config, client_config, ecdsa_cert, 1)); + + /* client_auth with cert */ + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, ecdsa_cert)); + EXPECT_SUCCESS(s2n_test_client_auth_negotiation(server_config, client_config, ecdsa_cert, 0)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + free(cert_chain_pem); + free(private_key_pem); + }; + + /* Test each message is sent and in the correct order */ + { + /* Test messsage by message with no cert */ + s2n_test_client_auth_message_by_message(1); + + /* Test message by message with a cert */ + s2n_test_client_auth_message_by_message(0); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_client_cert_request_context_test.c b/tests/unit/s2n_client_cert_request_context_test.c new file mode 100644 index 00000000000..cccfca9a542 --- /dev/null +++ b/tests/unit/s2n_client_cert_request_context_test.c @@ -0,0 +1,114 @@ +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + uint8_t empty_cert_len = 3; + uint8_t certificate_context_len = 1; + + /* Test certificate_request_context sent/recv only when TLS 1.3 enabled */ + { + struct s2n_config *client_config; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + struct s2n_connection *client_conn; + 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_client_auth_type(client_conn, S2N_CERT_AUTH_OPTIONAL)); + + /* Without TLS 1.3 enabled, there is no context length */ + EXPECT_SUCCESS(s2n_client_cert_send(client_conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(&client_conn->handshake.io), empty_cert_len); + EXPECT_SUCCESS(s2n_client_cert_recv(client_conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_EQUAL(s2n_stuffer_data_available(&client_conn->handshake.io), 0); + + /* With TLS 1.3 enabled, there is a context length */ + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + client_conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_client_cert_send(client_conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(&client_conn->handshake.io), empty_cert_len + certificate_context_len); + EXPECT_SUCCESS(s2n_client_cert_recv(client_conn)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* Test certificate_request_context is zero-length as currently + * only used for handshake authentication */ + { + struct s2n_config *client_config; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + struct s2n_connection *client_conn; + 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_client_auth_type(client_conn, S2N_CERT_AUTH_OPTIONAL)); + client_conn->actual_protocol_version = S2N_TLS13; + + EXPECT_SUCCESS(s2n_client_cert_send(client_conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(&client_conn->handshake.io), empty_cert_len + certificate_context_len); + + uint8_t expected_certificate_request_context_len = 0; + uint8_t actual_certificate_request_context_len; + + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&client_conn->handshake.io, &actual_certificate_request_context_len)); + EXPECT_EQUAL(expected_certificate_request_context_len, actual_certificate_request_context_len); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Test failure case of non-zero certificate_request_context */ + { + struct s2n_config *server_config; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + + struct s2n_connection *server_conn; + 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_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); + server_conn->actual_protocol_version = S2N_TLS13; + + /* write non-zero certificate_request_context + empty cert */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&server_conn->handshake.io, 2)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&server_conn->handshake.io, 0)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_client_cert_recv(server_conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + END_TEST(); +} diff --git a/tests/unit/s2n_client_cert_status_request_extension_test.c b/tests/unit/s2n_client_cert_status_request_extension_test.c new file mode 100644 index 00000000000..508a1c080fb --- /dev/null +++ b/tests/unit/s2n_client_cert_status_request_extension_test.c @@ -0,0 +1,145 @@ +/* + * 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 "s2n_test.h" +#include "tls/extensions/s2n_client_cert_status_request.h" +#include "tls/s2n_resume.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* status request should not be sent by default */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_FALSE(s2n_client_cert_status_request_extension.should_send(conn)); + } + + /* status request should be sent if OCSP stapling is requested */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + config->ocsp_status_requested_by_user = true; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_TRUE(s2n_client_cert_status_request_extension.should_send(conn)); + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + /* Enable status requests */ + config->ocsp_status_requested_by_user = true; + + /* Test send */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_client_cert_status_request_extension.send(conn, &stuffer)); + + uint8_t request_type; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&stuffer, &request_type)); + EXPECT_EQUAL(request_type, S2N_STATUS_REQUEST_OCSP); + + uint32_t unused_values; + EXPECT_SUCCESS(s2n_stuffer_read_uint32(&stuffer, &unused_values)); + EXPECT_EQUAL(unused_values, 0); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test recv */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_client_cert_status_request_extension.send(conn, &stuffer)); + + EXPECT_EQUAL(conn->status_type, S2N_STATUS_REQUEST_NONE); + EXPECT_SUCCESS(s2n_client_cert_status_request_extension.recv(conn, &stuffer)); + EXPECT_EQUAL(conn->status_type, S2N_STATUS_REQUEST_OCSP); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test recv - malformed length, ignore */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_client_cert_status_request_extension.send(conn, &stuffer)); + EXPECT_SUCCESS(s2n_stuffer_wipe_n(&stuffer, sizeof(uint16_t))); + + EXPECT_EQUAL(conn->status_type, S2N_STATUS_REQUEST_NONE); + EXPECT_SUCCESS(s2n_client_cert_status_request_extension.recv(conn, &stuffer)); + EXPECT_EQUAL(conn->status_type, S2N_STATUS_REQUEST_NONE); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test recv - not ocsp request, ignore */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_client_cert_status_request_extension.send(conn, &stuffer)); + + /* Set the status_type extension field to an unknown value */ + int length = s2n_stuffer_data_available(&stuffer); + EXPECT_SUCCESS(s2n_stuffer_rewrite(&stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, UINT8_MAX)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&stuffer, length - 1)); + + /* Ensure the extension isn't ignored due to malformed length */ + EXPECT_TRUE(s2n_stuffer_data_available(&stuffer) >= 5); + + EXPECT_EQUAL(conn->status_type, S2N_STATUS_REQUEST_NONE); + EXPECT_SUCCESS(s2n_client_cert_status_request_extension.recv(conn, &stuffer)); + EXPECT_EQUAL(conn->status_type, S2N_STATUS_REQUEST_NONE); + }; + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_client_cert_verify_test.c b/tests/unit/s2n_client_cert_verify_test.c new file mode 100644 index 00000000000..be643702642 --- /dev/null +++ b/tests/unit/s2n_client_cert_verify_test.c @@ -0,0 +1,322 @@ +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_async_pkey.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_result.h" +#include "utils/s2n_safety.h" + +static bool async_callback_invoked = false; +static bool async_sign_operation_called_s2n_client = false; +static struct s2n_async_pkey_op *pkey_op = NULL; +static struct s2n_connection *pkey_conn = NULL; + +const uint8_t test_signature_data[] = "I signed this"; +const uint32_t test_signature_size = sizeof(test_signature_data); +const uint32_t test_max_signature_size = 2 * sizeof(test_signature_data); + +typedef int(async_handler)(struct s2n_connection *conn, s2n_blocked_status *block); + +static S2N_RESULT test_size(const struct s2n_pkey *pkey, uint32_t *size_out) +{ + *size_out = test_max_signature_size; + return S2N_RESULT_OK; +} + +static int test_sign(const struct s2n_pkey *priv_key, s2n_signature_algorithm sig_alg, + struct s2n_hash_state *digest, struct s2n_blob *signature) +{ + POSIX_CHECKED_MEMCPY(signature->data, test_signature_data, test_signature_size); + signature->size = test_signature_size; + return S2N_SUCCESS; +} + +int s2n_async_pkey_store_op(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + EXPECT_NOT_NULL(conn); + EXPECT_NOT_NULL(op); + + async_callback_invoked = true; + pkey_op = op; + pkey_conn = conn; + + return S2N_SUCCESS; +} + +static int s2n_test_negotiate_with_async_pkey_op_handler(struct s2n_connection *conn, s2n_blocked_status *block) +{ + int rc = s2n_negotiate(conn, block); + if (!(rc == 0 || (*block && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED))) { + return S2N_FAILURE; + } + + if (*block == S2N_BLOCKED_ON_APPLICATION_INPUT && pkey_op != NULL) { + struct s2n_cert_chain_and_key *chain_and_key = s2n_connection_get_selected_cert(pkey_conn); + EXPECT_NOT_NULL(chain_and_key); + + s2n_cert_private_key *pkey = s2n_cert_chain_and_key_get_private_key(chain_and_key); + EXPECT_NOT_NULL(pkey); + + EXPECT_SUCCESS(s2n_async_pkey_op_perform(pkey_op, pkey)); + EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, conn)); + EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); + pkey_op = NULL; + pkey_conn = NULL; + } + + return S2N_SUCCESS; +} + +static int s2n_test_apply_with_invalid_signature_handler(struct s2n_connection *conn, s2n_blocked_status *block) +{ + int rc = s2n_negotiate(conn, block); + if (!(rc == 0 || (*block && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED))) { + return S2N_FAILURE; + } + + if (*block == S2N_BLOCKED_ON_APPLICATION_INPUT && pkey_op != NULL) { + struct s2n_cert_chain_and_key *chain_and_key = s2n_connection_get_selected_cert(pkey_conn); + EXPECT_NOT_NULL(chain_and_key); + + s2n_cert_private_key *pkey = s2n_cert_chain_and_key_get_private_key(chain_and_key); + EXPECT_NOT_NULL(pkey); + + EXPECT_SUCCESS(s2n_async_pkey_op_perform(pkey_op, pkey)); + + /* Get type for pkey_op */ + s2n_async_pkey_op_type type = { 0 }; + EXPECT_SUCCESS(s2n_async_pkey_op_get_op_type(pkey_op, &type)); + + /* Test if signature with wrong private key fails at verification when apply */ + if (type == S2N_ASYNC_SIGN) { + /* Create new chain and key, and modify current server conn */ + struct s2n_cert_chain_and_key *chain_and_key_2 = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key_2, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + /* Change server conn cert data */ + EXPECT_NOT_NULL(conn->handshake_params.our_chain_and_key); + conn->handshake_params.our_chain_and_key = chain_and_key_2; + + /* Test that async sign operation will fail as signature was performed over different private key */ + EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_apply(pkey_op, conn), S2N_ERR_VERIFY_SIGNATURE); + + /* Set pkey_op's validation mode to S2N_ASYNC_PKEY_VALIDATION_FAST and test that async sign apply will pass now */ + EXPECT_SUCCESS(s2n_async_pkey_op_set_validation_mode(pkey_op, S2N_ASYNC_PKEY_VALIDATION_FAST)); + EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, conn)); + + /* Set chain_and_key back to original value and free new chain_and_key */ + conn->handshake_params.our_chain_and_key = chain_and_key; + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key_2)); + + /* Set flag to test if sign operation called for S2N_CLIENT */ + if (conn->mode == S2N_CLIENT) { + async_sign_operation_called_s2n_client = true; + } + } else { + EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, conn)); + } + + EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); + pkey_op = NULL; + pkey_conn = NULL; + } + + return S2N_SUCCESS; +} + +static int s2n_try_handshake_with_async_pkey_op(struct s2n_connection *server_conn, struct s2n_connection *client_conn, + async_handler handler) +{ + s2n_blocked_status server_blocked = { 0 }; + s2n_blocked_status client_blocked = { 0 }; + + do { + EXPECT_SUCCESS(handler(client_conn, &client_blocked)); + EXPECT_SUCCESS(handler(server_conn, &server_blocked)); + } while (client_blocked || server_blocked); + + POSIX_GUARD(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Test that the signature size is written correctly when not equal to the maximum */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + /* Set any signature scheme. Our test pkey methods ignore it. */ + conn->handshake_params.client_cert_sig_scheme = &s2n_rsa_pkcs1_md5_sha1; + + struct s2n_cert_chain_and_key *chain_and_key; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + chain_and_key->private_key->size = test_size; + chain_and_key->private_key->sign = test_sign; + conn->handshake_params.our_chain_and_key = chain_and_key; + + EXPECT_SUCCESS(s2n_client_cert_verify_send(conn)); + + uint16_t signature_scheme_iana; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&conn->handshake.io, &signature_scheme_iana)); + EXPECT_EQUAL(signature_scheme_iana, s2n_rsa_pkcs1_md5_sha1.iana_value); + + uint16_t signature_size; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&conn->handshake.io, &signature_size)); + EXPECT_NOT_EQUAL(signature_size, test_max_signature_size); + EXPECT_EQUAL(signature_size, test_signature_size); + EXPECT_EQUAL(signature_size, s2n_stuffer_data_available(&conn->handshake.io)); + + uint8_t *signature_data = s2n_stuffer_raw_read(&conn->handshake.io, test_signature_size); + EXPECT_NOT_NULL(signature_data); + EXPECT_BYTEARRAY_EQUAL(signature_data, test_signature_data, test_signature_size); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + }; + + /* Test: async private key operations. */ + { + struct s2n_cert_chain_and_key *chain_and_key; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + struct s2n_config *client_config; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(client_config, s2n_async_pkey_store_op)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + /* This cipher preference is set to avoid TLS 1.3. */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20170210")); + /* Enable signature validation to test S2N_CLIENT connection signature for async sign operation */ + EXPECT_SUCCESS(s2n_config_set_async_pkey_validation_mode(client_config, S2N_ASYNC_PKEY_VALIDATION_STRICT)); + + /* Create connection */ + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); + + struct s2n_config *server_config; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(server_config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_try_handshake_with_async_pkey_op(server_conn, client_conn, + s2n_test_negotiate_with_async_pkey_op_handler)); + + /* Make sure async callback was used during the handshake. */ + EXPECT_TRUE(async_callback_invoked); + + /* Verify that both connections negotiated Mutual Auth */ + EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); + EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + + /* Free the data */ + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + }; + + /* Test: Apply with invalid signature */ + { + struct s2n_cert_chain_and_key *chain_and_key; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + struct s2n_config *client_config; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(client_config, s2n_async_pkey_store_op)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20170210")); + /* Enable signature validation to test S2N_CLIENT connection signature for async sign operation */ + EXPECT_SUCCESS(s2n_config_set_async_pkey_validation_mode(client_config, S2N_ASYNC_PKEY_VALIDATION_STRICT)); + + /* Create connection */ + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); + + struct s2n_config *server_config; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(server_config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_try_handshake_with_async_pkey_op(server_conn, client_conn, + s2n_test_apply_with_invalid_signature_handler)); + + /* Make sure async callback was used during the handshake. */ + EXPECT_TRUE(async_callback_invoked); + + /* Make sure async sign operation was called at least once for S2N_CLIENT */ + EXPECT_TRUE(async_sign_operation_called_s2n_client); + + /* Verify that both connections negotiated Mutual Auth */ + EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); + EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + + /* Free the data */ + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_client_early_data_indication_test.c b/tests/unit/s2n_client_early_data_indication_test.c new file mode 100644 index 00000000000..e25a458141c --- /dev/null +++ b/tests/unit/s2n_client_early_data_indication_test.c @@ -0,0 +1,511 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_client_psk.h" +#include "tls/extensions/s2n_early_data_indication.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" + +static S2N_RESULT s2n_set_early_data_app_protocol(struct s2n_connection *conn, struct s2n_blob *app_protocol) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(app_protocol); + + struct s2n_psk *psk = NULL; + RESULT_GUARD(s2n_array_get(&conn->psk_params.psk_list, 0, (void **) &psk)); + RESULT_GUARD_POSIX(s2n_psk_set_application_protocol(psk, app_protocol->data, app_protocol->size)); + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + /* Test s2n_client_early_data_indication_should_send */ + { + /* Safety check */ + EXPECT_FALSE(s2n_client_early_data_indication_extension.should_send(NULL)); + + const uint32_t nonzero_max_early_data = 10; + + /* All checks pass: send extension */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); + EXPECT_OK(s2n_append_test_psk_with_early_data(conn, nonzero_max_early_data, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(conn)); + + EXPECT_TRUE(s2n_client_early_data_indication_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Don't send if early data not supported */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); + EXPECT_OK(s2n_append_test_psk_with_early_data(conn, nonzero_max_early_data, &s2n_tls13_aes_256_gcm_sha384)); + + EXPECT_FALSE(s2n_client_early_data_indication_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(conn)); + EXPECT_TRUE(s2n_client_early_data_indication_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /** Don't send if no PSK extension is sent. + * + *= https://tools.ietf.org/rfc/rfc8446#section-4.2.10 + *= type=test + *# When a PSK is used and early data is allowed for that PSK, the client + *# can send Application Data in its first flight of messages. If the + *# client opts to do so, it MUST supply both the "pre_shared_key" and + *# "early_data" extensions. + */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(conn)); + + EXPECT_FALSE(s2n_client_psk_extension.should_send(conn)); + EXPECT_FALSE(s2n_client_early_data_indication_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /** + * Don't send when performing a retry. + * + *= https://tools.ietf.org/rfc/rfc8446#section-4.2.10 + *= type=test + *# A client MUST NOT include the + *# "early_data" extension in its followup ClientHello. + * + *= https://tools.ietf.org/rfc/rfc8446#4.1.2 + *= type=test + *# - Removing the "early_data" extension (Section 4.2.10) if one was + *# present. Early data is not permitted after a HelloRetryRequest. + **/ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); + EXPECT_OK(s2n_append_test_psk_with_early_data(conn, nonzero_max_early_data, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(conn)); + + EXPECT_TRUE(s2n_client_early_data_indication_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_set_hello_retry_required(conn)); + EXPECT_FALSE(s2n_client_early_data_indication_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Don't send if no early data allowed by first PSK */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(conn)); + + EXPECT_OK(s2n_append_test_psk_with_early_data(conn, 0, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_OK(s2n_append_test_psk_with_early_data(conn, nonzero_max_early_data, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_FALSE(s2n_client_early_data_indication_extension.should_send(conn)); + + EXPECT_OK(s2n_psk_parameters_wipe(&conn->psk_params)); + EXPECT_OK(s2n_append_test_psk_with_early_data(conn, nonzero_max_early_data, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_OK(s2n_append_test_psk_with_early_data(conn, 0, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_TRUE(s2n_client_early_data_indication_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Don't send if protocol version too low */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); + EXPECT_OK(s2n_append_test_psk_with_early_data(conn, nonzero_max_early_data, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(conn)); + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_FALSE(s2n_client_early_data_indication_extension.should_send(conn)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_TRUE(s2n_client_early_data_indication_extension.should_send(conn)); + + conn->actual_protocol_version = S2N_TLS13 + 1; + EXPECT_TRUE(s2n_client_early_data_indication_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Don't send if cipher suite not allowed by cipher preferences */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(conn)); + + EXPECT_OK(s2n_append_test_psk_with_early_data(conn, nonzero_max_early_data, &s2n_rsa_with_3des_ede_cbc_sha)); + EXPECT_FALSE(s2n_client_early_data_indication_extension.should_send(conn)); + + EXPECT_OK(s2n_psk_parameters_wipe(&conn->psk_params)); + EXPECT_OK(s2n_append_test_psk_with_early_data(conn, nonzero_max_early_data, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_TRUE(s2n_client_early_data_indication_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Don't send if application layer protocol not allowed by preferences */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); + EXPECT_OK(s2n_append_test_psk_with_early_data(conn, nonzero_max_early_data, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(conn)); + + uint8_t app_protocol_data[] = "protocol preference"; + uint8_t other_app_protocol_data[] = "different protocol"; + struct s2n_blob app_protocol = { 0 }, empty_app_protocol = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&app_protocol, app_protocol_data, sizeof(app_protocol_data))); + + /* No early data alp, empty alpn preferences: send */ + EXPECT_OK(s2n_set_early_data_app_protocol(conn, &empty_app_protocol)); + EXPECT_TRUE(s2n_client_early_data_indication_extension.should_send(conn)); + + /* Early data alp, empty alpn preferences: don't send */ + EXPECT_OK(s2n_set_early_data_app_protocol(conn, &app_protocol)); + EXPECT_FALSE(s2n_client_early_data_indication_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_append_protocol_preference(conn, other_app_protocol_data, + sizeof(other_app_protocol_data))); + EXPECT_SUCCESS(s2n_connection_append_protocol_preference(conn, other_app_protocol_data, + sizeof(other_app_protocol_data))); + + /* No early data alp, non-empty alpn preferences: send */ + EXPECT_OK(s2n_set_early_data_app_protocol(conn, &empty_app_protocol)); + EXPECT_TRUE(s2n_client_early_data_indication_extension.should_send(conn)); + + /* alpn preferences don't contain alp: don't send */ + EXPECT_OK(s2n_set_early_data_app_protocol(conn, &app_protocol)); + EXPECT_FALSE(s2n_client_early_data_indication_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_append_protocol_preference(conn, app_protocol.data, app_protocol.size)); + + /* alpn preferences contain alp: send */ + EXPECT_OK(s2n_set_early_data_app_protocol(conn, &app_protocol)); + EXPECT_TRUE(s2n_client_early_data_indication_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test s2n_client_early_data_indiction_send */ + { + /* Set MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS handshake type flags */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_OK(s2n_append_test_psk_with_early_data(conn, 0, &s2n_tls13_aes_128_gcm_sha256)); + + EXPECT_EQUAL(conn->handshake.handshake_type, 0); + + /* Don't set if < TLS1.3 */ + conn->actual_protocol_version = S2N_TLS12; + config->quic_enabled = false; + EXPECT_SUCCESS(s2n_client_early_data_indication_extension.send(conn, NULL)); + EXPECT_EQUAL(conn->handshake.handshake_type, 0); + + /* Don't set if middlebox compat disabled */ + conn->actual_protocol_version = S2N_TLS13; + config->quic_enabled = true; + EXPECT_SUCCESS(s2n_client_early_data_indication_extension.send(conn, NULL)); + EXPECT_EQUAL(conn->handshake.handshake_type, 0); + + /* Otherwise, set */ + conn->actual_protocol_version = S2N_TLS13; + config->quic_enabled = false; + EXPECT_SUCCESS(s2n_client_early_data_indication_extension.send(conn, NULL)); + EXPECT_EQUAL(conn->handshake.handshake_type, (MIDDLEBOX_COMPAT | EARLY_CLIENT_CCS)); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test s2n_client_early_data_indiction_recv */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS13; + + /* Successful if not retry */ + conn->early_data_state = S2N_UNKNOWN_EARLY_DATA_STATE; + conn->handshake.message_number = 0; + EXPECT_SUCCESS(s2n_client_early_data_indication_extension.recv(conn, NULL)); + EXPECT_EQUAL(conn->early_data_state, S2N_EARLY_DATA_REQUESTED); + + /** + *= https://tools.ietf.org/rfc/rfc8446#section-4.2.10 + *= type=test + *# A client MUST NOT include the + *# "early_data" extension in its followup ClientHello. + */ + conn->early_data_state = S2N_UNKNOWN_EARLY_DATA_STATE; + conn->handshake.message_number = 1; + EXPECT_FAILURE_WITH_ERRNO(s2n_client_early_data_indication_extension.recv(conn, NULL), + S2N_ERR_UNSUPPORTED_EXTENSION); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test state transitions */ + { + /* When early data not enabled on client */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_OK(s2n_append_test_psk_with_early_data(client_conn, 1, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_EQUAL(client_conn->early_data_state, S2N_UNKNOWN_EARLY_DATA_STATE); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + EXPECT_OK(s2n_append_test_psk_with_early_data(server_conn, 1, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_EQUAL(server_conn->early_data_state, S2N_UNKNOWN_EARLY_DATA_STATE); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_establish_session(server_conn)); + + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_NOT_REQUESTED); + EXPECT_EQUAL(server_conn->early_data_state, S2N_EARLY_DATA_NOT_REQUESTED); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* When early data not enabled on server */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_OK(s2n_append_test_psk_with_early_data(client_conn, 1, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_EQUAL(client_conn->early_data_state, S2N_UNKNOWN_EARLY_DATA_STATE); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + EXPECT_OK(s2n_append_test_psk_with_early_data(server_conn, 1, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_EQUAL(server_conn->early_data_state, S2N_UNKNOWN_EARLY_DATA_STATE); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_establish_session(server_conn)); + + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_REQUESTED); + EXPECT_EQUAL(server_conn->early_data_state, S2N_EARLY_DATA_REJECTED); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* When early data requested */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_OK(s2n_append_test_psk_with_early_data(client_conn, 1, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_EQUAL(client_conn->early_data_state, S2N_UNKNOWN_EARLY_DATA_STATE); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + EXPECT_OK(s2n_append_test_psk_with_early_data(server_conn, 1, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_EQUAL(server_conn->early_data_state, S2N_UNKNOWN_EARLY_DATA_STATE); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_establish_session(server_conn)); + + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_REQUESTED); + EXPECT_EQUAL(server_conn->early_data_state, S2N_EARLY_DATA_ACCEPTED); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* When early data not requested */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_OK(s2n_append_test_psk_with_early_data(client_conn, 0, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_EQUAL(client_conn->early_data_state, S2N_UNKNOWN_EARLY_DATA_STATE); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + EXPECT_OK(s2n_append_test_psk_with_early_data(server_conn, 0, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_EQUAL(server_conn->early_data_state, S2N_UNKNOWN_EARLY_DATA_STATE); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_establish_session(server_conn)); + + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_NOT_REQUESTED); + EXPECT_EQUAL(server_conn->early_data_state, S2N_EARLY_DATA_NOT_REQUESTED); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + }; + + /* Test state transitions with a HelloRetryRequest. + * + *= https://tools.ietf.org/rfc/rfc8446#section-4.2.10 + *= type=test + *# A server which receives an "early_data" extension MUST behave in one + *# of three ways: + * + *= https://tools.ietf.org/rfc/rfc8446#section-4.2.10 + *= type=test + *# - Request that the client send another ClientHello by responding + *# with a HelloRetryRequest. + */ + { + /* Hello Retry Request because of rejected early data. + * + * The S2N server does not reject early data via a HelloRetryRequest, but other implementations might. + * The S2N client should handle retries triggered by early data gracefully. + */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_OK(s2n_append_test_psk_with_early_data(client_conn, 1, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_EQUAL(client_conn->early_data_state, S2N_UNKNOWN_EARLY_DATA_STATE); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + EXPECT_OK(s2n_append_test_psk_with_early_data(server_conn, 0, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_EQUAL(server_conn->early_data_state, S2N_UNKNOWN_EARLY_DATA_STATE); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_establish_session(server_conn)); + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_REQUESTED); + + /* Force a retry */ + EXPECT_SUCCESS(s2n_set_hello_retry_required(server_conn)); + /* There is retry handling logic that checks that the current message + * is a hello retry message, which requires that we be at a specific message number. */ + server_conn->handshake.message_number = 2; + client_conn->handshake.message_number = 2; + + /* Update the selected_group to ensure the HRR is valid */ + client_conn->kex_params.client_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp521r1; + + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_REJECTED); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_REJECTED); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Hello Retry Request because of missing key share: still rejects early data */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + client_conn->security_policy_override = &security_policy_test_tls13_retry; + EXPECT_OK(s2n_append_test_psk_with_early_data(client_conn, 1, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_EQUAL(client_conn->early_data_state, S2N_UNKNOWN_EARLY_DATA_STATE); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + server_conn->security_policy_override = &security_policy_test_all_tls13; + EXPECT_OK(s2n_append_test_psk_with_early_data(server_conn, 1, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_EQUAL(server_conn->early_data_state, S2N_UNKNOWN_EARLY_DATA_STATE); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_establish_session(server_conn)); + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_REQUESTED); + EXPECT_EQUAL(server_conn->early_data_state, S2N_EARLY_DATA_REJECTED); + + EXPECT_TRUE(s2n_is_hello_retry_handshake(server_conn)); + /* There is retry handling logic that checks that the current message + * is a hello retry message, which requires that we be at a specific message number. */ + server_conn->handshake.message_number = 2; + client_conn->handshake.message_number = 2; + + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); + EXPECT_TRUE(s2n_is_hello_retry_handshake(client_conn)); + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_REJECTED); + EXPECT_EQUAL(server_conn->early_data_state, S2N_EARLY_DATA_REJECTED); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_REJECTED); + EXPECT_EQUAL(server_conn->early_data_state, S2N_EARLY_DATA_REJECTED); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_client_empty_cert_test.c b/tests/unit/s2n_client_empty_cert_test.c new file mode 100644 index 00000000000..1a1be1d71a8 --- /dev/null +++ b/tests/unit/s2n_client_empty_cert_test.c @@ -0,0 +1,122 @@ +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_safety.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Test s2n_send_empty_cert_chain */ + { + struct s2n_stuffer out = { 0 }; + /* Magic number 3 is the length of the certificate_length field */ + EXPECT_SUCCESS(s2n_stuffer_alloc(&out, 3)); + + EXPECT_SUCCESS(s2n_send_empty_cert_chain(&out)); + EXPECT_EQUAL(s2n_stuffer_data_available(&out), 3); + uint32_t cert_len; + EXPECT_SUCCESS(s2n_stuffer_read_uint24(&out, &cert_len)); + EXPECT_EQUAL(cert_len, 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + }; + + /* Client sends the empty cert when no client default chain and key */ + { + struct s2n_config *client_config; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + struct s2n_connection *client_conn; + 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_client_auth_type(client_conn, S2N_CERT_AUTH_OPTIONAL)); + + /* client send empty cert */ + EXPECT_SUCCESS(s2n_client_cert_send(client_conn)); + + /* verify post-conditions */ + EXPECT_TRUE(client_conn->handshake.handshake_type & NO_CLIENT_CERT); + /* Magic number 3 is the length of the certificate_length field */ + EXPECT_EQUAL(s2n_stuffer_data_available(&client_conn->handshake.io), 3); + + uint32_t cert_len; + EXPECT_SUCCESS(s2n_stuffer_read_uint24(&client_conn->handshake.io, &cert_len)); + EXPECT_EQUAL(cert_len, 0); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client fails to send empty cert when S2N_CERT_AUTH_REQUIRED */ + { + struct s2n_config *client_config; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + struct s2n_connection *client_conn; + 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_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); + + /* client send empty cert */ + EXPECT_FAILURE(s2n_client_cert_send(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Server receives empty cert */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_OPTIONAL)); + + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); + + /* client send empty cert */ + EXPECT_SUCCESS(s2n_client_cert_send(client_conn)); + + EXPECT_TRUE(client_conn->handshake.handshake_type & NO_CLIENT_CERT); + /* Magic number 3 is the length of the certificate_length field */ + EXPECT_EQUAL(s2n_stuffer_data_available(&client_conn->handshake.io), 3); + + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_EQUAL(s2n_stuffer_data_available(&server_conn->handshake.io), 3); + + /* server receives empty cert */ + EXPECT_SUCCESS(s2n_client_cert_recv(server_conn)); + EXPECT_TRUE(server_conn->handshake.handshake_type & NO_CLIENT_CERT); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_client_extensions_test.c b/tests/unit/s2n_client_extensions_test.c new file mode 100644 index 00000000000..0743391729b --- /dev/null +++ b/tests/unit/s2n_client_extensions_test.c @@ -0,0 +1,1491 @@ +/* + * 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 +#include +#include + +#include "api/s2n.h" +#include "pq-crypto/s2n_pq.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_kem.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls_parameters.h" + +#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F + +static uint8_t server_ocsp_status[] = { + /* clang-format off */ + 0x30, 0x82, 0x06, 0x45, 0x0a, 0x01, 0x00, 0xa0, 0x82, 0x06, 0x3e, 0x30, 0x82, 0x06, 0x3a, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x01, 0x04, 0x82, 0x06, 0x2b, 0x30, 0x82, 0x06, 0x27, 0x30, 0x81, 0xeb, 0xa1, 0x70, 0x30, 0x6e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, 0x4c, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x28, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x20, 0x28, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x72, 0x63, 0x69, 0x61, 0x6c, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x29, 0x31, 0x2c, 0x30, 0x2a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x23, 0x53, 0x74, 0x61, 0x72, + 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x31, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x4f, 0x43, 0x53, 0x50, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x18, 0x0f, 0x32, 0x30, 0x31, 0x35, 0x30, 0x32, 0x32, 0x37, 0x30, 0x36, 0x34, 0x36, 0x34, 0x35, 0x5a, 0x30, 0x66, 0x30, 0x64, 0x30, 0x3c, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14, 0x65, 0x68, 0x87, 0x4f, 0x40, 0x75, 0x0f, 0x01, 0x6a, 0x34, 0x75, 0x62, 0x5e, 0x1f, 0x5c, 0x93, 0xe5, 0xa2, 0x6d, 0x58, 0x04, 0x14, 0xeb, 0x42, 0x34, 0xd0, 0x98, 0xb0, 0xab, 0x9f, 0xf4, 0x1b, 0x6b, 0x08, 0xf7, 0xcc, 0x64, 0x2e, 0xef, 0x0e, 0x2c, 0x45, 0x02, 0x03, 0x0f, 0x87, 0x2c, 0x80, 0x00, 0x18, 0x0f, 0x32, 0x30, + 0x31, 0x35, 0x30, 0x32, 0x32, 0x37, 0x30, 0x36, 0x34, 0x36, 0x34, 0x35, 0x5a, 0xa0, 0x11, 0x18, 0x0f, 0x32, 0x30, 0x31, 0x35, 0x30, 0x33, 0x30, 0x31, 0x30, 0x36, 0x34, 0x36, 0x34, 0x35, 0x5a, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x3c, 0x16, 0x25, 0xa2, 0x0f, 0x46, 0xc2, 0xa6, 0xac, 0xb1, 0x6e, 0x54, 0xc8, 0xf1, 0x7f, 0xa9, 0xbe, 0x58, 0xf0, 0xdb, 0x81, 0x37, 0x23, 0x76, 0x65, 0x56, 0x90, 0x15, 0xb1, 0x30, 0x6f, 0x43, 0xe2, 0x59, 0x0d, 0x97, 0xa8, 0xa6, 0x05, 0x25, 0xe7, 0x94, 0x21, 0xd5, 0xda, 0x4b, 0x55, 0x13, 0xc7, 0xdf, 0x5d, 0xf6, 0x31, 0xe8, 0x2f, 0x0d, 0xa0, 0xac, 0xd4, 0xfe, 0xf8, 0x22, 0xe7, 0x12, 0xf4, 0x32, 0xcd, 0x53, + 0x03, 0x56, 0x98, 0x0a, 0xf8, 0x9e, 0xda, 0x2c, 0x0a, 0x43, 0x66, 0x6e, 0x0e, 0x9c, 0x9b, 0xf2, 0x0c, 0x66, 0x65, 0x1c, 0x65, 0xc4, 0xf0, 0x82, 0xc3, 0x17, 0x3d, 0x27, 0x11, 0xcc, 0xac, 0x37, 0xe3, 0xa8, 0x35, 0x46, 0x26, 0xcd, 0x08, 0x04, 0xfa, 0xb4, 0xdf, 0x9d, 0x12, 0xdf, 0x45, 0x8d, 0xf2, 0xef, 0x1a, 0xd1, 0x53, 0x50, 0x9a, 0xe3, 0xe8, 0x22, 0xda, 0xec, 0xeb, 0xc0, 0xa8, 0xea, 0xc4, 0x83, 0xc4, 0x47, 0xf2, 0x05, 0x3c, 0x14, 0x11, 0x3b, 0x25, 0xdc, 0xb9, 0x09, 0x5c, 0xd7, 0x74, 0x88, 0x96, 0x82, 0x4d, 0xbb, 0x8b, 0x7f, 0x6a, 0xbf, 0xa1, 0x44, 0x1b, 0x89, 0x67, 0xce, 0x45, 0xab, 0xca, 0xef, 0x48, 0xa6, 0x80, 0x76, 0x7d, 0xbe, 0xb7, 0x8a, 0xdf, 0x7a, 0x32, 0x8c, 0xa5, 0x86, 0x4e, 0x26, 0xf7, 0x15, 0x63, 0xbb, + 0xb1, 0xcc, 0xe0, 0x32, 0x82, 0x02, 0x5d, 0x2b, 0x60, 0x39, 0xdb, 0xd2, 0x04, 0x56, 0xb4, 0x7e, 0xe6, 0x3a, 0x69, 0x0c, 0x8a, 0xf0, 0x00, 0xf4, 0x56, 0xb0, 0xa7, 0x1a, 0x37, 0x05, 0x4b, 0xeb, 0x8c, 0x87, 0x05, 0x37, 0x92, 0xf7, 0x93, 0x5d, 0x93, 0x32, 0x7d, 0x6e, 0xa6, 0xda, 0x10, 0x4b, 0x49, 0xae, 0x86, 0xe4, 0xb4, 0x4d, 0x98, 0x42, 0x3e, 0xd3, 0x42, 0x46, 0x5d, 0xdd, 0x2f, 0x97, 0xd4, 0xb9, 0x7f, 0xbe, 0xa0, 0x82, 0x04, 0x21, 0x30, 0x82, 0x04, 0x1d, 0x30, 0x82, 0x04, 0x19, 0x30, 0x82, 0x03, 0x01, 0xa0, 0x03, 0x02, 0x01, 0x02, 0x02, 0x03, 0x15, 0xfa, 0xa9, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x81, 0x8c, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, + 0x06, 0x13, 0x02, 0x49, 0x4c, 0x31, 0x16, 0x30, 0x14, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x0d, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x31, 0x2b, 0x30, 0x29, 0x06, 0x03, 0x55, 0x04, 0x0b, 0x13, 0x22, 0x53, 0x65, 0x63, 0x75, 0x72, 0x65, 0x20, 0x44, 0x69, 0x67, 0x69, 0x74, 0x61, 0x6c, 0x20, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67, 0x31, 0x38, 0x30, 0x36, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x2f, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x31, 0x20, 0x50, 0x72, 0x69, 0x6d, 0x61, 0x72, 0x79, 0x20, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6d, 0x65, 0x64, 0x69, 0x61, + 0x74, 0x65, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x43, 0x41, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x35, 0x30, 0x31, 0x32, 0x32, 0x31, 0x38, 0x33, 0x35, 0x33, 0x37, 0x5a, 0x17, 0x0d, 0x31, 0x35, 0x30, 0x33, 0x30, 0x34, 0x30, 0x35, 0x31, 0x36, 0x33, 0x34, 0x5a, 0x30, 0x6e, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x49, 0x4c, 0x31, 0x31, 0x30, 0x2f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x28, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x4c, 0x74, 0x64, 0x2e, 0x20, 0x28, 0x53, 0x74, 0x61, 0x72, 0x74, 0x20, 0x43, 0x6f, 0x6d, 0x6d, 0x65, 0x72, 0x63, 0x69, 0x61, 0x6c, 0x20, 0x4c, 0x69, 0x6d, 0x69, 0x74, 0x65, 0x64, 0x29, 0x31, 0x2c, 0x30, 0x2a, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, + 0x23, 0x53, 0x74, 0x61, 0x72, 0x74, 0x43, 0x6f, 0x6d, 0x20, 0x43, 0x6c, 0x61, 0x73, 0x73, 0x20, 0x31, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, 0x20, 0x4f, 0x43, 0x53, 0x50, 0x20, 0x53, 0x69, 0x67, 0x6e, 0x65, 0x72, 0x30, 0x82, 0x01, 0x22, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, 0x02, 0x82, 0x01, 0x01, 0x00, 0xb9, 0x56, 0x1b, 0x4c, 0x45, 0x31, 0x87, 0x17, 0x17, 0x80, 0x84, 0xe9, 0x6e, 0x17, 0x8d, 0xf2, 0x25, 0x5e, 0x18, 0xed, 0x8d, 0x8e, 0xcc, 0x7c, 0x2b, 0x7b, 0x51, 0xa6, 0xc1, 0xc2, 0xe6, 0xbf, 0x0a, 0xa3, 0x60, 0x30, 0x66, 0xf1, 0x32, 0xfe, 0x10, 0xae, 0x97, 0xb5, 0x0e, 0x99, 0xfa, 0x24, 0xb8, 0x3f, 0xc5, + 0x3d, 0xd2, 0x77, 0x74, 0x96, 0x38, 0x7d, 0x14, 0xe1, 0xc3, 0xa9, 0xb6, 0xa4, 0x93, 0x3e, 0x2a, 0xc1, 0x24, 0x13, 0xd0, 0x85, 0x57, 0x0a, 0x95, 0xb8, 0x14, 0x74, 0x14, 0xa0, 0xbc, 0x00, 0x7c, 0x7b, 0xcf, 0x22, 0x24, 0x46, 0xef, 0x7f, 0x1a, 0x15, 0x6d, 0x7e, 0xa1, 0xc5, 0x77, 0xfc, 0x5f, 0x0f, 0xac, 0xdf, 0xd4, 0x2e, 0xb0, 0xf5, 0x97, 0x49, 0x90, 0xcb, 0x2f, 0x5c, 0xef, 0xeb, 0xce, 0xef, 0x4d, 0x1b, 0xdc, 0x7a, 0xe5, 0xc1, 0x07, 0x5c, 0x5a, 0x99, 0xa9, 0x31, 0x71, 0xf2, 0xb0, 0x84, 0x5b, 0x4f, 0xf0, 0x86, 0x4e, 0x97, 0x3f, 0xcf, 0xe3, 0x2f, 0x9d, 0x75, 0x11, 0xff, 0x87, 0xa3, 0xe9, 0x43, 0x41, 0x0c, 0x90, 0xa4, 0x49, 0x3a, 0x30, 0x6b, 0x69, 0x44, 0x35, 0x93, 0x40, 0xa9, 0xca, 0x96, 0xf0, 0x2b, 0x66, 0xce, 0x67, + 0xf0, 0x28, 0xdf, 0x29, 0x80, 0xa6, 0xaa, 0xee, 0x8d, 0x5d, 0x5d, 0x45, 0x2b, 0x8b, 0x0e, 0xb9, 0x3f, 0x92, 0x3c, 0xc1, 0xe2, 0x3f, 0xcc, 0xcb, 0xdb, 0xe7, 0xff, 0xcb, 0x11, 0x4d, 0x08, 0xfa, 0x7a, 0x6a, 0x3c, 0x40, 0x4f, 0x82, 0x5d, 0x1a, 0x0e, 0x71, 0x59, 0x35, 0xcf, 0x62, 0x3a, 0x8c, 0x7b, 0x59, 0x67, 0x00, 0x14, 0xed, 0x06, 0x22, 0xf6, 0x08, 0x9a, 0x94, 0x47, 0xa7, 0xa1, 0x90, 0x10, 0xf7, 0xfe, 0x58, 0xf8, 0x41, 0x29, 0xa2, 0x76, 0x5e, 0xa3, 0x67, 0x82, 0x4d, 0x1c, 0x3b, 0xb2, 0xfd, 0xa3, 0x08, 0x53, 0x02, 0x03, 0x01, 0x00, 0x01, 0xa3, 0x81, 0xa0, 0x30, 0x81, 0x9d, 0x30, 0x09, 0x06, 0x03, 0x55, 0x1d, 0x13, 0x04, 0x02, 0x30, 0x00, 0x30, 0x0b, 0x06, 0x03, 0x55, 0x1d, 0x0f, 0x04, 0x04, 0x03, 0x02, 0x03, 0xa8, + 0x30, 0x1e, 0x06, 0x03, 0x55, 0x1d, 0x25, 0x04, 0x17, 0x30, 0x15, 0x06, 0x08, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x03, 0x09, 0x06, 0x09, 0x2b, 0x06, 0x01, 0x05, 0x05, 0x07, 0x30, 0x01, 0x05, 0x30, 0x1d, 0x06, 0x03, 0x55, 0x1d, 0x0e, 0x04, 0x16, 0x04, 0x14, 0x45, 0xe0, 0xa3, 0x66, 0x95, 0x41, 0x4c, 0x5d, 0xd4, 0x49, 0xbc, 0x00, 0xe3, 0x3c, 0xdc, 0xdb, 0xd2, 0x34, 0x3e, 0x17, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x1d, 0x23, 0x04, 0x18, 0x30, 0x16, 0x80, 0x14, 0xeb, 0x42, 0x34, 0xd0, 0x98, 0xb0, 0xab, 0x9f, 0xf4, 0x1b, 0x6b, 0x08, 0xf7, 0xcc, 0x64, 0x2e, 0xef, 0x0e, 0x2c, 0x45, 0x30, 0x23, 0x06, 0x03, 0x55, 0x1d, 0x12, 0x04, 0x1c, 0x30, 0x1a, 0x86, 0x18, 0x68, 0x74, 0x74, 0x70, 0x3a, 0x2f, 0x2f, 0x77, 0x77, 0x77, 0x2e, + 0x73, 0x74, 0x61, 0x72, 0x74, 0x73, 0x73, 0x6c, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x42, 0xcd, 0x4c, 0x03, 0xd2, 0x9a, 0x55, 0xb2, 0xd6, 0x3e, 0x90, 0x4c, 0x89, 0x27, 0xd0, 0xcf, 0x87, 0xf6, 0x91, 0x9b, 0x86, 0x6a, 0x6d, 0x76, 0xd9, 0x5e, 0xbc, 0xc8, 0xfe, 0x74, 0xbe, 0x97, 0x29, 0xd1, 0xac, 0x92, 0x9b, 0x9e, 0x48, 0xab, 0xb1, 0xf4, 0xbe, 0xd5, 0x3f, 0xa8, 0x4c, 0xce, 0x0e, 0x2f, 0x39, 0x96, 0x4b, 0xde, 0xda, 0xac, 0x40, 0xce, 0xbb, 0x93, 0xdb, 0x1c, 0x39, 0x02, 0x03, 0x25, 0x32, 0x45, 0xde, 0x94, 0x5a, 0x63, 0xaf, 0xf7, 0xb0, 0x70, 0xc8, 0xcc, 0x2b, 0x34, 0x7b, 0x5f, 0x7d, 0xc6, 0x96, 0x1d, 0x59, + 0x1d, 0xdd, 0x8f, 0x7e, 0x55, 0xc4, 0x92, 0x11, 0x8d, 0xd9, 0x11, 0x11, 0x22, 0x20, 0xd3, 0x56, 0x1e, 0x11, 0xae, 0x97, 0xf2, 0x71, 0xea, 0x8c, 0xf5, 0x15, 0x2d, 0xb1, 0x59, 0xdd, 0x3e, 0x43, 0x9c, 0xf1, 0xda, 0x81, 0xd7, 0xc8, 0x6c, 0xf6, 0x08, 0x5d, 0x6f, 0xdf, 0x26, 0xa8, 0xfe, 0x84, 0xa2, 0x08, 0xaf, 0xdb, 0x9b, 0x39, 0xf5, 0x46, 0xfa, 0x5b, 0xfa, 0x97, 0x64, 0x1d, 0xf1, 0xd4, 0xbc, 0xb0, 0xa4, 0x2f, 0x36, 0xf1, 0x90, 0xb5, 0x3b, 0x67, 0x0b, 0x5b, 0xf3, 0x24, 0x50, 0x27, 0x63, 0xdc, 0xeb, 0xb6, 0x55, 0x0f, 0xb7, 0xbe, 0xee, 0x2e, 0xfb, 0xc8, 0x6a, 0x10, 0xab, 0xee, 0x9a, 0x27, 0xe4, 0x13, 0x16, 0xcf, 0xdd, 0x13, 0xa7, 0x0f, 0xde, 0x61, 0x8c, 0xfa, 0xed, 0x2d, 0x00, 0x60, 0xf9, 0xc4, 0x3d, 0xad, 0xd6, 0xa2, + 0xc0, 0xa3, 0x29, 0x11, 0x61, 0x0b, 0x65, 0xdb, 0x14, 0x79, 0xb1, 0x7d, 0x8a, 0x57, 0x91, 0x59, 0xa4, 0xfc, 0x4c, 0x60, 0x4f, 0x3c, 0xc8, 0x31, 0x9b, 0x69, 0x70, 0xb9, 0xae, 0xed, 0xb1, 0xde, 0x58, 0x8d, 0x62, 0x30, 0xb4, 0x7b, 0x46, 0xf2, 0xda, 0x7b, 0xbb, 0x72, 0xcf, 0xf0, 0x47, 0x8b, 0x84, + /* clang-format on */ +}; + +/* This data format is bogus, but sufficient to test the server is able + to return correctly what has been configured. Once the client does + validation we will need real data here. + */ +static uint8_t sct_list[] = { + 0xff, 0xff, 0xff, 0xff, 0xff +}; + +message_type_t s2n_conn_get_current_message_type(struct s2n_connection *conn); + +/* Helper function to allow us to easily repeat the PQ extension test for many scenarios. + * If the KEM negotiation is expected to fail (because of e.g. a client/server extension + * mismatch), pass in expected_kem_id = -1. The tests should always EXPECT_SUCCESS when + * calling this function. */ +static int negotiate_kem(const uint8_t client_extensions[], const size_t client_extensions_len, + const uint8_t client_hello_message[], const size_t client_hello_len, + const char cipher_pref_version[], const int expected_kem_id, struct s2n_test_io_pair *io_pair) +{ + char *cert_chain; + char *private_key; + + POSIX_GUARD_PTR(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + POSIX_GUARD_PTR(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + POSIX_GUARD(setenv("S2N_DONT_MLOCK", "1", 0)); + + struct s2n_connection *server_conn; + struct s2n_config *server_config; + s2n_blocked_status server_blocked; + struct s2n_cert_chain_and_key *chain_and_key; + + size_t body_len = client_hello_len + client_extensions_len; + uint8_t message_header[] = { + /* Handshake message type CLIENT HELLO */ + 0x01, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + size_t message_header_len = sizeof(message_header); + size_t message_len = message_header_len + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + size_t record_header_len = sizeof(record_header); + + POSIX_GUARD_PTR(server_conn = s2n_connection_new(S2N_SERVER)); + POSIX_GUARD(s2n_connection_set_io_pair(server_conn, io_pair)); + + POSIX_GUARD_PTR(server_config = s2n_config_new()); + POSIX_GUARD(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + POSIX_GUARD(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + POSIX_GUARD_PTR(chain_and_key = s2n_cert_chain_and_key_new()); + POSIX_GUARD(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + POSIX_GUARD(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + POSIX_GUARD(s2n_config_set_cipher_preferences(server_config, cipher_pref_version)); + POSIX_GUARD(s2n_connection_set_config(server_conn, server_config)); + server_conn->kex_params.kem_params.kem = NULL; + + /* Send the client hello */ + POSIX_ENSURE_EQ(write(io_pair->client, record_header, record_header_len), (int64_t) record_header_len); + POSIX_ENSURE_EQ(write(io_pair->client, message_header, message_header_len), (int64_t) message_header_len); + POSIX_ENSURE_EQ(write(io_pair->client, client_hello_message, client_hello_len), (int64_t) client_hello_len); + POSIX_ENSURE_EQ(write(io_pair->client, client_extensions, client_extensions_len), (int64_t) client_extensions_len); + + POSIX_GUARD(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + if (s2n_negotiate(server_conn, &server_blocked) == 0) { + /* We expect the overall negotiation to fail and return non-zero, but it should get far enough + * that a KEM extension was agreed upon. */ + return S2N_FAILURE; + } + + int negotiated_kem_id; + + if (server_conn->kex_params.kem_params.kem != NULL) { + negotiated_kem_id = server_conn->kex_params.kem_params.kem->kem_extension_id; + } else { + negotiated_kem_id = -1; + } + + POSIX_GUARD(s2n_connection_free(server_conn)); + POSIX_GUARD(s2n_cert_chain_and_key_free(chain_and_key)); + POSIX_GUARD(s2n_config_free(server_config)); + + free(cert_chain); + free(private_key); + + POSIX_ENSURE_EQ(negotiated_kem_id, expected_kem_id); + + return 0; +} + +int main(int argc, char **argv) +{ + char *cert_chain = NULL; + char *private_key = NULL; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + /* Client doesn't use the server name extension. */ + { + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + struct s2n_config *server_config; + struct s2n_cert_chain_and_key *chain_and_key; + + struct s2n_config *client_config; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server didn't receive the server name. */ + EXPECT_NULL(s2n_get_server_name(server_conn)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client uses the server name extension. */ + { + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + struct s2n_config *server_config; + struct s2n_cert_chain_and_key *chain_and_key; + + const char *sent_server_name = "www.alligator.com"; + const char *received_server_name; + + struct s2n_config *client_config; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + /* Set the server name */ + EXPECT_SUCCESS(s2n_set_server_name(client_conn, sent_server_name)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ALLIGATOR_SAN_CERT, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ALLIGATOR_SAN_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server name was received intact. */ + EXPECT_NOT_NULL(received_server_name = s2n_get_server_name(server_conn)); + EXPECT_EQUAL(strlen(received_server_name), strlen(sent_server_name)); + EXPECT_BYTEARRAY_EQUAL(received_server_name, sent_server_name, strlen(received_server_name)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends multiple server names. */ + { + struct s2n_connection *server_conn; + struct s2n_config *server_config; + s2n_blocked_status server_blocked; + const char *sent_server_name = "svr"; + const char *received_server_name; + struct s2n_cert_chain_and_key *chain_and_key; + uint32_t cert_chain_len = 0; + uint32_t private_key_len = 0; + + uint8_t client_extensions[] = { + /* Extension type TLS_EXTENSION_SERVER_NAME */ + 0x00, + 0x00, + /* Extension size */ + 0x00, + 0x0C, + /* All server names len */ + 0x00, + 0x0A, + /* First server name type - host name */ + 0x00, + /* First server name len */ + 0x00, + 0x03, + /* First server name, matches sent_server_name */ + 's', + 'v', + 'r', + /* Second server name type - host name */ + 0x00, + /* Second server name len */ + 0x00, + 0x01, + /* Second server name */ + 0xFF, + }; + int client_extensions_len = sizeof(client_extensions); + uint8_t client_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Client random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suites len */ + 0x00, + 0x02, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, + 0x3C, + /* Compression methods len */ + 0x01, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (client_extensions_len >> 8) & 0xff, + (client_extensions_len & 0xff), + }; + int body_len = sizeof(client_hello_message) + client_extensions_len; + uint8_t message_header[] = { + /* Handshake message type CLIENT HELLO */ + 0x01, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version garbage value. s2n should still accept this. */ + 0x01, + 0x01, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + /* Security policy must allow cipher suite hard coded into client hello */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_CERT_CHAIN, (uint8_t *) cert_chain, &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_PRIVATE_KEY, (uint8_t *) private_key, &private_key_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, (uint8_t *) cert_chain, cert_chain_len, (uint8_t *) private_key, private_key_len)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Send the client hello */ + EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.client, client_hello_message, sizeof(client_hello_message)), sizeof(client_hello_message)); + EXPECT_EQUAL(write(io_pair.client, client_extensions, sizeof(client_extensions)), sizeof(client_extensions)); + + /* Verify that the CLIENT HELLO is accepted */ + s2n_negotiate(server_conn, &server_blocked); + EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); + EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE); + + /* Verify that the server name was received intact. */ + EXPECT_NOT_NULL(received_server_name = s2n_get_server_name(server_conn)); + EXPECT_EQUAL(strlen(received_server_name), strlen(sent_server_name)); + EXPECT_BYTEARRAY_EQUAL(received_server_name, sent_server_name, strlen(received_server_name)); + + EXPECT_SUCCESS(s2n_shutdown(server_conn, &server_blocked)); + EXPECT_TRUE(server_conn->alert_sent); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Client sends duplicate server name extension */ + { + struct s2n_connection *server_conn; + struct s2n_config *server_config; + s2n_blocked_status server_blocked; + struct s2n_cert_chain_and_key *chain_and_key; + + uint8_t client_extensions[] = { + /* Extension type TLS_EXTENSION_SERVER_NAME */ + 0x00, + 0x00, + /* Extension size */ + 0x00, + 0x0C, + /* All server names len */ + 0x00, + 0x0A, + /* First server name type - host name */ + 0x00, + /* First server name len */ + 0x00, + 0x03, + /* First server name, matches sent_server_name */ + 's', + 'v', + 'r', + /* Second server name type - host name */ + 0x00, + /* Second server name len */ + 0x00, + 0x01, + /* Second server name */ + 0xFF, + /* And all that again... */ + /* Extension type TLS_EXTENSION_SERVER_NAME */ + 0x00, + 0x00, + /* Extension size */ + 0x00, + 0x0C, + /* All server names len */ + 0x00, + 0x0A, + /* First server name type - host name */ + 0x00, + /* First server name len */ + 0x00, + 0x03, + /* First server name, matches sent_server_name */ + 's', + 'v', + 'r', + /* Second server name type - host name */ + 0x00, + /* Second server name len */ + 0x00, + 0x01, + /* Second server name */ + 0xFF, + }; + int client_extensions_len = sizeof(client_extensions); + uint8_t client_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Client random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suites len */ + 0x00, + 0x02, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, + 0x3C, + /* Compression methods len */ + 0x01, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (client_extensions_len >> 8) & 0xff, + (client_extensions_len & 0xff), + }; + int body_len = sizeof(client_hello_message) + client_extensions_len; + uint8_t message_header[] = { + /* Handshake message type CLIENT HELLO */ + 0x01, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Send the client hello */ + EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.client, client_hello_message, sizeof(client_hello_message)), sizeof(client_hello_message)); + EXPECT_EQUAL(write(io_pair.client, client_extensions, sizeof(client_extensions)), sizeof(client_extensions)); + + /* Verify that we fail for duplicated extension type Bad Message */ + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + s2n_negotiate(server_conn, &server_blocked); + EXPECT_EQUAL(s2n_errno, S2N_ERR_DUPLICATE_EXTENSION); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Client sends a valid initial renegotiation_info */ + { + struct s2n_connection *server_conn; + struct s2n_config *server_config; + s2n_blocked_status server_blocked; + struct s2n_cert_chain_and_key *chain_and_key; + + uint8_t client_extensions[] = { + /* Extension type TLS_EXTENSION_RENEGOTIATION_INFO */ + 0xff, + 0x01, + /* Extension size */ + 0x00, + 0x01, + /* Empty renegotiated_connection */ + 0x00, + }; + int client_extensions_len = sizeof(client_extensions); + uint8_t client_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Client random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suites len */ + 0x00, + 0x02, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, + 0x3C, + /* Compression methods len */ + 0x01, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (client_extensions_len >> 8) & 0xff, + (client_extensions_len & 0xff), + }; + int body_len = sizeof(client_hello_message) + client_extensions_len; + uint8_t message_header[] = { + /* Handshake message type CLIENT HELLO */ + 0x01, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + /* Security policy must allow cipher suite hard coded into client hello */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Send the client hello */ + EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.client, client_hello_message, sizeof(client_hello_message)), sizeof(client_hello_message)); + EXPECT_EQUAL(write(io_pair.client, client_extensions, sizeof(client_extensions)), sizeof(client_extensions)); + + /* Verify that the CLIENT HELLO is accepted */ + s2n_negotiate(server_conn, &server_blocked); + EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); + EXPECT_EQUAL(server_conn->handshake.handshake_type & NEGOTIATED, NEGOTIATED); + + /* Verify that the that we detected secure_renegotiation */ + EXPECT_EQUAL(server_conn->secure_renegotiation, 1); + + EXPECT_SUCCESS(s2n_shutdown(server_conn, &server_blocked)); + EXPECT_TRUE(server_conn->alert_sent); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Client sends a non-empty initial renegotiation_info */ + { + struct s2n_connection *server_conn; + struct s2n_config *server_config; + s2n_blocked_status server_blocked; + struct s2n_cert_chain_and_key *chain_and_key; + uint8_t buf[5120]; + + uint8_t client_extensions[] = { + /* Extension type TLS_EXTENSION_RENEGOTIATION_INFO */ + 0xff, + 0x01, + /* Extension size */ + 0x00, + 0x21, + /* renegotiated_connection len */ + 0x20, + /* fake renegotiated_connection */ + ZERO_TO_THIRTY_ONE, + }; + int client_extensions_len = sizeof(client_extensions); + uint8_t client_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Client random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suites len */ + 0x00, + 0x02, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, + 0x3C, + /* Compression methods len */ + 0x01, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (client_extensions_len >> 8) & 0xff, + (client_extensions_len & 0xff), + }; + int body_len = sizeof(client_hello_message) + client_extensions_len; + uint8_t message_header[] = { + /* Handshake message type CLIENT HELLO */ + 0x01, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Send the client hello */ + EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.client, client_hello_message, sizeof(client_hello_message)), sizeof(client_hello_message)); + EXPECT_EQUAL(write(io_pair.client, client_extensions, sizeof(client_extensions)), sizeof(client_extensions)); + + /* Verify that we fail for non-empty renegotiated_connection */ + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + s2n_negotiate(server_conn, &server_blocked); + EXPECT_EQUAL(s2n_errno, S2N_ERR_NON_EMPTY_RENEGOTIATION_INFO); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + + /* Clear pipe since negotiation failed mid-handshake */ + EXPECT_SUCCESS(read(io_pair.client, buf, sizeof(buf))); + }; + + /* Client doesn't use the OCSP extension. */ + { + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + struct s2n_config *server_config; + uint32_t length; + + struct s2n_config *client_config; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_OCSP_STAPLING, + server_ocsp_status, sizeof(server_ocsp_status))); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server didn't send an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 0); + + /* Verify that the client didn't receive an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 0); + EXPECT_NULL(s2n_connection_get_ocsp_response(client_conn, &length)); + EXPECT_EQUAL(length, 0); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Cannot enable OCSP stapling if there's no support for it */ + if (!s2n_x509_ocsp_stapling_supported()) { + struct s2n_config *client_config; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_FAILURE(s2n_config_set_check_stapled_ocsp_response(client_config, 1)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Server doesn't support the OCSP extension. We can't run this test if ocsp isn't supported by the client. */ + if (s2n_x509_ocsp_stapling_supported()) { + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + struct s2n_config *server_config; + struct s2n_config *client_config; + uint32_t length; + struct s2n_cert_chain_and_key *chain_and_key; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server didn't send an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 0); + + /* Verify that the client didn't receive an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 0); + EXPECT_NULL(s2n_connection_get_ocsp_response(client_conn, &length)); + EXPECT_EQUAL(length, 0); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Test with s2n_config_set_extension_data(). Can be removed once API is deprecated */ + if (s2n_x509_ocsp_stapling_supported()) { + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + struct s2n_config *server_config; + struct s2n_config *client_config; + const uint8_t *server_ocsp_reply; + uint32_t length; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_OCSP_STAPLING, + server_ocsp_status, sizeof(server_ocsp_status))); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server sent an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 1); + + /* Verify that the client received an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 1); + EXPECT_NOT_NULL(server_ocsp_reply = s2n_connection_get_ocsp_response(client_conn, &length)); + EXPECT_EQUAL(length, sizeof(server_ocsp_status)); + + for (size_t i = 0; i < sizeof(server_ocsp_status); i++) { + EXPECT_EQUAL(server_ocsp_reply[i], server_ocsp_status[i]); + } + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Server and client support the OCSP extension. Test only runs if ocsp stapled responses are supported by the client */ + if (s2n_x509_ocsp_stapling_supported()) { + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + struct s2n_config *server_config; + struct s2n_config *client_config; + const uint8_t *server_ocsp_reply; + uint32_t length; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_OCSP_STAPLING, + server_ocsp_status, sizeof(server_ocsp_status))); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server sent an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 1); + + /* Verify that the client received an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 1); + EXPECT_NOT_NULL(server_ocsp_reply = s2n_connection_get_ocsp_response(client_conn, &length)); + EXPECT_EQUAL(length, sizeof(server_ocsp_status)); + + for (size_t i = 0; i < sizeof(server_ocsp_status); i++) { + EXPECT_EQUAL(server_ocsp_reply[i], server_ocsp_status[i]); + } + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Server and client support the OCSP extension. Test Behavior for TLS 1.3 */ + if (s2n_x509_ocsp_stapling_supported() && s2n_is_tls13_fully_supported()) { + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + struct s2n_config *server_config; + struct s2n_config *client_config; + const uint8_t *server_ocsp_reply; + uint32_t length; + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS13; + client_conn->server_protocol_version = S2N_TLS13; + client_conn->client_protocol_version = S2N_TLS13; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->server_protocol_version = S2N_TLS13; + server_conn->client_protocol_version = S2N_TLS13; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_OCSP_STAPLING, + server_ocsp_status, sizeof(server_ocsp_status))); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server sent an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(server_conn), 1); + + /* Verify that the client received an OCSP response. */ + EXPECT_EQUAL(s2n_connection_is_ocsp_stapled(client_conn), 1); + + EXPECT_NOT_NULL(server_ocsp_reply = s2n_connection_get_ocsp_response(client_conn, &length)); + EXPECT_EQUAL(length, sizeof(server_ocsp_status)); + + for (size_t i = 0; i < sizeof(server_ocsp_status); i++) { + EXPECT_EQUAL(server_ocsp_reply[i], server_ocsp_status[i]); + } + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + + /* Client does not request SCT, but server is configured to serve them. */ + { + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + struct s2n_config *server_config; + + uint32_t length; + + struct s2n_config *client_config; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, + sct_list, sizeof(sct_list))); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the client did *not* receive an SCT list */ + EXPECT_NULL(s2n_connection_get_sct_list(client_conn, &length)); + EXPECT_EQUAL(length, 0); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Client requests SCT and server does have it. */ + { + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + struct s2n_config *client_config; + struct s2n_config *server_config; + + uint32_t length; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + /* Indicate that the client wants CT if available */ + EXPECT_SUCCESS(s2n_config_set_ct_support_level(client_config, S2N_CT_SUPPORT_REQUEST)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(server_config, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_set_extension_data(server_config, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, + sct_list, sizeof(sct_list))); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the client did receive an SCT list */ + EXPECT_NOT_NULL(s2n_connection_get_sct_list(client_conn, &length)); + EXPECT_EQUAL(length, sizeof(sct_list)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client requests SCT and server does *not* have it. */ + { + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + struct s2n_config *client_config; + struct s2n_config *server_config; + struct s2n_cert_chain_and_key *chain_and_key; + + uint32_t length; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + /* Indicate that the client wants CT if available */ + EXPECT_SUCCESS(s2n_config_set_ct_support_level(client_config, S2N_CT_SUPPORT_REQUEST)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the client does not get a list */ + EXPECT_NULL(s2n_connection_get_sct_list(client_conn, &length)); + EXPECT_EQUAL(length, 0); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client requests 512, 1024, 2048, and 4096 maximum fragment lengths */ + for (uint8_t mfl_code = S2N_TLS_MAX_FRAG_LEN_512; mfl_code <= S2N_TLS_MAX_FRAG_LEN_4096; mfl_code++) { + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + struct s2n_config *server_config; + struct s2n_config *client_config; + struct s2n_cert_chain_and_key *chain_and_key; + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(client_config, mfl_code)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(server_config)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Preference should be ignored as the TlS Maximum Fragment Length Extension is Set */ + EXPECT_SUCCESS(s2n_connection_prefer_throughput(server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_EQUAL(server_conn->max_outgoing_fragment_length, mfl_code_to_length[mfl_code]); + EXPECT_EQUAL(server_conn->negotiated_mfl_code, mfl_code); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Client requests invalid maximum fragment length */ + { + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + struct s2n_config *server_config; + struct s2n_config *client_config; + struct s2n_cert_chain_and_key *chain_and_key; + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_FAILURE(s2n_config_send_max_fragment_length(client_config, 5)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(server_config)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* check that max_fragment_length did not get set due to invalid mfl_code */ + EXPECT_EQUAL(server_conn->max_outgoing_fragment_length, S2N_DEFAULT_FRAGMENT_LENGTH); + EXPECT_EQUAL(server_conn->negotiated_mfl_code, S2N_TLS_MAX_FRAG_LEN_EXT_NONE); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Server ignores client's request of S2N_TLS_MAX_FRAG_LEN_2048 maximum fragment length when accept_mfl is not set*/ + { + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + struct s2n_config *server_config; + struct s2n_config *client_config; + struct s2n_cert_chain_and_key *chain_and_key; + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(client_config, S2N_TLS_MAX_FRAG_LEN_2048)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* check that max_fragment_length did not get set since accept_mfl is not set */ + EXPECT_EQUAL(server_conn->max_outgoing_fragment_length, S2N_DEFAULT_FRAGMENT_LENGTH); + EXPECT_EQUAL(server_conn->negotiated_mfl_code, S2N_TLS_MAX_FRAG_LEN_EXT_NONE); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* All PQ KEM byte values are from https://tools.ietf.org/html/draft-campagna-tls-bike-sike-hybrid */ + { + /* Client requests Kyber ciphersuite but sends only PQ KEM extensions with bogus + * extension IDs; server is using the round 3 preference list. Expect to negotiate no KEM (-1) whether or + * not PQ is enabled. */ + int expected_kem_id = -1; + + uint8_t client_extensions[] = { + /* Extension type pq_kem_parameters */ + 0xFE, + 0x01, + /* Extension size */ + 0x00, + 0x08, + /* KEM names len */ + 0x00, + 0x06, + /* KEM values out of range of anything s2n supports */ + 0xcc, + 0x05, + 0xaa, + 0xbb, + 0xff, + 0xa1, + }; + size_t client_extensions_len = sizeof(client_extensions); + uint8_t client_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Client random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suites len */ + 0x00, + 0x02, + /* Cipher suite - TLS_ECDHE_KYBER_RSA_WITH_AES_256_GCM_SHA384 */ + 0xFF, + 0x0C, + /* Compression methods len */ + 0x01, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (client_extensions_len >> 8) & 0xff, + (client_extensions_len & 0xff), + }; + size_t client_hello_len = sizeof(client_hello_message); + EXPECT_SUCCESS(negotiate_kem(client_extensions, client_extensions_len, client_hello_message, + client_hello_len, "PQ-TLS-1-0-2021-05-24", expected_kem_id, &io_pair)); + }; + + { + /* Client sends PQ KEM extension with BIKE extensions, but requests SIKE ciphersuite; + * server is using the round 1 only preference list. Expect to negotiate no KEM (-1) + * whether or not PQ is enabled. */ + int expected_kem_id = -1; + + uint8_t client_extensions[] = { + /* Extension type pq_kem_parameters */ + 0xFE, + 0x01, + /* Extension size */ + 0x00, + 0x06, + /* KEM names len */ + 0x00, + 0x04, + /* BIKE1_L1_R1 */ + 0x00, + 0x01, + /* BIKE1_L1_R2 */ + 0x00, + 0x0D, + }; + size_t client_extensions_len = sizeof(client_extensions); + uint8_t client_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Client random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suites len */ + 0x00, + 0x02, + /* Cipher suite - TLS_ECDHE_SIKE_RSA_WITH_AES_256_GCM_SHA384 */ + 0xFF, + 0x08, + /* Compression methods len */ + 0x01, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (client_extensions_len >> 8) & 0xff, + (client_extensions_len & 0xff), + }; + size_t client_hello_len = sizeof(client_hello_message); + EXPECT_SUCCESS(negotiate_kem(client_extensions, client_extensions_len, client_hello_message, + client_hello_len, "KMS-PQ-TLS-1-0-2019-06", expected_kem_id, &io_pair)); + }; + + { + /* Client sends PQ KEM extensions for round 2 only; the server is using the round 1 + * only preference list. Expect to negotiate no KEM (-1) whether or not PQ is enabled. */ + int expected_kem_id = -1; + + uint8_t client_extensions[] = { + /* Extension type pq_kem_parameters */ + 0xFE, + 0x01, + /* Extension size */ + 0x00, + 0x06, + /* KEM names len */ + 0x00, + 0x04, + /* SIKE_P434_R3 */ + 0x00, + 0x13, + /* BIKE1_L1_R2 */ + 0x00, + 0x0D, + }; + size_t client_extensions_len = sizeof(client_extensions); + uint8_t client_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Client random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suites len */ + 0x00, + 0x02, + /* Cipher suite - TLS_ECDHE_SIKE_RSA_WITH_AES_256_GCM_SHA384 */ + 0xFF, + 0x08, + /* Compression methods len */ + 0x01, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (client_extensions_len >> 8) & 0xff, + (client_extensions_len & 0xff), + }; + size_t client_hello_len = sizeof(client_hello_message); + EXPECT_SUCCESS(negotiate_kem(client_extensions, client_extensions_len, client_hello_message, + client_hello_len, "KMS-PQ-TLS-1-0-2019-06", expected_kem_id, &io_pair)); + }; + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + free(cert_chain); + free(private_key); + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_client_finished_test.c b/tests/unit/s2n_client_finished_test.c new file mode 100644 index 00000000000..c997773456a --- /dev/null +++ b/tests/unit/s2n_client_finished_test.c @@ -0,0 +1,96 @@ +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_tls.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Send and receive correct ClientFinished */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + server_conn->secure->cipher_suite = &s2n_ecdhe_rsa_with_aes_256_gcm_sha384; + server_conn->actual_protocol_version = S2N_TLS12; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + client_conn->secure->cipher_suite = &s2n_ecdhe_rsa_with_aes_256_gcm_sha384; + client_conn->actual_protocol_version = S2N_TLS12; + + /* Calculate valid verify_data */ + POSIX_GUARD(s2n_prf_client_finished(server_conn)); + + EXPECT_EQUAL(client_conn->client, client_conn->initial); + + EXPECT_SUCCESS(s2n_client_finished_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_finished_recv(server_conn)); + + EXPECT_EQUAL(client_conn->client, client_conn->secure); + }; + + /* Server rejects incorrect ClientFinished */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + server_conn->secure->cipher_suite = &s2n_ecdhe_rsa_with_aes_256_gcm_sha384; + server_conn->actual_protocol_version = S2N_TLS12; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + client_conn->secure->cipher_suite = &s2n_ecdhe_rsa_with_aes_256_gcm_sha384; + client_conn->actual_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_client_finished_send(client_conn)); + + /* s2n_client_finished_send calculates and writes the handshake verify data + * to the io stuffer. Since the verify data is at least 12 bytes long + * we can flip a random bit in that range to mutate it. */ + client_conn->handshake.io.blob.data[10]++; + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_finished_recv(server_conn), S2N_ERR_BAD_MESSAGE); + }; + + /* Error if local verify_data has wrong length */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + server_conn->secure->cipher_suite = &s2n_ecdhe_rsa_with_aes_256_gcm_sha384; + server_conn->actual_protocol_version = S2N_TLS12; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + client_conn->secure->cipher_suite = &s2n_ecdhe_rsa_with_aes_256_gcm_sha384; + client_conn->actual_protocol_version = S2N_TLS12; + + /* Change the length of valid verify_data */ + POSIX_GUARD(s2n_prf_client_finished(server_conn)); + server_conn->handshake.finished_len = 1; + + EXPECT_SUCCESS(s2n_client_finished_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_finished_recv(server_conn), S2N_ERR_SAFETY); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_client_hello_get_supported_groups_test.c b/tests/unit/s2n_client_hello_get_supported_groups_test.c new file mode 100644 index 00000000000..8cb7de97f2b --- /dev/null +++ b/tests/unit/s2n_client_hello_get_supported_groups_test.c @@ -0,0 +1,388 @@ +/* +* 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 "pq-crypto/s2n_pq.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_client_supported_groups.h" +#include "tls/s2n_client_hello.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_random.h" + +#define S2N_TEST_SUPPORTED_GROUPS_LIST_COUNT 16 + +/* Each supported group is 2 bytes. */ +#define S2N_TEST_SUPPORTED_GROUPS_LIST_SIZE (S2N_TEST_SUPPORTED_GROUPS_LIST_COUNT * S2N_SUPPORTED_GROUP_SIZE) + +/* 2 length bytes + space for the list of supported groups. */ +#define S2N_TEST_SUPPORTED_GROUPS_EXTENSION_SIZE (2 + S2N_TEST_SUPPORTED_GROUPS_LIST_SIZE) + +struct s2n_client_hello_context { + struct s2n_stuffer *sent_supported_groups_extension; + int invoked_count; +}; + +int s2n_check_received_supported_groups_cb(struct s2n_connection *conn, void *ctx) +{ + EXPECT_NOT_NULL(ctx); + + struct s2n_client_hello_context *context = (struct s2n_client_hello_context *) ctx; + EXPECT_NOT_NULL(context->sent_supported_groups_extension); + context->invoked_count += 1; + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(conn); + EXPECT_NOT_NULL(client_hello); + + uint16_t received_groups[S2N_TEST_SUPPORTED_GROUPS_LIST_COUNT] = { 0 }; + uint16_t received_groups_count = 0; + EXPECT_SUCCESS(s2n_client_hello_get_supported_groups(client_hello, received_groups, + s2n_array_len(received_groups), &received_groups_count)); + + uint16_t sent_groups_count = 0; + EXPECT_OK(s2n_supported_groups_parse_count(context->sent_supported_groups_extension, &sent_groups_count)); + EXPECT_EQUAL(received_groups_count, sent_groups_count); + + for (size_t i = 0; i < received_groups_count; i++) { + uint16_t received_group = received_groups[i]; + + /* s2n_stuffer_read_uint16 is used to read each of the sent supported groups in + * network-order endianness, and compare them to the received supported groups which have + * already been converted to the machine's endianness. + */ + uint16_t sent_group = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(context->sent_supported_groups_extension, &sent_group)); + + EXPECT_EQUAL(received_group, sent_group); + } + + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + s2n_extension_type_id supported_groups_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(S2N_EXTENSION_SUPPORTED_GROUPS, &supported_groups_id)); + + /* Safety */ + { + struct s2n_client_hello client_hello = { 0 }; + uint16_t supported_groups[S2N_TEST_SUPPORTED_GROUPS_LIST_COUNT] = { 0 }; + uint16_t supported_groups_count = 0; + + int ret = s2n_client_hello_get_supported_groups(NULL, supported_groups, s2n_array_len(supported_groups), + &supported_groups_count); + EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_NULL); + EXPECT_EQUAL(supported_groups_count, 0); + + ret = s2n_client_hello_get_supported_groups(&client_hello, NULL, s2n_array_len(supported_groups), + &supported_groups_count); + EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_NULL); + EXPECT_EQUAL(supported_groups_count, 0); + + ret = s2n_client_hello_get_supported_groups(&client_hello, supported_groups, s2n_array_len(supported_groups), NULL); + EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_NULL); + EXPECT_EQUAL(supported_groups_count, 0); + } + + /* Ensure that the maximum size of the provided supported groups list is respected. */ + { + struct s2n_client_hello client_hello = { 0 }; + + uint8_t extension_data[S2N_TEST_SUPPORTED_GROUPS_EXTENSION_SIZE] = { 0 }; + struct s2n_blob extension_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&extension_blob, extension_data, sizeof(extension_data))); + + s2n_parsed_extension *supported_groups_extension = &client_hello.extensions.parsed_extensions[supported_groups_id]; + supported_groups_extension->extension_type = S2N_EXTENSION_SUPPORTED_GROUPS; + supported_groups_extension->extension = extension_blob; + + struct s2n_stuffer extension_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&extension_stuffer, &extension_blob)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&extension_stuffer, S2N_TEST_SUPPORTED_GROUPS_LIST_SIZE)); + + uint16_t supported_groups[S2N_TEST_SUPPORTED_GROUPS_LIST_COUNT] = { 0 }; + uint16_t supported_groups_count = 0; + + /* Fail if the provided buffer is too small. */ + int ret = s2n_client_hello_get_supported_groups(&client_hello, supported_groups, + S2N_TEST_SUPPORTED_GROUPS_LIST_COUNT - 1, &supported_groups_count); + EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_INSUFFICIENT_MEM_SIZE); + EXPECT_EQUAL(supported_groups_count, 0); + + EXPECT_SUCCESS(s2n_stuffer_reread(&extension_stuffer)); + + /* Succeed with a correctly sized buffer. */ + EXPECT_SUCCESS(s2n_client_hello_get_supported_groups(&client_hello, supported_groups, + S2N_TEST_SUPPORTED_GROUPS_LIST_COUNT, &supported_groups_count)); + EXPECT_EQUAL(supported_groups_count, S2N_TEST_SUPPORTED_GROUPS_LIST_COUNT); + } + + /* Error if the client hello isn't parsed yet. */ + { + struct s2n_client_hello client_hello = { 0 }; + + uint16_t supported_groups[S2N_TEST_SUPPORTED_GROUPS_LIST_COUNT] = { 0 }; + uint16_t supported_groups_count = 0; + int ret = s2n_client_hello_get_supported_groups(&client_hello, supported_groups, + s2n_array_len(supported_groups), &supported_groups_count); + EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_EXTENSION_NOT_RECEIVED); + } + + /* Error if a supported groups extension wasn't received. */ + for (int disable_ecc = 0; disable_ecc <= 1; disable_ecc++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + if (disable_ecc) { + /* The 20150202 security policy doesn't contain any ECDHE cipher suites, so the + * supported groups extension won't be sent. + */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20150202")); + } else { + /* The 20170210 security policy contains ECDHE cipher suites, so the supported groups + * extension will be sent. + */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20170210")); + } + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client->handshake.io, &server->handshake.io, + s2n_stuffer_data_available(&client->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server)); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server); + EXPECT_NOT_NULL(client_hello); + + bool supported_groups_extension_exists = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(client_hello, S2N_EXTENSION_SUPPORTED_GROUPS, + &supported_groups_extension_exists)); + EXPECT_EQUAL(supported_groups_extension_exists, !disable_ecc); + + uint16_t supported_groups[S2N_TEST_SUPPORTED_GROUPS_LIST_COUNT] = { 0 }; + uint16_t supported_groups_count = 0; + int ret = s2n_client_hello_get_supported_groups(client_hello, supported_groups, s2n_array_len(supported_groups), + &supported_groups_count); + + if (disable_ecc) { + EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_EXTENSION_NOT_RECEIVED); + EXPECT_EQUAL(supported_groups_count, 0); + } else { + EXPECT_SUCCESS(ret); + + /* The 20170210 security policy contains 2 ECC curves. */ + EXPECT_EQUAL(supported_groups_count, 2); + } + } + + /* Test parsing a supported groups extension with a malformed groups list length. */ + { + struct s2n_client_hello client_hello = { 0 }; + + s2n_parsed_extension *supported_groups_extension = &client_hello.extensions.parsed_extensions[supported_groups_id]; + supported_groups_extension->extension_type = S2N_EXTENSION_SUPPORTED_GROUPS; + + /* Test parsing a correct groups list length */ + { + uint8_t extension_data[S2N_TEST_SUPPORTED_GROUPS_EXTENSION_SIZE] = { 0 }; + struct s2n_blob extension_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&extension_blob, extension_data, sizeof(extension_data))); + supported_groups_extension->extension = extension_blob; + + struct s2n_stuffer extension_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&extension_stuffer, &extension_blob)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&extension_stuffer, S2N_TEST_SUPPORTED_GROUPS_LIST_SIZE)); + + uint16_t supported_groups[S2N_TEST_SUPPORTED_GROUPS_LIST_COUNT] = { 0 }; + uint16_t supported_groups_count = 0; + EXPECT_SUCCESS(s2n_client_hello_get_supported_groups(&client_hello, supported_groups, + s2n_array_len(supported_groups), &supported_groups_count)); + + EXPECT_EQUAL(supported_groups_count, S2N_TEST_SUPPORTED_GROUPS_LIST_COUNT); + } + + /* Test parsing a groups list length that is larger than the extension length */ + { + uint8_t extension_data[S2N_TEST_SUPPORTED_GROUPS_EXTENSION_SIZE] = { 0 }; + struct s2n_blob extension_blob = { 0 }; + uint32_t extension_too_small_size = S2N_TEST_SUPPORTED_GROUPS_EXTENSION_SIZE - 2; + EXPECT_SUCCESS(s2n_blob_init(&extension_blob, extension_data, extension_too_small_size)); + supported_groups_extension->extension = extension_blob; + + struct s2n_stuffer extension_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&extension_stuffer, &extension_blob)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&extension_stuffer, S2N_TEST_SUPPORTED_GROUPS_LIST_SIZE)); + + uint16_t supported_groups[S2N_TEST_SUPPORTED_GROUPS_LIST_COUNT] = { 0 }; + uint16_t supported_groups_count = 0; + int ret = s2n_client_hello_get_supported_groups(&client_hello, supported_groups, + s2n_array_len(supported_groups), &supported_groups_count); + EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_INVALID_PARSED_EXTENSIONS); + + EXPECT_EQUAL(supported_groups_count, 0); + } + + /* Test parsing a groups list that contains a malformed supported group */ + { + uint8_t extension_data[S2N_TEST_SUPPORTED_GROUPS_EXTENSION_SIZE] = { 0 }; + struct s2n_blob extension_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&extension_blob, extension_data, sizeof(extension_data))); + supported_groups_extension->extension = extension_blob; + + struct s2n_stuffer extension_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&extension_stuffer, &extension_blob)); + + uint16_t one_and_a_half_groups_size = 3; + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&extension_stuffer, one_and_a_half_groups_size)); + + uint16_t supported_groups[S2N_TEST_SUPPORTED_GROUPS_LIST_COUNT] = { 0 }; + uint16_t supported_groups_count = 0; + int ret = s2n_client_hello_get_supported_groups(&client_hello, supported_groups, + s2n_array_len(supported_groups), &supported_groups_count); + EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_INVALID_PARSED_EXTENSIONS); + + EXPECT_EQUAL(supported_groups_count, 0); + } + } + + /* Ensure that the supported groups in the client hello are written to the output array. */ + { + struct s2n_client_hello client_hello = { 0 }; + + s2n_parsed_extension *supported_groups_extension = &client_hello.extensions.parsed_extensions[supported_groups_id]; + supported_groups_extension->extension_type = S2N_EXTENSION_SUPPORTED_GROUPS; + + for (uint16_t test_groups_count = 0; test_groups_count < S2N_TEST_SUPPORTED_GROUPS_LIST_COUNT; test_groups_count++) { + uint16_t test_groups_list_size = test_groups_count * 2; + + uint8_t test_groups_list_data[S2N_TEST_SUPPORTED_GROUPS_LIST_SIZE] = { 0 }; + struct s2n_blob test_groups_list_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&test_groups_list_blob, test_groups_list_data, test_groups_list_size)); + EXPECT_OK(s2n_get_public_random_data(&test_groups_list_blob)); + + uint8_t extension_data[S2N_TEST_SUPPORTED_GROUPS_EXTENSION_SIZE] = { 0 }; + struct s2n_blob extension_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&extension_blob, extension_data, sizeof(extension_data))); + supported_groups_extension->extension = extension_blob; + + struct s2n_stuffer extension_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&extension_stuffer, &extension_blob)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&extension_stuffer, test_groups_list_size)); + EXPECT_SUCCESS(s2n_stuffer_write(&extension_stuffer, &test_groups_list_blob)); + + uint16_t supported_groups[S2N_TEST_SUPPORTED_GROUPS_LIST_COUNT] = { 0 }; + uint16_t supported_groups_count = 0; + EXPECT_SUCCESS(s2n_client_hello_get_supported_groups(&client_hello, supported_groups, + s2n_array_len(supported_groups), &supported_groups_count)); + EXPECT_EQUAL(supported_groups_count, test_groups_count); + + struct s2n_stuffer test_groups_list_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init_written(&test_groups_list_stuffer, &test_groups_list_blob)); + + for (size_t i = 0; i < supported_groups_count; i++) { + uint16_t test_group = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&test_groups_list_stuffer, &test_group)); + uint16_t written_group = supported_groups[i]; + + EXPECT_EQUAL(test_group, written_group); + } + } + } + + /* Self-talk: Ensure that the retrieved supported groups match what was sent by the client. + * + * This test also ensures that s2n_client_hello_get_supported_groups is usable from within the + * client hello callback. + */ + { + /* Test security policies with a range of different ECC curves and KEM groups. */ + const char *policies[] = { + "20170210", + "20190801", + "AWS-CRT-SDK-TLSv1.2-2023", + "20230317", + "20210816", + "PQ-TLS-1-0-2021-05-20", + "PQ-TLS-1-2-2023-04-08", + "test_all" + }; + + for (int version_index = 0; version_index < s2n_array_len(policies); version_index++) { + const char *policy = policies[version_index]; + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, policy)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(server_config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, policy)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + struct s2n_client_hello_context context = { + .invoked_count = 0, + }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(server_config, s2n_check_received_supported_groups_cb, + &context)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + uint8_t sent_supported_groups_data[S2N_TEST_SUPPORTED_GROUPS_EXTENSION_SIZE]; + struct s2n_blob sent_supported_groups_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&sent_supported_groups_blob, sent_supported_groups_data, + s2n_array_len(sent_supported_groups_data))); + + struct s2n_stuffer sent_supported_groups_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&sent_supported_groups_stuffer, &sent_supported_groups_blob)); + EXPECT_SUCCESS(s2n_client_supported_groups_extension.send(client_conn, &sent_supported_groups_stuffer)); + context.sent_supported_groups_extension = &sent_supported_groups_stuffer; + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_EQUAL(context.invoked_count, 1); + } + } + + END_TEST(); +} diff --git a/tests/unit/s2n_client_hello_recv_test.c b/tests/unit/s2n_client_hello_recv_test.c new file mode 100644 index 00000000000..a0698945ee8 --- /dev/null +++ b/tests/unit/s2n_client_hello_recv_test.c @@ -0,0 +1,522 @@ +/* + * 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 +#include +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_sslv2_client_hello.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_client_hello.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_quic_support.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_safety.h" + +int main(int argc, char **argv) +{ + struct s2n_connection *server_conn; + struct s2n_connection *client_conn; + struct s2n_stuffer *hello_stuffer; + struct s2n_config *tls12_config; + struct s2n_config *tls13_config; + struct s2n_cert_chain_and_key *chain_and_key; + struct s2n_cert_chain_and_key *tls13_chain_and_key; + char *cert_chain; + char *tls13_cert_chain; + char *private_key; + char *tls13_private_key; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(tls12_config = s2n_config_new()); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls12_config, "test_all_tls12")); + + EXPECT_NOT_NULL(tls13_cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(tls13_private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(tls13_config = s2n_config_new()); + EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls13_config, "test_all")); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(tls12_config, chain_and_key)); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(tls13_config, tls13_chain_and_key)); + + /* These tests verify the logic behind the setting of these three connection fields: + server_protocol_version, client_protocol_version, and actual_protocol version. */ + + /* Test we can successfully receive an sslv2 client hello and set a + * tls12 connection */ + for (uint8_t i = 0; i < 2; i++) { + if (i == 1) { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + } + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); + + /* Record version and protocol version are in the header for SSLv2 */ + server_conn->client_hello_version = S2N_SSLv2; + server_conn->client_protocol_version = S2N_TLS12; + + uint8_t sslv2_client_hello[] = { + SSLv2_CLIENT_HELLO_PREFIX, + SSLv2_CLIENT_HELLO_CIPHER_SUITES, + SSLv2_CLIENT_HELLO_CHALLENGE, + }; + + struct s2n_blob client_hello = { + .data = sslv2_client_hello, + .size = sizeof(sslv2_client_hello), + .allocated = 0, + .growable = 0 + }; + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_hello)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(server_conn->server_protocol_version, i == 0 ? S2N_TLS12 : S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_hello_version, S2N_SSLv2); + EXPECT_EQUAL(server_conn->client_hello.callback_invoked, 1); + + s2n_connection_free(server_conn); + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + + /* Test that a tls12 client legacy version and tls12 server version + will successfully set a tls12 connection, since tls13 is not enabled. */ + { + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->client_hello_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); + + s2n_connection_free(server_conn); + s2n_connection_free(client_conn); + }; + + /* Test that a tls12 client legacy version and tls12 server version + will successfully set a tls12 connection, even when tls13 is enabled. */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->client_hello_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_hello_version, S2N_TLS12); + + s2n_connection_free(server_conn); + s2n_connection_free(client_conn); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* Test that a tls11 client legacy version and tls12 server version + will successfully set a tls11 connection. */ + for (uint8_t i = 0; i < 2; i++) { + if (i == 1) { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + } + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); + + client_conn->client_protocol_version = S2N_TLS11; + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_EQUAL(client_conn->client_hello_version, S2N_TLS11); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS11); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS11); + EXPECT_EQUAL(server_conn->client_hello_version, S2N_TLS11); + + s2n_connection_free(server_conn); + s2n_connection_free(client_conn); + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + /* Test that a tls12 client and tls13 server will successfully + set a tls12 connection. */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->client_hello_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_hello_version, S2N_TLS12); + + s2n_connection_free(server_conn); + s2n_connection_free(client_conn); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + /* Test that an erroneous client legacy version and tls13 server version + will still successfully set a tls13 connection, when real client version is tls13. */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + + hello_stuffer = &client_conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + + /* Overwrite the client legacy version so that it reads tls13 (incorrectly) */ + uint8_t incorrect_protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; + incorrect_protocol_version[0] = S2N_TLS13 / 10; + incorrect_protocol_version[1] = S2N_TLS13 % 10; + EXPECT_SUCCESS(s2n_stuffer_rewrite(hello_stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(hello_stuffer, incorrect_protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &hello_stuffer->blob)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(client_conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(client_conn->client_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(client_conn->client_hello_version, S2N_TLS12); + + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(server_conn->client_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(server_conn->client_hello_version, S2N_TLS12); + + s2n_connection_free(server_conn); + s2n_connection_free(client_conn); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* Test that a tls12 client legacy version and tls13 server version + will still successfully set a tls13 connection, if possible. */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_conn->handshake.io.blob)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(server_conn->client_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(server_conn->client_hello_version, S2N_TLS12); + + s2n_connection_free(server_conn); + s2n_connection_free(client_conn); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + /* Test that an erroneous(tls13) client legacy version and tls13 server version + will still successfully set a tls12 connection, if tls12 is the true client version. */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + + hello_stuffer = &client_conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + + /* Overwrite the client legacy version so that it reads tls13 (incorrectly) */ + uint8_t incorrect_protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN]; + incorrect_protocol_version[0] = S2N_TLS13 / 10; + incorrect_protocol_version[1] = S2N_TLS13 % 10; + EXPECT_SUCCESS(s2n_stuffer_rewrite(hello_stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(hello_stuffer, incorrect_protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &hello_stuffer->blob)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(server_conn->server_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_hello_version, S2N_TLS12); + + s2n_connection_free(server_conn); + s2n_connection_free(client_conn); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* s2n receiving a client hello will error when parsing an empty cipher suite */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + + hello_stuffer = &client_conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + + uint8_t empty_cipher_suite[S2N_TLS_CIPHER_SUITE_LEN] = { 0 }; + + /* Move write_cursor to cipher_suite position */ + EXPECT_SUCCESS(s2n_stuffer_rewrite(hello_stuffer)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(hello_stuffer, S2N_TLS_PROTOCOL_VERSION_LEN + S2N_TLS_RANDOM_DATA_LEN + 1)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(hello_stuffer, empty_cipher_suite, S2N_TLS_CIPHER_SUITE_LEN)); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &hello_stuffer->blob)); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_BAD_MESSAGE); + + s2n_connection_free(server_conn); + s2n_connection_free(client_conn); + }; + + /* s2n receiving a sslv2 client hello will error when parsing an empty cipher suite */ + { + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_config)); + + /* Record version and protocol version are in the header for SSLv2 */ + server_conn->client_hello_version = S2N_SSLv2; + server_conn->client_protocol_version = S2N_TLS12; + + /* Writing a sslv2 client hello with a length 0 cipher suite list */ + uint8_t sslv2_client_hello[] = { + 0x00, + 0x00, + 0x00, + 0x00, + 0x00, + 0x20, + SSLv2_CLIENT_HELLO_CIPHER_SUITES, + SSLv2_CLIENT_HELLO_CHALLENGE, + }; + + struct s2n_blob client_hello = { + .data = sslv2_client_hello, + .size = sizeof(sslv2_client_hello), + .allocated = 0, + .growable = 0 + }; + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &client_hello)); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_BAD_MESSAGE); + + s2n_connection_free(server_conn); + }; + + /* Test that S2N will accept a ClientHello with legacy_session_id set when running with QUIC. + * Since this requirement is a SHOULD, we're accepting it for non-compliant endpoints. + * https://tools.ietf.org/html/draft-ietf-quic-tls-32#section-8.4*/ + if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_reset_tls13_in_test()); + + const size_t test_session_id_len = 10; + + struct s2n_config *quic_config; + EXPECT_NOT_NULL(quic_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_enable_quic(quic_config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(quic_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(quic_config, "default_tls13")); + + /* Succeeds without a session id */ + { + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, quic_config)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, quic_config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + s2n_connection_free(client_conn); + s2n_connection_free(server_conn); + }; + + /* Also, succeeds with a session id */ + { + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, quic_config)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, quic_config)); + + /* Directly set session id, which is not set by default when using QUIC */ + client_conn->session_id_len = test_session_id_len; + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + s2n_connection_free(client_conn); + s2n_connection_free(server_conn); + }; + + s2n_config_free(quic_config); + } + + /* Test that the server will not choose a signature algorithm or certificate if using PSKs */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + struct s2n_psk chosen_psk = { 0 }; + chosen_psk.hmac_alg = S2N_HMAC_SHA256; + server_conn->psk_params.chosen_psk = &chosen_psk; + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(server_conn->handshake_params.server_cert_sig_scheme->iana_value, 0); + EXPECT_NULL(server_conn->handshake_params.our_chain_and_key); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test that curve selection will be NIST P-256 when tls12 client does not send curve extension. + * + *= https://tools.ietf.org/rfc/rfc4492#section-4 + *= type=test + *# A client that proposes ECC cipher suites may choose not to include these extensions. + *# In this case, the server is free to choose any one of the elliptic curves or point formats listed in Section 5. + */ + { + S2N_BLOB_FROM_HEX(tls12_client_hello_no_curves, + /* clang-format off */ + "030307de81928fe1" "7cba77904c2798da" "2521a76b013a16e4" "21ade32208f658d4" "327d000048000400" + "05000a0016002f00" "3300350039003c00" "3d0067006b009c00" "9d009e009fc009c0" "0ac011c012c013c0" + "14c023c024c027c0" "28c02bc02cc02fc0" "30cca8cca9ccaaff" "04ff0800ff010000" "30000d0016001404" + "0105010601030104" "0305030603030302" "010203000b000201" "00fe01000c000a00" "17000d0013000100" + "0a" /* clang-format on */); + + /* The above code is generated the following code, + disabling s2n_client_supported_groups_extension + from client_hello_extensions (s2n_extension_type_lists.c) + and exporting the resulting client_conn->handshake.io.blob + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_config)); + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->client_hello_version, S2N_TLS12); + s2n_connection_free(client_conn); + */ + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls13_config)); + + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &tls12_client_hello_no_curves)); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + /* ensure negotiated_curve == secp256r1 for maximum client compatibility */ + EXPECT_EQUAL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve, &s2n_ecc_curve_secp256r1); + + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_hello_version, S2N_TLS12); + + s2n_connection_free(server_conn); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + s2n_config_free(tls12_config); + s2n_config_free(tls13_config); + s2n_cert_chain_and_key_free(chain_and_key); + free(cert_chain); + free(private_key); + s2n_cert_chain_and_key_free(tls13_chain_and_key); + free(tls13_cert_chain); + free(tls13_private_key); + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_client_hello_request_test.c b/tests/unit/s2n_client_hello_request_test.c new file mode 100644 index 00000000000..f1d3743b6f0 --- /dev/null +++ b/tests/unit/s2n_client_hello_request_test.c @@ -0,0 +1,562 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_tls.h" + +static const uint8_t hello_request_msg[] = { + /* message header */ + TLS_HELLO_REQUEST, /* msg_type = hello_request */ + 0, 0, 0, /* length = 0 */ + /* empty message body */ +}; + +static S2N_RESULT s2n_test_send_and_recv(struct s2n_connection *send_conn, struct s2n_connection *recv_conn) +{ + RESULT_ENSURE_REF(send_conn); + RESULT_ENSURE_REF(recv_conn); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + const uint8_t send_data[] = "hello world"; + ssize_t send_size = s2n_send(send_conn, send_data, sizeof(send_data), &blocked); + RESULT_GUARD_POSIX(send_size); + RESULT_ENSURE_EQ(send_size, sizeof(send_data)); + + uint8_t recv_data[sizeof(send_data)] = { 0 }; + ssize_t recv_size = s2n_recv(recv_conn, recv_data, send_size, &blocked); + RESULT_GUARD_POSIX(recv_size); + RESULT_ENSURE_EQ(recv_size, send_size); + EXPECT_BYTEARRAY_EQUAL(recv_data, send_data, send_size); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_send_client_hello_request(struct s2n_connection *server_conn) +{ + RESULT_ENSURE_REF(server_conn); + + DEFER_CLEANUP(struct s2n_blob message_blob = { 0 }, s2n_free); + RESULT_GUARD_POSIX(s2n_realloc(&message_blob, sizeof(hello_request_msg))); + RESULT_CHECKED_MEMCPY(message_blob.data, hello_request_msg, message_blob.size); + + /* Send */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + RESULT_GUARD(s2n_record_write(server_conn, TLS_HANDSHAKE, &message_blob)); + RESULT_GUARD_POSIX(s2n_flush(server_conn, &blocked)); + + /* Cleanup */ + RESULT_GUARD_POSIX(s2n_stuffer_wipe(&server_conn->out)); + RESULT_GUARD_POSIX(s2n_stuffer_wipe(&server_conn->handshake.io)); + + return S2N_RESULT_OK; +} + +struct s2n_test_reneg_req_ctx { + s2n_renegotiate_response app_decision; + uint8_t call_count; +}; + +static int s2n_test_reneg_req_cb(struct s2n_connection *conn, void *context, s2n_renegotiate_response *response) +{ + POSIX_ENSURE_REF(context); + POSIX_ENSURE_REF(response); + + struct s2n_test_reneg_req_ctx *test_context = (struct s2n_test_reneg_req_ctx *) context; + *response = test_context->app_decision; + POSIX_ENSURE_LT(test_context->call_count, UINT8_MAX); + test_context->call_count++; + + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + static struct s2n_cert_chain_and_key *chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + DEFER_CLEANUP(struct s2n_config *config_with_reneg_cb = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config_with_reneg_cb); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config_with_reneg_cb, "default")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config_with_reneg_cb)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config_with_reneg_cb, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config_with_reneg_cb, s2n_test_reneg_req_cb, NULL)); + + /* Test: Hello requests received during the handshake are a no-op */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + /* Start the handshake. + * We should be able to receive the hello request in the middle of the handshake. */ + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + SERVER_HELLO_DONE)); + EXPECT_EQUAL(server_conn->server, server_conn->initial); + + /* Send a hello request */ + EXPECT_OK(s2n_send_client_hello_request(server_conn)); + + /* Successfully complete the handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + }; + + /* Test: Hello requests received during the handshake are an error for TLS1.3 */ + if (s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + /* Start the handshake */ + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + SERVER_HELLO)); + + /* Send a hello request. + * We should be able to receive the hello request before the version is negotiated. */ + EXPECT_OK(s2n_send_client_hello_request(server_conn)); + + /* Continue the handshake */ + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + ENCRYPTED_EXTENSIONS)); + + /* Send a hello request. + * We should NOT be able to receive the hello request after TLS1.3 is negotiated */ + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + EXPECT_OK(s2n_send_client_hello_request(server_conn)); + + /* Handshake should fail because the HelloRequest is unexpected in TLS1.3 */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + } + + /* Test: Hello requests received after the handshake can be ignored. + * + * We can continue sending and receiving data after the request. + * s2n-tls treats warnings as fatals by default though, so we must disable that behavior. + */ + { + DEFER_CLEANUP(struct s2n_config *config_with_warns = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config_with_warns); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config_with_warns, "default")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config_with_warns)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config_with_warns, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_alert_behavior(config_with_warns, S2N_ALERT_IGNORE_WARNINGS)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_warns)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_with_warns)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + /* Complete the handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(client_conn->secure_renegotiation); + + /* Send some data */ + EXPECT_OK(s2n_test_send_and_recv(server_conn, client_conn)); + EXPECT_OK(s2n_test_send_and_recv(client_conn, server_conn)); + + /* Send the hello request message. */ + EXPECT_OK(s2n_send_client_hello_request(server_conn)); + + /* Send some more data */ + for (size_t i = 0; i < 10; i++) { + EXPECT_OK(s2n_test_send_and_recv(server_conn, client_conn)); + EXPECT_OK(s2n_test_send_and_recv(client_conn, server_conn)); + EXPECT_TRUE(s2n_connection_check_io_status(client_conn, S2N_IO_FULL_DUPLEX)); + } + }; + + /* Test: Hello requests received after the handshake do NOT trigger a no_renegotiation alert + * if renegotiation callbacks not set. + * + *= https://tools.ietf.org/rfc/rfc5246#section-7.4.1.1 + *= type=test + *# This message MAY be ignored by + *# the client if it does not wish to renegotiate a session, or the + *# client may, if it wishes, respond with a no_renegotiation alert. + **/ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_NULL(client_conn->config->renegotiate_request_cb); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + /* Complete the handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(client_conn->secure_renegotiation); + + /* Send the hello request message. */ + EXPECT_OK(s2n_send_client_hello_request(server_conn)); + + /* no_renegotation alert NOT sent and received */ + EXPECT_OK(s2n_test_send_and_recv(server_conn, client_conn)); + EXPECT_OK(s2n_test_send_and_recv(client_conn, server_conn)); + + /* Callback was not set */ + EXPECT_NULL(client_conn->config->renegotiate_request_cb); + } + + /* Test: Hello requests received after the handshake trigger a no_renegotiation alert + * if the application rejects the renegotiation request + * + *= https://tools.ietf.org/rfc/rfc5746#5 + *= type=test + *# TLS implementations SHOULD provide a mechanism to disable and enable + *# renegotiation. + * + *= https://tools.ietf.org/rfc/rfc5246#section-7.4.1.1 + *= type=test + *# This message MAY be ignored by + *# the client if it does not wish to renegotiate a session, or the + *# client may, if it wishes, respond with a no_renegotiation alert. + **/ + { + struct s2n_test_reneg_req_ctx ctx = { .app_decision = S2N_RENEGOTIATE_REJECT }; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_reneg_cb)); + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config_with_reneg_cb, s2n_test_reneg_req_cb, &ctx)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + /* Complete the handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(client_conn->secure_renegotiation); + + /* Send the hello request message. */ + EXPECT_OK(s2n_send_client_hello_request(server_conn)); + + /* no_renegotation alert sent and received */ + EXPECT_OK(s2n_test_send_and_recv(server_conn, client_conn)); + EXPECT_ERROR_WITH_ERRNO(s2n_test_send_and_recv(client_conn, server_conn), S2N_ERR_ALERT); + EXPECT_EQUAL(s2n_connection_get_alert(server_conn), S2N_TLS_ALERT_NO_RENEGOTIATION); + + /* Callback triggered */ + EXPECT_NOT_NULL(client_conn->config->renegotiate_request_cb); + EXPECT_EQUAL(ctx.call_count, 1); + }; + + /* Test: Hello requests received after the handshake do not trigger a no_renegotiation alert + * if the application accepts the renegotiation request + * + *= https://tools.ietf.org/rfc/rfc5746#5 + *= type=test + *# TLS implementations SHOULD provide a mechanism to disable and enable + *# renegotiation. + */ + { + struct s2n_test_reneg_req_ctx ctx = { .app_decision = S2N_RENEGOTIATE_ACCEPT }; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_reneg_cb)); + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config_with_reneg_cb, s2n_test_reneg_req_cb, &ctx)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + /* Complete the handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(client_conn->secure_renegotiation); + + /* Send the hello request message. */ + EXPECT_OK(s2n_send_client_hello_request(server_conn)); + + /* no_renegotation alert NOT sent and received */ + EXPECT_OK(s2n_test_send_and_recv(server_conn, client_conn)); + EXPECT_OK(s2n_test_send_and_recv(client_conn, server_conn)); + + /* Callback triggered */ + EXPECT_NOT_NULL(client_conn->config->renegotiate_request_cb); + EXPECT_EQUAL(ctx.call_count, 1); + } + + /* Test: Hello requests received after the handshake do not trigger a no_renegotiation alert + * if the application ignores the renegotiation request + */ + { + struct s2n_test_reneg_req_ctx ctx = { .app_decision = S2N_RENEGOTIATE_IGNORE }; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_reneg_cb)); + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config_with_reneg_cb, s2n_test_reneg_req_cb, &ctx)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + /* Complete the handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(client_conn->secure_renegotiation); + + /* Send the hello request message. */ + EXPECT_OK(s2n_send_client_hello_request(server_conn)); + + /* no_renegotation alert NOT sent and received */ + EXPECT_OK(s2n_test_send_and_recv(server_conn, client_conn)); + EXPECT_OK(s2n_test_send_and_recv(client_conn, server_conn)); + + /* Callback triggered */ + EXPECT_NOT_NULL(client_conn->config->renegotiate_request_cb); + EXPECT_EQUAL(ctx.call_count, 1); + } + + /* Test: Hello requests received after the handshake trigger a no_renegotiation alert + * if secure renegotiation is not supported, even if the application would have accepted the request. + * + *= https://tools.ietf.org/rfc/rfc5746#section-4.2 + *= type=test + *# This text applies if the connection's "secure_renegotiation" flag is + *# set to FALSE. + *# + *# It is possible that un-upgraded servers will request that the client + *# renegotiate. It is RECOMMENDED that clients refuse this + *# renegotiation request. Clients that do so MUST respond to such + *# requests with a "no_renegotiation" alert (RFC 5246 requires this + *# alert to be at the "warning" level). It is possible that the + *# apparently un-upgraded server is in fact an attacker who is then + *# allowing the client to renegotiate with a different, legitimate, + *# upgraded server. + **/ + { + struct s2n_test_reneg_req_ctx ctx = { .app_decision = S2N_RENEGOTIATE_ACCEPT }; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_reneg_cb)); + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config_with_reneg_cb, s2n_test_reneg_req_cb, &ctx)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + /* Complete the handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Force secure_renegotiation to be false */ + client_conn->secure_renegotiation = false; + + /* Send the hello request message. */ + EXPECT_OK(s2n_send_client_hello_request(server_conn)); + + /* no_renegotation alert sent and received */ + EXPECT_OK(s2n_test_send_and_recv(server_conn, client_conn)); + EXPECT_ERROR_WITH_ERRNO(s2n_test_send_and_recv(client_conn, server_conn), S2N_ERR_ALERT); + EXPECT_EQUAL(s2n_connection_get_alert(server_conn), S2N_TLS_ALERT_NO_RENEGOTIATION); + + /* Callback was not triggered */ + EXPECT_NOT_NULL(client_conn->config->renegotiate_request_cb); + EXPECT_EQUAL(ctx.call_count, 0); + } + + /* Test: Application callback method fails */ + { + struct s2n_test_reneg_req_ctx ctx = { .app_decision = S2N_RENEGOTIATE_ACCEPT }; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_reneg_cb)); + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config_with_reneg_cb, s2n_test_reneg_req_cb, &ctx)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + /* Complete the handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Send the hello request message. */ + EXPECT_OK(s2n_send_client_hello_request(server_conn)); + + /* Force the callback to fail. + * We're only allowed to call it UINT8_MAX times, so calling + * one more time will cause it to fail with S2N_ERR_SAFETY. + */ + EXPECT_NOT_NULL(client_conn->config->renegotiate_request_cb); + ctx.call_count = UINT8_MAX; + + /* s2n_recv should not surface the callback error as S2N_ERR_SAFETY. + * Applications won't be able to set s2n_errno to a meaningful value, + * so we need to set it to S2N_ERR_CANCELED for them. + */ + EXPECT_ERROR_WITH_ERRNO(s2n_test_send_and_recv(server_conn, client_conn), S2N_ERR_CANCELLED); + }; + + /* Test: SSLv3 sends a fatal handshake_failure alert instead of no_renegotiate + * + *= https://tools.ietf.org/rfc/rfc5746#4.5 + *= type=test + *# SSLv3 does not define the "no_renegotiation" alert (and does + *# not offer a way to indicate a refusal to renegotiate at a "warning" + *# level). SSLv3 clients that refuse renegotiation SHOULD use a fatal + *# handshake_failure alert. + **/ + if (s2n_hash_is_available(S2N_HASH_MD5)) { + struct s2n_test_reneg_req_ctx ctx = { .app_decision = S2N_RENEGOTIATE_REJECT }; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_reneg_cb)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "test_all")); + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config_with_reneg_cb, s2n_test_reneg_req_cb, &ctx)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all")); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + uint8_t buffer[1] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + /* Force an SSLv3 handshake */ + client_conn->client_protocol_version = S2N_SSLv3; + client_conn->actual_protocol_version = S2N_SSLv3; + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_SSLv3); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_SSLv3); + + /* Send the hello request message. */ + EXPECT_OK(s2n_send_client_hello_request(server_conn)); + + /* handshake_failure alert queued */ + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(client_conn, buffer, sizeof(buffer), &blocked), S2N_ERR_BAD_MESSAGE); + EXPECT_TRUE(s2n_connection_check_io_status(client_conn, S2N_IO_CLOSED)); + + /* handshake_failure alert send. + * Skip blinding. */ + EXPECT_TRUE(client_conn->delay > 0); + client_conn->delay = 0; + EXPECT_SUCCESS(s2n_shutdown_send(client_conn, &blocked)); + + /* handshake_failure alert received */ + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, buffer, sizeof(buffer), &blocked), S2N_ERR_ALERT); + EXPECT_TRUE(s2n_connection_check_io_status(server_conn, S2N_IO_CLOSED)); + EXPECT_EQUAL(s2n_connection_get_alert(server_conn), S2N_TLS_ALERT_HANDSHAKE_FAILURE); + + /* Callback triggered */ + EXPECT_NOT_NULL(client_conn->config->renegotiate_request_cb); + EXPECT_EQUAL(ctx.call_count, 1); + } + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + END_TEST(); +} diff --git a/tests/unit/s2n_client_hello_retry_test.c b/tests/unit/s2n_client_hello_retry_test.c new file mode 100644 index 00000000000..f86901788f5 --- /dev/null +++ b/tests/unit/s2n_client_hello_retry_test.c @@ -0,0 +1,1681 @@ +/* + * 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 "pq-crypto/s2n_pq.h" +#include "s2n.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_client_renegotiation_info.h" +#include "tls/extensions/s2n_cookie.h" +#include "tls/extensions/s2n_extension_type_lists.h" +#include "tls/extensions/s2n_server_supported_versions.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "tls/s2n_tls13_handshake.h" + +/* This include is required to access static function s2n_server_hello_parse */ +#include "error/s2n_errno.h" +#include "tls/extensions/s2n_early_data_indication.h" +#include "tls/extensions/s2n_server_key_share.h" +#include "tls/s2n_server_hello.c" +#include "utils/s2n_bitmap.h" +#include "utils/s2n_safety.h" + +#define HELLO_RETRY_MSG_NO 1 +#define SERVER_HELLO_MSG_NO 5 + +static int s2n_client_hello_cb_with_get_server_name(struct s2n_connection *conn, void *ctx) +{ + const char *expected_server_name = (const char *) ctx; + const char *actual_server_name = s2n_get_server_name(conn); + POSIX_ENSURE_EQ(strcmp(expected_server_name, actual_server_name), 0); + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* Test s2n_server_hello_retry_recv */ + { + /* s2n_server_hello_retry_recv must fail when a keyshare for a matching curve was already present */ + { + struct s2n_config *config; + struct s2n_connection *conn; + + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + POSIX_GUARD(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + conn->actual_protocol_version = S2N_TLS13; + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + + EXPECT_NULL(conn->kex_params.client_ecc_evp_params.evp_pkey); + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + EXPECT_NOT_NULL(conn->kex_params.client_ecc_evp_params.evp_pkey); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* s2n_server_hello_retry_recv must fail for a connection with actual protocol version less than TLS13 */ + { + struct s2n_config *config; + struct s2n_connection *conn; + + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + conn->actual_protocol_version = S2N_TLS12; + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test ECC success case for s2n_server_hello_retry_recv */ + { + struct s2n_config *server_config; + struct s2n_config *client_config; + + struct s2n_connection *server_conn; + struct s2n_connection *client_conn; + + struct s2n_cert_chain_and_key *tls13_chain_and_key; + char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Server receives ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + server_conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + /* Server sends HelloRetryMessage */ + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + /* Read the message off the wire */ + EXPECT_SUCCESS(s2n_server_hello_parse(client_conn)); + client_conn->actual_protocol_version_established = 1; + + EXPECT_SUCCESS(s2n_conn_set_handshake_type(client_conn)); + /* Client receives the HelloRetryRequest mesage */ + EXPECT_SUCCESS(s2n_server_hello_retry_recv(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + }; + + { + const struct s2n_kem_group *test_kem_groups[] = { + &s2n_secp256r1_kyber_512_r3, +#if EVP_APIS_SUPPORTED + &s2n_x25519_kyber_512_r3, +#endif + }; + + const struct s2n_kem_preferences test_kem_prefs = { + .kem_count = 0, + .kems = NULL, + .tls13_kem_group_count = s2n_array_len(test_kem_groups), + .tls13_kem_groups = test_kem_groups, + }; + + const struct s2n_security_policy test_security_policy = { + .minimum_protocol_version = S2N_SSLv3, + .cipher_preferences = &cipher_preferences_test_all_tls13, + .kem_preferences = &test_kem_prefs, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20200310, + }; + + if (!s2n_pq_is_enabled()) { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + conn->actual_protocol_version = S2N_TLS13; + conn->security_policy_override = &test_security_policy; + + const struct s2n_kem_preferences *kem_pref = NULL; + POSIX_GUARD(s2n_connection_get_kem_preferences(conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + + conn->kex_params.server_kem_group_params.kem_group = kem_pref->tls13_kem_groups[0]; + EXPECT_NULL(conn->kex_params.server_ecc_evp_params.negotiated_curve); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_PQ_DISABLED); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } else { + /* s2n_server_hello_retry_recv must fail when a keyshare for a matching PQ KEM was already present */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + conn->actual_protocol_version = S2N_TLS13; + conn->security_policy_override = &test_security_policy; + + const struct s2n_kem_preferences *kem_pref = NULL; + POSIX_GUARD(s2n_connection_get_kem_preferences(conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + + conn->kex_params.server_kem_group_params.kem_group = kem_pref->tls13_kem_groups[0]; + EXPECT_NULL(conn->kex_params.server_ecc_evp_params.negotiated_curve); + + struct s2n_kem_group_params *client_params = &conn->kex_params.client_kem_group_params; + client_params->kem_group = kem_pref->tls13_kem_groups[0]; + client_params->kem_params.kem = kem_pref->tls13_kem_groups[0]->kem; + client_params->ecc_params.negotiated_curve = kem_pref->tls13_kem_groups[0]->curve; + + EXPECT_NULL(client_params->ecc_params.evp_pkey); + EXPECT_NULL(client_params->kem_params.private_key.data); + + kem_public_key_size public_key_size = kem_pref->tls13_kem_groups[0]->kem->public_key_length; + EXPECT_SUCCESS(s2n_alloc(&client_params->kem_params.public_key, public_key_size)); + + EXPECT_OK(s2n_kem_generate_keypair(&client_params->kem_params)); + EXPECT_NOT_NULL(client_params->kem_params.private_key.data); + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_params->ecc_params)); + EXPECT_NOT_NULL(client_params->ecc_params.evp_pkey); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); + + EXPECT_SUCCESS(s2n_free(&client_params->kem_params.public_key)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + /* Test failure if exactly one of {named_curve, kem_group} isn't non-null */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + conn->actual_protocol_version = S2N_TLS13; + conn->security_policy_override = &test_security_policy; + + conn->kex_params.server_kem_group_params.kem_group = &s2n_secp256r1_kyber_512_r3; + conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp256r1; + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); + + conn->kex_params.server_kem_group_params.kem_group = NULL; + conn->kex_params.server_ecc_evp_params.negotiated_curve = NULL; + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), S2N_ERR_INVALID_HELLO_RETRY); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + /* Test PQ KEM success case for s2n_server_hello_retry_recv. */ + /* Need at least two KEM's to test fallback */ + if (test_security_policy.kem_preferences->tls13_kem_group_count >= 2) { + struct s2n_config *config; + struct s2n_connection *conn; + + struct s2n_cert_chain_and_key *tls13_chain_and_key; + char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + conn->security_policy_override = &test_security_policy; + + EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, tls13_chain_and_key)); + + /* Client sends ClientHello containing key share for p256+Kyber + * (but indicates support for x25519+Kyber in supported_groups) */ + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->handshake.io)); + conn->session_id_len = 0; /* Wipe the session id to match the HRR hex */ + + /* Server responds with HRR indicating x25519+Kyber as choice for negotiation; + * the last 6 bytes (0033 0002 2F39) are the key share extension with x25519+Kyber */ + DEFER_CLEANUP(struct s2n_stuffer hrr = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_alloc_ro_from_hex_string(&hrr, + "0303CF21AD74E59A6111BE1D8C021E65B891C2A211167ABB8C5E079E09E2C8A8339C00130200000C002B00020304003300022F39")); + + EXPECT_SUCCESS(s2n_stuffer_copy(&hrr, &conn->handshake.io, s2n_stuffer_data_available(&hrr))); + conn->handshake.message_number = HELLO_RETRY_MSG_NO; + /* Read the message off the wire */ + EXPECT_SUCCESS(s2n_server_hello_parse(conn)); + conn->actual_protocol_version_established = 1; + + EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); + /* Client receives the HelloRetryRequest message */ + EXPECT_SUCCESS(s2n_server_hello_retry_recv(conn)); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + } + } + }; + }; + + /* Verify that the hash transcript recreation function is called correctly, + * within the s2n_server_hello_retry_send and s2n_server_hello_retry_recv functions. + * The hash transcript recreation function, if called correctly takes the existing ClientHello1 + * hash, and generates a synthetic message. This test verifies that transcript hash recreated is the same + * on both the server and client side. */ + { + struct s2n_config *server_config; + struct s2n_config *client_config; + + struct s2n_connection *server_conn; + struct s2n_connection *client_conn; + + struct s2n_cert_chain_and_key *tls13_chain_and_key; + char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Server receives ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_TRUE(s2n_is_hello_retry_handshake(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + server_conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + + /* Server sends HelloRetryRequest message, note that s2n_server_hello_retry_recreate_transcript + * is called within the s2n_server_hello_retry_send function */ + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + + s2n_tls13_connection_keys(server_keys, server_conn); + uint8_t hash_digest_length = server_keys.size; + + /* Obtain the transcript hash recreated within the HelloRetryRequest message */ + DEFER_CLEANUP(struct s2n_hash_state server_hash = { 0 }, s2n_hash_free); + uint8_t server_digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; + POSIX_GUARD(s2n_hash_new(&server_hash)); + POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(server_conn, server_keys.hash_algorithm, &server_hash)); + POSIX_GUARD(s2n_hash_digest(&server_hash, server_digest_out, hash_digest_length)); + + struct s2n_blob server_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&server_blob, server_digest_out, hash_digest_length)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + /* Client receives the HelloRetryRequest mesage, note that s2n_server_hello_retry_recreate_transcript + * is called within the s2n_server_hello_recv function */ + EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); + + s2n_tls13_connection_keys(client_keys, client_conn); + hash_digest_length = client_keys.size; + + /* Obtain the transcript hash recreated within ClientHello2 message */ + DEFER_CLEANUP(struct s2n_hash_state client_hash = { 0 }, s2n_hash_free); + uint8_t client_digest_out[S2N_MAX_DIGEST_LEN]; + POSIX_GUARD(s2n_hash_new(&client_hash)); + POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(client_conn, client_keys.hash_algorithm, &client_hash)); + POSIX_GUARD(s2n_hash_digest(&client_hash, client_digest_out, hash_digest_length)); + + struct s2n_blob client_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&client_blob, client_digest_out, hash_digest_length)); + + /* Test that the transcript hash recreated MUST be the same on the server and client side */ + S2N_BLOB_EXPECT_EQUAL(client_blob, server_blob); + + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + }; + + /** + * Self-Talk test: the client initiates a handshake with an unknown keyshare. + * The server sends a HelloRetryRequest that requires the client to generate a + * key share on the server negotiated curve. + * + *= https://tools.ietf.org/rfc/rfc8446#4.1.4 + *= type=test + *# Otherwise, the client MUST process all extensions in the + *# HelloRetryRequest and send a second updated ClientHello. + **/ + { + struct s2n_config *server_config; + struct s2n_config *client_config; + + struct s2n_connection *server_conn; + struct s2n_connection *client_conn; + + struct s2n_cert_chain_and_key *tls13_chain_and_key; + char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + server_conn->x509_validator.skip_cert_validation = 1; + client_conn->x509_validator.skip_cert_validation = 1; + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /** + * Self-Talk test: the client initiates a handshake with an X25519 share. + * The server, however does not support x25519 and prefers P-256. + * The server then sends a HelloRetryRequest that requires the + * client to generate a key share on the P-256 curve. + * + *= https://tools.ietf.org/rfc/rfc8446#4.1.1 + *= type=test + *# If the server selects an (EC)DHE group and the client did not offer a + *# compatible "key_share" extension in the initial ClientHello, the + *# server MUST respond with a HelloRetryRequest (Section 4.1.4) message. + **/ + if (s2n_is_evp_apis_supported()) { + struct s2n_config *server_config; + struct s2n_config *client_config; + + struct s2n_connection *server_conn; + struct s2n_connection *client_conn; + + struct s2n_cert_chain_and_key *tls13_chain_and_key; + char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20190801")); /* contains x25519 */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20190802")); /* does not contain x25519 */ + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + server_conn->x509_validator.skip_cert_validation = 1; + client_conn->x509_validator.skip_cert_validation = 1; + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + /* Ensure the handshake included a hello retry request */ + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + /** + * Ensure the client aborts the handshake if more than one + * HelloRetryRequest is received + * + *= https://tools.ietf.org/rfc/rfc8446#4.1.4 + *= type=test + *# If a client receives a second + *# HelloRetryRequest in the same connection (i.e., where the ClientHello + *# was itself in response to a HelloRetryRequest), it MUST abort the + *# handshake with an "unexpected_message" alert. + **/ + { + struct s2n_config *server_config; + struct s2n_config *client_config; + + struct s2n_connection *server_conn; + struct s2n_connection *client_conn; + + struct s2n_cert_chain_and_key *tls13_chain_and_key; + char tls13_cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char tls13_private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_NOT_NULL(tls13_chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, tls13_cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, tls13_private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(tls13_chain_and_key, tls13_cert_chain, tls13_private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_TRUE(s2n_is_hello_retry_handshake(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + /* Server HelloRetryRequest 1 */ + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); + + /* ClientHello 2 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + /* Server HelloRetryRequest 2 */ + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), S2N_ERR_INVALID_HELLO_RETRY); + + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + }; + + /** + * Ensure that s2n_random_value_is_hello_retry returns true for hello + * retry random values, and false otherwise + * + *= https://tools.ietf.org/rfc/rfc8446#4.1.3 + *= type=test + *# Upon receiving a message with type server_hello, implementations MUST + *# first examine the Random value and, if it matches this value, process + *# it as described in Section 4.1.4). + **/ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + const uint8_t not_hello_retry_request_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, not_hello_retry_request_random, + S2N_TLS_RANDOM_DATA_LEN); + + EXPECT_FAILURE_WITH_ERRNO(s2n_random_value_is_hello_retry(conn), S2N_ERR_INVALID_HELLO_RETRY); + + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); + EXPECT_SUCCESS(s2n_random_value_is_hello_retry(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /** + *= https://tools.ietf.org/rfc/rfc8446#4.1.4 + *= type=test + *# Upon receiving + *# the ServerHello, clients MUST check that the cipher suite supplied in + *# the ServerHello is the same as that in the HelloRetryRequest and + *# otherwise abort the handshake with an "illegal_parameter" alert. + **/ + { + struct s2n_connection *server_conn; + struct s2n_connection *client_conn; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + /* A Hello Retry Request has been processed */ + EXPECT_SUCCESS(s2n_set_hello_retry_required(client_conn)); + client_conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + client_conn->server_protocol_version = S2N_TLS13; + client_conn->handshake.handshake_type |= NEGOTIATED; + client_conn->handshake.handshake_type |= FULL_HANDSHAKE; + client_conn->handshake.message_number = SERVER_HELLO_MSG_NO; + + /* Server Hello with cipher suite that does not match Hello Retry Request cipher suite */ + server_conn->secure->cipher_suite = &s2n_tls13_chacha20_poly1305_sha256; + EXPECT_SUCCESS(s2n_server_hello_send(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), S2N_ERR_CIPHER_NOT_SUPPORTED); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* + * Self-Talk + * + *= https://tools.ietf.org/rfc/rfc8446#section-4.1.2 + *= type=test + *# The client will also send a + *# ClientHello when the server has responded to its ClientHello with a + *# HelloRetryRequest. In that case, the client MUST send the same + *# ClientHello without modification + */ + if (s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + /* Sanity Check: The server accepts an unchanged ClientHello */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Finish handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + }; + + /* Test: The server rejects a second ClientHello with changed message fields */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Change session id */ + client_conn->session_id[0]++; + + /* Expect failure because second client hello doesn't match */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + }; + + /* Test: The server rejects a second ClientHello with changed client random */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Change client random */ + client_conn->handshake_params.client_random[0]++; + + /* Expect failure because second client hello doesn't match */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + }; + + /* Test: outside of testing, the server accepts an incorrectly updated ClientHello */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Change client random */ + client_conn->handshake_params.client_random[0]++; + + /* Expect success if we pretend that this isn't a unit test */ + EXPECT_SUCCESS(s2n_in_unit_test_set(false)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_in_unit_test_set(true)); + } + + /* Test: The server rejects a second ClientHello with a changed extension */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Change server name */ + client_conn->server_name[0]++; + + /* Expect failure because second client hello doesn't match */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + }; + + /* Test: The server rejects a second ClientHello with a removed extension */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Clear server name. + * Without a server name, we don't send the server name extension. */ + client_conn->server_name[0] = '\0'; + + /* Expect failure because second client hello doesn't match */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + }; + + /* Test: The server rejects a second ClientHello with an added extension */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Add a server name. + * Without a server name, we don't send the server name extension. */ + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); + + /* Expect failure because second client hello doesn't match */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + }; + + /* + * Test: If the initial ClientHello includes all extensions, so does the second ClientHello. + * + * This includes TLS1.2 extensions, since the ClientHello is sent before + * the client knows what version the server will negotiate. + * + * We have to test with all extensions to ensure that an s2n server will never reject + * an s2n client's second ClientHello. + * + * TLS1.2 and TLS1.3 session tickets are mutually exclusive and use different + * extensions, so we test once with each. + */ + for (size_t tls13_tickets = 0; tls13_tickets < 2; tls13_tickets++) { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + const struct s2n_security_policy security_policy_test_tls13_retry_with_pq = { + .minimum_protocol_version = S2N_TLS11, + .cipher_preferences = &cipher_preferences_pq_tls_1_1_2021_05_21, + .kem_preferences = &kem_preferences_pq_tls_1_0_2021_05, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &ecc_preferences_for_retry, + }; + client_conn->security_policy_override = &security_policy_test_tls13_retry_with_pq; + + /* Setup all extensions */ + uint8_t apn[] = "https"; + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "PQ-TLS-1-1-2021-05-21")); + EXPECT_SUCCESS(s2n_config_set_status_request_type(client_config, S2N_STATUS_REQUEST_OCSP)); + EXPECT_SUCCESS(s2n_config_set_ct_support_level(client_config, S2N_CT_SUPPORT_REQUEST)); + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(client_config, S2N_TLS_MAX_FRAG_LEN_4096)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); + EXPECT_SUCCESS(s2n_connection_append_protocol_preference(client_conn, apn, sizeof(apn))); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + client_conn->config->npn_supported = true; + if (tls13_tickets) { + EXPECT_OK(s2n_append_test_psk_with_early_data(client_conn, 1, &s2n_tls13_aes_256_gcm_sha384)); + } + EXPECT_SUCCESS(s2n_connection_enable_quic(client_conn)); + /* Need to enable quic on both sides so they can communicate */ + EXPECT_SUCCESS(s2n_connection_enable_quic(server_conn)); + + /* Send and receive ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + + /* All ClientHello extensions must be present (except very specific exceptions) */ + s2n_extension_type_list *extensions = NULL; + EXPECT_SUCCESS(s2n_extension_type_list_get(S2N_EXTENSION_LIST_CLIENT_HELLO, &extensions)); + for (size_t i = 0; i < extensions->count; i++) { + uint16_t iana = extensions->extension_types[i]->iana_value; + + /* The cookie is a special case and only appears AFTER the retry */ + if (iana == TLS_EXTENSION_COOKIE) { + continue; + } + + /* No pq extension if pq not enabled for the build */ + if (iana == TLS_EXTENSION_PQ_KEM_PARAMETERS && !s2n_pq_is_enabled()) { + continue; + } + + /* TLS1.2 session tickets and TLS1.3 session tickets are mutually exclusive */ + if (tls13_tickets && iana == TLS_EXTENSION_SESSION_TICKET) { + continue; + } else if (!tls13_tickets + && (iana == TLS_EXTENSION_PRE_SHARED_KEY + || iana == TLS_EXTENSION_PSK_KEY_EXCHANGE_MODES + || iana == TLS_EXTENSION_EARLY_DATA)) { + continue; + } + + /* No extension is sent for an initial handshake, + * and TLS1.3 doesn't support renegotiation handshakes. + */ + if (iana == TLS_EXTENSION_RENEGOTIATION_INFO) { + continue; + } + + bool extension_exists = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&server_conn->client_hello, + iana, &extension_exists)); + EXPECT_TRUE(extension_exists); + } + + /* Expect successful retry */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } + + /* Processing an extension early does not affect the extension matching check. + * + * This test exists because of a previously released bug. Triggering the bug + * required a specific series of events: + * - The first ClientHello is received. + * - The extensions are parsed. + * - The ClientHello callback triggers. + * - The customer's ClientHello callback implementation calls the s2n_get_server_name API. + * - To retrieve the server name, s2n_get_server_name processes the server name extension early. + * - The server name extension is wiped. Before the "processed" flag, we wiped extensions + * to mark that they had been processed. + * - The server sends a HelloRetryRequest, triggering a retry. + * - The second ClientHello is received. + * - The extensions are parsed. + * - The customer's ClientHello callback does NOT trigger this time. The callback only + * triggers after the first ClientHello. + * - Therefore, s2n_get_server_name is not called and the server name extension is not + * processed early or wiped. + * - The first ClientHello is compared to the second ClientHello. The second ClientHello + * appears to contain a server name extension not present in the first ClientHello. + * - The handshake fails because the ClientHellos must match. + */ + { + char server_name[] = "test server name"; + + DEFER_CLEANUP(struct s2n_config *config_with_cb = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config_with_cb, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config_with_cb, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config_with_cb)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_with_cb)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_cb)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Setup server name and client hello callback */ + EXPECT_SUCCESS(s2n_set_server_name(client_conn, server_name)); + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config_with_cb, + s2n_client_hello_cb_with_get_server_name, server_name)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Handshake should complete as expected */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(strcmp(s2n_get_server_name(server_conn), server_name), 0); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + }; + } + + /** + * Ensure all hello retry extensions sent by the server will have first + * been sent by the client. + * + *= https://tools.ietf.org/rfc/rfc8446#4.1.4 + *= type=test + *# As with the ServerHello, a HelloRetryRequest MUST NOT contain any + *# extensions that were not first offered by the client in its + *# ClientHello, with the exception of optionally the "cookie" (see + *# Section 4.2.2) extension. + **/ + { + s2n_extension_type_list *hello_retry_extension_types = 0; + POSIX_GUARD(s2n_extension_type_list_get(S2N_EXTENSION_LIST_HELLO_RETRY_REQUEST, &hello_retry_extension_types)); + + for (int i = 0; i < hello_retry_extension_types->count; ++i) { + const s2n_extension_type *const extension_type = hello_retry_extension_types->extension_types[i]; + + /* with the exception of optionally the "cookie" extension. */ + if (extension_type->iana_value == TLS_EXTENSION_COOKIE) { + continue; + } + + EXPECT_TRUE(extension_type->is_response); + } + }; + + /** + * Ensure each of the following are checked: legacy_version, + * legacy_session_id_echo, cipher_suite, and + * legacy_compression_method + * + *= https://tools.ietf.org/rfc/rfc8446#4.1.4 + *= type=test + *# Upon receipt of a HelloRetryRequest, the client MUST check the + *# legacy_version, legacy_session_id_echo, cipher_suite, and + *# legacy_compression_method as specified in Section 4.1.3 and then + *# process the extensions, starting with determining the version using + *# "supported_versions". + **/ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + /* The client MUST check the legacy_version */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Server receives ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + /* Force the server to send an erroneous legacy protocol version in the HelloRetryRequest message */ + server_conn->actual_protocol_version = S2N_TLS11; + + /* Server sends HelloRetryRequest */ + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + + /* Client receives HelloRetryRequest */ + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), + S2N_ERR_INVALID_HELLO_RETRY); + }; + + /* The client MUST check the legacy_session_id_echo */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Server receives ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + /* Set a session id that's different from the client hello */ + POSIX_CHECKED_MEMSET(&server_conn->session_id, 0, S2N_TLS_SESSION_ID_MAX_LEN); + + /* Server sends HelloRetryRequest */ + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + + /* Client receives HelloRetryRequest */ + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), + S2N_ERR_BAD_MESSAGE); + }; + + /** + * The client MUST check the cipher_suite + * + *= https://tools.ietf.org/rfc/rfc8446#4.1.4 + *= type=test + *# A client which receives a cipher suite that was not offered MUST + *# abort the handshake. + **/ + { + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20200207")); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Receive ClientHello */ + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + + /* + * Pick a cipher that wasn't offered in the CH, and should cause the + * handshake to abort. + */ + server_conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384; + + /* Finish handshake */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_CIPHER_NOT_SUPPORTED); + }; + + /* The client MUST check the legacy_compression_method */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Server receives ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + /* Server sends HelloRetryRequest */ + POSIX_CHECKED_MEMCPY(server_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); + EXPECT_SUCCESS(s2n_server_hello_write_message(server_conn)); + + /* Overwrite compression method to 1 */ + struct s2n_stuffer *io = &server_conn->handshake.io; + io->write_cursor -= 1; + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, 1)); + + /* Write the extensions */ + EXPECT_SUCCESS(s2n_server_extensions_send(server_conn, &server_conn->handshake.io)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + + /* Client receives HelloRetryRequest */ + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), + S2N_ERR_BAD_MESSAGE); + }; + }; + + /** + *= https://tools.ietf.org/rfc/rfc8446#4.1.4 + *= type=test + *# The server's extensions MUST contain "supported_versions". + **/ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Server receives ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + POSIX_CHECKED_MEMCPY(server_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); + POSIX_GUARD(s2n_server_hello_write_message(server_conn)); + struct s2n_stuffer_reservation total_extensions_size = { 0 }; + POSIX_GUARD(s2n_stuffer_reserve_uint16(&server_conn->handshake.io, &total_extensions_size)); + + /* Only send key share extension - exclude supported_versions */ + s2n_extension_send(&s2n_server_key_share_extension, server_conn, &server_conn->handshake.io); + + POSIX_GUARD(s2n_stuffer_write_vector_size(&total_extensions_size)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + + /* Client receives HelloRetryRequest */ + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), + S2N_ERR_MISSING_EXTENSION); + }; + + /** + *= https://tools.ietf.org/rfc/rfc8446#4.1.4 + *= type=test + *# Servers MUST ensure that they negotiate the + *# same cipher suite when receiving a conformant updated ClientHello (if + *# the server selects the cipher suite as the first step in the + *# negotiation, then this will happen automatically). + **/ + { + /* Create a custom security policy so it can be changed mid-handshake */ + struct s2n_cipher_suite *test_cipher_suites[] = { + &s2n_tls13_aes_128_gcm_sha256, + &s2n_tls13_aes_256_gcm_sha384 + }; + struct s2n_cipher_preferences test_cipher_preferences = { + .count = s2n_array_len(test_cipher_suites), + .suites = test_cipher_suites, + }; + struct s2n_security_policy security_policy_test_tls13_retry_temp = { + .minimum_protocol_version = S2N_TLS10, + .cipher_preferences = &test_cipher_preferences, + .kem_preferences = &kem_preferences_null, + .signature_preferences = &s2n_signature_preferences_20200207, + .certificate_signature_preferences = &s2n_certificate_signature_preferences_20201110, + .ecc_preferences = &ecc_preferences_for_retry, + }; + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry_temp; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Receive ClientHello */ + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + + /* Rearrange the cipher preference order so that a different cipher will be + * picked by the server */ + server_conn->config->security_policy->cipher_preferences->suites[0] = &s2n_tls13_aes_256_gcm_sha384; + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + }; + + /** + * Ensure that the client aborts the handshake if selected_version + * differs in the received server hellos + * + *= https://tools.ietf.org/rfc/rfc8446#4.1.4 + *= type=test + *# The value of selected_version in the HelloRetryRequest + *# "supported_versions" extension MUST be retained in the ServerHello, + *# and a client MUST abort the handshake with an "illegal_parameter" + *# alert if the value changes. + **/ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send ClientHello */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Receive ClientHello */ + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + + /* Skip to before server sends ServerHello */ + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); + + /* Change the server_protocol_version so the value found in the ServerHello + * differs from the value found in the HelloRetryRequest */ + server_conn->server_protocol_version = S2N_TLS13 + 10; + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + }; + + /** + *= https://tools.ietf.org/rfc/rfc8446#4.2.8 + *= type=test + *# Upon receipt of this extension in a HelloRetryRequest, the client + *# MUST verify that (1) the selected_group field corresponds to a group + *# which was provided in the "supported_groups" extension in the + *# original ClientHello + **/ + { + /* Create a custom security policy without secp521r1 */ + const struct s2n_ecc_named_curve *const test_ecc_pref_list_for_retry[] = { + &s2n_ecc_curve_secp256r1, + &s2n_ecc_curve_secp384r1, + }; + const struct s2n_ecc_preferences test_ecc_preferences_for_retry = { + .count = s2n_array_len(test_ecc_pref_list_for_retry), + .ecc_curves = test_ecc_pref_list_for_retry, + }; + struct s2n_security_policy security_policy_test_tls13_retry_temp = { + .minimum_protocol_version = S2N_TLS10, + .cipher_preferences = &cipher_preferences_20190801, + .kem_preferences = &kem_preferences_null, + .signature_preferences = &s2n_signature_preferences_20200207, + .certificate_signature_preferences = &s2n_certificate_signature_preferences_20201110, + .ecc_preferences = &test_ecc_preferences_for_retry, + }; + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, + S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry_temp; + + /* ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Server receives ClientHello 1 */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + + /* Server sends HelloRetryRequest */ + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + + /* Set the curve to secp521r1, which was not provided in supported_groups */ + client_conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp521r1; + + /* Client receives HelloRetryRequest */ + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), + S2N_ERR_INVALID_HELLO_RETRY); + }; + + /** + *= https://tools.ietf.org/rfc/rfc8446#4.2.8 + *= type=test + *# If using (EC)DHE key establishment and a HelloRetryRequest containing a + *# "key_share" extension was received by the client, the client MUST + *# verify that the selected NamedGroup in the ServerHello is the same as + *# that in the HelloRetryRequest. If this check fails, the client MUST + *# abort the handshake with an "illegal_parameter" alert. + **/ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, + S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Skip to before client receives ServerHello 2 */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, ENCRYPTED_EXTENSIONS)); + + /* Set the negotiated curve to something other than what was sent in the HRR */ + client_conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp521r1; + + /* Client receives ServerHello 2 */ + EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_until_message(client_conn, &blocked, ENCRYPTED_EXTENSIONS), + S2N_ERR_BAD_MESSAGE); + }; + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + END_TEST(); +} diff --git a/tests/unit/s2n_client_hello_test.c b/tests/unit/s2n_client_hello_test.c new file mode 100644 index 00000000000..a9f0479186f --- /dev/null +++ b/tests/unit/s2n_client_hello_test.c @@ -0,0 +1,1689 @@ +/* + * 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 "tls/s2n_client_hello.h" + +#include +#include +#include +#include +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_sslv2_client_hello.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_client_hello.c" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_quic_support.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "tls/s2n_tls_parameters.h" +#include "utils/s2n_safety.h" + +#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F + +#define LENGTH_TO_SESSION_ID (S2N_TLS_PROTOCOL_VERSION_LEN + S2N_TLS_RANDOM_DATA_LEN) +#define TLS12_LENGTH_TO_CIPHER_LIST (LENGTH_TO_SESSION_ID + 1) +#define TLS13_LENGTH_TO_CIPHER_LIST (TLS12_LENGTH_TO_CIPHER_LIST + S2N_TLS_SESSION_ID_MAX_LEN) + +int s2n_parse_client_hello(struct s2n_connection *conn); + +int main(int argc, char **argv) +{ + struct s2n_cert_chain_and_key *chain_and_key, *ecdsa_chain_and_key; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); + + /* Test s2n_client_hello_get_extension_by_id */ + { + /* Test with invalid parsed extensions */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + s2n_tls_extension_type test_extension_type = S2N_EXTENSION_SERVER_NAME; + + s2n_extension_type_id test_extension_type_id; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(test_extension_type, &test_extension_type_id)); + + uint8_t data[] = "data"; + s2n_parsed_extension *parsed_extension = &conn->client_hello.extensions.parsed_extensions[test_extension_type_id]; + parsed_extension->extension_type = test_extension_type; + parsed_extension->extension.data = data; + parsed_extension->extension.size = sizeof(data); + + /* Succeeds with correct extension type */ + EXPECT_EQUAL(s2n_client_hello_get_extension_by_id(&conn->client_hello, + test_extension_type, data, sizeof(data)), + sizeof(data)); + + /* Fails with wrong extension type */ + parsed_extension->extension_type = test_extension_type + 1; + EXPECT_EQUAL(s2n_client_hello_get_extension_by_id(&conn->client_hello, + test_extension_type, data, sizeof(data)), + 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test s2n_client_hello_has_extension */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + uint8_t data[] = { + /* arbitrary extension with 2 data */ + 0xFF, 0x00, /* extension type */ + 0x00, 0x02, /* extension payload length */ + 0xAB, 0xCD, /* extension payload */ + /* Encrypt then mac extension without data */ + 0x00, 0x16, + 0x00, 0x00 + }; + + struct s2n_blob *raw_extension = &conn->client_hello.extensions.raw; + raw_extension->data = data; + raw_extension->size = sizeof(data); + + /* Succeeds on an unsupported extension with no payload */ + bool exists = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&conn->client_hello, 0x0016, &exists)); + EXPECT_TRUE(exists); + + /* Succeeds on an unsupported extension with payload */ + exists = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&conn->client_hello, 0xFF00, &exists)); + EXPECT_TRUE(exists); + + /* Succeeds with an invalid extension */ + exists = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&conn->client_hello, 0xFFFF, &exists)); + EXPECT_FALSE(exists); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_client_hello_has_extension with a zero-length extension */ + for (int send_sct = 0; send_sct <= 1; send_sct++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + /* The SCT extension is zero-length. */ + if (send_sct) { + EXPECT_SUCCESS(s2n_config_set_ct_support_level(config, S2N_CT_SUPPORT_REQUEST)); + } + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client->handshake.io, &server->handshake.io, + s2n_stuffer_data_available(&client->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server)); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server); + EXPECT_NOT_NULL(client_hello); + + s2n_parsed_extension *sct_extension = NULL; + int ret = s2n_client_hello_get_parsed_extension(S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, &client_hello->extensions, + &sct_extension); + + if (send_sct) { + /* Ensure that the extension was received. */ + EXPECT_SUCCESS(ret); + POSIX_ENSURE_REF(sct_extension); + + /* Ensure that the extension is zero-length. */ + EXPECT_EQUAL(sct_extension->extension.size, 0); + } else { + /* The extension shouldn't have been received because it wasn't requested. */ + EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_EXTENSION_NOT_RECEIVED); + } + } + + /* Test s2n_client_hello_get_raw_extension */ + { + uint8_t data[] = { + /* arbitrary extension with 2 data */ + 0xFF, 0x00, /* extension type */ + 0x00, 0x02, /* extension payload length */ + 0xAB, 0xCD, /* extension payload */ + /* NPN extension without data */ + 0x33, 0x74, + 0x00, 0x00 + }; + struct s2n_blob raw_extension = { + .data = data, + .size = sizeof(data), + }; + + struct s2n_blob extension = { 0 }; + /* Succeeds with extension exists without payload */ + EXPECT_OK(s2n_client_hello_get_raw_extension(0x3374, &raw_extension, &extension)); + EXPECT_EQUAL(extension.size, 0); + EXPECT_NOT_NULL(extension.data); + + /* Succeeds with extension exists with payload */ + extension = (struct s2n_blob){ 0 }; + EXPECT_OK(s2n_client_hello_get_raw_extension(0xFF00, &raw_extension, &extension)); + EXPECT_EQUAL(extension.size, 2); + EXPECT_NOT_NULL(extension.data); + EXPECT_BYTEARRAY_EQUAL(extension.data, &data[4], 2); + + /* Failed with extension not exist */ + extension = (struct s2n_blob){ 0 }; + EXPECT_OK(s2n_client_hello_get_raw_extension(0xFFFF, &raw_extension, &extension)); + EXPECT_EQUAL(extension.size, 0); + EXPECT_NULL(extension.data); + }; + + /* Test setting cert chain on recv */ + { + s2n_enable_tls13_in_test(); + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + + /* TLS13 fails to parse client hello when no certs set */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->client_protocol_version = conn->server_protocol_version; + conn->actual_protocol_version = conn->client_protocol_version; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_TRUE(s2n_stuffer_data_available(&conn->handshake.io) > 0); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(conn), S2N_ERR_INVALID_SIGNATURE_SCHEME); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); + + /* TLS13 successfully sets certs */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->client_protocol_version = conn->server_protocol_version; + conn->actual_protocol_version = conn->client_protocol_version; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_TRUE(s2n_stuffer_data_available(&conn->handshake.io) > 0); + EXPECT_SUCCESS(s2n_client_hello_recv(conn)); + + EXPECT_NOT_NULL(conn->handshake_params.our_chain_and_key); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + EXPECT_SUCCESS(s2n_config_free(config)); + s2n_disable_tls13_in_test(); + }; + + /* Test getting supported versions from the client hello */ + if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); + /* TLS13 has supported versions in the client hello */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + uint8_t supported_versions[256] = { 0 }; + uint8_t size_of_version_list = 0; + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, + &server_conn->handshake.io, s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_get_extension_by_id(&server_conn->client_hello, + S2N_EXTENSION_SUPPORTED_VERSIONS, supported_versions, sizeof(supported_versions))); + size_of_version_list = supported_versions[0]; + /* No supported versions before the handshake is received */ + EXPECT_EQUAL(0, size_of_version_list); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_SUCCESS(s2n_client_hello_get_extension_by_id(&server_conn->client_hello, + S2N_EXTENSION_SUPPORTED_VERSIONS, supported_versions, sizeof(supported_versions))); + size_of_version_list = supported_versions[0]; + EXPECT_TRUE(size_of_version_list > 0); + bool found_tls13 = false; + const uint8_t tls13_bytes[] = { 0x03, 0x04 }; + const size_t supported_version_size = sizeof(tls13_bytes); + for (uint16_t offset = 1; offset < size_of_version_list; offset += supported_version_size) { + if (memcmp(tls13_bytes, &supported_versions[offset], supported_version_size) == 0) { + found_tls13 = true; + } + } + EXPECT_TRUE(found_tls13); + }; + s2n_disable_tls13_in_test(); + }; + + /* Test generating session id */ + { + const uint8_t test_session_id[S2N_TLS_SESSION_ID_MAX_LEN] = { 7 }; + + /* Use session id if already generated */ + for (uint8_t i = S2N_TLS10; i <= S2N_TLS13; i++) { + if (i >= S2N_TLS13) { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + } + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + conn->session_id_len = S2N_TLS_SESSION_ID_MAX_LEN; + EXPECT_MEMCPY_SUCCESS(conn->session_id, test_session_id, S2N_TLS_SESSION_ID_MAX_LEN); + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); + + uint8_t session_id_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); + EXPECT_EQUAL(session_id_length, S2N_TLS_SESSION_ID_MAX_LEN); + + uint8_t *session_id; + EXPECT_NOT_NULL(session_id = s2n_stuffer_raw_read(hello_stuffer, S2N_TLS_SESSION_ID_MAX_LEN)); + EXPECT_BYTEARRAY_EQUAL(session_id, test_session_id, S2N_TLS_SESSION_ID_MAX_LEN); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* With TLS1.3 */ + if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* Generate a session id by default */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); + + uint8_t session_id_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); + EXPECT_EQUAL(session_id_length, S2N_TLS_SESSION_ID_MAX_LEN); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Do NOT generate a session id if middlebox compatibility mode is disabled. + * For now, middlebox compatibility mode is only disabled by QUIC. + */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_enable_quic(config)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); + + uint8_t session_id_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); + EXPECT_EQUAL(session_id_length, 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Generate a session id if trying to resume a handshake.io; + conn->resume_protocol_version = S2N_TLS12; + EXPECT_TRUE(conn->actual_protocol_version >= S2N_TLS13); + EXPECT_TRUE(conn->client_protocol_version >= S2N_TLS13); + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); + + uint8_t session_id_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); + EXPECT_EQUAL(session_id_length, S2N_TLS_SESSION_ID_MAX_LEN); + }; + + /* Fail if we need to generate a session id to resume a resume_protocol_version = S2N_TLS12; + EXPECT_TRUE(conn->actual_protocol_version >= S2N_TLS13); + EXPECT_TRUE(conn->client_protocol_version >= S2N_TLS13); + conn->quic_enabled = true; + + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_send(conn), + S2N_ERR_UNSUPPORTED_WITH_QUIC); + }; + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + + /* With TLS1.2 */ + { + /* Do NOT generate a session id by default */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); + + uint8_t session_id_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); + EXPECT_EQUAL(session_id_length, 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Generate a session id if using tickets */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, true)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, LENGTH_TO_SESSION_ID)); + + uint8_t session_id_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &session_id_length)); + EXPECT_EQUAL(session_id_length, S2N_TLS_SESSION_ID_MAX_LEN); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + }; + }; + + /* Test cipher suites list */ + { + /* When TLS 1.3 NOT supported */ + { + /* TLS 1.3 cipher suites NOT written by client by default */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_TRUE(conn->client_protocol_version < S2N_TLS13); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, TLS12_LENGTH_TO_CIPHER_LIST)); + + uint16_t list_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(hello_stuffer, &list_length)); + EXPECT_NOT_EQUAL(list_length, 0); + + uint8_t first_cipher_byte; + for (int i = 0; i < list_length; i++) { + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &first_cipher_byte)); + EXPECT_NOT_EQUAL(first_cipher_byte, 0x13); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, 1)); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS 1.3 cipher suites NOT written by client even if included in security policy */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); + + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_TRUE(conn->client_protocol_version < S2N_TLS13); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, TLS12_LENGTH_TO_CIPHER_LIST)); + + uint16_t list_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(hello_stuffer, &list_length)); + EXPECT_NOT_EQUAL(list_length, 0); + + uint8_t first_cipher_byte; + for (int i = 0; i < list_length; i++) { + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &first_cipher_byte)); + EXPECT_NOT_EQUAL(first_cipher_byte, 0x13); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, 1)); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* When TLS 1.3 supported */ + if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + s2n_config_set_session_tickets_onoff(config, 0); + + /* TLS 1.3 cipher suites written by client */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + + EXPECT_TRUE(conn->actual_protocol_version >= S2N_TLS13); + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, TLS13_LENGTH_TO_CIPHER_LIST)); + + uint16_t list_length = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(hello_stuffer, &list_length)); + EXPECT_NOT_EQUAL(list_length, 0); + + uint8_t first_cipher_byte; + int tls13_ciphers_found = 0; + for (int i = 0; i < list_length; i++) { + EXPECT_SUCCESS(s2n_stuffer_read_uint8(hello_stuffer, &first_cipher_byte)); + if (first_cipher_byte == 0x13) { + tls13_ciphers_found++; + } + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, 1)); + } + EXPECT_NOT_EQUAL(tls13_ciphers_found, 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + + /* TLS_EMPTY_RENEGOTIATION_INFO_SCSV included if TLS1.2 ciphers included + * + *= https://tools.ietf.org/rfc/rfc5746#3.4 + *= type=test + *# o The client MUST include either an empty "renegotiation_info" + *# extension, or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling + *# cipher suite value in the ClientHello. + */ + if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_reset_tls13_in_test()); + const uint8_t empty_renegotiation_info_scsv[S2N_TLS_CIPHER_SUITE_LEN] = { TLS_EMPTY_RENEGOTIATION_INFO_SCSV }; + + struct { + const char *security_policy; + bool expect_renegotiation_info; + } test_cases[] = { + { .security_policy = "test_all_tls13", .expect_renegotiation_info = false }, + { .security_policy = "default_tls13", .expect_renegotiation_info = true }, + { .security_policy = "default", .expect_renegotiation_info = true }, + }; + + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, test_cases[i].security_policy)); + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_SUCCESS(s2n_parse_client_hello(conn)); + + struct s2n_blob *cipher_suites = &conn->client_hello.cipher_suites; + EXPECT_TRUE(cipher_suites->size > 0); + + uint8_t *iana = cipher_suites->data; + bool found_renegotiation_info = false; + for (size_t j = 0; j < cipher_suites->size; j += S2N_TLS_CIPHER_SUITE_LEN) { + if (memcmp(iana + j, empty_renegotiation_info_scsv, S2N_TLS_CIPHER_SUITE_LEN) == 0) { + found_renegotiation_info = true; + } + } + + EXPECT_EQUAL(found_renegotiation_info, test_cases[i].expect_renegotiation_info); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + } + + /* TLS1.2 cipher suites not written if QUIC enabled */ + { + EXPECT_SUCCESS(s2n_reset_tls13_in_test()); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + + bool quic_enabled[] = { false, s2n_is_tls13_fully_supported() }; + + /* TLS 1.2 cipher suites only written if QUIC not enabled */ + for (size_t i = 0; i < s2n_array_len(quic_enabled); i++) { + config->quic_enabled = quic_enabled[i]; + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_SUCCESS(s2n_parse_client_hello(conn)); + + struct s2n_blob *cipher_suites = &conn->client_hello.cipher_suites; + EXPECT_TRUE(cipher_suites->size > 0); + + bool tls12_cipher_found = false; + uint8_t *iana = cipher_suites->data; + for (size_t j = 0; j < cipher_suites->size; j += S2N_TLS_CIPHER_SUITE_LEN) { + /* All TLS1.3 cipher suites have IANAs starting with 0x13 */ + if (iana[j] != 0x13) { + tls12_cipher_found = true; + } + } + + /* TLS1.2 and QUIC are mutually exclusive */ + EXPECT_TRUE(tls12_cipher_found != quic_enabled[i]); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + }; + + /* Test that negotiating TLS1.2 with QUIC-enabled server fails */ + if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_reset_tls13_in_test()); + + struct s2n_config *config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_enable_quic(config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); + + /* Succeeds when negotiating TLS1.3 */ + if (s2n_is_tls13_fully_supported()) { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, + &server_conn->handshake.io, s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + } + + /* Fails when negotiating TLS1.2 */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "test_all_tls12")); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, + &server_conn->handshake.io, s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + + /* Test that cipher suites enforce proper highest supported versions. + * Eg. server configs TLS 1.2 only ciphers should never negotiate TLS 1.3 + */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + + { + /* TLS 1.3 client cipher preference uses TLS13 version */ + struct s2n_connection *conn; + const struct s2n_security_policy *security_policy; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_TRUE(s2n_security_policy_supports_tls13(security_policy)); + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_EQUAL(conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(conn->client_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(conn->client_hello_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + { + /* TLS 1.2 client cipher preference uses TLS12 version */ + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default")); + + const struct s2n_security_policy *security_policy; + POSIX_GUARD(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_FALSE(s2n_security_policy_supports_tls13(security_policy)); + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(conn->client_hello_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + { + /* TLS 1.3 client cipher preference uses TLS13 version */ + struct s2n_connection *client_conn, *server_conn; + const struct s2n_security_policy *security_policy; + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + + POSIX_GUARD(s2n_connection_get_security_policy(client_conn, &security_policy)); + EXPECT_TRUE(s2n_security_policy_supports_tls13(security_policy)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_EQUAL(client_conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(client_conn->client_protocol_version, s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(client_conn->client_hello_version, S2N_TLS12); + + /* Server configured with TLS 1.2 negotiates TLS12 version */ + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + struct s2n_config *server_config; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all_tls12")); + + POSIX_GUARD(s2n_connection_get_security_policy(server_conn, &security_policy)); + EXPECT_FALSE(s2n_security_policy_supports_tls13(security_policy)); + + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, s2n_stuffer_data_available(&client_conn->handshake.io))); + + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->client_hello_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* SSlv2 client hello */ + { + struct s2n_connection *server_conn; + struct s2n_config *server_config; + s2n_blocked_status server_blocked; + + uint8_t sslv2_client_hello[] = { + SSLv2_CLIENT_HELLO_PREFIX, + SSLv2_CLIENT_HELLO_CIPHER_SUITES, + SSLv2_CLIENT_HELLO_CHALLENGE, + }; + + int sslv2_client_hello_len = sizeof(sslv2_client_hello); + + uint8_t sslv2_client_hello_header[] = { + SSLv2_CLIENT_HELLO_HEADER, + }; + + int sslv2_client_hello_header_len = sizeof(sslv2_client_hello_header); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* The security policy does not need to support SSLv2. + * + * s2n-tls does NOT support SSLv2. However, it does accept ClientHellos in the SSLv2 + * format but advertising higher protocol versions. Clients use this strategy to + * communicate with servers in a backwards-compatible way. + * + * Our test SSLv2 ClientHello advertises TLS1.2. + * So the security policy only needs to support TLS1.2. + */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default")); + + /* Send the client hello message */ + EXPECT_EQUAL(write(io_pair.client, sslv2_client_hello_header, sslv2_client_hello_header_len), sslv2_client_hello_header_len); + EXPECT_EQUAL(write(io_pair.client, sslv2_client_hello, sslv2_client_hello_len), sslv2_client_hello_len); + + /* Verify that the sent client hello message is accepted */ + s2n_negotiate(server_conn, &server_blocked); + EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); + EXPECT_TRUE(IS_NEGOTIATED(server_conn)); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); + + /* Verify s2n_connection_get_client_hello returns the handle to the s2n_client_hello on the connection */ + EXPECT_EQUAL(client_hello, &server_conn->client_hello); + + uint8_t *collected_client_hello = client_hello->raw_message.data; + uint16_t collected_client_hello_len = client_hello->raw_message.size; + + /* Verify correctly identified as SSLv2 */ + EXPECT_TRUE(client_hello->sslv2); + + /* Verify collected client hello message length */ + EXPECT_EQUAL(collected_client_hello_len, sslv2_client_hello_len); + + /* Verify the collected client hello matches what was sent */ + EXPECT_BYTEARRAY_EQUAL(collected_client_hello, sslv2_client_hello, sslv2_client_hello_len); + + /* Verify s2n_client_hello_get_raw_message_length correct */ + EXPECT_EQUAL(s2n_client_hello_get_raw_message_length(client_hello), sslv2_client_hello_len); + + uint8_t expected_cs[] = { + SSLv2_CLIENT_HELLO_CIPHER_SUITES, + }; + + /* Verify collected cipher_suites size correct */ + EXPECT_EQUAL(client_hello->cipher_suites.size, sizeof(expected_cs)); + + /* Verify collected cipher_suites correct */ + EXPECT_BYTEARRAY_EQUAL(client_hello->cipher_suites.data, expected_cs, sizeof(expected_cs)); + + /* Verify s2n_client_hello_get_cipher_suites_length correct */ + EXPECT_EQUAL(s2n_client_hello_get_cipher_suites_length(client_hello), sizeof(expected_cs)); + + /* Verify collected extensions size correct */ + EXPECT_EQUAL(client_hello->extensions.raw.size, 0); + + /* Verify s2n_client_hello_get_extensions_length correct */ + EXPECT_EQUAL(s2n_client_hello_get_extensions_length(client_hello), 0); + + /* Verify s2n_client_hello_get_session_id_length correct */ + uint32_t ch_session_id_length; + EXPECT_SUCCESS(s2n_client_hello_get_session_id_length(client_hello, &ch_session_id_length)); + EXPECT_EQUAL(ch_session_id_length, 0); + + /* Free all handshake data */ + EXPECT_SUCCESS(s2n_connection_free_handshake(server_conn)); + + /* Verify free_handshake resized the s2n_client_hello.raw_message stuffer back to 0 */ + EXPECT_NULL(client_hello->raw_message.data); + EXPECT_EQUAL(client_hello->raw_message.size, 0); + + EXPECT_SUCCESS(s2n_shutdown(server_conn, &server_blocked)); + + /* Wipe connection */ + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + + /* Verify connection_wipe resized the s2n_client_hello.raw_message stuffer back to 0 */ + EXPECT_NULL(client_hello->raw_message.data); + EXPECT_EQUAL(client_hello->raw_message.size, 0); + + /* Verify the s2n blobs referencing cipher_suites and extensions have cleared */ + EXPECT_EQUAL(client_hello->cipher_suites.size, 0); + EXPECT_NULL(client_hello->cipher_suites.data); + EXPECT_EQUAL(client_hello->extensions.raw.size, 0); + EXPECT_NULL(client_hello->extensions.raw.data); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /* Minimal TLS 1.2 client hello. */ + { + struct s2n_connection *server_conn; + struct s2n_config *server_config; + s2n_blocked_status server_blocked; + uint8_t *sent_client_hello; + uint8_t *expected_client_hello; + + uint8_t client_extensions[] = { + /* Extension type TLS_EXTENSION_SERVER_NAME */ + 0x00, + 0x00, + /* Extension size */ + 0x00, + 0x08, + /* Server names len */ + 0x00, + 0x06, + /* First server name type - host name */ + 0x00, + /* First server name len */ + 0x00, + 0x03, + /* First server name, matches sent_server_name */ + 's', + 'v', + 'r', + }; + + uint8_t server_name_extension[] = { + /* Server names len */ + 0x00, + 0x06, + /* First server name type - host name */ + 0x00, + /* First server name len */ + 0x00, + 0x03, + /* First server name, matches sent_server_name */ + 's', + 'v', + 'r', + }; + int server_name_extension_len = sizeof(server_name_extension); + + size_t client_extensions_len = sizeof(client_extensions); + uint8_t client_hello_prefix[] = { + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Client random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suites len */ + 0x00, + 0x02, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, + 0x3C, + /* Compression methods len */ + 0x01, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (client_extensions_len >> 8) & 0xff, + (client_extensions_len & 0xff), + }; + int client_hello_prefix_len = sizeof(client_hello_prefix); + int sent_client_hello_len = client_hello_prefix_len + client_extensions_len; + uint8_t message_header[] = { + /* Handshake message type CLIENT HELLO */ + 0x01, + /* Body len */ + (sent_client_hello_len >> 16) & 0xff, + (sent_client_hello_len >> 8) & 0xff, + (sent_client_hello_len & 0xff), + }; + int message_len = sizeof(message_header) + sent_client_hello_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + EXPECT_NOT_NULL(sent_client_hello = malloc(sent_client_hello_len)); + EXPECT_MEMCPY_SUCCESS(sent_client_hello, client_hello_prefix, client_hello_prefix_len); + EXPECT_MEMCPY_SUCCESS(sent_client_hello + client_hello_prefix_len, client_extensions, client_extensions_len); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + /* Security policy must allow cipher suite hard coded into client hello */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Verify s2n_connection_get_client_hello returns null if client hello not yet processed */ + EXPECT_NULL(s2n_connection_get_client_hello(server_conn)); + + uint8_t *ext_data; + EXPECT_NOT_NULL(ext_data = malloc(server_name_extension_len)); + /* Verify we don't get extension and it's length when client hello is not yet processed */ + EXPECT_FAILURE(s2n_client_hello_get_extension_length(s2n_connection_get_client_hello(server_conn), S2N_EXTENSION_SERVER_NAME)); + EXPECT_FAILURE(s2n_client_hello_get_extension_by_id(s2n_connection_get_client_hello(server_conn), S2N_EXTENSION_SERVER_NAME, ext_data, server_name_extension_len)); + free(ext_data); + ext_data = NULL; + + /* Send the client hello message */ + EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.client, sent_client_hello, sent_client_hello_len), sent_client_hello_len); + + /* Verify that the sent client hello message is accepted */ + s2n_negotiate(server_conn, &server_blocked); + EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); + EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server_conn); + + /* Verify correctly identified as NOT sslv2 */ + EXPECT_FALSE(client_hello->sslv2); + + /* Verify s2n_connection_get_client_hello returns the handle to the s2n_client_hello on the connection */ + EXPECT_EQUAL(client_hello, &server_conn->client_hello); + + uint8_t *collected_client_hello = client_hello->raw_message.data; + uint16_t collected_client_hello_len = client_hello->raw_message.size; + + /* Verify collected client hello message length */ + EXPECT_EQUAL(collected_client_hello_len, sent_client_hello_len); + + /* Verify the collected client hello has client random zero-ed out */ + uint8_t client_random_offset = S2N_TLS_PROTOCOL_VERSION_LEN; + uint8_t expected_client_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; + EXPECT_BYTEARRAY_EQUAL(collected_client_hello + client_random_offset, expected_client_random, S2N_TLS_RANDOM_DATA_LEN); + + /* Verify the collected client hello matches what was sent except for the zero-ed client random */ + EXPECT_NOT_NULL(expected_client_hello = malloc(sent_client_hello_len)); + EXPECT_MEMCPY_SUCCESS(expected_client_hello, sent_client_hello, sent_client_hello_len); + POSIX_CHECKED_MEMSET(expected_client_hello + client_random_offset, 0, S2N_TLS_RANDOM_DATA_LEN); + EXPECT_BYTEARRAY_EQUAL(collected_client_hello, expected_client_hello, sent_client_hello_len); + + /* Verify s2n_client_hello_get_raw_message_length correct */ + EXPECT_EQUAL(s2n_client_hello_get_raw_message_length(client_hello), sent_client_hello_len); + + uint8_t *raw_ch_out; + + /* Verify s2n_client_hello_get_raw_message retrieves the full message when its len <= max_len */ + EXPECT_TRUE(collected_client_hello_len < S2N_LARGE_RECORD_LENGTH); + EXPECT_NOT_NULL(raw_ch_out = malloc(S2N_LARGE_RECORD_LENGTH)); + EXPECT_EQUAL(sent_client_hello_len, s2n_client_hello_get_raw_message(client_hello, raw_ch_out, S2N_LARGE_RECORD_LENGTH)); + EXPECT_BYTEARRAY_EQUAL(raw_ch_out, expected_client_hello, sent_client_hello_len); + free(raw_ch_out); + raw_ch_out = NULL; + + /* Verify s2n_client_hello_get_raw_message retrieves truncated message when its len > max_len */ + EXPECT_TRUE(collected_client_hello_len > 0); + uint32_t max_len = collected_client_hello_len - 1; + EXPECT_NOT_NULL(raw_ch_out = malloc(max_len)); + EXPECT_EQUAL(max_len, s2n_client_hello_get_raw_message(client_hello, raw_ch_out, max_len)); + EXPECT_BYTEARRAY_EQUAL(raw_ch_out, expected_client_hello, max_len); + free(raw_ch_out); + raw_ch_out = NULL; + + uint8_t expected_cs[] = { 0x00, 0x3C }; + + /* Verify collected cipher_suites size correct */ + EXPECT_EQUAL(client_hello->cipher_suites.size, sizeof(expected_cs)); + + /* Verify collected cipher_suites correct */ + EXPECT_BYTEARRAY_EQUAL(client_hello->cipher_suites.data, expected_cs, sizeof(expected_cs)); + + /* Verify s2n_client_hello_get_cipher_suites_length correct */ + EXPECT_EQUAL(s2n_client_hello_get_cipher_suites_length(client_hello), sizeof(expected_cs)); + + /* Verify s2n_client_hello_get_cipher_suites correct */ + uint8_t *cs_out; + + /* Verify s2n_client_hello_get_cipher_suites retrieves the full cipher_suites when its len <= max_len */ + EXPECT_TRUE(client_hello->cipher_suites.size < S2N_LARGE_RECORD_LENGTH); + EXPECT_NOT_NULL(cs_out = malloc(S2N_LARGE_RECORD_LENGTH)); + EXPECT_EQUAL(sizeof(expected_cs), s2n_client_hello_get_cipher_suites(client_hello, cs_out, S2N_LARGE_RECORD_LENGTH)); + EXPECT_BYTEARRAY_EQUAL(cs_out, client_hello->cipher_suites.data, sizeof(expected_cs)); + free(cs_out); + cs_out = NULL; + + /* Verify s2n_client_hello_get_cipher_suites retrieves truncated message when cipher_suites len > max_len */ + max_len = sizeof(expected_cs) - 1; + EXPECT_TRUE(max_len > 0); + + EXPECT_NOT_NULL(cs_out = malloc(max_len)); + EXPECT_EQUAL(max_len, s2n_client_hello_get_cipher_suites(client_hello, cs_out, max_len)); + EXPECT_BYTEARRAY_EQUAL(cs_out, client_hello->cipher_suites.data, max_len); + free(cs_out); + cs_out = NULL; + + /* Verify collected extensions size correct */ + EXPECT_EQUAL(client_hello->extensions.raw.size, client_extensions_len); + + /* Verify collected extensions correct */ + EXPECT_BYTEARRAY_EQUAL(client_hello->extensions.raw.data, client_extensions, client_extensions_len); + + /* Verify s2n_client_hello_get_extensions_length correct */ + EXPECT_EQUAL(s2n_client_hello_get_extensions_length(client_hello), client_extensions_len); + + /* Verify s2n_client_hello_get_extensions correct */ + uint8_t *extensions_out; + + /* Verify s2n_client_hello_get_extensions retrieves the full cipher_suites when its len <= max_len */ + EXPECT_TRUE(client_hello->extensions.raw.size < S2N_LARGE_RECORD_LENGTH); + EXPECT_NOT_NULL(extensions_out = malloc(S2N_LARGE_RECORD_LENGTH)); + EXPECT_EQUAL(client_extensions_len, s2n_client_hello_get_extensions(client_hello, extensions_out, S2N_LARGE_RECORD_LENGTH)); + EXPECT_BYTEARRAY_EQUAL(extensions_out, client_extensions, client_extensions_len); + free(extensions_out); + extensions_out = NULL; + + /* Verify s2n_client_hello_get_extensions retrieves truncated message when cipher_suites len > max_len */ + max_len = client_extensions_len - 1; + EXPECT_TRUE(max_len > 0); + + EXPECT_NOT_NULL(extensions_out = malloc(max_len)); + EXPECT_EQUAL(max_len, s2n_client_hello_get_extensions(client_hello, extensions_out, max_len)); + EXPECT_BYTEARRAY_EQUAL(extensions_out, client_hello->extensions.raw.data, max_len); + free(extensions_out); + extensions_out = NULL; + + /* Verify server name extension and it's length are returned correctly */ + EXPECT_EQUAL(s2n_client_hello_get_extension_length(client_hello, S2N_EXTENSION_SERVER_NAME), server_name_extension_len); + EXPECT_NOT_NULL(ext_data = malloc(server_name_extension_len)); + EXPECT_EQUAL(s2n_client_hello_get_extension_by_id(client_hello, S2N_EXTENSION_SERVER_NAME, ext_data, server_name_extension_len), server_name_extension_len); + EXPECT_BYTEARRAY_EQUAL(ext_data, server_name_extension, server_name_extension_len); + free(ext_data); + ext_data = NULL; + + /* Verify server name extension is truncated if extension_size > max_len */ + EXPECT_NOT_NULL(ext_data = malloc(server_name_extension_len - 1)); + EXPECT_EQUAL(s2n_client_hello_get_extension_by_id(client_hello, S2N_EXTENSION_SERVER_NAME, ext_data, server_name_extension_len - 1), server_name_extension_len - 1); + EXPECT_BYTEARRAY_EQUAL(ext_data, server_name_extension, server_name_extension_len - 1); + free(ext_data); + ext_data = NULL; + + /* Verify get extension and it's length calls for a non-existing extension type */ + EXPECT_EQUAL(s2n_client_hello_get_extension_length(client_hello, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY), 0); + EXPECT_NOT_NULL(ext_data = malloc(server_name_extension_len)); + EXPECT_EQUAL(s2n_client_hello_get_extension_by_id(client_hello, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, ext_data, server_name_extension_len), 0); + EXPECT_EQUAL(s2n_errno, S2N_ERR_EXTENSION_NOT_RECEIVED); + free(ext_data); + ext_data = NULL; + + /* Verify server name extension exists */ + bool extension_exists = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(client_hello, S2N_EXTENSION_SERVER_NAME, &extension_exists)); + EXPECT_TRUE(extension_exists); + + /* Verify expected result for non-existing extension */ + extension_exists = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(client_hello, S2N_EXTENSION_CERTIFICATE_TRANSPARENCY, &extension_exists)); + EXPECT_FALSE(extension_exists); + + /* Verify s2n_client_hello_get_session_id is what we received in ClientHello */ + uint8_t expected_ch_session_id[] = { ZERO_TO_THIRTY_ONE }; + uint8_t ch_session_id[sizeof(expected_ch_session_id)]; + uint32_t ch_session_id_length; + EXPECT_SUCCESS(s2n_client_hello_get_session_id_length(client_hello, &ch_session_id_length)); + EXPECT_EQUAL(ch_session_id_length, sizeof(ch_session_id)); + EXPECT_SUCCESS(s2n_client_hello_get_session_id(client_hello, ch_session_id, &ch_session_id_length, sizeof(ch_session_id))); + EXPECT_EQUAL(ch_session_id_length, sizeof(ch_session_id)); + EXPECT_BYTEARRAY_EQUAL(ch_session_id, expected_ch_session_id, sizeof(expected_ch_session_id)); + + /* Verify s2n_connection_get_session_id is different from the one we received in ClientHello, as we generated a new one in ServerHello */ + uint8_t conn_session_id[sizeof(expected_ch_session_id)]; + EXPECT_EQUAL(s2n_connection_get_session_id_length(server_conn), sizeof(conn_session_id)); + EXPECT_SUCCESS(s2n_connection_get_session_id(server_conn, conn_session_id, sizeof(conn_session_id))); + EXPECT_BYTEARRAY_NOT_EQUAL(conn_session_id, ch_session_id, sizeof(expected_ch_session_id)); + + /* Free all handshake data */ + EXPECT_SUCCESS(s2n_connection_free_handshake(server_conn)); + + /* Verify free_handshake resized the s2n_client_hello.raw_message stuffer back to 0 */ + EXPECT_NULL(client_hello->raw_message.data); + EXPECT_EQUAL(client_hello->raw_message.size, 0); + + EXPECT_SUCCESS(s2n_shutdown(server_conn, &server_blocked)); + + /* Wipe connection */ + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + + /* Verify connection_wipe resized the s2n_client_hello.raw_message stuffer back to 0 */ + EXPECT_NULL(client_hello->raw_message.data); + EXPECT_EQUAL(client_hello->raw_message.size, 0); + + /* Verify the s2n blobs referencing cipher_suites and extensions have cleared */ + EXPECT_EQUAL(client_hello->cipher_suites.size, 0); + EXPECT_NULL(client_hello->cipher_suites.data); + EXPECT_EQUAL(client_hello->extensions.raw.size, 0); + EXPECT_NULL(client_hello->extensions.raw.data); + + /* Verify the connection is successfully reused after connection_wipe */ + + /* Re-configure connection */ + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + /* Security policy must allow cipher suite hard coded into client hello */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + /* Recreate config */ + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Re-send the client hello message */ + EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.client, sent_client_hello, sent_client_hello_len), sent_client_hello_len); + + /* Verify that the sent client hello message is accepted */ + s2n_negotiate(server_conn, &server_blocked); + EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); + EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE); + + /* Verify the collected client hello on the reused connection matches the expected client hello */ + client_hello = s2n_connection_get_client_hello(server_conn); + collected_client_hello = client_hello->raw_message.data; + EXPECT_BYTEARRAY_EQUAL(collected_client_hello, expected_client_hello, sent_client_hello_len); + + EXPECT_SUCCESS(s2n_shutdown(server_conn, &server_blocked)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + free(expected_client_hello); + free(sent_client_hello); + }; + + /* Client hello api with NULL inputs */ + { + uint32_t len = 128; + uint8_t *out; + EXPECT_NOT_NULL(out = malloc(len)); + + EXPECT_FAILURE(s2n_client_hello_get_raw_message_length(NULL)); + EXPECT_FAILURE(s2n_client_hello_get_raw_message(NULL, out, len)); + EXPECT_FAILURE(s2n_client_hello_get_cipher_suites_length(NULL)); + EXPECT_FAILURE(s2n_client_hello_get_cipher_suites(NULL, out, len)); + EXPECT_FAILURE(s2n_client_hello_get_extensions_length(NULL)); + EXPECT_FAILURE(s2n_client_hello_get_extensions(NULL, out, len)); + EXPECT_FAILURE(s2n_client_hello_get_extension_length(NULL, S2N_EXTENSION_SERVER_NAME)); + EXPECT_FAILURE(s2n_client_hello_get_extension_by_id(NULL, S2N_EXTENSION_SERVER_NAME, out, len)); + free(out); + out = NULL; + + bool exists = false; + EXPECT_FAILURE(s2n_client_hello_has_extension(NULL, S2N_EXTENSION_SERVER_NAME, &exists)); + EXPECT_FALSE(exists); + }; + + /* test_weird_client_hello_version() */ + { + struct s2n_connection *server_conn; + struct s2n_config *server_config; + s2n_blocked_status server_blocked; + uint8_t *sent_client_hello; + + uint8_t client_extensions[] = { + /* Extension type TLS_EXTENSION_SERVER_NAME */ + 0x00, + 0x00, + /* Extension size */ + 0x00, + 0x08, + /* Server names len */ + 0x00, + 0x06, + /* First server name type - host name */ + 0x00, + /* First server name len */ + 0x00, + 0x03, + /* First server name, matches sent_server_name */ + 's', + 'v', + 'r', + }; + + int client_extensions_len = sizeof(client_extensions); + uint8_t client_hello_prefix[] = { + /* Protocol version TLS ??? */ + 0xFF, + 0xFF, + /* Client random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suites len */ + 0x00, + 0x02, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, + 0x3C, + /* Compression methods len */ + 0x01, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (client_extensions_len >> 8) & 0xff, + (client_extensions_len & 0xff), + }; + int client_hello_prefix_len = sizeof(client_hello_prefix); + int sent_client_hello_len = client_hello_prefix_len + client_extensions_len; + uint8_t message_header[] = { + /* Handshake message type CLIENT HELLO */ + 0x01, + /* Body len */ + (sent_client_hello_len >> 16) & 0xff, + (sent_client_hello_len >> 8) & 0xff, + (sent_client_hello_len & 0xff), + }; + int message_len = sizeof(message_header) + sent_client_hello_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + EXPECT_NOT_NULL(sent_client_hello = malloc(sent_client_hello_len)); + EXPECT_MEMCPY_SUCCESS(sent_client_hello, client_hello_prefix, client_hello_prefix_len); + EXPECT_MEMCPY_SUCCESS(sent_client_hello + client_hello_prefix_len, client_extensions, client_extensions_len); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + /* Security policy must allow cipher suite hard coded into client hello */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Send the client hello message */ + EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.client, sent_client_hello, sent_client_hello_len), sent_client_hello_len); + + /* Verify that the sent client hello message is accepted */ + s2n_negotiate(server_conn, &server_blocked); + EXPECT_TRUE(s2n_conn_get_current_message_type(server_conn) > CLIENT_HELLO); + EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE); + /* Client sent an invalid legacy protocol version. We should still have negotiate the maximum value(TLS1.2) */ + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + s2n_connection_free(server_conn); + s2n_config_free(server_config); + free(sent_client_hello); + }; + + { + struct s2n_cipher_suite *client_cipher_suites[] = { + &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha, + }; + + struct s2n_cipher_preferences client_cipher_preferences = { + .count = s2n_array_len(client_cipher_suites), + .suites = client_cipher_suites, + }; + + const struct s2n_signature_scheme *const client_sig_scheme_pref_list[] = { + &s2n_rsa_pkcs1_sha1, + + /* Intentionally do not send and ECDSA SignatureScheme in the Client Hello. This is malformed since the + * Client's only Ciphersuite uses ECDSA, meaning that technically the Server could reject it, but there are + * some clients that send this form of malformed Client Hello's in the wild. So ensure we are compatible + * with them by assuming that the Client does support ECDSA, even though it's missing from the ClientHello. + */ + + /* &s2n_ecdsa_sha1, */ + }; + + struct s2n_signature_preferences client_signature_preferences = { + .count = s2n_array_len(client_sig_scheme_pref_list), + .signature_schemes = client_sig_scheme_pref_list, + }; + + struct s2n_security_policy client_security_policy = { + .minimum_protocol_version = S2N_TLS10, + .cipher_preferences = &client_cipher_preferences, + .kem_preferences = &kem_preferences_null, + .signature_preferences = &client_signature_preferences, + .ecc_preferences = &s2n_ecc_preferences_20140601, + }; + + EXPECT_TRUE(client_cipher_suites[0]->available); + + struct s2n_cert_chain_and_key *ecdsa_cert_chain; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_cert_chain, S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); + + char dhparams_pem[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + + /* Create Configs */ + struct s2n_config *server_config, *client_config; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_cert_chain)); + + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + server_config->security_policy = &security_policy_20190214; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + client_config->security_policy = &client_security_policy; + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* Create connection */ + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + /* We have to update the client's security policy after it sends the ClientHello. + * The client sends all signature algorithms in its security policy, and + * won't accept any signature algorithm it receives that's not in its security policy. + * So we need to change the security policy between sending and receiving. + */ + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, SERVER_HELLO)); + client_config->security_policy = &security_policy_20190214; + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_EQUAL(server_conn->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha); + EXPECT_EQUAL(client_conn->secure->cipher_suite, &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha); + EXPECT_EQUAL(server_conn->handshake_params.server_cert_sig_scheme->sig_alg, S2N_SIGNATURE_ECDSA); + EXPECT_EQUAL(server_conn->handshake_params.server_cert_sig_scheme->hash_alg, S2N_HASH_SHA1); + EXPECT_EQUAL(client_conn->handshake_params.server_cert_sig_scheme->sig_alg, S2N_SIGNATURE_ECDSA); + EXPECT_EQUAL(client_conn->handshake_params.server_cert_sig_scheme->hash_alg, S2N_HASH_SHA1); + + /* Free the data */ + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert_chain)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* s2n_client_hello_recv should fail when reading an SSLv2 client hello during a hello retry handshake */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + /* Handshake is hello retry and TLS1.3 was negotiated */ + server_conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_handshake_type_set_tls13_flag(server_conn, HELLO_RETRY_REQUEST)); + + /* Second client hello has version SSLv2 */ + server_conn->client_hello_version = S2N_SSLv2; + + /* Mock having some data in the client hello */ + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&server_conn->handshake.io, 100)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_parse_client_hello(server_conn), S2N_ERR_SAFETY); + }; + + /* Test s2n_client_hello_parse_message + * + * Comparing ClientHellos produced by connection IO parsing vs + * produced by s2n_client_hello_parse_message is difficult, but we can + * use JA3 fingerprints as an approximation. See s2n_fingerprint_ja3_test.c + */ + { + const char *security_policies[] = { "default", "default_tls13", "test_all" }; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + /* Test: Can parse ClientHellos sent by the s2n client */ + for (size_t i = 0; i < s2n_array_len(security_policies); i++) { + const char *security_policy = security_policies[i]; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client, security_policy)); + + EXPECT_SUCCESS(s2n_handshake_write_header(&client->handshake.io, TLS_CLIENT_HELLO)); + EXPECT_SUCCESS(s2n_client_hello_send(client)); + EXPECT_SUCCESS(s2n_handshake_finish_header(&client->handshake.io)); + + uint32_t raw_size = s2n_stuffer_data_available(&client->handshake.io); + EXPECT_NOT_EQUAL(raw_size, 0); + uint8_t *raw = s2n_stuffer_raw_read(&client->handshake.io, raw_size); + EXPECT_NOT_NULL(raw); + + DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL, s2n_client_hello_free); + EXPECT_NOT_NULL(client_hello = s2n_client_hello_parse_message(raw, raw_size)); + EXPECT_TRUE(client_hello->alloced); + }; + + /* Test: Rejects invalid ClientHellos + * + * This test is important to verify that no memory is leaked when parsing fails. + */ + { + struct s2n_client_hello *client_hello = NULL; + + uint8_t wrong_message_type[50] = { 0x02, 0x00, 0x00, 1 }; + client_hello = s2n_client_hello_parse_message(wrong_message_type, sizeof(wrong_message_type)); + EXPECT_NULL(client_hello); + EXPECT_EQUAL(s2n_errno, S2N_ERR_BAD_MESSAGE); + + uint8_t wrong_message_size[50] = { 0x01, 0x00, 0x00, UINT8_MAX }; + client_hello = s2n_client_hello_parse_message(wrong_message_size, sizeof(wrong_message_size)); + EXPECT_NULL(client_hello); + EXPECT_EQUAL(s2n_errno, S2N_ERR_BAD_MESSAGE); + + uint8_t too_short[5] = { 0x01, 0x00, 0x00, 1 }; + client_hello = s2n_client_hello_parse_message(too_short, sizeof(too_short)); + EXPECT_NULL(client_hello); + EXPECT_EQUAL(s2n_errno, S2N_ERR_STUFFER_OUT_OF_DATA); + + uint8_t all_zeroes[50] = { 0x01, 0x00, 0x00, 46 }; + client_hello = s2n_client_hello_parse_message(all_zeroes, sizeof(all_zeroes)); + EXPECT_NULL(client_hello); + EXPECT_EQUAL(s2n_errno, S2N_ERR_BAD_MESSAGE); + }; + + /* Test: Rejects SSLv2 */ + { + uint8_t sslv2_client_hello[] = { + SSLv2_CLIENT_HELLO_HEADER, + SSLv2_CLIENT_HELLO_PREFIX, + SSLv2_CLIENT_HELLO_CIPHER_SUITES, + SSLv2_CLIENT_HELLO_CHALLENGE, + }; + + /* Try parsing variations on the complete record vs just the message. + * The sslv2 record header is technically the first two bytes, + * but s2n-tls usually starts parsing after the first five bytes. + */ + for (size_t i = 0; i <= S2N_TLS_RECORD_HEADER_LENGTH; i++) { + struct s2n_client_hello *client_hello = s2n_client_hello_parse_message( + sslv2_client_hello + i, sizeof(sslv2_client_hello) - i); + EXPECT_NULL(client_hello); + EXPECT_EQUAL(s2n_errno, S2N_ERR_BAD_MESSAGE); + } + + /* Sanity check: s2n accepts the test sslv2 message via the connection */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server, "test_all")); + + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&server->header_in, + sslv2_client_hello, S2N_TLS_RECORD_HEADER_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&server->in, + sslv2_client_hello + S2N_TLS_RECORD_HEADER_LENGTH, + sizeof(sslv2_client_hello) - S2N_TLS_RECORD_HEADER_LENGTH)); + + EXPECT_FALSE(server->client_hello.sslv2); + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_OK(s2n_negotiate_until_message(server, &blocked, SERVER_HELLO)); + EXPECT_TRUE(server->client_hello.sslv2); + EXPECT_FALSE(server->client_hello.alloced); + } + }; + }; + + /* Test s2n_client_hello_free */ + { + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_free(NULL), S2N_ERR_NULL); + + /* Test: Accepts but ignores NULL / already freed */ + { + struct s2n_client_hello *client_hello = NULL; + for (size_t i = 0; i < 3; i++) { + EXPECT_SUCCESS(s2n_client_hello_free(&client_hello)); + EXPECT_NULL(client_hello); + } + }; + + /* Test: Errors on client hello associated with a connection */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client->handshake.io, &server->handshake.io, + s2n_stuffer_data_available(&client->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server)); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server); + EXPECT_NOT_NULL(client_hello); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_free(&client_hello), S2N_ERR_INVALID_ARGUMENT); + EXPECT_NOT_NULL(s2n_connection_get_client_hello(server)); + EXPECT_NOT_EQUAL(server->client_hello.raw_message.size, 0); + }; + + /* Test: Frees client hello from raw message */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + + EXPECT_SUCCESS(s2n_handshake_write_header(&client->handshake.io, TLS_CLIENT_HELLO)); + EXPECT_SUCCESS(s2n_client_hello_send(client)); + EXPECT_SUCCESS(s2n_handshake_finish_header(&client->handshake.io)); + + uint32_t raw_size = s2n_stuffer_data_available(&client->handshake.io); + EXPECT_NOT_EQUAL(raw_size, 0); + uint8_t *raw = s2n_stuffer_raw_read(&client->handshake.io, raw_size); + EXPECT_NOT_NULL(raw); + + struct s2n_client_hello *client_hello = s2n_client_hello_parse_message( + raw, raw_size); + EXPECT_NOT_NULL(client_hello); + + for (size_t i = 0; i < 3; i++) { + EXPECT_SUCCESS(s2n_client_hello_free(&client_hello)); + EXPECT_NULL(client_hello); + } + }; + }; + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_chain_and_key)); + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_client_key_share_extension_pq_test.c b/tests/unit/s2n_client_key_share_extension_pq_test.c new file mode 100644 index 00000000000..5bb16323a3b --- /dev/null +++ b/tests/unit/s2n_client_key_share_extension_pq_test.c @@ -0,0 +1,896 @@ +/* + * 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 "s2n_test.h" + +/* clang-format off */ +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls13.h" +#include "tls/extensions/s2n_client_key_share.h" +#include "tls/extensions/s2n_key_share.h" +#include "tls/s2n_security_policies.h" + +#include "testlib/s2n_testlib.h" +#include "stuffer/s2n_stuffer.h" +#include "utils/s2n_safety.h" +#include "pq-crypto/s2n_pq.h" +/* clang-format on */ + +#define HELLO_RETRY_MSG_NO 1 +#define MEM_FOR_EXTENSION 4096 + +static int s2n_generate_pq_hybrid_key_share_for_test(struct s2n_stuffer *out, struct s2n_kem_group_params *kem_group_params); +static int s2n_copy_pq_share(struct s2n_stuffer *from, struct s2n_blob *to, const struct s2n_kem_group *kem_group, bool len_prefixed); + +int main() +{ + BEGIN_TEST(); + /* PQ hybrid tests for s2n_client_key_share_extension */ + for (int len_prefixed = 0; len_prefixed < 2; len_prefixed++) { + int draft_revision = (len_prefixed) ? 0 : 5; + const struct s2n_kem_preferences kem_prefs_all = { + .kem_count = 0, + .kems = NULL, + .tls13_kem_group_count = S2N_SUPPORTED_KEM_GROUPS_COUNT, + .tls13_kem_groups = ALL_SUPPORTED_KEM_GROUPS, + .tls13_pq_hybrid_draft_revision = draft_revision + }; + + const struct s2n_security_policy security_policy_all = { + .minimum_protocol_version = S2N_SSLv3, + .cipher_preferences = &cipher_preferences_test_all_tls13, + .kem_preferences = &kem_prefs_all, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20200310, + }; + + /* Tests for s2n_client_key_share_extension.send */ + { + /* Test that s2n_client_key_share_extension.send sends only ECC key shares + * when PQ is disabled, even if tls13_kem_groups is non-null. */ + if (!s2n_pq_is_enabled()) { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + conn->security_policy_override = &security_policy_all; + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + EXPECT_EQUAL(kem_pref->tls13_kem_group_count, S2N_SUPPORTED_KEM_GROUPS_COUNT); + + const struct s2n_ecc_preferences *ecc_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + EXPECT_NOT_NULL(ecc_preferences); + + DEFER_CLEANUP(struct s2n_stuffer key_share_extension = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 1024)); + EXPECT_SUCCESS(s2n_client_key_share_extension.send(conn, &key_share_extension)); + + /* Assert total key shares extension size is correct */ + uint16_t sent_key_shares_size; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&key_share_extension, &sent_key_shares_size)); + EXPECT_EQUAL(sent_key_shares_size, s2n_stuffer_data_available(&key_share_extension)); + + /* ECC key shares should have the format: IANA ID || size || share. Only one ECC key share + * should be sent (as per default s2n behavior). */ + uint16_t iana_value, share_size; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&key_share_extension, &iana_value)); + EXPECT_EQUAL(iana_value, ecc_preferences->ecc_curves[0]->iana_id); + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&key_share_extension, &share_size)); + EXPECT_EQUAL(share_size, ecc_preferences->ecc_curves[0]->share_size); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&key_share_extension, share_size)); + + /* If all the sizes/bytes were correctly written, there should be nothing left over */ + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Test that s2n_client_key_share_extension.send generates and sends PQ hybrid + * and ECC shares correctly when PQ is enabled. */ + if (s2n_pq_is_enabled()) { + for (size_t i = 0; i < S2N_SUPPORTED_KEM_GROUPS_COUNT; i++) { + /* The PQ hybrid key share send function only sends the highest priority PQ key share. On each + * iteration of the outer loop of this test (index i), we populate test_kem_groups[] with a + * different permutation of all_kem_groups[] to ensure we handle each kem_group key share + * correctly. */ + const struct s2n_kem_group *test_kem_groups[S2N_SUPPORTED_KEM_GROUPS_COUNT]; + for (size_t j = 0; j < S2N_SUPPORTED_KEM_GROUPS_COUNT; j++) { + /* cppcheck-suppress moduloofone */ + test_kem_groups[j] = ALL_SUPPORTED_KEM_GROUPS[(j + i) % S2N_SUPPORTED_KEM_GROUPS_COUNT]; + } + + const struct s2n_kem_preferences test_kem_prefs = { + .kem_count = 0, + .kems = NULL, + .tls13_kem_group_count = s2n_array_len(test_kem_groups), + .tls13_kem_groups = test_kem_groups, + .tls13_pq_hybrid_draft_revision = draft_revision + }; + + const struct s2n_security_policy test_security_policy = { + .minimum_protocol_version = S2N_SSLv3, + .cipher_preferences = &cipher_preferences_test_all_tls13, + .kem_preferences = &test_kem_prefs, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20200310, + }; + + /* Test sending of default hybrid key share (non-HRR) */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + conn->security_policy_override = &test_security_policy; + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + EXPECT_EQUAL(kem_pref->tls13_kem_group_count, S2N_SUPPORTED_KEM_GROUPS_COUNT); + EXPECT_EQUAL(test_kem_groups[0], kem_pref->tls13_kem_groups[0]); + const struct s2n_kem_group *test_kem_group = kem_pref->tls13_kem_groups[0]; + + DEFER_CLEANUP(struct s2n_stuffer key_share_extension = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, MEM_FOR_EXTENSION)); + EXPECT_SUCCESS(s2n_client_key_share_extension.send(conn, &key_share_extension)); + + /* Assert that the client saved its private keys correctly in the connection state + * for both hybrid PQ and classic ECC */ + struct s2n_kem_group_params *kem_group_params = &conn->kex_params.client_kem_group_params; + EXPECT_EQUAL(kem_group_params->kem_group, test_kem_group); + EXPECT_EQUAL(kem_group_params->kem_params.kem, test_kem_group->kem); + EXPECT_NOT_NULL(kem_group_params->kem_params.private_key.data); + EXPECT_EQUAL(kem_group_params->kem_params.private_key.size, test_kem_group->kem->private_key_length); + EXPECT_EQUAL(kem_group_params->ecc_params.negotiated_curve, test_kem_group->curve); + EXPECT_NOT_NULL(kem_group_params->ecc_params.evp_pkey); + + struct s2n_ecc_evp_params *ecc_params = &conn->kex_params.client_ecc_evp_params; + EXPECT_EQUAL(ecc_params->negotiated_curve, ecc_pref->ecc_curves[0]); + EXPECT_NOT_NULL(ecc_params->evp_pkey); + + /* Now, assert that the client sent the correct bytes over the wire for the key share extension */ + /* Assert total key shares extension size is correct */ + uint16_t sent_key_shares_size; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&key_share_extension, &sent_key_shares_size)); + EXPECT_EQUAL(sent_key_shares_size, s2n_stuffer_data_available(&key_share_extension)); + + /* Assert that the hybrid key share is correct: + * IANA ID || total hybrid share size || ECC share size || ECC share || PQ share size || PQ share */ + uint16_t sent_hybrid_iana_id; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&key_share_extension, &sent_hybrid_iana_id)); + EXPECT_EQUAL(sent_hybrid_iana_id, kem_pref->tls13_kem_groups[0]->iana_id); + + uint16_t expected_hybrid_share_size = 0; + + if (len_prefixed) { + expected_hybrid_share_size = S2N_SIZE_OF_KEY_SHARE_SIZE + + test_kem_group->curve->share_size + + S2N_SIZE_OF_KEY_SHARE_SIZE + + test_kem_group->kem->public_key_length; + } else { + expected_hybrid_share_size = test_kem_group->curve->share_size + test_kem_group->kem->public_key_length; + } + + uint16_t sent_hybrid_share_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&key_share_extension, &sent_hybrid_share_size)); + EXPECT_EQUAL(sent_hybrid_share_size, expected_hybrid_share_size); + + if (len_prefixed) { + uint16_t hybrid_ecc_share_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&key_share_extension, &hybrid_ecc_share_size)); + EXPECT_EQUAL(hybrid_ecc_share_size, test_kem_group->curve->share_size); + } + EXPECT_SUCCESS(s2n_stuffer_skip_read(&key_share_extension, test_kem_group->curve->share_size)); + + if (len_prefixed) { + uint16_t hybrid_pq_share_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&key_share_extension, &hybrid_pq_share_size)); + EXPECT_EQUAL(hybrid_pq_share_size, test_kem_group->kem->public_key_length); + } + EXPECT_SUCCESS(s2n_stuffer_skip_read(&key_share_extension, test_kem_group->kem->public_key_length)); + + /* Assert that the ECC key share is correct: IANA ID || size || share */ + uint16_t ecc_iana_value, ecc_share_size; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&key_share_extension, &ecc_iana_value)); + EXPECT_EQUAL(ecc_iana_value, ecc_pref->ecc_curves[0]->iana_id); + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&key_share_extension, &ecc_share_size)); + EXPECT_EQUAL(ecc_share_size, ecc_pref->ecc_curves[0]->share_size); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&key_share_extension, ecc_share_size)); + + /* If all the sizes/bytes were correctly written, there should be nothing left over */ + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Test sending key share in response to HRR */ + /* Need at least two KEM's to test ClientHelloRetry fallback */ + if (test_security_policy.kem_preferences->tls13_kem_group_count >= 2) { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + conn->security_policy_override = &test_security_policy; + conn->actual_protocol_version = S2N_TLS13; + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + + /* This is for pre-HRR set up: force the client to generate its default hybrid key share. */ + DEFER_CLEANUP(struct s2n_stuffer key_share_extension = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, MEM_FOR_EXTENSION)); + EXPECT_SUCCESS(s2n_client_key_share_extension.send(conn, &key_share_extension)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&key_share_extension)); + /* Quick sanity check */ + EXPECT_NOT_NULL(conn->kex_params.client_kem_group_params.kem_params.private_key.data); + EXPECT_NOT_NULL(conn->kex_params.client_kem_group_params.ecc_params.evp_pkey); + + /* Prepare client for HRR. Client would have sent a key share for kem_pref->tls13_kem_groups[0], + * but server selects something else for negotiation. */ + conn->handshake.handshake_type = HELLO_RETRY_REQUEST; + conn->handshake.message_number = HELLO_RETRY_MSG_NO; + conn->actual_protocol_version_established = 1; + uint8_t chosen_index = kem_pref->tls13_kem_group_count - 1; + EXPECT_NOT_EQUAL(chosen_index, 0); + const struct s2n_kem_group *negotiated_kem_group = kem_pref->tls13_kem_groups[chosen_index]; + conn->kex_params.server_kem_group_params.kem_group = negotiated_kem_group; + + EXPECT_SUCCESS(s2n_client_key_share_extension.send(conn, &key_share_extension)); + + /* Assert that the client saved its private keys correctly in the connection state for hybrid */ + struct s2n_kem_group_params *kem_group_params = &conn->kex_params.client_kem_group_params; + EXPECT_EQUAL(kem_group_params->kem_group, negotiated_kem_group); + EXPECT_EQUAL(kem_group_params->kem_params.kem, negotiated_kem_group->kem); + EXPECT_NOT_NULL(kem_group_params->kem_params.private_key.data); + EXPECT_EQUAL(kem_group_params->kem_params.private_key.size, negotiated_kem_group->kem->private_key_length); + EXPECT_EQUAL(kem_group_params->ecc_params.negotiated_curve, negotiated_kem_group->curve); + EXPECT_NOT_NULL(kem_group_params->ecc_params.evp_pkey); + + /* Assert that the client sent the correct bytes over the wire for the key share extension */ + /* Assert total key shares extension size is correct */ + uint16_t sent_key_shares_size; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&key_share_extension, &sent_key_shares_size)); + EXPECT_EQUAL(sent_key_shares_size, s2n_stuffer_data_available(&key_share_extension)); + + /* Assert that the hybrid key share is correct: + * IANA ID || total hybrid share size || ECC share size || ECC share || PQ share size || PQ share */ + uint16_t sent_hybrid_iana_id; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&key_share_extension, &sent_hybrid_iana_id)); + EXPECT_EQUAL(sent_hybrid_iana_id, kem_pref->tls13_kem_groups[chosen_index]->iana_id); + + uint16_t expected_hybrid_share_size; + + if (len_prefixed) { + expected_hybrid_share_size = S2N_SIZE_OF_KEY_SHARE_SIZE + + negotiated_kem_group->curve->share_size + + S2N_SIZE_OF_KEY_SHARE_SIZE + + negotiated_kem_group->kem->public_key_length; + } else { + expected_hybrid_share_size = negotiated_kem_group->curve->share_size + negotiated_kem_group->kem->public_key_length; + } + + uint16_t sent_hybrid_share_size; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&key_share_extension, &sent_hybrid_share_size)); + EXPECT_EQUAL(sent_hybrid_share_size, expected_hybrid_share_size); + + if (len_prefixed) { + uint16_t hybrid_ecc_share_size; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&key_share_extension, &hybrid_ecc_share_size)); + EXPECT_EQUAL(hybrid_ecc_share_size, negotiated_kem_group->curve->share_size); + } + EXPECT_SUCCESS(s2n_stuffer_skip_read(&key_share_extension, negotiated_kem_group->curve->share_size)); + + if (len_prefixed) { + uint16_t hybrid_pq_share_size; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&key_share_extension, &hybrid_pq_share_size)); + EXPECT_EQUAL(hybrid_pq_share_size, negotiated_kem_group->kem->public_key_length); + } + EXPECT_SUCCESS(s2n_stuffer_skip_read(&key_share_extension, negotiated_kem_group->kem->public_key_length)); + + /* If all the sizes/bytes were correctly written, there should be nothing left over */ + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Test sending in response to HRR for early data */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->security_policy_override = &test_security_policy; + EXPECT_NOT_NULL(conn); + + const struct s2n_ecc_preferences *ecc_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + EXPECT_NOT_NULL(ecc_preferences); + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + + struct s2n_stuffer first_extension = { 0 }, second_extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&first_extension, MEM_FOR_EXTENSION)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&second_extension, MEM_FOR_EXTENSION)); + + EXPECT_SUCCESS(s2n_client_key_share_extension.send(conn, &first_extension)); + + conn->kex_params.server_kem_group_params.kem_group = conn->kex_params.client_kem_group_params.kem_group; + conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve = + conn->kex_params.client_kem_group_params.ecc_params.negotiated_curve; + + /* Setup the client to have received a HelloRetryRequest */ + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(conn, S2N_TLS13)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(conn)); + conn->early_data_state = S2N_EARLY_DATA_REJECTED; + + EXPECT_SUCCESS(s2n_client_key_share_extension.send(conn, &second_extension)); + + /* Read the total length of both extensions. + * The first keys extension contains multiple shares, so should be longer than the second. */ + uint16_t first_sent_key_shares_size = 0, second_sent_key_shares_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&first_extension, &first_sent_key_shares_size)); + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&second_extension, &second_sent_key_shares_size)); + EXPECT_EQUAL(first_sent_key_shares_size, s2n_stuffer_data_available(&first_extension)); + EXPECT_EQUAL(second_sent_key_shares_size, s2n_stuffer_data_available(&second_extension)); + EXPECT_TRUE(second_sent_key_shares_size < first_sent_key_shares_size); + + /* Read the iana of the first share. + * Both shares should contain the same iana, and it should be equal to the server's chosen kem group. */ + uint16_t first_sent_hybrid_iana_id = 0, second_sent_hybrid_iana_id = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&first_extension, &first_sent_hybrid_iana_id)); + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&second_extension, &second_sent_hybrid_iana_id)); + EXPECT_EQUAL(first_sent_hybrid_iana_id, conn->kex_params.server_kem_group_params.kem_group->iana_id); + EXPECT_EQUAL(first_sent_hybrid_iana_id, second_sent_hybrid_iana_id); + + /* Read the total share size, including both ecc and kem. + * The first extension contains multiple shares, so should contain more data than the share size. + * The second extension only contains one share, so should contain only the share size. */ + uint16_t first_total_hybrid_share_size = 0, second_total_hybrid_share_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&first_extension, &first_total_hybrid_share_size)); + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&second_extension, &second_total_hybrid_share_size)); + EXPECT_TRUE(first_total_hybrid_share_size < s2n_stuffer_data_available(&first_extension)); + EXPECT_EQUAL(second_total_hybrid_share_size, s2n_stuffer_data_available(&second_extension)); + + if (len_prefixed) { + /* Read the ecc share size. + * The ecc share should be identical for both, so the size should be the same. */ + uint16_t first_ecc_share_size = 0, second_ecc_share_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&first_extension, &first_ecc_share_size)); + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&second_extension, &second_ecc_share_size)); + EXPECT_EQUAL(first_ecc_share_size, second_ecc_share_size); + } + + /* Read the ecc share. + * The ecc share should be identical for both. */ + struct s2n_kem_group_params *kem_group_params = &conn->kex_params.client_kem_group_params; + int ecc_share_size = kem_group_params->ecc_params.negotiated_curve->share_size; + uint8_t *first_ecc_share_data = NULL, *second_ecc_share_data = NULL; + EXPECT_NOT_NULL(first_ecc_share_data = s2n_stuffer_raw_read(&first_extension, ecc_share_size)); + EXPECT_NOT_NULL(second_ecc_share_data = s2n_stuffer_raw_read(&second_extension, ecc_share_size)); + EXPECT_BYTEARRAY_EQUAL(first_ecc_share_data, second_ecc_share_data, ecc_share_size); + + if (len_prefixed) { + /* The pq share should take up the rest of the key share. + * For now the pq share is different between extensions, so we can't assert anything else. */ + uint16_t second_pq_share_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&second_extension, &second_pq_share_size)); + EXPECT_EQUAL(second_pq_share_size, s2n_stuffer_data_available(&second_extension)); + } + + EXPECT_SUCCESS(s2n_stuffer_free(&first_extension)); + EXPECT_SUCCESS(s2n_stuffer_free(&second_extension)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + } + } + } + + /* Tests for s2n_client_key_share_extension.recv */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* Test that s2n_client_key_share_extension.recv ignores PQ key shares when PQ is disabled */ + if (!s2n_pq_is_enabled()) { + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->security_policy_override = &security_policy_all; + EXPECT_OK(s2n_set_all_mutually_supported_groups(server_conn)); + + DEFER_CLEANUP(struct s2n_stuffer key_share_extension = { 0 }, s2n_stuffer_free); + /* The key shares in this extension are fake - that's OK, the server should ignore the + * KEM group ID and skip the share. */ + EXPECT_SUCCESS(s2n_stuffer_alloc_ro_from_hex_string(&key_share_extension, + /* Shares size: 12 bytes */ + "000C" + /* IANA ID for secp256r1_sikep434r3 */ + "2F1F" + /* KEM group share size: 8 bytes */ + "0008" + /* ECC share size: 2 bytes */ + "0002" + /* Fake ECC share */ + "FFFF" + /* PQ share size: 2 bytes */ + "0002" + /* Fake PQ share */ + "FFFF")); + + EXPECT_SUCCESS(s2n_client_key_share_extension.recv(server_conn, &key_share_extension)); + + /* .recv should have read all data */ + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + /* Server should not have accepted any key shares */ + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + struct s2n_ecc_evp_params *received_ecc_params = &server_conn->kex_params.client_ecc_evp_params; + EXPECT_NULL(received_ecc_params->negotiated_curve); + EXPECT_NULL(received_ecc_params->evp_pkey); + + const struct s2n_kem_preferences *server_kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(server_conn, &server_kem_pref)); + EXPECT_NOT_NULL(server_kem_pref); + + struct s2n_kem_group_params *received_pq_params = &server_conn->kex_params.client_kem_group_params; + EXPECT_NULL(received_pq_params->kem_group); + EXPECT_NULL(received_pq_params->ecc_params.negotiated_curve); + EXPECT_NULL(received_pq_params->ecc_params.evp_pkey); + EXPECT_NULL(received_pq_params->kem_params.kem); + EXPECT_NULL(received_pq_params->kem_params.public_key.data); + EXPECT_EQUAL(received_pq_params->kem_params.public_key.size, 0); + EXPECT_EQUAL(received_pq_params->kem_params.public_key.allocated, 0); + + /* Server should have indicated HRR */ + EXPECT_TRUE(s2n_is_hello_retry_handshake(server_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + } + + if (s2n_pq_is_enabled()) { + /* Test that s2n_client_key_share_extension.recv correctly handles the extension + * generated by s2n_client_key_share_extension.send */ + { + for (size_t i = 0; i < S2N_SUPPORTED_KEM_GROUPS_COUNT; i++) { + /* The PQ hybrid key share send function only sends the highest priority PQ key share. On each + * iteration of the outer loop of this test (index i), we populate test_kem_groups[] with a + * different permutation of all_kem_groups[] to ensure we handle each kem_group key share + * correctly. */ + const struct s2n_kem_group *test_kem_groups[S2N_SUPPORTED_KEM_GROUPS_COUNT]; + for (size_t j = 0; j < S2N_SUPPORTED_KEM_GROUPS_COUNT; j++) { + /* cppcheck-suppress moduloofone */ + test_kem_groups[j] = ALL_SUPPORTED_KEM_GROUPS[(j + i) % S2N_SUPPORTED_KEM_GROUPS_COUNT]; + } + + const struct s2n_kem_preferences test_kem_prefs = { + .kem_count = 0, + .kems = NULL, + .tls13_kem_group_count = s2n_array_len(test_kem_groups), + .tls13_kem_groups = test_kem_groups, + .tls13_pq_hybrid_draft_revision = draft_revision + }; + + const struct s2n_security_policy test_security_policy = { + .minimum_protocol_version = S2N_SSLv3, + .cipher_preferences = &cipher_preferences_test_all_tls13, + .kem_preferences = &test_kem_prefs, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20200310, + }; + + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->security_policy_override = &test_security_policy; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS13; + /* Server security policy contains all the same KEM groups, but in a different order than client */ + server_conn->security_policy_override = &security_policy_all; + EXPECT_OK(s2n_set_all_mutually_supported_groups(server_conn)); + + DEFER_CLEANUP(struct s2n_stuffer key_share_extension = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 8192)); + + EXPECT_SUCCESS(s2n_client_key_share_extension.send(client_conn, &key_share_extension)); + + /* The client writes its PQ key share directly to IO without saving it, + * so we make a copy from the wire to ensure that server saved it correctly. */ + DEFER_CLEANUP(struct s2n_blob pq_key_share_copy = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_copy_pq_share(&key_share_extension, &pq_key_share_copy, + client_conn->kex_params.client_kem_group_params.kem_group, len_prefixed)); + + EXPECT_SUCCESS(s2n_client_key_share_extension.recv(server_conn, &key_share_extension)); + + /* .recv should have read all data */ + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + /* Client should have sent only the first ECC key share, server should have accepted it */ + struct s2n_ecc_evp_params *sent_ecc_params = &client_conn->kex_params.client_ecc_evp_params; + struct s2n_ecc_evp_params *received_ecc_params = &server_conn->kex_params.client_ecc_evp_params; + EXPECT_NOT_NULL(received_ecc_params->negotiated_curve); + EXPECT_NOT_NULL(received_ecc_params->evp_pkey); + EXPECT_TRUE(s2n_public_ecc_keys_are_equal(received_ecc_params, sent_ecc_params)); + + const struct s2n_kem_preferences *server_kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(server_conn, &server_kem_pref)); + EXPECT_NOT_NULL(server_kem_pref); + + /* Client should have sent only the first hybrid PQ share, server should have accepted it; + * the client and server KEM preferences include all the same KEM groups, but may be in + * different order. */ + struct s2n_kem_group_params *sent_pq_params = &client_conn->kex_params.client_kem_group_params; + struct s2n_kem_group_params *received_pq_params = &server_conn->kex_params.client_kem_group_params; + + EXPECT_EQUAL(received_pq_params->ecc_params.negotiated_curve, sent_pq_params->ecc_params.negotiated_curve); + EXPECT_NOT_NULL(received_pq_params->ecc_params.evp_pkey); + EXPECT_TRUE(s2n_public_ecc_keys_are_equal(&received_pq_params->ecc_params, &sent_pq_params->ecc_params)); + + EXPECT_EQUAL(received_pq_params->kem_params.kem, test_kem_prefs.tls13_kem_groups[0]->kem); + EXPECT_NOT_NULL(received_pq_params->kem_params.public_key.data); + EXPECT_EQUAL(received_pq_params->kem_params.public_key.size, test_kem_prefs.tls13_kem_groups[0]->kem->public_key_length); + EXPECT_BYTEARRAY_EQUAL(received_pq_params->kem_params.public_key.data, pq_key_share_copy.data, + sent_pq_params->kem_group->kem->public_key_length); + + /* Server should not have indicated HRR */ + EXPECT_FALSE(s2n_is_hello_retry_handshake(server_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + } + } + + /* Test that s2n_client_key_share_extension.recv selects the highest priority share, + * even if it appears last in the client's list of shares. */ + /* Need at least two KEM's to test fallback */ + if (security_policy_all.kem_preferences->tls13_kem_group_count >= 2) { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->security_policy_override = &security_policy_all; + EXPECT_OK(s2n_set_all_mutually_supported_groups(server_conn)); + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(server_conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + EXPECT_TRUE(kem_pref->tls13_kem_group_count >= 2); + + struct s2n_kem_group_params client_pq_params[] = { + { .kem_group = kem_pref->tls13_kem_groups[0], .kem_params = { .len_prefixed = len_prefixed } }, + { .kem_group = kem_pref->tls13_kem_groups[1], .kem_params = { .len_prefixed = len_prefixed } } + }; + + struct s2n_stuffer key_share_extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 8192)); + + struct s2n_stuffer_reservation keyshare_list_size = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&key_share_extension, &keyshare_list_size)); + EXPECT_SUCCESS(s2n_generate_pq_hybrid_key_share_for_test(&key_share_extension, &client_pq_params[1])); + EXPECT_SUCCESS(s2n_generate_pq_hybrid_key_share_for_test(&key_share_extension, &client_pq_params[0])); + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&keyshare_list_size)); + + EXPECT_SUCCESS(s2n_client_key_share_extension.recv(server_conn, &key_share_extension)); + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + /* Does not trigger retries */ + EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + + /* Highest priority group (0) share present */ + struct s2n_kem_group_params *server_params = &server_conn->kex_params.client_kem_group_params; + EXPECT_EQUAL(server_params->kem_group, kem_pref->tls13_kem_groups[0]); + EXPECT_NOT_NULL(server_params->kem_params.public_key.data); + EXPECT_NOT_NULL(server_params->ecc_params.evp_pkey); + + for (size_t i = 0; i < s2n_array_len(client_pq_params); i++) { + EXPECT_SUCCESS(s2n_kem_group_free(&client_pq_params[i])); + } + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + } + + /* Test that s2n_client_key_share_extension.recv ignores shares for groups not offered + * by the client / "mutually supported", and triggers a retry instead. + */ + /* Need at least two KEM's to test fallback */ + if (security_policy_all.kem_preferences->tls13_kem_group_count >= 2) { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->security_policy_override = &security_policy_all; + + /* Do NOT mark group 0 as mutually supported */ + EXPECT_OK(s2n_set_all_mutually_supported_groups(server_conn)); + server_conn->kex_params.mutually_supported_kem_groups[0] = NULL; + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(server_conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + EXPECT_TRUE(kem_pref->tls13_kem_group_count >= 2); + + struct s2n_kem_group_params client_pq_params = { .kem_group = kem_pref->tls13_kem_groups[0], + .kem_params = { .len_prefixed = len_prefixed } }; + + struct s2n_stuffer key_share_extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 8192)); + + struct s2n_stuffer_reservation keyshare_list_size = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&key_share_extension, &keyshare_list_size)); + EXPECT_SUCCESS(s2n_generate_pq_hybrid_key_share_for_test(&key_share_extension, &client_pq_params)); + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&keyshare_list_size)); + + EXPECT_SUCCESS(s2n_client_key_share_extension.recv(server_conn, &key_share_extension)); + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + /* Client key share ignored, so retry triggered */ + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + + /* No valid client key share present */ + struct s2n_kem_group_params *server_params = &server_conn->kex_params.client_kem_group_params; + EXPECT_NULL(server_params->kem_group); + EXPECT_NULL(server_params->kem_params.public_key.data); + EXPECT_NULL(server_params->ecc_params.evp_pkey); + + EXPECT_SUCCESS(s2n_kem_group_free(&client_pq_params)); + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + } + + /* Test that s2n_client_key_share_extension.recv ignores shares for curves not offered + * by the client / "mutually supported", and chooses a lower priority curve instead. + */ + /* Need at least two KEM's to test fallback */ + if (security_policy_all.kem_preferences->tls13_kem_group_count >= 2) { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->security_policy_override = &security_policy_all; + + /* Do NOT mark curve 0 as mutually supported */ + EXPECT_OK(s2n_set_all_mutually_supported_groups(server_conn)); + server_conn->kex_params.mutually_supported_kem_groups[0] = NULL; + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(server_conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + EXPECT_TRUE(kem_pref->tls13_kem_group_count >= 2); + + struct s2n_kem_group_params client_pq_params[] = { + { .kem_group = kem_pref->tls13_kem_groups[0], .kem_params = { .len_prefixed = len_prefixed } }, + { .kem_group = kem_pref->tls13_kem_groups[1], .kem_params = { .len_prefixed = len_prefixed } } + }; + + struct s2n_stuffer key_share_extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 8192)); + + struct s2n_stuffer_reservation keyshare_list_size = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&key_share_extension, &keyshare_list_size)); + EXPECT_SUCCESS(s2n_generate_pq_hybrid_key_share_for_test(&key_share_extension, &client_pq_params[0])); + EXPECT_SUCCESS(s2n_generate_pq_hybrid_key_share_for_test(&key_share_extension, &client_pq_params[1])); + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&keyshare_list_size)); + + EXPECT_SUCCESS(s2n_client_key_share_extension.recv(server_conn, &key_share_extension)); + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + /* Does not trigger a retry */ + EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + + /* Second highest priority group (1) share present, because highest priority not "mutually supported" */ + struct s2n_kem_group_params *server_params = &server_conn->kex_params.client_kem_group_params; + EXPECT_EQUAL(server_params->kem_group, kem_pref->tls13_kem_groups[1]); + EXPECT_NOT_NULL(server_params->kem_params.public_key.data); + EXPECT_NOT_NULL(server_params->ecc_params.evp_pkey); + + for (size_t i = 0; i < s2n_array_len(client_pq_params); i++) { + EXPECT_SUCCESS(s2n_kem_group_free(&client_pq_params[i])); + } + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + } + + /* Test that s2n_client_key_share_extension.recv ignores shares that can't be parsed, + * and continues to parse valid shares afterwards. */ + /* Need at least two KEM's to test fallback */ + if (security_policy_all.kem_preferences->tls13_kem_group_count >= 2) { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + server_conn->security_policy_override = &security_policy_all; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + EXPECT_OK(s2n_set_all_mutually_supported_groups(server_conn)); + + struct s2n_stuffer key_share_extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 8192)); + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(server_conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + EXPECT_TRUE(kem_pref->tls13_kem_group_count >= 2); + + struct s2n_kem_group_params client_pq_params[] = { + { .kem_group = kem_pref->tls13_kem_groups[0], .kem_params = { .len_prefixed = len_prefixed } }, + { .kem_group = kem_pref->tls13_kem_groups[1], .kem_params = { .len_prefixed = len_prefixed } } + }; + + /* Write share list length */ + struct s2n_stuffer_reservation keyshare_list_size = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&key_share_extension, &keyshare_list_size)); + /* Write first share. Mess up point by erasing most of it */ + EXPECT_SUCCESS(s2n_generate_pq_hybrid_key_share_for_test(&key_share_extension, &client_pq_params[0])); + const struct s2n_kem_group *kem_group = kem_pref->tls13_kem_groups[0]; + size_t hybrid_share_size = kem_group->curve->share_size + kem_group->kem->public_key_length; + + if (len_prefixed) { + hybrid_share_size += (2 * S2N_SIZE_OF_KEY_SHARE_SIZE); + } + + EXPECT_SUCCESS(s2n_stuffer_wipe_n(&key_share_extension, hybrid_share_size)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&key_share_extension, hybrid_share_size)); + /* Write second, valid share */ + EXPECT_SUCCESS(s2n_generate_pq_hybrid_key_share_for_test(&key_share_extension, &client_pq_params[1])); + /* Finish share list length */ + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&keyshare_list_size)); + + EXPECT_SUCCESS(s2n_client_key_share_extension.recv(server_conn, &key_share_extension)); + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + /* Should have chosen curve 1, because curve 0 was malformed */ + struct s2n_kem_group_params *server_params = &server_conn->kex_params.client_kem_group_params; + EXPECT_EQUAL(server_params->kem_group, kem_pref->tls13_kem_groups[1]); + EXPECT_NOT_NULL(server_params->kem_params.public_key.data); + EXPECT_NOT_NULL(server_params->ecc_params.evp_pkey); + + for (size_t i = 0; i < s2n_array_len(client_pq_params); i++) { + EXPECT_SUCCESS(s2n_kem_group_free(&client_pq_params[i])); + } + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + } + + /* Test that s2n_client_key_share_extension.recv ignores shares that can't be parsed, + * and doesn't ignore / forget / overwrite valid shares already parsed. */ + /* Need at least two KEM's to test fallback */ + if (security_policy_all.kem_preferences->tls13_kem_group_count >= 2) { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + server_conn->security_policy_override = &security_policy_all; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + EXPECT_OK(s2n_set_all_mutually_supported_groups(server_conn)); + + struct s2n_stuffer key_share_extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 8192)); + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(server_conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + EXPECT_TRUE(kem_pref->tls13_kem_group_count >= 2); + + struct s2n_kem_group_params client_pq_params[] = { + { .kem_group = kem_pref->tls13_kem_groups[0], .kem_params = { .len_prefixed = len_prefixed } }, + { .kem_group = kem_pref->tls13_kem_groups[1], .kem_params = { .len_prefixed = len_prefixed } } + }; + + /* Write share list length */ + struct s2n_stuffer_reservation keyshare_list_size = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&key_share_extension, &keyshare_list_size)); + /* Write first, valid share */ + EXPECT_SUCCESS(s2n_generate_pq_hybrid_key_share_for_test(&key_share_extension, &client_pq_params[0])); + /* Write second share. Mess up point by erasing most of it */ + EXPECT_SUCCESS(s2n_generate_pq_hybrid_key_share_for_test(&key_share_extension, &client_pq_params[1])); + const struct s2n_kem_group *kem_group = kem_pref->tls13_kem_groups[0]; + size_t hybrid_share_size = kem_group->curve->share_size + kem_group->kem->public_key_length; + + if (len_prefixed) { + hybrid_share_size += (2 * S2N_SIZE_OF_KEY_SHARE_SIZE); + } + + EXPECT_SUCCESS(s2n_stuffer_wipe_n(&key_share_extension, hybrid_share_size / 2)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&key_share_extension, hybrid_share_size / 2)); + /* Finish share list length */ + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&keyshare_list_size)); + + EXPECT_SUCCESS(s2n_client_key_share_extension.recv(server_conn, &key_share_extension)); + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + /* Should have chosen highest priority key share (0) */ + struct s2n_kem_group_params *server_params = &server_conn->kex_params.client_kem_group_params; + EXPECT_EQUAL(server_params->kem_group, kem_pref->tls13_kem_groups[0]); + EXPECT_NOT_NULL(server_params->kem_params.public_key.data); + EXPECT_NOT_NULL(server_params->ecc_params.evp_pkey); + + for (size_t i = 0; i < s2n_array_len(client_pq_params); i++) { + EXPECT_SUCCESS(s2n_kem_group_free(&client_pq_params[i])); + } + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + } + } + } + } + + END_TEST(); + + return 0; +} + +/* Copies the PQ portion of the keyshare. Assumes that the read cursor of *from is + * pointing to the beginning of the hybrid share. After copying, rewinds *from so + * that read cursor is at the original position. */ +static int s2n_copy_pq_share(struct s2n_stuffer *from, struct s2n_blob *to, const struct s2n_kem_group *kem_group, bool len_prefixed) +{ + POSIX_ENSURE_REF(from); + POSIX_ENSURE_REF(to); + POSIX_ENSURE_REF(kem_group); + + int keyshare_extension_offset = 10; + + if (!len_prefixed) { + keyshare_extension_offset -= (2 * S2N_SIZE_OF_KEY_SHARE_SIZE); + } + + POSIX_GUARD(s2n_alloc(to, kem_group->kem->public_key_length)); + /* Skip all the two-byte IDs/sizes and the ECC portion of the share */ + POSIX_GUARD(s2n_stuffer_skip_read(from, keyshare_extension_offset + kem_group->curve->share_size)); + POSIX_GUARD(s2n_stuffer_read(from, to)); + POSIX_GUARD(s2n_stuffer_rewind_read(from, keyshare_extension_offset + kem_group->curve->share_size + kem_group->kem->public_key_length)); + + return S2N_SUCCESS; +} + +static int s2n_generate_pq_hybrid_key_share_for_test(struct s2n_stuffer *out, struct s2n_kem_group_params *kem_group_params) +{ + POSIX_ENSURE_REF(out); + POSIX_ENSURE_REF(kem_group_params); + + /* This function should never be called when PQ is disabled */ + POSIX_ENSURE(s2n_pq_is_enabled(), S2N_ERR_PQ_DISABLED); + + const struct s2n_kem_group *kem_group = kem_group_params->kem_group; + POSIX_ENSURE_REF(kem_group); + + POSIX_GUARD(s2n_stuffer_write_uint16(out, kem_group->iana_id)); + + struct s2n_stuffer_reservation total_share_size = { 0 }; + POSIX_GUARD(s2n_stuffer_reserve_uint16(out, &total_share_size)); + + struct s2n_ecc_evp_params *ecc_params = &kem_group_params->ecc_params; + ecc_params->negotiated_curve = kem_group->curve; + struct s2n_kem_params *kem_params = &kem_group_params->kem_params; + + if (kem_params->len_prefixed) { + POSIX_GUARD(s2n_stuffer_write_uint16(out, ecc_params->negotiated_curve->share_size)); + } + POSIX_GUARD(s2n_ecc_evp_generate_ephemeral_key(ecc_params)); + POSIX_GUARD(s2n_ecc_evp_write_params_point(ecc_params, out)); + + kem_params->kem = kem_group->kem; + POSIX_GUARD(s2n_kem_send_public_key(out, kem_params)); + + POSIX_GUARD(s2n_stuffer_write_vector_size(&total_share_size)); + + return S2N_SUCCESS; +} diff --git a/tests/unit/s2n_client_key_share_extension_test.c b/tests/unit/s2n_client_key_share_extension_test.c new file mode 100644 index 00000000000..e92bea06382 --- /dev/null +++ b/tests/unit/s2n_client_key_share_extension_test.c @@ -0,0 +1,1157 @@ +/* + * 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 "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_client_key_share.h" +#include "tls/extensions/s2n_key_share.h" +#include "tls/s2n_alerts.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +#define HELLO_RETRY_MSG_NO 1 + +#define S2N_SIZE_OF_CLIENT_SHARE_SIZE 2 + +#define S2N_PREPARE_DATA_LENGTH(stuffer) \ + EXPECT_SUCCESS(s2n_stuffer_skip_write(stuffer, S2N_SIZE_OF_CLIENT_SHARE_SIZE)) +#define S2N_WRITE_DATA_LENGTH(stuffer) \ + EXPECT_SUCCESS(s2n_test_rewrite_length(stuffer)) + +static int s2n_test_rewrite_length(struct s2n_stuffer *stuffer); +static int s2n_write_named_curve(struct s2n_stuffer *out, const struct s2n_ecc_named_curve *existing_curve); +static int s2n_write_key_share(struct s2n_stuffer *out, uint16_t iana_value, uint16_t share_size, + const struct s2n_ecc_named_curve *existing_curve); + +S2N_RESULT s2n_extensions_client_key_share_size(struct s2n_connection *conn, uint32_t *size) +{ + RESULT_ENSURE_REF(conn); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + RESULT_GUARD_POSIX(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + RESULT_ENSURE_REF(ecc_pref); + + uint32_t s2n_client_key_share_extension_size = S2N_SIZE_OF_EXTENSION_TYPE + + S2N_SIZE_OF_EXTENSION_DATA_SIZE + + S2N_SIZE_OF_CLIENT_SHARES_SIZE; + + s2n_client_key_share_extension_size += S2N_SIZE_OF_KEY_SHARE_SIZE + S2N_SIZE_OF_NAMED_GROUP; + s2n_client_key_share_extension_size += ecc_pref->ecc_curves[0]->share_size; + + *size = s2n_client_key_share_extension_size; + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* Test that s2n_extensions_key_share_size produces the expected constant result */ + { + struct s2n_stuffer key_share_extension = { 0 }; + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + uint32_t key_share_size = 0; + EXPECT_OK(s2n_extensions_client_key_share_size(conn, &key_share_size)); + + /* should produce the same result if called twice */ + uint32_t key_share_size_again = 0; + EXPECT_OK(s2n_extensions_client_key_share_size(conn, &key_share_size_again)); + EXPECT_EQUAL(key_share_size, key_share_size_again); + + /* should equal the size of the data written on send */ + EXPECT_SUCCESS(s2n_stuffer_alloc(&key_share_extension, key_share_size)); + EXPECT_SUCCESS(s2n_client_key_share_extension.send(conn, &key_share_extension)); + EXPECT_EQUAL(key_share_size - S2N_EXTENSION_TYPE_FIELD_LENGTH - S2N_EXTENSION_LENGTH_FIELD_LENGTH, + s2n_stuffer_data_available(&key_share_extension)); + + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_client_key_share_extension.send */ + { + /* Test that s2n_client_key_share_extension.send initializes the client key share list */ + { + struct s2n_stuffer key_share_extension = { 0 }; + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 0)); + + EXPECT_SUCCESS(s2n_client_key_share_extension.send(conn, &key_share_extension)); + + const struct s2n_ecc_preferences *ecc_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + EXPECT_NOT_NULL(ecc_preferences); + + struct s2n_ecc_evp_params *ecc_evp_params = &conn->kex_params.client_ecc_evp_params; + EXPECT_EQUAL(ecc_evp_params->negotiated_curve, ecc_preferences->ecc_curves[0]); + EXPECT_NOT_NULL(ecc_evp_params->evp_pkey); + + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test that s2n_client_key_share_extension.send writes a well-formed list of key shares */ + { + struct s2n_stuffer key_share_extension = { 0 }; + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 0)); + EXPECT_SUCCESS(s2n_client_key_share_extension.send(conn, &key_share_extension)); + + /* should have correct shares size */ + uint16_t key_shares_size; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&key_share_extension, &key_shares_size)); + uint16_t actual_key_shares_size = s2n_stuffer_data_available(&key_share_extension); + EXPECT_EQUAL(key_shares_size, actual_key_shares_size); + EXPECT_EQUAL(key_shares_size, s2n_stuffer_data_available(&key_share_extension)); + + const struct s2n_ecc_preferences *ecc_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + EXPECT_NOT_NULL(ecc_preferences); + + /* should contain only the default supported curve */ + uint16_t iana_value, share_size; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&key_share_extension, &iana_value)); + EXPECT_EQUAL(iana_value, ecc_preferences->ecc_curves[0]->iana_id); + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&key_share_extension, &share_size)); + EXPECT_EQUAL(share_size, ecc_preferences->ecc_curves[0]->share_size); + + EXPECT_SUCCESS(s2n_stuffer_skip_read(&key_share_extension, share_size)); + + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_client_key_share_extension.send for a supported curve present in s2n_all_supported_curves_list, + * but not present in the ecc_preferences list selected */ + if (s2n_is_evp_apis_supported()) { + struct s2n_stuffer key_share_extension = { 0 }; + struct s2n_connection *conn; + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + /* Explicitly set the ecc_preferences list to contain the curves p-256 and p-384 */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20140601")); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + const struct s2n_ecc_preferences *ecc_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + EXPECT_NOT_NULL(ecc_preferences); + + /* x25519 is present in s2n_all_supported_curves_list but not in the "default" list */ + const struct s2n_ecc_named_curve *test_curve = &s2n_ecc_curve_x25519; + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 0)); + + EXPECT_SUCCESS(s2n_client_key_share_extension.send(conn, &key_share_extension)); + + struct s2n_ecc_evp_params *ecc_evp_params = &conn->kex_params.client_ecc_evp_params; + EXPECT_NOT_EQUAL(ecc_evp_params->negotiated_curve, test_curve); + + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + } + }; + + /* Test s2n_client_key_share_extension.send with HelloRetryRequest */ + { + /** + * For HelloRetryRequests when a keyshare does not match, test that s2n_client_key_share_extension.send replaces + * the list of keyshares with a list containing a single KeyShareEntry for the server selected group. + * + *= https://tools.ietf.org/rfc/rfc8446#4.1.2 + *= type=test + *# - If a "key_share" extension was supplied in the HelloRetryRequest, + *# replacing the list of shares with a list containing a single + *# KeyShareEntry from the indicated group. + * + *= https://tools.ietf.org/rfc/rfc8446#4.2.8 + *= type=test + *# Otherwise, when sending the new ClientHello, the client MUST + *# replace the original "key_share" extension with one containing only a + *# new KeyShareEntry for the group indicated in the selected_group field + *# of the triggering HelloRetryRequest. + **/ + if (s2n_is_evp_apis_supported()) { + struct s2n_connection *conn; + struct s2n_config *config; + struct s2n_stuffer key_share_extension = { 0 }; + + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + /* Security policy "20190801" contains x25519 */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20190801")); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 0)); + + /* Force the client to send an unsupported curve in keyshare list */ + conn->security_policy_override = &security_policy_test_tls13_retry; + + const struct s2n_ecc_preferences *ecc_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + EXPECT_NOT_NULL(ecc_preferences); + + EXPECT_SUCCESS(s2n_client_key_share_extension.send(conn, &key_share_extension)); + + /* Setup the client to have received a HelloRetryRequest with server negotiated curve as x25519 */ + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(conn, S2N_TLS13)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(conn)); + conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_x25519; + + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 0)); + + EXPECT_SUCCESS(s2n_client_key_share_extension.send(conn, &key_share_extension)); + + uint16_t key_shares_size; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&key_share_extension, &key_shares_size)); + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), key_shares_size); + + /* should contain keyshare for only server negotiated curve */ + uint32_t bytes_processed = 0; + EXPECT_EQUAL(key_shares_size, conn->kex_params.server_ecc_evp_params.negotiated_curve->share_size + S2N_SIZE_OF_NAMED_GROUP + S2N_SIZE_OF_KEY_SHARE_SIZE); + + uint16_t iana_value, share_size; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&key_share_extension, &iana_value)); + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&key_share_extension, &share_size)); + bytes_processed += conn->kex_params.server_ecc_evp_params.negotiated_curve->share_size + S2N_SIZE_OF_NAMED_GROUP + + S2N_SIZE_OF_KEY_SHARE_SIZE; + + EXPECT_EQUAL(iana_value, conn->kex_params.server_ecc_evp_params.negotiated_curve->iana_id); + EXPECT_EQUAL(share_size, conn->kex_params.server_ecc_evp_params.negotiated_curve->share_size); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&key_share_extension, share_size)); + EXPECT_EQUAL(bytes_processed, key_shares_size); + + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* For HelloRetryRequests, test that s2n_client_key_share_extension.recv can read and parse + * the result of s2n_client_key_share_extension.send */ + { + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + struct s2n_stuffer key_share_extension = { 0 }; + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_OK(s2n_set_all_mutually_supported_groups(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 0)); + + const struct s2n_ecc_preferences *ecc_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_preferences)); + EXPECT_NOT_NULL(ecc_preferences); + + /* Setup the client to have received a HelloRetryRequest */ + POSIX_CHECKED_MEMCPY(client_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(client_conn, S2N_TLS13)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(client_conn)); + client_conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; + + /* During HRR, A key_share list with a single key_share entry, + * corresponding to the server negotiated curve is sent by the client */ + EXPECT_SUCCESS(s2n_client_key_share_extension.send(client_conn, &key_share_extension)); + + /* should contain keyshare for only server negotiated curve */ + struct s2n_ecc_evp_params *client_ecc_evp_params = &client_conn->kex_params.client_ecc_evp_params; + EXPECT_EQUAL(client_ecc_evp_params->negotiated_curve, client_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_NOT_NULL(client_ecc_evp_params->negotiated_curve); + EXPECT_NOT_NULL(client_ecc_evp_params->evp_pkey); + + server_conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + + EXPECT_SUCCESS(s2n_client_key_share_extension.recv(server_conn, &key_share_extension)); + + /* should read all data */ + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + /* Verify that a keyshare list with a single keyshare corresponding to the server negotiated curve is received */ + struct s2n_ecc_evp_params *server_ecc_evp_params = &server_conn->kex_params.client_ecc_evp_params; + EXPECT_EQUAL(server_ecc_evp_params->negotiated_curve, server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_NOT_NULL(server_ecc_evp_params->negotiated_curve); + EXPECT_NOT_NULL(server_ecc_evp_params->evp_pkey); + + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* For HelloRetryRequests, test that s2n_client_key_share_extension.send fails, + * if the server negotiated_curve is not set and is NULL. */ + { + struct s2n_connection *conn; + struct s2n_stuffer key_share_extension = { 0 }; + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 0)); + + /* Setup the client to have received a HelloRetryRequest */ + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(conn, S2N_TLS13)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(conn)); + conn->kex_params.server_ecc_evp_params.negotiated_curve = NULL; + conn->kex_params.server_kem_group_params.kem_group = NULL; + + EXPECT_FAILURE_WITH_ERRNO(s2n_client_key_share_extension.send(conn, &key_share_extension), + S2N_ERR_BAD_KEY_SHARE); + + const struct s2n_ecc_preferences *ecc_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + EXPECT_NOT_NULL(ecc_preferences); + + struct s2n_ecc_evp_params *ecc_evp_params = &conn->kex_params.client_ecc_evp_params; + EXPECT_NULL(ecc_evp_params->negotiated_curve); + EXPECT_NULL(ecc_evp_params->evp_pkey); + + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* For HelloRetryRequests, verify that we cannot resend an existing share. */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + const struct s2n_ecc_preferences *ecc_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + EXPECT_NOT_NULL(ecc_preferences); + + const struct s2n_ecc_named_curve *curve = ecc_preferences->ecc_curves[0]; + + struct s2n_stuffer first_extension = { 0 }, second_extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&first_extension, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&second_extension, 0)); + + EXPECT_SUCCESS(s2n_client_key_share_extension.send(conn, &first_extension)); + EXPECT_EQUAL(conn->kex_params.client_ecc_evp_params.negotiated_curve, curve); + EXPECT_NOT_NULL(conn->kex_params.client_ecc_evp_params.evp_pkey); + + /* Setup the client to have received a HelloRetryRequest */ + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(conn, S2N_TLS13)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(conn)); + conn->early_data_state = S2N_EARLY_DATA_REJECTED; + conn->kex_params.server_ecc_evp_params.negotiated_curve = curve; + + EXPECT_FAILURE_WITH_ERRNO(s2n_client_key_share_extension.send(conn, &second_extension), + S2N_ERR_BAD_KEY_SHARE); + + EXPECT_SUCCESS(s2n_stuffer_free(&first_extension)); + EXPECT_SUCCESS(s2n_stuffer_free(&second_extension)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test s2n_client_key_share_extension.recv */ + { + /* Test that s2n_client_key_share_extension.recv is a no-op + * if not using TLS1.3 */ + { + struct s2n_connection *client_conn, *server_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_OK(s2n_set_all_mutually_supported_groups(server_conn)); + + DEFER_CLEANUP(struct s2n_stuffer key_share_extension, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 0)); + + EXPECT_SUCCESS(s2n_client_key_share_extension.send(client_conn, &key_share_extension)); + uint16_t key_share_extension_size = s2n_stuffer_data_available(&key_share_extension); + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + server_conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_extension_recv(&s2n_client_key_share_extension, server_conn, &key_share_extension)); + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), key_share_extension_size); + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + server_conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_client_key_share_extension.recv(server_conn, &key_share_extension)); + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test that s2n_client_key_share_extension.recv can read and parse + * the result of s2n_client_key_share_extension.send */ + { + struct s2n_connection *client_conn, *server_conn; + struct s2n_stuffer key_share_extension = { 0 }; + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 0)); + EXPECT_OK(s2n_set_all_mutually_supported_groups(server_conn)); + + EXPECT_SUCCESS(s2n_client_key_share_extension.send(client_conn, &key_share_extension)); + EXPECT_SUCCESS(s2n_client_key_share_extension.recv(server_conn, &key_share_extension)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + /* should read all data */ + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + /* should set internal state the same as the client */ + struct s2n_ecc_evp_params *server_ecc_evp_params = &server_conn->kex_params.client_ecc_evp_params; + struct s2n_ecc_evp_params *client_ecc_evp_params = &client_conn->kex_params.client_ecc_evp_params; + EXPECT_NOT_NULL(server_ecc_evp_params->negotiated_curve); + EXPECT_NOT_NULL(server_ecc_evp_params->evp_pkey); + EXPECT_TRUE(s2n_public_ecc_keys_are_equal(server_ecc_evp_params, client_ecc_evp_params)); + + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test that s2n_client_key_share_extension.recv can handle an empty keyshare list */ + { + /* Just the uint16_t length as "0" */ + uint8_t empty_keyshare_extension[] = { 0x00, 0x00 }; + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + server_conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_set_all_mutually_supported_groups(server_conn)); + + struct s2n_stuffer key_share_extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_alloc(&key_share_extension, sizeof(empty_keyshare_extension))); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&key_share_extension, empty_keyshare_extension, sizeof(empty_keyshare_extension))); + + EXPECT_SUCCESS(s2n_client_key_share_extension.recv(server_conn, &key_share_extension)); + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + /* Triggers retry */ + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test that s2n_client_key_share_extension.recv can handle a keyshare for + * a supported curve that isn't its first choice curve. */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + server_conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_set_all_mutually_supported_groups(server_conn)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + EXPECT_TRUE(ecc_pref->count >= 2); + + struct s2n_ecc_evp_params client_ecc_evp_params = { .negotiated_curve = ecc_pref->ecc_curves[1] }; + + struct s2n_stuffer key_share_extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 0)); + + struct s2n_stuffer_reservation keyshare_list_size = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&key_share_extension, &keyshare_list_size)); + EXPECT_SUCCESS(s2n_ecdhe_parameters_send(&client_ecc_evp_params, &key_share_extension)); + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&keyshare_list_size)); + + EXPECT_SUCCESS(s2n_client_key_share_extension.recv(server_conn, &key_share_extension)); + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + /* Does not trigger retries */ + EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + + /* Expected share present */ + struct s2n_ecc_evp_params *server_ecc_evp_params = &server_conn->kex_params.client_ecc_evp_params; + EXPECT_NOT_NULL(server_ecc_evp_params->negotiated_curve); + EXPECT_NOT_NULL(server_ecc_evp_params->evp_pkey); + EXPECT_TRUE(s2n_public_ecc_keys_are_equal(server_ecc_evp_params, &client_ecc_evp_params)); + + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&client_ecc_evp_params)); + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test that s2n_client_key_share_extension.recv can handle multiple keyshares */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + server_conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_set_all_mutually_supported_groups(server_conn)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + EXPECT_TRUE(ecc_pref->count >= 2); + + struct s2n_ecc_evp_params client_ecc_evp_params[] = { + { .negotiated_curve = ecc_pref->ecc_curves[0] }, + { .negotiated_curve = ecc_pref->ecc_curves[1] } + }; + + struct s2n_stuffer key_share_extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 0)); + + struct s2n_stuffer_reservation keyshare_list_size = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&key_share_extension, &keyshare_list_size)); + EXPECT_SUCCESS(s2n_ecdhe_parameters_send(&client_ecc_evp_params[0], &key_share_extension)); + EXPECT_SUCCESS(s2n_ecdhe_parameters_send(&client_ecc_evp_params[1], &key_share_extension)); + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&keyshare_list_size)); + + EXPECT_SUCCESS(s2n_client_key_share_extension.recv(server_conn, &key_share_extension)); + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + /* Does not trigger retries */ + EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + + /* Highest priority share (0) present */ + struct s2n_ecc_evp_params *server_ecc_evp_params = &server_conn->kex_params.client_ecc_evp_params; + EXPECT_NOT_NULL(server_ecc_evp_params->negotiated_curve); + EXPECT_NOT_NULL(server_ecc_evp_params->evp_pkey); + EXPECT_TRUE(s2n_public_ecc_keys_are_equal(server_ecc_evp_params, &client_ecc_evp_params[0])); + + for (size_t i = 0; i < s2n_array_len(client_ecc_evp_params); i++) { + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&client_ecc_evp_params[i])); + } + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test that s2n_client_key_share_extension.recv selects the highest priority share, + * even if it appears last in the client's list of shares. */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + server_conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_set_all_mutually_supported_groups(server_conn)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + EXPECT_TRUE(ecc_pref->count >= 2); + + struct s2n_ecc_evp_params client_ecc_evp_params[] = { + { .negotiated_curve = ecc_pref->ecc_curves[0] }, + { .negotiated_curve = ecc_pref->ecc_curves[1] } + }; + + struct s2n_stuffer key_share_extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 0)); + + struct s2n_stuffer_reservation keyshare_list_size = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&key_share_extension, &keyshare_list_size)); + EXPECT_SUCCESS(s2n_ecdhe_parameters_send(&client_ecc_evp_params[1], &key_share_extension)); + EXPECT_SUCCESS(s2n_ecdhe_parameters_send(&client_ecc_evp_params[1], &key_share_extension)); + EXPECT_SUCCESS(s2n_ecdhe_parameters_send(&client_ecc_evp_params[0], &key_share_extension)); + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&keyshare_list_size)); + + EXPECT_SUCCESS(s2n_client_key_share_extension.recv(server_conn, &key_share_extension)); + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + /* Does not trigger retries */ + EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + + /* Highest priority curve (0) share present */ + struct s2n_ecc_evp_params *server_ecc_evp_params = &server_conn->kex_params.client_ecc_evp_params; + EXPECT_EQUAL(server_ecc_evp_params->negotiated_curve, ecc_pref->ecc_curves[0]); + EXPECT_NOT_NULL(server_ecc_evp_params->evp_pkey); + EXPECT_TRUE(s2n_public_ecc_keys_are_equal(server_ecc_evp_params, &client_ecc_evp_params[0])); + + for (size_t i = 0; i < s2n_array_len(client_ecc_evp_params); i++) { + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&client_ecc_evp_params[i])); + } + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test that s2n_client_key_share_extension.recv ignores shares for curves not offered + * by the client / "mutually supported", and triggers a retry instead. + */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + server_conn->actual_protocol_version = S2N_TLS13; + + /* Do NOT mark curve 0 as mutually supported */ + EXPECT_OK(s2n_set_all_mutually_supported_groups(server_conn)); + server_conn->kex_params.mutually_supported_curves[0] = NULL; + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + EXPECT_TRUE(ecc_pref->count >= 2); + + struct s2n_ecc_evp_params client_ecc_evp_params = { .negotiated_curve = ecc_pref->ecc_curves[0] }; + + struct s2n_stuffer key_share_extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 0)); + + struct s2n_stuffer_reservation keyshare_list_size = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&key_share_extension, &keyshare_list_size)); + EXPECT_SUCCESS(s2n_ecdhe_parameters_send(&client_ecc_evp_params, &key_share_extension)); + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&keyshare_list_size)); + + EXPECT_SUCCESS(s2n_client_key_share_extension.recv(server_conn, &key_share_extension)); + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + /* Client key share ignored, so retry triggered */ + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + + /* No valid client key share present */ + struct s2n_ecc_evp_params *server_ecc_evp_params = &server_conn->kex_params.client_ecc_evp_params; + EXPECT_NULL(server_ecc_evp_params->negotiated_curve); + EXPECT_NULL(server_ecc_evp_params->evp_pkey); + + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&client_ecc_evp_params)); + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test that s2n_client_key_share_extension.recv ignores shares for curves not offered + * by the client / "mutually supported", and chooses a lower priority curve instead. + */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + server_conn->actual_protocol_version = S2N_TLS13; + + /* Do NOT mark curve 0 as mutually supported */ + EXPECT_OK(s2n_set_all_mutually_supported_groups(server_conn)); + server_conn->kex_params.mutually_supported_curves[0] = NULL; + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + EXPECT_TRUE(ecc_pref->count >= 2); + + struct s2n_ecc_evp_params client_ecc_evp_params[] = { + { .negotiated_curve = ecc_pref->ecc_curves[0] }, + { .negotiated_curve = ecc_pref->ecc_curves[1] } + }; + + struct s2n_stuffer key_share_extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 0)); + + struct s2n_stuffer_reservation keyshare_list_size = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&key_share_extension, &keyshare_list_size)); + EXPECT_SUCCESS(s2n_ecdhe_parameters_send(&client_ecc_evp_params[0], &key_share_extension)); + EXPECT_SUCCESS(s2n_ecdhe_parameters_send(&client_ecc_evp_params[1], &key_share_extension)); + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&keyshare_list_size)); + + EXPECT_SUCCESS(s2n_client_key_share_extension.recv(server_conn, &key_share_extension)); + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + /* Does not trigger a retry */ + EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + + /* Second highest priority curve (1) share present, because highest priority not "mutually supported" */ + struct s2n_ecc_evp_params *server_ecc_evp_params = &server_conn->kex_params.client_ecc_evp_params; + EXPECT_EQUAL(server_ecc_evp_params->negotiated_curve, ecc_pref->ecc_curves[1]); + EXPECT_NOT_NULL(server_ecc_evp_params->evp_pkey); + EXPECT_TRUE(s2n_public_ecc_keys_are_equal(server_ecc_evp_params, &client_ecc_evp_params[1])); + + for (size_t i = 0; i < s2n_array_len(client_ecc_evp_params); i++) { + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&client_ecc_evp_params[i])); + } + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test that s2n_client_key_share_extension.recv errors on client shares size larger + * than available data */ + { + struct s2n_connection *conn; + struct s2n_stuffer key_share_extension = { 0 }; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_set_all_mutually_supported_groups(conn)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 0)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&key_share_extension, ecc_pref->ecc_curves[0]->share_size * 10)); + EXPECT_SUCCESS(s2n_write_named_curve(&key_share_extension, ecc_pref->ecc_curves[0])); + + EXPECT_FAILURE(s2n_client_key_share_extension.recv(conn, &key_share_extension)); + + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test that s2n_client_key_share_extension.recv errors on key share size longer than data */ + { + struct s2n_connection *conn; + struct s2n_stuffer key_share_extension = { 0 }; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_set_all_mutually_supported_groups(conn)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 0)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + /* Write curve with huge length */ + S2N_PREPARE_DATA_LENGTH(&key_share_extension); + EXPECT_SUCCESS(s2n_write_key_share(&key_share_extension, + ecc_pref->ecc_curves[0]->iana_id, ecc_pref->ecc_curves[0]->share_size * 10, ecc_pref->ecc_curves[0])); + S2N_WRITE_DATA_LENGTH(&key_share_extension); + + EXPECT_FAILURE(s2n_client_key_share_extension.recv(conn, &key_share_extension)); + + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test that s2n_client_key_share_extension.recv accepts a subset of supported curves */ + { + struct s2n_connection *conn; + struct s2n_stuffer key_share_extension = { 0 }; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_set_all_mutually_supported_groups(conn)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 0)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + /* Write only first curve */ + S2N_PREPARE_DATA_LENGTH(&key_share_extension); + EXPECT_SUCCESS(s2n_write_named_curve(&key_share_extension, ecc_pref->ecc_curves[0])); + S2N_WRITE_DATA_LENGTH(&key_share_extension); + + EXPECT_SUCCESS(s2n_client_key_share_extension.recv(conn, &key_share_extension)); + + /* should read all data */ + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + /* should have initialized first curve */ + struct s2n_ecc_evp_params *ecc_evp_params = &conn->kex_params.client_ecc_evp_params; + EXPECT_NOT_NULL(ecc_evp_params->negotiated_curve); + EXPECT_NOT_NULL(ecc_evp_params->evp_pkey); + EXPECT_EQUAL(ecc_evp_params->negotiated_curve, ecc_pref->ecc_curves[0]); + + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test that s2n_client_key_share_extension.recv handles empty client share list */ + { + struct s2n_connection *server_conn; + struct s2n_stuffer key_share_extension = { 0 }; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + EXPECT_OK(s2n_set_all_mutually_supported_groups(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 0)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&key_share_extension, 0)); + + EXPECT_SUCCESS(s2n_client_key_share_extension.recv(server_conn, &key_share_extension)); + + /* should not have initialized any curves */ + struct s2n_ecc_evp_params *ecc_evp_params = &server_conn->kex_params.client_ecc_evp_params; + EXPECT_NULL(ecc_evp_params->negotiated_curve); + EXPECT_NULL(ecc_evp_params->evp_pkey); + + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test that s2n_client_key_share_extension.recv ignores unsupported curves */ + { + struct s2n_connection *conn; + struct s2n_stuffer key_share_extension = { 0 }; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(conn, S2N_TLS13)); + EXPECT_OK(s2n_set_all_mutually_supported_groups(conn)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 0)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + S2N_PREPARE_DATA_LENGTH(&key_share_extension); + + /* Write unsupported curves */ + /* 0 -> unallocated_RESERVED */ + EXPECT_SUCCESS(s2n_write_key_share(&key_share_extension, + 0, ecc_pref->ecc_curves[0]->share_size, ecc_pref->ecc_curves[0])); + /* 0xFF01 -> obsolete_RESERVED */ + EXPECT_SUCCESS(s2n_write_key_share(&key_share_extension, + 65281, ecc_pref->ecc_curves[0]->share_size, ecc_pref->ecc_curves[0])); + + S2N_WRITE_DATA_LENGTH(&key_share_extension); + + EXPECT_SUCCESS(s2n_client_key_share_extension.recv(conn, &key_share_extension)); + + /* should read all data */ + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + /* should not have initialized any curves */ + struct s2n_ecc_evp_params *ecc_evp_params = &conn->kex_params.client_ecc_evp_params; + EXPECT_NULL(ecc_evp_params->negotiated_curve); + EXPECT_NULL(ecc_evp_params->evp_pkey); + + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test that s2n_client_key_share_extension.recv ignores curves with incorrect key size */ + { + struct s2n_connection *conn; + struct s2n_stuffer key_share_extension = { 0 }; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(conn, S2N_TLS13)); + EXPECT_OK(s2n_set_all_mutually_supported_groups(conn)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 0)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + /* Write supported curve, but with a different curve's size */ + S2N_PREPARE_DATA_LENGTH(&key_share_extension); + EXPECT_SUCCESS(s2n_write_key_share(&key_share_extension, + ecc_pref->ecc_curves[0]->iana_id, ecc_pref->ecc_curves[1]->share_size, ecc_pref->ecc_curves[1])); + S2N_WRITE_DATA_LENGTH(&key_share_extension); + + EXPECT_SUCCESS(s2n_client_key_share_extension.recv(conn, &key_share_extension)); + + /* should read all data */ + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + /* should not have initialized any curves */ + struct s2n_ecc_evp_params *ecc_evp_params = &conn->kex_params.client_ecc_evp_params; + EXPECT_NULL(ecc_evp_params->negotiated_curve); + EXPECT_NULL(ecc_evp_params->evp_pkey); + + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test that s2n_client_key_share_extension.recv uses first instance of duplicate curves */ + { + struct s2n_connection *server_conn; + struct s2n_stuffer key_share_extension = { 0 }; + struct s2n_ecc_evp_params first_params, second_params; + int supported_curve_index = 0; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + EXPECT_OK(s2n_set_all_mutually_supported_groups(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 0)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + S2N_PREPARE_DATA_LENGTH(&key_share_extension); + + /* Write first curve once */ + first_params.negotiated_curve = ecc_pref->ecc_curves[supported_curve_index]; + first_params.evp_pkey = NULL; + EXPECT_SUCCESS(s2n_ecdhe_parameters_send(&first_params, &key_share_extension)); + + /* Write first curve again */ + second_params.negotiated_curve = ecc_pref->ecc_curves[supported_curve_index]; + second_params.evp_pkey = NULL; + EXPECT_SUCCESS(s2n_ecdhe_parameters_send(&second_params, &key_share_extension)); + + S2N_WRITE_DATA_LENGTH(&key_share_extension); + + EXPECT_SUCCESS(s2n_client_key_share_extension.recv(server_conn, &key_share_extension)); + + /* should read all data */ + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + /* should have only copied the first set of params */ + EXPECT_TRUE(s2n_public_ecc_keys_are_equal( + &server_conn->kex_params.client_ecc_evp_params, &first_params)); + + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&first_params)); + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&second_params)); + }; + + /* Test that s2n_client_key_share_extension.recv ignores ECDHE points that can't be parsed */ + { + struct s2n_connection *conn; + struct s2n_stuffer key_share_extension = { 0 }; + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + /* Explicitly set the ecc_preferences list to only contain the curves p-256 and p-384 */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20140601")); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(conn, S2N_TLS13)); + EXPECT_OK(s2n_set_all_mutually_supported_groups(conn)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 0)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + /* Write first curve */ + S2N_PREPARE_DATA_LENGTH(&key_share_extension); + EXPECT_SUCCESS(s2n_write_named_curve(&key_share_extension, ecc_pref->ecc_curves[0])); + S2N_WRITE_DATA_LENGTH(&key_share_extension); + + /* Mess up point by erasing most of it */ + int data_size = s2n_stuffer_data_available(&key_share_extension); + EXPECT_SUCCESS(s2n_stuffer_wipe_n(&key_share_extension, data_size / 2)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&key_share_extension, data_size / 2)); + + EXPECT_SUCCESS(s2n_client_key_share_extension.recv(conn, &key_share_extension)); + + /* should read all data */ + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + /* should not have initialized any curves */ + struct s2n_ecc_evp_params *ecc_evp_params = &conn->kex_params.client_ecc_evp_params; + EXPECT_NULL(ecc_evp_params->negotiated_curve); + EXPECT_NULL(ecc_evp_params->evp_pkey); + + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test that s2n_client_key_share_extension.recv ignores ECDHE points that can't be parsed, + * and continues to parse valid key shares afterwards. */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + /* Explicitly set the ecc_preferences list to only contain the curves p-256 and p-384 */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20140601")); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + EXPECT_OK(s2n_set_all_mutually_supported_groups(server_conn)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + struct s2n_stuffer key_share_extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 0)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + EXPECT_TRUE(ecc_pref->count >= 2); + + struct s2n_ecc_evp_params client_ecc_evp_params[] = { + { .negotiated_curve = ecc_pref->ecc_curves[0] }, + { .negotiated_curve = ecc_pref->ecc_curves[1] } + }; + + /* Write share list length */ + struct s2n_stuffer_reservation keyshare_list_size = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&key_share_extension, &keyshare_list_size)); + /* Write first share. Mess up point by erasing most of it */ + EXPECT_SUCCESS(s2n_ecdhe_parameters_send(&client_ecc_evp_params[0], &key_share_extension)); + int data_size = s2n_stuffer_data_available(&key_share_extension); + EXPECT_SUCCESS(s2n_stuffer_wipe_n(&key_share_extension, data_size / 2)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&key_share_extension, data_size / 2)); + /* Write second, valid share */ + EXPECT_SUCCESS(s2n_ecdhe_parameters_send(&client_ecc_evp_params[1], &key_share_extension)); + /* Finish share list length */ + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&keyshare_list_size)); + + EXPECT_SUCCESS(s2n_client_key_share_extension.recv(server_conn, &key_share_extension)); + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + /* Should have chosen curve 1, because curve 0 was malformed */ + struct s2n_ecc_evp_params *server_ecc_evp_params = &server_conn->kex_params.client_ecc_evp_params; + EXPECT_EQUAL(server_ecc_evp_params->negotiated_curve, ecc_pref->ecc_curves[1]); + EXPECT_NOT_NULL(server_ecc_evp_params->evp_pkey); + EXPECT_TRUE(s2n_public_ecc_keys_are_equal(server_ecc_evp_params, &client_ecc_evp_params[1])); + + for (size_t i = 0; i < s2n_array_len(client_ecc_evp_params); i++) { + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&client_ecc_evp_params[i])); + } + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test that s2n_client_key_share_extension.recv ignores ECDHE points that can't be parsed, + * and doesn't ignore / forget / overwrite valid key shares already parsed. */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + EXPECT_OK(s2n_set_all_mutually_supported_groups(server_conn)); + + struct s2n_stuffer key_share_extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 0)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + EXPECT_TRUE(ecc_pref->count >= 2); + + struct s2n_ecc_evp_params client_ecc_evp_params[] = { + { .negotiated_curve = ecc_pref->ecc_curves[0] }, + { .negotiated_curve = ecc_pref->ecc_curves[1] } + }; + + /* Write share list length */ + struct s2n_stuffer_reservation keyshare_list_size = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&key_share_extension, &keyshare_list_size)); + /* Write first, valid share */ + EXPECT_SUCCESS(s2n_ecdhe_parameters_send(&client_ecc_evp_params[0], &key_share_extension)); + /* Write second share. Mess up point by erasing most of it */ + EXPECT_SUCCESS(s2n_ecdhe_parameters_send(&client_ecc_evp_params[1], &key_share_extension)); + EXPECT_SUCCESS(s2n_stuffer_wipe_n(&key_share_extension, ecc_pref->ecc_curves[1]->share_size / 2)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&key_share_extension, ecc_pref->ecc_curves[1]->share_size / 2)); + /* Finish share list length */ + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&keyshare_list_size)); + + EXPECT_SUCCESS(s2n_client_key_share_extension.recv(server_conn, &key_share_extension)); + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + /* Should have chosen highest priority key share (0) */ + struct s2n_ecc_evp_params *server_ecc_evp_params = &server_conn->kex_params.client_ecc_evp_params; + EXPECT_EQUAL(server_ecc_evp_params->negotiated_curve, ecc_pref->ecc_curves[0]); + EXPECT_NOT_NULL(server_ecc_evp_params->evp_pkey); + EXPECT_TRUE(s2n_public_ecc_keys_are_equal(server_ecc_evp_params, &client_ecc_evp_params[0])); + + for (size_t i = 0; i < s2n_array_len(client_ecc_evp_params); i++) { + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&client_ecc_evp_params[i])); + } + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test that s2n_client_key_share_extension.recv ignores a supported curve present in + * s2n_all_supported_curves_list but not in s2n_ecc_preferences list selected + */ + { + if (s2n_is_evp_apis_supported()) { + struct s2n_connection *conn; + struct s2n_stuffer key_share_extension = { 0 }; + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(conn, S2N_TLS13)); + EXPECT_OK(s2n_set_all_mutually_supported_groups(conn)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 0)); + + /* Explicitly set the ecc_preferences list to contain the curves p-256 and p-384 */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20140601")); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + /* x25519 is present in s2n_all_supported_curves_list but not in the "default" list */ + const struct s2n_ecc_named_curve *test_curve = &s2n_ecc_curve_x25519; + + S2N_PREPARE_DATA_LENGTH(&key_share_extension); + + EXPECT_SUCCESS(s2n_write_key_share(&key_share_extension, + test_curve->iana_id, test_curve->share_size, test_curve)); + + S2N_WRITE_DATA_LENGTH(&key_share_extension); + + EXPECT_SUCCESS(s2n_client_key_share_extension.recv(conn, &key_share_extension)); + + /* should read all data */ + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + /* should not have initialized any curves */ + struct s2n_ecc_evp_params *ecc_evp_params = &conn->kex_params.client_ecc_evp_params; + EXPECT_NULL(ecc_evp_params->negotiated_curve); + EXPECT_NULL(ecc_evp_params->evp_pkey); + + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + } + }; + }; + + END_TEST(); + return 0; +} + +static int s2n_test_rewrite_length(struct s2n_stuffer *stuffer) +{ + POSIX_ENSURE_REF(stuffer); + + int length = s2n_stuffer_data_available(stuffer) - S2N_SIZE_OF_CLIENT_SHARE_SIZE; + POSIX_GUARD(s2n_stuffer_rewrite(stuffer)); + POSIX_GUARD(s2n_stuffer_write_uint16(stuffer, length)); + POSIX_GUARD(s2n_stuffer_skip_write(stuffer, length)); + return 0; +} + +static int s2n_write_named_curve(struct s2n_stuffer *out, + const struct s2n_ecc_named_curve *existing_curve) +{ + return s2n_write_key_share(out, existing_curve->iana_id, existing_curve->share_size, existing_curve); +} + +static int s2n_write_key_share(struct s2n_stuffer *out, + uint16_t iana_value, uint16_t share_size, + const struct s2n_ecc_named_curve *existing_curve) +{ + POSIX_ENSURE_REF(out); + POSIX_ENSURE_REF(existing_curve); + + struct s2n_ecc_evp_params ecc_evp_params; + const struct s2n_ecc_named_curve test_curve = { + .iana_id = iana_value, + .libcrypto_nid = existing_curve->libcrypto_nid, + .name = existing_curve->name, + .share_size = share_size, + .generate_key = existing_curve->generate_key + }; + + ecc_evp_params.negotiated_curve = &test_curve; + ecc_evp_params.evp_pkey = NULL; + if (s2n_ecdhe_parameters_send(&ecc_evp_params, out) < 0) { + POSIX_GUARD(s2n_ecc_evp_params_free(&ecc_evp_params)); + return 1; + } + + POSIX_GUARD(s2n_ecc_evp_params_free(&ecc_evp_params)); + return 0; +} diff --git a/tests/unit/s2n_client_max_frag_len_extension_test.c b/tests/unit/s2n_client_max_frag_len_extension_test.c new file mode 100644 index 00000000000..e282d633caf --- /dev/null +++ b/tests/unit/s2n_client_max_frag_len_extension_test.c @@ -0,0 +1,159 @@ +/* + * 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 "s2n_test.h" +#include "tls/extensions/s2n_client_max_frag_len.h" +#include "tls/s2n_tls.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Test should_send */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(config, S2N_TLS_MAX_FRAG_LEN_EXT_NONE)); + EXPECT_FALSE(s2n_client_max_frag_len_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(config, S2N_TLS_MAX_FRAG_LEN_512)); + EXPECT_TRUE(s2n_client_max_frag_len_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test send */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(config, S2N_TLS_MAX_FRAG_LEN_512)); + EXPECT_SUCCESS(s2n_client_max_frag_len_extension.send(conn, &stuffer)); + + /* Should have correct fragment length */ + uint8_t actual_frag_len; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&stuffer, &actual_frag_len)); + EXPECT_EQUAL(actual_frag_len, S2N_TLS_MAX_FRAG_LEN_512); + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test receive - accept_mfl not set */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(config, S2N_TLS_MAX_FRAG_LEN_512)); + EXPECT_SUCCESS(s2n_client_max_frag_len_extension.send(conn, &stuffer)); + + /* Ignore fragment length if not accepting max fragment length */ + EXPECT_FALSE(config->accept_mfl); + EXPECT_SUCCESS(s2n_client_max_frag_len_extension.recv(conn, &stuffer)); + EXPECT_EQUAL(conn->negotiated_mfl_code, S2N_TLS_MAX_FRAG_LEN_EXT_NONE); + EXPECT_EQUAL(conn->max_outgoing_fragment_length, S2N_DEFAULT_FRAGMENT_LENGTH); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test receive - invalid mfl code + * + *= https://tools.ietf.org/rfc/rfc6066#section-4 + *= type=test + *# If a server receives a maximum fragment length negotiation request + *# for a value other than the allowed values, it MUST abort the + *# handshake with an "illegal_parameter" alert. + */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(config)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + /* Send invalid mfl code */ + conn->config->mfl_code = UINT8_MAX; + EXPECT_SUCCESS(s2n_client_max_frag_len_extension.send(conn, &stuffer)); + + /* Ignore invalid mfl code */ + EXPECT_SUCCESS(s2n_client_max_frag_len_extension.recv(conn, &stuffer)); + EXPECT_EQUAL(conn->negotiated_mfl_code, S2N_TLS_MAX_FRAG_LEN_EXT_NONE); + EXPECT_EQUAL(conn->max_outgoing_fragment_length, S2N_DEFAULT_FRAGMENT_LENGTH); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test receive */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(config)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(conn->config, S2N_TLS_MAX_FRAG_LEN_512)); + EXPECT_SUCCESS(s2n_client_max_frag_len_extension.send(conn, &stuffer)); + + /* Accept valid mfl code */ + EXPECT_SUCCESS(s2n_client_max_frag_len_extension.recv(conn, &stuffer)); + EXPECT_EQUAL(conn->negotiated_mfl_code, S2N_TLS_MAX_FRAG_LEN_512); + EXPECT_EQUAL(conn->max_outgoing_fragment_length, mfl_code_to_length[S2N_TLS_MAX_FRAG_LEN_512]); + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_client_pq_kem_extension_test.c b/tests/unit/s2n_client_pq_kem_extension_test.c new file mode 100644 index 00000000000..599843f71f9 --- /dev/null +++ b/tests/unit/s2n_client_pq_kem_extension_test.c @@ -0,0 +1,134 @@ +/* + * 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 "pq-crypto/s2n_pq.h" +#include "s2n_test.h" +#include "tls/extensions/s2n_client_pq_kem.h" +#include "tls/s2n_security_policies.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + const char *pq_security_policy_versions[] = { + "PQ-TLS-1-0-2021-05-24", + "PQ-TLS-1-0-2021-05-25", + "PQ-TLS-1-0-2021-05-26", + }; + + for (size_t policy_index = 0; policy_index < s2n_array_len(pq_security_policy_versions); policy_index++) { + const char *pq_security_policy_version = pq_security_policy_versions[policy_index]; + const struct s2n_security_policy *security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version(pq_security_policy_version, &security_policy)); + const struct s2n_kem_preferences *kem_preferences = security_policy->kem_preferences; + + /* Test should_send */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + /* Default cipher preferences do not include PQ, so extension not sent */ + EXPECT_FALSE(s2n_client_pq_kem_extension.should_send(conn)); + + /* Use cipher preferences that do include PQ */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, pq_security_policy_version)); + if (s2n_pq_is_enabled()) { + EXPECT_TRUE(s2n_client_pq_kem_extension.should_send(conn)); + } else { + EXPECT_FALSE(s2n_client_pq_kem_extension.should_send(conn)); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test send */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, pq_security_policy_version)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_client_pq_kem_extension.send(conn, &stuffer)); + + /* Should write correct size */ + uint16_t size; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &size)); + EXPECT_EQUAL(size, s2n_stuffer_data_available(&stuffer)); + EXPECT_EQUAL(size, kem_preferences->kem_count * sizeof(kem_extension_size)); + + /* Should write ids */ + uint16_t actual_id; + for (size_t i = 0; i < kem_preferences->kem_count; i++) { + POSIX_GUARD(s2n_stuffer_read_uint16(&stuffer, &actual_id)); + EXPECT_EQUAL(actual_id, kem_preferences->kems[i]->kem_extension_id); + } + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test receive - malformed length */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, pq_security_policy_version)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_client_pq_kem_extension.send(conn, &stuffer)); + EXPECT_SUCCESS(s2n_stuffer_wipe_n(&stuffer, 1)); + + EXPECT_SUCCESS(s2n_client_pq_kem_extension.recv(conn, &stuffer)); + EXPECT_EQUAL(conn->kex_params.client_pq_kem_extension.size, 0); + EXPECT_NULL(conn->kex_params.client_pq_kem_extension.data); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test receive */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, pq_security_policy_version)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_client_pq_kem_extension.send(conn, &stuffer)); + + EXPECT_SUCCESS(s2n_client_pq_kem_extension.recv(conn, &stuffer)); + + if (s2n_pq_is_enabled()) { + EXPECT_EQUAL(conn->kex_params.client_pq_kem_extension.size, kem_preferences->kem_count * sizeof(kem_extension_size)); + EXPECT_NOT_NULL(conn->kex_params.client_pq_kem_extension.data); + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + } else { + /* Server should ignore the extension if PQ is disabled */ + EXPECT_EQUAL(conn->kex_params.client_pq_kem_extension.size, 0); + EXPECT_NULL(conn->kex_params.client_pq_kem_extension.data); + } + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + } + + END_TEST(); +} diff --git a/tests/unit/s2n_client_psk_extension_test.c b/tests/unit/s2n_client_psk_extension_test.c new file mode 100644 index 00000000000..13c626a57b2 --- /dev/null +++ b/tests/unit/s2n_client_psk_extension_test.c @@ -0,0 +1,1657 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" + +/* Include source to test static methods. */ +#include "tls/extensions/s2n_client_psk.c" + +#define TEST_BYTES 0x01, 0xFF, 0x23 +#define TEST_BYTES_SIZE 0x00, 0x03 + +#define TEST_BYTES_2 0x0A, 0x0B, 0x0C +#define TEST_BYTES_SIZE_2 0x00, 0x03 + +#define MILLIS_TO_NANOS(millis) (millis * (uint64_t) ONE_MILLISEC_IN_NANOS) + +#define BINDER_SIZE 32 + +struct s2n_psk_test_case { + s2n_hmac_algorithm hmac_alg; + uint8_t hash_size; + const uint8_t *identity; + size_t identity_size; +}; + +static int s2n_test_select_psk_identity_callback(struct s2n_connection *conn, void *context, + struct s2n_offered_psk_list *psk_identity_list) +{ + uint16_t *wire_index_choice = (uint16_t *) context; + + struct s2n_offered_psk offered_psk = { 0 }; + uint16_t idx = 0; + while (s2n_offered_psk_list_has_next(psk_identity_list)) { + POSIX_GUARD(s2n_offered_psk_list_next(psk_identity_list, &offered_psk)); + if (idx == *wire_index_choice) { + POSIX_GUARD(s2n_offered_psk_list_choose_psk(psk_identity_list, &offered_psk)); + break; + } + idx++; + }; + return S2N_SUCCESS; +} + +static int s2n_test_error_select_psk_identity_callback(struct s2n_connection *conn, void *context, + struct s2n_offered_psk_list *psk_identity_list) +{ + POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); +} + +static S2N_RESULT s2n_write_test_identity(struct s2n_stuffer *out, struct s2n_blob *identity) +{ + RESULT_GUARD_POSIX(s2n_stuffer_write_uint16(out, identity->size)); + RESULT_GUARD_POSIX(s2n_stuffer_write(out, identity)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint32(out, 0)); + return S2N_RESULT_OK; +} + +static int mock_time(void *data, uint64_t *nanoseconds) +{ + *nanoseconds = MILLIS_TO_NANOS(500); + return S2N_SUCCESS; +} + +static int s2n_setup_ticket_key(struct s2n_config *config) +{ + /** + *= https://tools.ietf.org/rfc/rfc5869#appendix-A.1 + *# PRK = 0x077709362c2e32df0ddc3f0dc47bba63 + *# 90b6c73bb50f9c3122ec844ad7c2b3e5 (32 octets) + **/ + S2N_BLOB_FROM_HEX(ticket_key, + "077709362c2e32df0ddc3f0dc47bba63" + "90b6c73bb50f9c3122ec844ad7c2b3e5"); + + /* Set up encryption key */ + uint64_t current_time = 0; + uint8_t ticket_key_name[16] = "2016.07.26.15\0"; + + POSIX_GUARD(s2n_config_set_session_tickets_onoff(config, 1)); + POSIX_GUARD(config->wall_clock(config->sys_clock_ctx, ¤t_time)); + POSIX_GUARD(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), + ticket_key.data, ticket_key.size, current_time / ONE_SEC_IN_NANOS)); + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_setup_encrypted_ticket(struct s2n_connection *conn, struct s2n_stuffer *output) +{ + conn->tls13_ticket_fields = (struct s2n_ticket_fields){ 0 }; + uint8_t test_secret_data[] = "test secret"; + RESULT_GUARD_POSIX(s2n_alloc(&conn->tls13_ticket_fields.session_secret, sizeof(test_secret_data))); + RESULT_CHECKED_MEMCPY(conn->tls13_ticket_fields.session_secret.data, test_secret_data, sizeof(test_secret_data)); + + /* Create a valid resumption psk identity */ + RESULT_GUARD_POSIX(s2n_encrypt_session_ticket(conn, output)); + output->blob.size = s2n_stuffer_data_available(output); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + uint8_t test_bytes_data[] = { TEST_BYTES }; + uint8_t test_bytes_data_2[] = { TEST_BYTES_2 }; + uint8_t test_identity[] = "test identity"; + uint8_t test_identity_2[] = "another identity"; + uint8_t test_identity_3[] = "identity 3"; + uint8_t test_secret[] = "test secret"; + + uint8_t single_wire_identity[] = { + TEST_BYTES_SIZE, /* identity size */ + TEST_BYTES, /* identity */ + 0x00, 0x00, 0x00, 0x00, /* ticket_age */ + }; + + uint8_t wire_identities[] = { + 0x00, 0x01, /* identity size */ + 0x01, /* identity */ + 0x00, 0x00, 0x00, 0x00, /* ticket_age */ + + 0x00, 0x01, /* identity size */ + 0xFF, /* identity */ + 0x00, 0x00, 0x00, 0x00, /* ticket_age */ + + TEST_BYTES_SIZE, /* identity size */ + TEST_BYTES, /* identity */ + 0x00, 0x00, 0x00, 0x00, /* ticket_age */ + + TEST_BYTES_SIZE, /* identity size */ + TEST_BYTES, /* identity */ + 0x00, 0x00, 0x00, 0x00, /* ticket_age */ + + 0x00, 0x02, /* identity size */ + 0x00, 0x01, /* identity */ + 0x00, 0x00, 0x00, 0x00, /* ticket_age */ + }; + + /* Test: s2n_client_psk_is_missing */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + /* Okay if early data not requested */ + conn->early_data_state = S2N_EARLY_DATA_NOT_REQUESTED; + EXPECT_SUCCESS(s2n_client_psk_extension.if_missing(conn)); + + /** + *= https://tools.ietf.org/rfc/rfc8446#section-4.2.10 + *= type=test + *# When a PSK is used and early data is allowed for that PSK, the client + *# can send Application Data in its first flight of messages. If the + *# client opts to do so, it MUST supply both the "pre_shared_key" and + *# "early_data" extensions. + */ + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + EXPECT_FAILURE_WITH_ERRNO(s2n_client_psk_extension.if_missing(conn), S2N_ERR_UNSUPPORTED_EXTENSION); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: s2n_client_psk_should_send */ + { + struct s2n_psk *psk = NULL; + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_FALSE(s2n_client_psk_extension.should_send(NULL)); + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_FALSE(s2n_client_psk_extension.should_send(conn)); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FALSE(s2n_client_psk_extension.should_send(conn)); + + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); + + /* If send is called with a NULL stuffer, it will fail. + * So a failure indicates that send was called. + */ + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_extension_send(&s2n_client_psk_extension, conn, NULL)); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FAILURE(s2n_extension_send(&s2n_client_psk_extension, conn, NULL)); + + /* Only send the extension after a retry if at least one PSK matches the cipher suite */ + { + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + const s2n_hmac_algorithm matching_hmac_alg = conn->secure->cipher_suite->prf_alg; + const s2n_hmac_algorithm different_hmac_alg = conn->secure->cipher_suite->prf_alg + 1; + + /* Do send if the PSK does NOT match the cipher suite, but this is NOT a retry */ + conn->handshake.handshake_type = INITIAL; + psk->hmac_alg = different_hmac_alg; + EXPECT_TRUE(s2n_client_psk_extension.should_send(conn)); + + /* Do send if the PSK matches the cipher suite */ + conn->handshake.handshake_type = HELLO_RETRY_REQUEST; + psk->hmac_alg = matching_hmac_alg; + EXPECT_TRUE(s2n_client_psk_extension.should_send(conn)); + + /* Do NOT send if the PSK does NOT match the cipher suite */ + conn->handshake.handshake_type = HELLO_RETRY_REQUEST; + psk->hmac_alg = different_hmac_alg; + EXPECT_FALSE(s2n_client_psk_extension.should_send(conn)); + + /* Do send if there are two PSKs, and one matches the cipher suite */ + conn->handshake.handshake_type = HELLO_RETRY_REQUEST; + psk->hmac_alg = different_hmac_alg; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); + psk->hmac_alg = matching_hmac_alg; + EXPECT_TRUE(s2n_client_psk_extension.should_send(conn)); + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: s2n_generate_obfuscated_ticket_age */ + { + uint32_t output = 0; + uint64_t current_time = 0; + struct s2n_psk psk = { 0 }; + psk.type = S2N_PSK_TYPE_RESUMPTION; + + /* Current time is smaller than ticket issue time */ + { + current_time = 0; + psk.ticket_issue_time = 10; + EXPECT_ERROR_WITH_ERRNO(s2n_generate_obfuscated_ticket_age(&psk, current_time, &output), S2N_ERR_SAFETY); + }; + + /* Ticket age is too large to fit in a uint32_t */ + { + current_time = UINT64_MAX; + psk.ticket_issue_time = 0; + EXPECT_ERROR_WITH_ERRNO(s2n_generate_obfuscated_ticket_age(&psk, current_time, &output), S2N_ERR_SAFETY); + }; + + /* External psk */ + { + psk.type = S2N_PSK_TYPE_EXTERNAL; + EXPECT_OK(s2n_generate_obfuscated_ticket_age(&psk, current_time, &output)); + EXPECT_EQUAL(output, 0); + }; + + struct { + uint64_t current_time; + uint64_t ticket_issue_time; + uint32_t ticket_age_add; + uint32_t expected_output; + } test_cases[] = { + { .current_time = MILLIS_TO_NANOS(50), .ticket_issue_time = 0, .ticket_age_add = 50, .expected_output = 100 }, + { .current_time = MILLIS_TO_NANOS(500), .ticket_issue_time = 0, .ticket_age_add = 50, .expected_output = 550 }, + { .current_time = MILLIS_TO_NANOS(UINT32_MAX), .ticket_issue_time = 0, .ticket_age_add = 1, .expected_output = 0 }, + { .current_time = MILLIS_TO_NANOS(UINT32_MAX), .ticket_issue_time = 0, .ticket_age_add = 2, .expected_output = 1 }, + { .current_time = MILLIS_TO_NANOS(UINT32_MAX), .ticket_issue_time = 0, .ticket_age_add = 50, .expected_output = 49 }, + { .current_time = MILLIS_TO_NANOS(0), .ticket_issue_time = 0, .ticket_age_add = 50, .expected_output = 50 }, + }; + + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + output = 0; + current_time = test_cases[i].current_time; + psk.ticket_issue_time = test_cases[i].ticket_issue_time; + psk.ticket_age_add = test_cases[i].ticket_age_add; + psk.type = S2N_PSK_TYPE_RESUMPTION; + + EXPECT_OK(s2n_generate_obfuscated_ticket_age(&psk, current_time, &output)); + EXPECT_EQUAL(output, test_cases[i].expected_output); + } + }; + + /* Test: s2n_client_psk_send */ + { + /* Send a single PSK identity */ + { + struct s2n_stuffer out = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); + EXPECT_OK(s2n_psk_init(psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(psk, test_identity, sizeof(test_identity))); + psk->hmac_alg = S2N_HMAC_SHA384; + + EXPECT_SUCCESS(s2n_client_psk_extension.send(conn, &out)); + + uint32_t offered_psks_size = 0; + EXPECT_OK(s2n_psk_parameters_offered_psks_size(&conn->psk_params, &offered_psks_size)); + EXPECT_EQUAL(offered_psks_size, s2n_stuffer_data_available(&out)); + + uint16_t identity_list_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&out, &identity_list_size)); + + uint16_t identity_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&out, &identity_size)); + EXPECT_EQUAL(identity_size, sizeof(test_identity)); + + uint8_t *identity_data; + EXPECT_NOT_NULL(identity_data = s2n_stuffer_raw_read(&out, identity_size)); + EXPECT_BYTEARRAY_EQUAL(identity_data, test_identity, sizeof(test_identity)); + + uint32_t obfuscated_ticket_age = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint32(&out, &obfuscated_ticket_age)); + EXPECT_EQUAL(obfuscated_ticket_age, 0); + + EXPECT_EQUAL(s2n_stuffer_data_available(&out), + SHA384_DIGEST_LENGTH /* binder size */ + + sizeof(uint8_t) /* size of binder size */ + + sizeof(uint16_t)); /* size of binder list size */ + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + }; + + /* Send multiple PSK identities */ + { + struct s2n_stuffer out = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_psk_test_case test_cases[] = { + { .hmac_alg = S2N_HMAC_SHA224, .hash_size = SHA224_DIGEST_LENGTH, .identity = test_identity, .identity_size = sizeof(test_identity) }, + { .hmac_alg = S2N_HMAC_SHA384, .hash_size = SHA384_DIGEST_LENGTH, .identity = test_identity_2, .identity_size = sizeof(test_identity_2) }, + }; + + uint16_t binder_list_size = 0; + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); + EXPECT_OK(s2n_psk_init(psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(psk, test_cases[i].identity, test_cases[i].identity_size)); + psk->hmac_alg = test_cases[i].hmac_alg; + + binder_list_size += test_cases[i].hash_size + + sizeof(uint8_t) /* size of binder size */; + } + + EXPECT_SUCCESS(s2n_client_psk_extension.send(conn, &out)); + + uint32_t offered_psks_size = 0; + EXPECT_OK(s2n_psk_parameters_offered_psks_size(&conn->psk_params, &offered_psks_size)); + EXPECT_EQUAL(offered_psks_size, s2n_stuffer_data_available(&out)); + + uint16_t identity_list_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&out, &identity_list_size)); + + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + uint16_t identity_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&out, &identity_size)); + EXPECT_EQUAL(identity_size, test_cases[i].identity_size); + + uint8_t *identity_data; + EXPECT_NOT_NULL(identity_data = s2n_stuffer_raw_read(&out, identity_size)); + EXPECT_BYTEARRAY_EQUAL(identity_data, test_cases[i].identity, test_cases[i].identity_size); + + uint32_t obfuscated_ticket_age = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint32(&out, &obfuscated_ticket_age)); + EXPECT_EQUAL(obfuscated_ticket_age, 0); + } + + EXPECT_EQUAL(s2n_stuffer_data_available(&out), + binder_list_size /* binder list size */ + + sizeof(uint16_t)); /* size of binder list size */ + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + }; + + /* Send a resumption psk identity */ + { + struct s2n_stuffer out = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_wall_clock(config, mock_time, NULL)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); + EXPECT_OK(s2n_psk_init(psk, S2N_PSK_TYPE_RESUMPTION)); + EXPECT_SUCCESS(s2n_psk_set_identity(psk, test_identity, sizeof(test_identity))); + psk->hmac_alg = S2N_HMAC_SHA384; + psk->ticket_age_add = 50; + psk->ticket_issue_time = 0; + + EXPECT_SUCCESS(s2n_client_psk_extension.send(conn, &out)); + + EXPECT_SUCCESS(s2n_stuffer_skip_read(&out, sizeof(uint16_t) /* identity_list_size */ + sizeof(uint16_t) /* identity_size */ + sizeof(test_identity))); + + uint32_t obfuscated_ticket_age = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint32(&out, &obfuscated_ticket_age)); + EXPECT_TRUE(obfuscated_ticket_age == 550); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + }; + + /* On the second ClientHello after a retry request, + * do not send any PSKs that do not match the cipher suite. + * + *= https://tools.ietf.org/rfc/rfc8446#section-4.1.4 + *= type=test + *# In addition, in its updated ClientHello, the client SHOULD NOT offer + *# any pre-shared keys associated with a hash other than that of the + *# selected cipher suite. + */ + { + struct s2n_stuffer out = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + + const struct s2n_psk_test_case matching_psk = { + .hmac_alg = S2N_HMAC_SHA384, + .hash_size = SHA384_DIGEST_LENGTH, + .identity = test_identity, + .identity_size = sizeof(test_identity) + }; + const struct s2n_psk_test_case non_matching_psk = { + .hmac_alg = S2N_HMAC_SHA224, + .hash_size = SHA224_DIGEST_LENGTH, + .identity = test_identity, + .identity_size = sizeof(test_identity) + }; + struct s2n_psk_test_case test_cases[] = { matching_psk, non_matching_psk }; + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + conn->handshake.handshake_type = HELLO_RETRY_REQUEST; + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + EXPECT_EQUAL(conn->secure->cipher_suite->prf_alg, matching_psk.hmac_alg); + + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); + EXPECT_OK(s2n_psk_init(psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(psk, test_cases[i].identity, test_cases[i].identity_size)); + psk->hmac_alg = test_cases[i].hmac_alg; + } + + EXPECT_SUCCESS(s2n_client_psk_extension.send(conn, &out)); + + /* The identity list should only contain the matching psk's identity. + * It should NOT contain the non-matching psk. */ + + uint16_t identity_list_size = 0; + uint16_t identity_size = 0; + uint8_t *identity_data = NULL; + uint32_t obfuscated_ticket_age = 0; + + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&out, &identity_list_size)); + EXPECT_EQUAL(identity_list_size, sizeof(identity_size) + matching_psk.identity_size + sizeof(obfuscated_ticket_age)); + + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&out, &identity_size)); + EXPECT_EQUAL(identity_size, matching_psk.identity_size); + EXPECT_NOT_NULL(identity_data = s2n_stuffer_raw_read(&out, identity_size)); + EXPECT_BYTEARRAY_EQUAL(identity_data, matching_psk.identity, matching_psk.identity_size); + + EXPECT_SUCCESS(s2n_stuffer_read_uint32(&out, &obfuscated_ticket_age)); + + /* The binder list should only reserve space for the matching psk's binder. + * It should NOT reserve space for the binder for the non-matching psk. */ + EXPECT_EQUAL(s2n_stuffer_data_available(&out), + matching_psk.hash_size + + sizeof(uint8_t) /* size of binder size */ + + sizeof(uint16_t)) + /* size of binder list size */; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + } + + /* On the second ClientHello after a retry request, + * do not send the PSK extension if no valid PSKs. + * + *= https://tools.ietf.org/rfc/rfc8446#section-4.1.4 + *= type=test + *# In addition, in its updated ClientHello, the client SHOULD NOT offer + *# any pre-shared keys associated with a hash other than that of the + *# selected cipher suite. + */ + if (s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* The server will choose the first cipher in the security policy, + * which uses SHA256 for its PRF. So setup a PSK that requires SHA384 + * so that the algorithms will not match and the PSK will be discarded. + */ + DEFER_CLEANUP(struct s2n_psk *psk = s2n_test_psk_new(client_conn), s2n_psk_free); + psk->hmac_alg = S2N_HMAC_SHA384; + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, psk)); + EXPECT_TRUE(s2n_client_psk_extension.should_send(client_conn)); + + /* Send and receive ClientHello and RetryRequest */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); + + /* Verify that the PSK extension will not be sent in the second hello */ + EXPECT_FALSE(s2n_client_psk_extension.should_send(client_conn)); + + /* Verify the handshake could complete successfully */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } + }; + + /* Test: s2n_select_external_psk */ + { + /* Safety checks */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_ERROR_WITH_ERRNO(s2n_select_external_psk(conn, NULL), S2N_ERR_NULL); + + struct s2n_offered_psk_list wire_identity_list = { 0 }; + EXPECT_ERROR_WITH_ERRNO(s2n_select_external_psk(NULL, &wire_identity_list), S2N_ERR_NULL); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + struct s2n_blob identity_1 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&identity_1, test_identity, sizeof(test_identity))); + struct s2n_blob identity_2 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&identity_2, test_identity_2, sizeof(test_identity_2))); + struct s2n_blob identity_3 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&identity_3, test_identity_3, sizeof(test_identity_3))); + + struct s2n_blob *all_psks_list[] = { &identity_1, &identity_2, &identity_3 }; + struct s2n_blob *reverse_order_list[] = { &identity_3, &identity_2, &identity_1 }; + struct s2n_blob *list_without_psk1[] = { &identity_2, &identity_3 }; + struct s2n_blob *multiple_psk1[] = { &identity_1, &identity_1, &identity_1 }; + struct s2n_blob *multiple_psk2[] = { &identity_2, &identity_2, &identity_2 }; + struct s2n_blob *only_psk1[] = { &identity_1 }; + struct s2n_blob *only_psk2[] = { &identity_2 }; + struct s2n_blob *only_psk3[] = { &identity_3 }; + + struct { + bool match; + struct s2n_blob **wire_identities; + size_t wire_identities_len; + struct s2n_blob **local_identities; + size_t local_identities_len; + size_t wire_match_index; + size_t local_match_index; + } test_cases[] = { +#define WIRE_IDENTITIES(list) .wire_identities = list, .wire_identities_len = s2n_array_len(list) +#define LOCAL_IDENTITIES(list) .local_identities = list, .local_identities_len = s2n_array_len(list) + /* No wire or local identities */ + { .match = false }, + + /* Only wire identities */ + { .match = false, WIRE_IDENTITIES(only_psk1) }, + { .match = false, WIRE_IDENTITIES(all_psks_list) }, + + /* Only local identities */ + { .match = false, LOCAL_IDENTITIES(only_psk1) }, + { .match = false, LOCAL_IDENTITIES(all_psks_list) }, + + /* No match with valid lists */ + { .match = false, WIRE_IDENTITIES(only_psk1), LOCAL_IDENTITIES(only_psk2) }, + { .match = false, WIRE_IDENTITIES(only_psk1), LOCAL_IDENTITIES(list_without_psk1) }, + { .match = false, WIRE_IDENTITIES(list_without_psk1), LOCAL_IDENTITIES(only_psk1) }, + + /* Single option matches */ + { .match = true, WIRE_IDENTITIES(only_psk1), LOCAL_IDENTITIES(only_psk1), .wire_match_index = 0, .local_match_index = 0 }, + + /* Single wire option matches */ + { .match = true, WIRE_IDENTITIES(only_psk1), LOCAL_IDENTITIES(all_psks_list), .wire_match_index = 0, .local_match_index = 0 }, + { .match = true, WIRE_IDENTITIES(only_psk2), LOCAL_IDENTITIES(all_psks_list), .wire_match_index = 0, .local_match_index = 1 }, + { .match = true, WIRE_IDENTITIES(only_psk3), LOCAL_IDENTITIES(all_psks_list), .wire_match_index = 0, .local_match_index = 2 }, + + /* Single local option matches */ + { .match = true, WIRE_IDENTITIES(all_psks_list), LOCAL_IDENTITIES(only_psk1), .wire_match_index = 0, .local_match_index = 0 }, + { .match = true, WIRE_IDENTITIES(all_psks_list), LOCAL_IDENTITIES(only_psk2), .wire_match_index = 1, .local_match_index = 0 }, + { .match = true, WIRE_IDENTITIES(all_psks_list), LOCAL_IDENTITIES(only_psk3), .wire_match_index = 2, .local_match_index = 0 }, + + /* Match with multiple wire and local options: choose first local */ + { .match = true, WIRE_IDENTITIES(all_psks_list), LOCAL_IDENTITIES(all_psks_list), .wire_match_index = 0, .local_match_index = 0 }, + { .match = true, WIRE_IDENTITIES(reverse_order_list), LOCAL_IDENTITIES(all_psks_list), .wire_match_index = 2, .local_match_index = 0 }, + { .match = true, WIRE_IDENTITIES(all_psks_list), LOCAL_IDENTITIES(reverse_order_list), .wire_match_index = 2, .local_match_index = 0 }, + { .match = true, WIRE_IDENTITIES(list_without_psk1), LOCAL_IDENTITIES(all_psks_list), .wire_match_index = 0, .local_match_index = 1 }, + { .match = true, WIRE_IDENTITIES(all_psks_list), LOCAL_IDENTITIES(list_without_psk1), .wire_match_index = 1, .local_match_index = 0 }, + { .match = true, WIRE_IDENTITIES(list_without_psk1), LOCAL_IDENTITIES(reverse_order_list), .wire_match_index = 1, .local_match_index = 0 }, + { .match = true, WIRE_IDENTITIES(reverse_order_list), LOCAL_IDENTITIES(list_without_psk1), .wire_match_index = 1, .local_match_index = 0 }, + + /* Handle duplicates */ + { .match = true, WIRE_IDENTITIES(multiple_psk1), LOCAL_IDENTITIES(multiple_psk1), .wire_match_index = 0, .local_match_index = 0 }, + { .match = false, WIRE_IDENTITIES(multiple_psk1), LOCAL_IDENTITIES(multiple_psk2) }, + { .match = true, WIRE_IDENTITIES(multiple_psk1), LOCAL_IDENTITIES(all_psks_list), .wire_match_index = 0, .local_match_index = 0 }, + { .match = true, WIRE_IDENTITIES(all_psks_list), LOCAL_IDENTITIES(multiple_psk1), .wire_match_index = 0, .local_match_index = 0 }, + { .match = true, WIRE_IDENTITIES(multiple_psk1), LOCAL_IDENTITIES(reverse_order_list), .wire_match_index = 0, .local_match_index = 2 }, + { .match = true, WIRE_IDENTITIES(reverse_order_list), LOCAL_IDENTITIES(multiple_psk1), .wire_match_index = 2, .local_match_index = 0 }, + }; + + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; + + struct s2n_offered_psk_list client_identity_list = { .conn = conn }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_identity_list.wire_data, 0)); + for (size_t wire_i = 0; wire_i < test_cases[i].wire_identities_len; wire_i++) { + EXPECT_OK(s2n_write_test_identity(&client_identity_list.wire_data, test_cases[i].wire_identities[wire_i])); + } + + struct s2n_psk *expected_chosen_psk = NULL; + for (size_t local_i = 0; local_i < test_cases[i].local_identities_len; local_i++) { + struct s2n_psk *server_psk = NULL; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &server_psk)); + EXPECT_NOT_NULL(server_psk); + EXPECT_SUCCESS(s2n_psk_set_identity(server_psk, test_cases[i].local_identities[local_i]->data, + test_cases[i].local_identities[local_i]->size)); + + if (local_i == test_cases[i].local_match_index) { + expected_chosen_psk = server_psk; + } + } + + if (test_cases[i].match) { + EXPECT_OK(s2n_select_external_psk(conn, &client_identity_list)); + EXPECT_EQUAL(conn->psk_params.chosen_psk_wire_index, test_cases[i].wire_match_index); + EXPECT_EQUAL(conn->psk_params.chosen_psk, expected_chosen_psk); + } else { + EXPECT_ERROR_WITH_ERRNO(s2n_select_external_psk(conn, &client_identity_list), S2N_ERR_NULL); + EXPECT_NULL(conn->psk_params.chosen_psk); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&client_identity_list.wire_data)); + } + }; + + /* Test: s2n_select_resumption_psk */ + { + /* Safety checks */ + { + struct s2n_offered_psk_list identity_list = { 0 }; + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + + EXPECT_ERROR_WITH_ERRNO(s2n_select_resumption_psk(NULL, &identity_list), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_select_resumption_psk(conn, NULL), S2N_ERR_NULL); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* One valid resumption psk is received */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_setup_ticket_key(config)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + DEFER_CLEANUP(struct s2n_stuffer psk_identity = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&psk_identity, 0)); + EXPECT_OK(s2n_setup_encrypted_ticket(conn, &psk_identity)); + + struct s2n_offered_psk_list identity_list = { .conn = conn }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&identity_list.wire_data, 0)); + + EXPECT_OK(s2n_write_test_identity(&identity_list.wire_data, &psk_identity.blob)); + + EXPECT_OK(s2n_select_resumption_psk(conn, &identity_list)); + + EXPECT_EQUAL(conn->psk_params.chosen_psk_wire_index, 0); + EXPECT_NOT_NULL(conn->psk_params.chosen_psk); + + /* Sanity check psk creation is correct */ + EXPECT_EQUAL(conn->psk_params.chosen_psk->hmac_alg, s2n_tls13_aes_128_gcm_sha256.prf_alg); + + EXPECT_SUCCESS(s2n_stuffer_free(&identity_list.wire_data)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* First valid resumption psk is chosen */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_setup_ticket_key(config)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + DEFER_CLEANUP(struct s2n_stuffer psk_identity = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&psk_identity, 0)); + EXPECT_OK(s2n_setup_encrypted_ticket(conn, &psk_identity)); + + struct s2n_offered_psk_list identity_list = { .conn = conn }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&identity_list.wire_data, 0)); + + /* Write invalid resumption psk */ + uint8_t bad_identity_data[] = "hello"; + struct s2n_blob bad_identity = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&bad_identity, bad_identity_data, sizeof(bad_identity_data))); + + uint8_t psk_idx = MAX_REJECTED_TICKETS - 1; + for (size_t i = 0; i < psk_idx; i++) { + EXPECT_OK(s2n_write_test_identity(&identity_list.wire_data, &bad_identity)); + } + + /* Write valid resumption psk */ + EXPECT_OK(s2n_write_test_identity(&identity_list.wire_data, &psk_identity.blob)); + + /* Write second valid resumption psk */ + EXPECT_OK(s2n_write_test_identity(&identity_list.wire_data, &psk_identity.blob)); + + EXPECT_OK(s2n_select_resumption_psk(conn, &identity_list)); + + EXPECT_EQUAL(conn->psk_params.chosen_psk_wire_index, psk_idx); + EXPECT_NOT_NULL(conn->psk_params.chosen_psk); + + /* Sanity check psk creation is correct */ + EXPECT_EQUAL(conn->psk_params.chosen_psk->hmac_alg, s2n_tls13_aes_128_gcm_sha256.prf_alg); + + EXPECT_SUCCESS(s2n_stuffer_free(&identity_list.wire_data)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* No valid resumption psks */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + struct s2n_offered_psk_list identity_list = { .conn = conn }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&identity_list.wire_data, 0)); + + /* Write invalid resumption psk */ + uint8_t bad_identity_data[] = "hello"; + struct s2n_blob bad_identity = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&bad_identity, bad_identity_data, sizeof(bad_identity_data))); + EXPECT_OK(s2n_write_test_identity(&identity_list.wire_data, &bad_identity)); + + EXPECT_ERROR_WITH_ERRNO(s2n_select_resumption_psk(conn, &identity_list), S2N_ERR_INVALID_SESSION_TICKET); + EXPECT_NULL(conn->psk_params.chosen_psk); + + EXPECT_SUCCESS(s2n_stuffer_free(&identity_list.wire_data)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Fail if too many invalid resumption psks */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_setup_ticket_key(config)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + DEFER_CLEANUP(struct s2n_stuffer psk_identity = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&psk_identity, 0)); + EXPECT_OK(s2n_setup_encrypted_ticket(conn, &psk_identity)); + + /* "hello" is mysteriously not a valid session ticket */ + uint8_t bad_identity_data[] = "hello"; + struct s2n_blob bad_identity = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&bad_identity, bad_identity_data, sizeof(bad_identity_data))); + + struct s2n_offered_psk_list identity_list = { .conn = conn }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&identity_list.wire_data, 0)); + + uint8_t bad_psk_count = MAX_REJECTED_TICKETS; + for (size_t i = 0; i < bad_psk_count; i++) { + EXPECT_OK(s2n_write_test_identity(&identity_list.wire_data, &bad_identity)); + } + EXPECT_OK(s2n_write_test_identity(&identity_list.wire_data, &psk_identity.blob)); + + EXPECT_ERROR_WITH_ERRNO(s2n_select_resumption_psk(conn, &identity_list), S2N_ERR_INVALID_SESSION_TICKET); + EXPECT_NULL(conn->psk_params.chosen_psk); + + EXPECT_SUCCESS(s2n_stuffer_free(&identity_list.wire_data)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + }; + + /* Test: s2n_client_psk_recv_identity_list */ + { + /* Safety checks */ + { + struct s2n_stuffer wire_identities_in = { 0 }; + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_ERROR_WITH_ERRNO(s2n_client_psk_recv_identity_list(conn, NULL), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_client_psk_recv_identity_list(NULL, &wire_identities_in), S2N_ERR_NULL); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Receive an empty list */ + { + struct s2n_stuffer empty_wire_identities_in = { 0 }; + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_OK(s2n_connection_set_psk_type(conn, S2N_PSK_TYPE_EXTERNAL)); + + EXPECT_ERROR(s2n_client_psk_recv_identity_list(conn, &empty_wire_identities_in)); + EXPECT_NULL(conn->psk_params.chosen_psk); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Default selection logic: receive a list without a match */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_OK(s2n_connection_set_psk_type(conn, S2N_PSK_TYPE_EXTERNAL)); + + struct s2n_stuffer wire_identities_in = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_alloc(&wire_identities_in, sizeof(wire_identities))); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&wire_identities_in, wire_identities, sizeof(wire_identities))); + + struct s2n_psk *no_match_psk = NULL; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &no_match_psk)); + EXPECT_OK(s2n_psk_init(no_match_psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(no_match_psk, test_bytes_data_2, sizeof(test_bytes_data_2))); + + EXPECT_ERROR_WITH_ERRNO(s2n_client_psk_recv_identity_list(conn, &wire_identities_in), S2N_ERR_NULL); + EXPECT_NULL(conn->psk_params.chosen_psk); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&wire_identities_in)); + }; + + /* Default selection logic: receive a list with a match */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_OK(s2n_connection_set_psk_type(conn, S2N_PSK_TYPE_EXTERNAL)); + + struct s2n_stuffer wire_identities_in = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_alloc(&wire_identities_in, sizeof(single_wire_identity))); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&wire_identities_in, single_wire_identity, sizeof(single_wire_identity))); + + struct s2n_psk *match_psk = NULL; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &match_psk)); + EXPECT_OK(s2n_psk_init(match_psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(match_psk, test_bytes_data, sizeof(test_bytes_data))); + + EXPECT_OK(s2n_client_psk_recv_identity_list(conn, &wire_identities_in)); + EXPECT_EQUAL(conn->psk_params.chosen_psk_wire_index, 0); + EXPECT_EQUAL(conn->psk_params.chosen_psk, match_psk); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&wire_identities_in)); + }; + + /* Customer selection logic: customer rejects all identities */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_psk_selection_callback(config, s2n_test_error_select_psk_identity_callback, NULL)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_OK(s2n_connection_set_psk_type(conn, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_stuffer wire_identities_in = { 0 }; + EXPECT_ERROR_WITH_ERRNO(s2n_client_psk_recv_identity_list(conn, &wire_identities_in), + S2N_ERR_UNIMPLEMENTED); + EXPECT_EQUAL(conn->psk_params.chosen_psk, NULL); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_stuffer_free(&wire_identities_in)); + }; + + /* Customer selection logic: customer selects valid identity */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + + uint16_t expected_wire_choice = 0; + EXPECT_SUCCESS(s2n_config_set_psk_selection_callback(config, s2n_test_select_psk_identity_callback, &expected_wire_choice)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_OK(s2n_connection_set_psk_type(conn, S2N_PSK_TYPE_EXTERNAL)); + + struct s2n_psk *match_psk = NULL; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &match_psk)); + EXPECT_OK(s2n_psk_init(match_psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(match_psk, test_bytes_data, sizeof(test_bytes_data))); + + struct s2n_stuffer wire_identities_in = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&wire_identities_in, 0)); + EXPECT_OK(s2n_write_test_identity(&wire_identities_in, &match_psk->identity)); + + EXPECT_OK(s2n_client_psk_recv_identity_list(conn, &wire_identities_in)); + EXPECT_EQUAL(conn->psk_params.chosen_psk, match_psk); + EXPECT_EQUAL(conn->psk_params.chosen_psk_wire_index, 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_stuffer_free(&wire_identities_in)); + }; + + /* Customer selection logic: customer selects out of bounds index */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + + uint16_t expected_wire_choice = 10; + EXPECT_SUCCESS(s2n_config_set_psk_selection_callback(config, s2n_test_select_psk_identity_callback, &expected_wire_choice)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_OK(s2n_connection_set_psk_type(conn, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_psk *match_psk = NULL; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &match_psk)); + EXPECT_OK(s2n_psk_init(match_psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(match_psk, test_bytes_data, sizeof(test_bytes_data))); + + struct s2n_stuffer wire_identities_in = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&wire_identities_in, 0)); + EXPECT_OK(s2n_write_test_identity(&wire_identities_in, &match_psk->identity)); + + EXPECT_ERROR(s2n_client_psk_recv_identity_list(conn, &wire_identities_in)); + EXPECT_EQUAL(conn->psk_params.chosen_psk, NULL); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_stuffer_free(&wire_identities_in)); + }; + + /* Customer selection logic: customer selects index without a local match */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + + uint16_t expected_wire_choice = 0; + EXPECT_SUCCESS(s2n_config_set_psk_selection_callback(config, s2n_test_select_psk_identity_callback, &expected_wire_choice)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_OK(s2n_connection_set_psk_type(conn, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_psk *local_psk = NULL; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &local_psk)); + EXPECT_OK(s2n_psk_init(local_psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(local_psk, test_bytes_data, sizeof(test_bytes_data))); + + struct s2n_blob wire_identity = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&wire_identity, test_bytes_data_2, sizeof(test_bytes_data_2))); + + struct s2n_stuffer wire_identities_in = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&wire_identities_in, 0)); + EXPECT_OK(s2n_write_test_identity(&wire_identities_in, &wire_identity)); + + EXPECT_ERROR(s2n_client_psk_recv_identity_list(conn, &wire_identities_in)); + EXPECT_EQUAL(conn->psk_params.chosen_psk, NULL); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_stuffer_free(&wire_identities_in)); + }; + + /* PSK type is resumption */ + { + DEFER_CLEANUP(struct s2n_stuffer wire_identities_in = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&wire_identities_in, 0)); + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_psk_type(conn, S2N_PSK_TYPE_RESUMPTION)); + + struct s2n_config *config_with_cb = s2n_config_new(); + EXPECT_NOT_NULL(config_with_cb); + uint16_t expected_wire_choice = 0; + EXPECT_SUCCESS(s2n_config_set_psk_selection_callback(config_with_cb, s2n_test_select_psk_identity_callback, &expected_wire_choice)); + EXPECT_SUCCESS(s2n_setup_ticket_key(config_with_cb)); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_setup_ticket_key(config)); + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + DEFER_CLEANUP(struct s2n_stuffer psk_identity = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&psk_identity, 0)); + EXPECT_OK(s2n_setup_encrypted_ticket(conn, &psk_identity)); + + /* Resumption psk was selected */ + { + /* Write invalid resumption PSK */ + struct s2n_blob wire_identity = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&wire_identity, test_bytes_data_2, sizeof(test_bytes_data_2))); + EXPECT_OK(s2n_write_test_identity(&wire_identities_in, &wire_identity)); + + /* Write valid resumption PSK */ + EXPECT_OK(s2n_write_test_identity(&wire_identities_in, &psk_identity.blob)); + + EXPECT_OK(s2n_client_psk_recv_identity_list(conn, &wire_identities_in)); + + EXPECT_EQUAL(conn->psk_params.chosen_psk_wire_index, 1); + EXPECT_NOT_NULL(conn->psk_params.chosen_psk); + }; + + /* Customer selects a valid resumption psk with a callback */ + { + conn->psk_params.chosen_psk = NULL; + EXPECT_SUCCESS(s2n_stuffer_rewrite(&wire_identities_in)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config_with_cb)); + EXPECT_OK(s2n_write_test_identity(&wire_identities_in, &psk_identity.blob)); + + EXPECT_OK(s2n_client_psk_recv_identity_list(conn, &wire_identities_in)); + + EXPECT_EQUAL(conn->psk_params.chosen_psk_wire_index, 0); + EXPECT_NOT_NULL(conn->psk_params.chosen_psk); + }; + + /* Customer selects an invalid resumption psk with a callback */ + { + conn->psk_params.chosen_psk = NULL; + EXPECT_SUCCESS(s2n_stuffer_rewrite(&wire_identities_in)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config_with_cb)); + + struct s2n_blob wire_identity = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&wire_identity, test_bytes_data_2, sizeof(test_bytes_data_2))); + EXPECT_OK(s2n_write_test_identity(&wire_identities_in, &wire_identity)); + + EXPECT_ERROR(s2n_client_psk_recv_identity_list(conn, &wire_identities_in)); + + EXPECT_EQUAL(conn->psk_params.chosen_psk_wire_index, 0); + EXPECT_NULL(conn->psk_params.chosen_psk); + }; + + EXPECT_SUCCESS(s2n_config_free(config_with_cb)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + }; + + /* Test: s2n_client_psk_recv_binder_list */ + { + const uint8_t filler_binders[] = { + 0x00, /* binder size */ + + 0x01, /* binder size */ + 0xFF, /* binder value */ + }; + const size_t zero_length_binder_index = 0; + const size_t valid_binder_index = 2; + + uint8_t partial_client_hello_data[] = "Hello"; + uint8_t secret_data[] = "Secret"; + uint8_t binder_hash_data[SHA256_DIGEST_LENGTH] = { 0 }; + uint8_t valid_binder_data[SHA256_DIGEST_LENGTH] = { 0 }; + + struct s2n_blob partial_client_hello = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&partial_client_hello, + partial_client_hello_data, sizeof(partial_client_hello_data))); + + struct s2n_blob binder_hash = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&binder_hash, binder_hash_data, sizeof(binder_hash_data))); + + struct s2n_blob valid_binder = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&valid_binder, valid_binder_data, sizeof(valid_binder_data))); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + DEFER_CLEANUP(struct s2n_psk psk = { 0 }, s2n_psk_wipe); + EXPECT_OK(s2n_psk_init(&psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_secret(&psk, secret_data, sizeof(secret_data))); + + EXPECT_SUCCESS(s2n_psk_calculate_binder_hash(conn, psk.hmac_alg, &partial_client_hello, &binder_hash)); + EXPECT_SUCCESS(s2n_psk_calculate_binder(&psk, &binder_hash, &valid_binder)); + + struct s2n_stuffer wire_binders_in = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&wire_binders_in, sizeof(filler_binders))); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&wire_binders_in, filler_binders, sizeof(filler_binders))); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&wire_binders_in, valid_binder.size)); + EXPECT_SUCCESS(s2n_stuffer_write(&wire_binders_in, &valid_binder)); + + /* Receive an empty list */ + { + uint8_t empty_wire_binders[] = { 0 }; + conn->psk_params.chosen_psk = &psk; + conn->psk_params.chosen_psk_wire_index = valid_binder_index; + + struct s2n_stuffer empty_wire_binders_in = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_alloc(&empty_wire_binders_in, sizeof(empty_wire_binders))); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&empty_wire_binders_in, empty_wire_binders, sizeof(empty_wire_binders))); + + EXPECT_ERROR_WITH_ERRNO(s2n_client_psk_recv_binder_list(conn, &partial_client_hello, &empty_wire_binders_in), + S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_stuffer_free(&empty_wire_binders_in)); + }; + + /* No chosen identity */ + { + EXPECT_SUCCESS(s2n_stuffer_reread(&wire_binders_in)); + conn->psk_params.chosen_psk = NULL; + conn->psk_params.chosen_psk_wire_index = 0; + + EXPECT_ERROR_WITH_ERRNO(s2n_client_psk_recv_binder_list(conn, &partial_client_hello, &wire_binders_in), + S2N_ERR_NULL); + }; + + /* Binder list too short for chosen identity index */ + { + EXPECT_SUCCESS(s2n_stuffer_reread(&wire_binders_in)); + conn->psk_params.chosen_psk = &psk; + conn->psk_params.chosen_psk_wire_index = valid_binder_index + 10; + + EXPECT_ERROR_WITH_ERRNO(s2n_client_psk_recv_binder_list(conn, &partial_client_hello, &wire_binders_in), + S2N_ERR_BAD_MESSAGE); + }; + + /* Binder for chosen identity is zero-length */ + { + EXPECT_SUCCESS(s2n_stuffer_reread(&wire_binders_in)); + conn->psk_params.chosen_psk = &psk; + conn->psk_params.chosen_psk_wire_index = zero_length_binder_index; + + EXPECT_ERROR_WITH_ERRNO(s2n_client_psk_recv_binder_list(conn, &partial_client_hello, &wire_binders_in), + S2N_ERR_SAFETY); + }; + + /* Binder for chosen identity is invalid */ + { + EXPECT_SUCCESS(s2n_stuffer_reread(&wire_binders_in)); + conn->psk_params.chosen_psk = &psk; + conn->psk_params.chosen_psk_wire_index = valid_binder_index; + + /* Using a different partial client hello produces a different binder */ + struct s2n_blob *different_partial_client_hello = &valid_binder; + + EXPECT_ERROR_WITH_ERRNO(s2n_client_psk_recv_binder_list(conn, different_partial_client_hello, &wire_binders_in), + S2N_ERR_BAD_MESSAGE); + }; + + /* Binder for chosen identity is valid */ + { + EXPECT_SUCCESS(s2n_stuffer_reread(&wire_binders_in)); + conn->psk_params.chosen_psk = &psk; + conn->psk_params.chosen_psk_wire_index = valid_binder_index; + + EXPECT_OK(s2n_client_psk_recv_binder_list(conn, &partial_client_hello, &wire_binders_in)); + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&wire_binders_in)); + }; + + /* Test: s2n_client_psk_recv */ + { + const uint8_t client_hello_data[] = "ClientHello"; + s2n_extension_type_id key_share_id; + s2n_extension_type_id psk_ke_mode_id; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_PSK_KEY_EXCHANGE_MODES, &psk_ke_mode_id)); + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_KEY_SHARE, &key_share_id)); + + /* Receive an extension with no valid identity */ + { + const uint8_t extension_data[] = { + 0x00, 0x00, /* identity list size */ + 0x00, 0x00, /* binder list size */ + }; + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->client_hello.extensions.count = 1; + + /* The psk key exchange modes and keyshare extensions need to be received to use a psk */ + conn->psk_params.psk_ke_mode = S2N_PSK_DHE_KE; + S2N_CBIT_SET(conn->extension_requests_received, psk_ke_mode_id); + S2N_CBIT_SET(conn->extension_requests_received, key_share_id); + + /* Setup the ClientHello */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->handshake.io, client_hello_data, sizeof(client_hello_data))); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->handshake.io, extension_data, sizeof(extension_data))); + + /* Setup the extension */ + struct s2n_stuffer extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&extension, extension_data, sizeof(extension_data))); + + /* Verify it is successful, but no PSK is chosen */ + EXPECT_SUCCESS(s2n_client_psk_recv(conn, &extension)); + EXPECT_EQUAL(conn->psk_params.chosen_psk_wire_index, 0); + EXPECT_EQUAL(conn->psk_params.chosen_psk, NULL); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + }; + + /* Receive an extension with an invalid binder */ + /* Receive an extension when running with TLS1.2 */ + { + const uint8_t identity = 0x12; + const uint8_t identity_bytes[] = { identity }; + const uint8_t extension_data[] = { + 0x00, 0x07, /* identity list size */ + 0x00, 0x01, /* identity size */ + identity, /* identity */ + 0x00, 0x00, 0x00, 0x00, /* ticket_age */ + 0x00, 0x00, /* binder list size */ + }; + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_OK(s2n_connection_set_psk_type(conn, S2N_PSK_TYPE_EXTERNAL)); + conn->client_hello.extensions.count = 1; + + /* The psk_ke_modes and keyshare extensions need to be received to use a psk */ + conn->psk_params.psk_ke_mode = S2N_PSK_DHE_KE; + S2N_CBIT_SET(conn->extension_requests_received, psk_ke_mode_id); + S2N_CBIT_SET(conn->extension_requests_received, key_share_id); + + /* Setup the ClientHello */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->handshake.io, client_hello_data, sizeof(client_hello_data))); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->handshake.io, extension_data, sizeof(extension_data))); + + /* Setup the extension */ + struct s2n_stuffer extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&extension, extension_data, sizeof(extension_data))); + + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); + EXPECT_OK(s2n_psk_init(psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(psk, identity_bytes, sizeof(identity_bytes))); + + /* Should be a no-op if using TLS1.2 */ + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_extension_recv(&s2n_client_psk_extension, conn, &extension)); + EXPECT_NULL(conn->psk_params.chosen_psk); + EXPECT_EQUAL(s2n_stuffer_data_available(&extension), sizeof(extension_data)); + + /* Should be a failure if using TLS1.3 */ + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FAILURE_WITH_ERRNO(s2n_client_psk_recv(conn, &extension), S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + }; + + /* Receive a psk extension with no psk_ke_modes extension */ + { + const uint8_t extension_data[] = { 0 }; + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->client_hello.extensions.count = 1; + + /* Setup the extension */ + struct s2n_stuffer extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&extension, extension_data, sizeof(extension_data))); + + EXPECT_EQUAL(conn->psk_params.psk_ke_mode, S2N_PSK_KE_UNKNOWN); + + EXPECT_FALSE(S2N_CBIT_TEST(conn->extension_requests_received, psk_ke_mode_id)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_client_psk_recv(conn, &extension), S2N_ERR_MISSING_EXTENSION); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + }; + + /* Receive a psk extension with an unknown psk key exchange mode */ + { + const uint8_t extension_data[] = { 0 }; + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->client_hello.extensions.count = 1; + + /* Setup the extension */ + struct s2n_stuffer extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&extension, extension_data, sizeof(extension_data))); + + conn->psk_params.psk_ke_mode = S2N_PSK_KE_UNKNOWN; + + S2N_CBIT_SET(conn->extension_requests_received, psk_ke_mode_id); + + EXPECT_SUCCESS(s2n_client_psk_recv(conn, &extension)); + EXPECT_NULL(conn->psk_params.chosen_psk); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + }; + + /* Receive a psk extension and a psk key exchange extension with (EC)DHE key establishment but no + * keyshare_extension */ + { + const uint8_t extension_data[] = { 0 }; + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->client_hello.extensions.count = 1; + + /* Setup the extension */ + struct s2n_stuffer extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&extension, extension_data, sizeof(extension_data))); + + conn->psk_params.psk_ke_mode = S2N_PSK_DHE_KE; + + S2N_CBIT_SET(conn->extension_requests_received, psk_ke_mode_id); + EXPECT_FALSE(S2N_CBIT_TEST(conn->extension_requests_received, key_share_id)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_client_psk_recv(conn, &extension), S2N_ERR_MISSING_EXTENSION); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + }; + + /* The extension does not appear last in the extension list */ + { + s2n_extension_type_id psk_ext_id; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_PRE_SHARED_KEY, &psk_ext_id)); + + struct s2n_stuffer extension = { 0 }; + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + conn->client_hello.extensions.count = 2; + conn->client_hello.extensions.parsed_extensions[psk_ext_id].wire_index = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_client_psk_recv(conn, &extension), S2N_ERR_UNSUPPORTED_EXTENSION); + + conn->client_hello.extensions.count = 5; + conn->client_hello.extensions.parsed_extensions[psk_ext_id].wire_index = 1; + EXPECT_FAILURE_WITH_ERRNO(s2n_client_psk_recv(conn, &extension), S2N_ERR_UNSUPPORTED_EXTENSION); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Receive a valid extension */ + { + const uint8_t client_hello_prefix_data[] = { + 0x01, /* Message Type: ClientHello */ + 0x00, 0x00, 0x00, /* Message size: not set yet */ + 0x12, 0x34, 0x56, /* Message: random data */ + }; + + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + struct s2n_stuffer *client_out = &client_conn->handshake.io; + + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_OK(s2n_connection_set_psk_type(server_conn, S2N_PSK_TYPE_EXTERNAL)); + struct s2n_stuffer *server_in = &server_conn->handshake.io; + server_conn->client_hello.extensions.count = 1; + + /* The psk key exchange modes and keyshare extensions need to be received to use a psk */ + server_conn->psk_params.psk_ke_mode = S2N_PSK_DHE_KE; + S2N_CBIT_SET(server_conn->extension_requests_received, psk_ke_mode_id); + S2N_CBIT_SET(server_conn->extension_requests_received, key_share_id); + + struct s2n_psk *shared_psk = NULL; + EXPECT_OK(s2n_array_pushback(&client_conn->psk_params.psk_list, (void **) &shared_psk)); + EXPECT_OK(s2n_psk_init(shared_psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(shared_psk, test_identity, sizeof(test_identity))); + EXPECT_SUCCESS(s2n_psk_set_secret(shared_psk, test_secret, sizeof(test_secret))); + EXPECT_OK(s2n_array_pushback(&server_conn->psk_params.psk_list, (void **) &shared_psk)); + EXPECT_OK(s2n_psk_init(shared_psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(shared_psk, test_identity, sizeof(test_identity))); + EXPECT_SUCCESS(s2n_psk_set_secret(shared_psk, test_secret, sizeof(test_secret))); + + struct s2n_psk *other_server_psk = NULL; + EXPECT_OK(s2n_array_pushback(&server_conn->psk_params.psk_list, (void **) &other_server_psk)); + EXPECT_OK(s2n_psk_init(other_server_psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(other_server_psk, test_identity_2, sizeof(test_identity_2))); + + /* Write the ClientHello prefix */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(client_out, + client_hello_prefix_data, sizeof(client_hello_prefix_data))); + + EXPECT_SUCCESS(s2n_client_psk_extension.send(client_conn, client_out)); + EXPECT_OK(s2n_finish_psk_extension(client_conn)); + + /* Copy the ClientHello over to the server's input buffer, but skip ClientHello prefix */ + EXPECT_SUCCESS(s2n_stuffer_copy(client_out, server_in, + s2n_stuffer_data_available(client_out))); + EXPECT_SUCCESS(s2n_stuffer_skip_read(server_in, sizeof(client_hello_prefix_data))); + + EXPECT_SUCCESS(s2n_client_psk_recv(server_conn, server_in)); + EXPECT_EQUAL(server_conn->psk_params.chosen_psk_wire_index, 0); + EXPECT_EQUAL(server_conn->psk_params.chosen_psk, shared_psk); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + }; + + /* Functional test */ + if (s2n_is_tls13_fully_supported()) { + /* Setup connections */ + struct s2n_connection *client_conn, *server_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_OK(s2n_connection_set_psk_type(server_conn, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + /* Setup other client PSK */ + uint8_t other_client_data[] = "other client data"; + struct s2n_psk *other_client_psk = NULL; + EXPECT_OK(s2n_array_pushback(&client_conn->psk_params.psk_list, (void **) &other_client_psk)); + EXPECT_OK(s2n_psk_init(other_client_psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(other_client_psk, other_client_data, sizeof(other_client_data))); + EXPECT_SUCCESS(s2n_psk_set_secret(other_client_psk, other_client_data, sizeof(other_client_data))); + + /* Setup other server PSK */ + uint8_t other_server_data[] = "other server data"; + struct s2n_psk *other_server_psk = NULL; + EXPECT_OK(s2n_array_pushback(&server_conn->psk_params.psk_list, (void **) &other_server_psk)); + EXPECT_OK(s2n_psk_init(other_server_psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(other_server_psk, other_server_data, sizeof(other_server_data))); + EXPECT_SUCCESS(s2n_psk_set_secret(other_server_psk, other_server_data, sizeof(other_server_data))); + + /* Setup shared PSK for client */ + struct s2n_psk *shared_psk = NULL; + EXPECT_OK(s2n_array_pushback(&client_conn->psk_params.psk_list, (void **) &shared_psk)); + EXPECT_OK(s2n_psk_init(shared_psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(shared_psk, test_identity, sizeof(test_identity))); + EXPECT_SUCCESS(s2n_psk_set_secret(shared_psk, test_secret, sizeof(test_secret))); + + /* Setup shared PSK for server */ + EXPECT_OK(s2n_array_pushback(&server_conn->psk_params.psk_list, (void **) &shared_psk)); + EXPECT_OK(s2n_psk_init(shared_psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(shared_psk, test_identity, sizeof(test_identity))); + EXPECT_SUCCESS(s2n_psk_set_secret(shared_psk, test_secret, sizeof(test_secret))); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + /* Verify shared PSK chosen */ + EXPECT_EQUAL(server_conn->psk_params.chosen_psk_wire_index, 1); + EXPECT_EQUAL(server_conn->psk_params.chosen_psk, shared_psk); + EXPECT_EQUAL(shared_psk->secret.size, sizeof(test_secret)); + EXPECT_BYTEARRAY_EQUAL(shared_psk->secret.data, test_secret, sizeof(test_secret)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + /** + * Ensure obfuscated_ticket_age and binder values are updated on a client hello after a HRR + * + *= https://tools.ietf.org/rfc/rfc8446#section-4.1.2 + *= type=test + *# - Updating the "pre_shared_key" extension if present by recomputing + *# the "obfuscated_ticket_age" and binder values and (optionally) + *# removing any PSKs which are incompatible with the server's + *# indicated cipher suite. + **/ + if (s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, + S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + EXPECT_SUCCESS(s2n_setup_ticket_key(config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_stuffer io_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&io_stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&io_stuffer, &io_stuffer, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&io_stuffer, &io_stuffer, server_conn)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + client_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + /* Set a resumption psk */ + DEFER_CLEANUP(struct s2n_stuffer psk_identity = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&psk_identity, 0)); + EXPECT_OK(s2n_setup_encrypted_ticket(client_conn, &psk_identity)); + struct s2n_offered_psk_list identity_list = { .conn = client_conn }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&identity_list.wire_data, 0)); + EXPECT_OK(s2n_write_test_identity(&identity_list.wire_data, &psk_identity.blob)); + EXPECT_OK(s2n_select_resumption_psk(server_conn, &identity_list)); + + /* Set the ticket issue time to be 10 milliseconds ago, so the obfuscated ticket age will be non-zero */ + struct s2n_psk *psk = client_conn->psk_params.chosen_psk; + psk->ticket_issue_time -= MILLIS_TO_NANOS(10); + + /* Calculate the size of the identity without the key name. Used to first seek to the key name as the target for + * s2n_stuffer_skip_read_until, and then seek to the end of the identity. */ + const unsigned long identity_data_size = psk->identity.size - strlen((char *) psk->identity.data); + + s2n_blocked_status blocked = 0; + + /* Client sends ClientHello 1 */ + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Read the obfuscated ticket age from ClientHello 1 */ + EXPECT_SUCCESS(s2n_stuffer_skip_read_until(&io_stuffer, (char *) psk->identity.data)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&io_stuffer, identity_data_size)); + uint32_t obfuscated_ticket_age_1 = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint32(&io_stuffer, &obfuscated_ticket_age_1)); + + /* Skip over the size of the binders list */ + EXPECT_SUCCESS(s2n_stuffer_skip_read(&io_stuffer, sizeof(uint16_t))); + + /* Ensure the binder size is as expected */ + uint8_t binder_1_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&io_stuffer, &binder_1_size)); + EXPECT_TRUE(binder_1_size == BINDER_SIZE); + + /* Read the binder from ClientHello 1 */ + uint8_t binder_1[BINDER_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&io_stuffer, binder_1, BINDER_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_reread(&io_stuffer)); + + /* Ensure that the ticket age and binder are non-zero */ + EXPECT_TRUE(obfuscated_ticket_age_1 != 0); + uint8_t zero_array[BINDER_SIZE] = { 0 }; + EXPECT_FALSE(s2n_constant_time_equals(binder_1, zero_array, BINDER_SIZE)); + + /* Skip to before the client sends ClientHello 2 */ + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, HELLO_RETRY_MSG)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, CLIENT_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, CLIENT_HELLO)); + + /* Change the ticket issue time so a new obfuscated ticket age will change */ + psk->ticket_issue_time -= MILLIS_TO_NANOS(1); + + /* Client sends ClientHello 2 */ + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Read the obfuscated ticket age from ClientHello 2 */ + EXPECT_SUCCESS(s2n_stuffer_skip_read_until(&io_stuffer, (char *) psk->identity.data)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&io_stuffer, identity_data_size)); + uint32_t obfuscated_ticket_age_2 = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint32(&io_stuffer, &obfuscated_ticket_age_2)); + + /* Skip over the size of the binders list */ + EXPECT_SUCCESS(s2n_stuffer_skip_read(&io_stuffer, sizeof(uint16_t))); + + /* Ensure the binder size is as expected */ + uint8_t binder_2_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&io_stuffer, &binder_2_size)); + EXPECT_TRUE(binder_2_size == BINDER_SIZE); + + /* Read the binder from ClientHello 2 */ + uint8_t binder_2[BINDER_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&io_stuffer, binder_2, BINDER_SIZE)); + + /* Ensure that the ticket age and binder were updated after ClientHello 2 */ + EXPECT_TRUE(obfuscated_ticket_age_1 != obfuscated_ticket_age_2); + EXPECT_FALSE(s2n_constant_time_equals(binder_1, binder_2, BINDER_SIZE)); + + EXPECT_SUCCESS(s2n_stuffer_free(&identity_list.wire_data)); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_client_record_version_test.c b/tests/unit/s2n_client_record_version_test.c new file mode 100644 index 00000000000..7e626a1417b --- /dev/null +++ b/tests/unit/s2n_client_record_version_test.c @@ -0,0 +1,316 @@ +/* + * 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 +#include +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls_parameters.h" + +#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F + +int main(int argc, char **argv) +{ + char *cert_chain; + char *private_key; + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); + + /* Server negotiates TLS1.2 */ + { + struct s2n_connection *client_conn; + struct s2n_config *client_config; + s2n_blocked_status client_blocked; + + uint8_t server_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, 0x03, + /* Server random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, 0x3C, + /* Compression method - none */ + 0x00, + /* Extensions len */ + 0x00, 0x00 + }; + int body_len = sizeof(server_hello_message); + uint8_t message_header[] = { + /* Handshake message type SERVER HELLO */ + 0x02, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + /* Send the client hello */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Read ClientHello s2n wrote */ + uint8_t buf[1024]; + size_t buf_occupied = 0; + + /* we need only first 10 bytes to get to ClientHello protocol version */ + while (buf_occupied < 10) { + ssize_t n = read(io_pair.server, buf + buf_occupied, sizeof(buf) - buf_occupied); + + /* We should be able to read 10 bytes without blocking */ + EXPECT_TRUE(n > 0); + buf_occupied += n; + } + /* Record Type is Handshake */ + EXPECT_EQUAL(buf[0], 0x16); + /* Protocol version is TLS1.0 */ + EXPECT_EQUAL(buf[1], 0x03); + EXPECT_EQUAL(buf[2], 0x01); + /* Handshake Type is ClientHello */ + EXPECT_EQUAL(buf[5], 0x01); + /* Handshake Protocol Version is TLS1.2 */ + EXPECT_EQUAL(buf[9], 0x03); + EXPECT_EQUAL(buf[10], 0x03); + + /* Read the rest of the pipe */ + while (1) { + ssize_t n = read(io_pair.server, buf, sizeof(buf)); + + if (n > 0) { + continue; + } + + EXPECT_EQUAL(n, -1); + if (errno == EAGAIN || errno == EWOULDBLOCK) { + break; + } + } + + /* Write the server hello */ + EXPECT_EQUAL(write(io_pair.server, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.server, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.server, server_hello_message, sizeof(server_hello_message)), sizeof(server_hello_message)); + + /* Verify that we proceed with handshake */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Verify that protocol versions are TLS1.2 now */ + EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->server_protocol_version, S2N_TLS12); + + /* Now lets shutdown the connection and verify that alert is sent in record with protocol version TLS1.2 */ + EXPECT_SUCCESS(s2n_shutdown(client_conn, &client_blocked)); + + /* Receive the next record from client and ensure that record protocol version is TLS1.2 */ + buf_occupied = 0; + /* We need only first 5 bytes to get to record protocol version */ + while (buf_occupied < 5) { + ssize_t n = read(io_pair.server, buf + buf_occupied, sizeof(buf) - buf_occupied); + + /* We should be able to read 5 bytes without blocking */ + EXPECT_TRUE(n > 0); + buf_occupied += n; + } + /* Protocol version is TLS1.2 now */ + EXPECT_EQUAL(buf[1], 0x03); + EXPECT_EQUAL(buf[2], 0x03); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /* Server negotiates SSLv3 */ + { + struct s2n_connection *client_conn; + struct s2n_config *client_config; + s2n_blocked_status client_blocked; + + uint8_t server_hello_message[] = { + /* Protocol version SSLv3 */ + 0x03, 0x00, + /* Server random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suite - DES-CBC3-SHA */ + 0x00, 0x0A, + /* Compression method - none */ + 0x00, + /* Extensions len */ + 0x00, 0x00 + }; + int body_len = sizeof(server_hello_message); + uint8_t message_header[] = { + /* Handshake message type SERVER HELLO */ + 0x02, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version SSLv3 */ + 0x03, + 0x00, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + /* Send the client hello */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Read ClientHello s2n wrote */ + uint8_t buf[1024]; + size_t buf_occupied = 0; + + /* we need only first 10 bytes to get to ClientHello protocol version */ + while (buf_occupied < 10) { + ssize_t n = read(io_pair.server, buf + buf_occupied, sizeof(buf) - buf_occupied); + + /* We should be able to read 10 bytes without blocking */ + EXPECT_TRUE(n > 0); + buf_occupied += n; + } + /* Record Type is Handshake */ + EXPECT_EQUAL(buf[0], 0x16); + /* Protocol version is TLS1.0 */ + EXPECT_EQUAL(buf[1], 0x03); + EXPECT_EQUAL(buf[2], 0x01); + /* Handshake Type is ClientHello */ + EXPECT_EQUAL(buf[5], 0x01); + /* Handshake Protocol Version is TLS1.2 */ + EXPECT_EQUAL(buf[9], 0x03); + EXPECT_EQUAL(buf[10], 0x03); + + /* Read the rest of the pipe */ + while (1) { + ssize_t n = read(io_pair.server, buf, sizeof(buf)); + + if (n > 0) { + continue; + } + + EXPECT_EQUAL(n, -1); + if (errno == EAGAIN || errno == EWOULDBLOCK) { + break; + } + } + + /* Write the server hello */ + EXPECT_EQUAL(write(io_pair.server, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.server, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.server, server_hello_message, sizeof(server_hello_message)), sizeof(server_hello_message)); + + /* Verify that we proceed with handshake */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Verify that protocol versions are SSLv3 with the exeption of client which supports TLS1.2 */ + EXPECT_EQUAL(client_conn->client_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_SSLv3); + EXPECT_EQUAL(client_conn->server_protocol_version, S2N_SSLv3); + + /* Now lets shutdown the connection and verify that alert is sent in record with protocol version SSLv3 */ + EXPECT_SUCCESS(s2n_shutdown(client_conn, &client_blocked)); + + /* Receive the next record from client and ensure that record protocol version is SSLv3 */ + buf_occupied = 0; + /* We need only first 5 bytes to get to record protocol version */ + while (buf_occupied < 5) { + ssize_t n = read(io_pair.server, buf + buf_occupied, sizeof(buf) - buf_occupied); + + /* We should be able to read 5 bytes without blocking */ + EXPECT_TRUE(n > 0); + buf_occupied += n; + } + /* Protocol version is SSLv3 now */ + EXPECT_EQUAL(buf[1], 0x03); + EXPECT_EQUAL(buf[2], 0x00); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + free(cert_chain); + free(private_key); + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_client_renegotiation_info_extension_test.c b/tests/unit/s2n_client_renegotiation_info_extension_test.c new file mode 100644 index 00000000000..d2ac150a0e0 --- /dev/null +++ b/tests/unit/s2n_client_renegotiation_info_extension_test.c @@ -0,0 +1,477 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_client_renegotiation_info.h" +#include "tls/s2n_tls.h" + +int s2n_parse_client_hello(struct s2n_connection *conn); + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + const uint8_t renegotiation_info_scsv_iana[] = { TLS_EMPTY_RENEGOTIATION_INFO_SCSV }; + const uint8_t client_verify_data[] = "client verify data"; + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + /* Test receive - too much data */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, 0)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_client_renegotiation_info_extension.recv(conn, &stuffer), + S2N_ERR_NON_EMPTY_RENEGOTIATION_INFO); + EXPECT_FALSE(conn->secure_renegotiation); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test receive - value not 0 + * + *= https://tools.ietf.org/rfc/rfc5746#3.6 + *= type=test + *# The server MUST then verify + *# that the length of the "renegotiated_connection" field is zero, + *# and if it is not, MUST abort the handshake. + */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 1)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_client_renegotiation_info_extension.recv(conn, &stuffer), + S2N_ERR_NON_EMPTY_RENEGOTIATION_INFO); + EXPECT_FALSE(conn->secure_renegotiation); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test receive */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_client_renegotiation_info_extension.recv(conn, &stuffer)); + EXPECT_TRUE(conn->secure_renegotiation); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test receive when using SSLv3 + * + *= https://tools.ietf.org/rfc/rfc5746#4.5 + *= type=test + *# TLS servers that support secure renegotiation and support SSLv3 MUST accept SCSV or the + *# "renegotiation_info" extension and respond as described in this + *# specification even if the offered client version is {0x03, 0x00}. + **/ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + DEFER_CLEANUP(struct s2n_stuffer extension = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension, 0)); + + server_conn->server_protocol_version = S2N_SSLv3; + server_conn->actual_protocol_version = S2N_SSLv3; + EXPECT_SUCCESS(s2n_client_renegotiation_info_extension.recv(server_conn, &extension)); + EXPECT_TRUE(server_conn->secure_renegotiation); + }; + + /* + *= https://tools.ietf.org/rfc/rfc5746#3.4 + *= type=test + *# o The client MUST include either an empty "renegotiation_info" + *# extension, or the TLS_EMPTY_RENEGOTIATION_INFO_SCSV signaling + *# cipher suite value in the ClientHello. Including both is NOT + *# RECOMMENDED. + */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + /* Process the client hello on the server */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_parse_client_hello(server_conn)); + + /* Expect TLS_EMPTY_RENEGOTIATION_INFO_SCSV */ + bool found_renegotiation_info_scsv = false; + for (size_t i = 0; i < server_conn->client_hello.cipher_suites.size; i += S2N_TLS_CIPHER_SUITE_LEN) { + uint8_t *iana = server_conn->client_hello.cipher_suites.data + i; + if (memcmp(iana, renegotiation_info_scsv_iana, S2N_TLS_CIPHER_SUITE_LEN) == 0) { + found_renegotiation_info_scsv = true; + } + } + EXPECT_TRUE(found_renegotiation_info_scsv); + + /* Do NOT expect "renegotiation_info" extension */ + s2n_extension_type_id extension_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id( + s2n_client_renegotiation_info_extension.iana_value, &extension_id)); + s2n_parsed_extension *extension = &server_conn->client_hello.extensions.parsed_extensions[extension_id]; + EXPECT_EQUAL(extension->extension.size, 0); + EXPECT_EQUAL(extension->extension_type, 0); + }; + + /** + *= https://tools.ietf.org/rfc/rfc5746#3.6 + *= type=test + *# o The server MUST check if the "renegotiation_info" extension is + *# included in the ClientHello. + */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + /* s2n-tls clients do not send the "renegotiation_info" extension. + * Instead, they send the TLS_EMPTY_RENEGOTIATION_INFO_SCSV cipher suite. + * See previous test. + */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + + /* Process the extensions on the server */ + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_parse_client_hello(server_conn)); + EXPECT_SUCCESS(s2n_extension_list_process(S2N_EXTENSION_LIST_CLIENT_HELLO, server_conn, + &server_conn->client_hello.extensions)); + + /* Expect secure renegotiation to still be false: no extension received */ + EXPECT_FALSE(server_conn->secure_renegotiation); + + /* Manually append the "renegotiation_info" extension to the original list. */ + uint8_t extension[] = { + 0xff, 0x01, /* extension type: renegotiation_info */ + 0x00, 0x01, /* extension length: 1 */ + 0x00, /* renegotiated_connection length: 0 */ + }; + size_t client_hello_size = server_conn->client_hello.raw_message.size; + size_t old_extensions_size = server_conn->client_hello.extensions.raw.size; + size_t new_extensions_size = old_extensions_size + sizeof(extension); + EXPECT_SUCCESS(s2n_stuffer_rewrite(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&client_conn->handshake.io, + client_hello_size - old_extensions_size - sizeof(uint16_t))); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&client_conn->handshake.io, new_extensions_size)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&client_conn->handshake.io, old_extensions_size)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&client_conn->handshake.io, extension, sizeof(extension))); + + /* Process the extensions on the server again */ + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_parse_client_hello(server_conn)); + EXPECT_SUCCESS(s2n_extension_list_process(S2N_EXTENSION_LIST_CLIENT_HELLO, server_conn, + &server_conn->client_hello.extensions)); + + /* Expect secure renegotiation to be true: extension received */ + EXPECT_TRUE(server_conn->secure_renegotiation); + }; + + /* Test: should_send during renegotiation handshake + * + *= https://tools.ietf.org/rfc/rfc5746#3.5 + *= type=test + *# o The client MUST include the "renegotiation_info" extension in the + *# ClientHello + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + /* Not included by default */ + EXPECT_FALSE(s2n_client_renegotiation_info_extension.should_send(conn)); + + /* Included if renegotiation enabled */ + conn->handshake.renegotiation = true; + EXPECT_TRUE(s2n_client_renegotiation_info_extension.should_send(conn)); + }; + + /* Test: send during renegotiation handshake + * + *= https://tools.ietf.org/rfc/rfc5746#3.5 + *= type=test + *# o The client MUST include the "renegotiation_info" extension in the + *# ClientHello, containing the saved client_verify_data. + */ + { + /* Send client_verify_data */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->handshake.renegotiation = true; + + /* Setup client_verify_data */ + EXPECT_MEMCPY_SUCCESS(conn->handshake.client_finished, + client_verify_data, sizeof(client_verify_data)); + conn->handshake.finished_len = sizeof(client_verify_data); + + /* Error if secure renegotiation not supported */ + { + conn->secure_renegotiation = false; + + DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_renegotiation_info_extension.send(conn, &out), + S2N_ERR_NO_RENEGOTIATION); + }; + + /* Success if secure renegotiation supported */ + { + conn->secure_renegotiation = true; + + DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_SUCCESS(s2n_client_renegotiation_info_extension.send(conn, &out)); + + uint8_t actual_len = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&out, &actual_len)); + EXPECT_EQUAL(actual_len, sizeof(client_verify_data)); + + uint8_t *actual_data = s2n_stuffer_raw_read(&out, actual_len); + EXPECT_BYTEARRAY_EQUAL(actual_data, client_verify_data, actual_len); + }; + }; + + /* + *= https://tools.ietf.org/rfc/rfc5746#3.5 + *= type=test + *# The SCSV MUST NOT be included. + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->secure_renegotiation = true; + conn->handshake.renegotiation = true; + + /* Setup client_verify_data */ + EXPECT_MEMCPY_SUCCESS(conn->handshake.client_finished, + client_verify_data, sizeof(client_verify_data)); + conn->handshake.finished_len = sizeof(client_verify_data); + + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + EXPECT_SUCCESS(s2n_parse_client_hello(conn)); + + /* Expect TLS_EMPTY_RENEGOTIATION_INFO_SCSV */ + bool found_renegotiation_info_scsv = false; + for (size_t i = 0; i < conn->client_hello.cipher_suites.size; i += S2N_TLS_CIPHER_SUITE_LEN) { + uint8_t *iana = conn->client_hello.cipher_suites.data + i; + if (memcmp(iana, renegotiation_info_scsv_iana, S2N_TLS_CIPHER_SUITE_LEN) == 0) { + found_renegotiation_info_scsv = true; + } + } + EXPECT_FALSE(found_renegotiation_info_scsv); + }; + }; + + /* Test: recv during renegotiation handshake + * + *= https://tools.ietf.org/rfc/rfc5746#3.7 + *= type=test + *# o The server MUST verify that the value of the + *# "renegotiated_connection" field is equal to the saved + *# client_verify_data value; if it is not, the server MUST abort the + *# handshake. + */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + client_conn->handshake.renegotiation = true; + client_conn->secure_renegotiation = true; + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + server_conn->handshake.renegotiation = true; + server_conn->secure_renegotiation = true; + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_MEMCPY_SUCCESS(client_conn->handshake.client_finished, + client_verify_data, sizeof(client_verify_data)); + client_conn->handshake.finished_len = sizeof(client_verify_data); + + /* Test: client_verify matches */ + { + EXPECT_MEMCPY_SUCCESS(server_conn->handshake.client_finished, + client_verify_data, sizeof(client_verify_data)); + server_conn->handshake.finished_len = sizeof(client_verify_data); + + EXPECT_SUCCESS(s2n_client_renegotiation_info_extension.send(client_conn, &stuffer)); + EXPECT_SUCCESS(s2n_client_renegotiation_info_extension.recv(server_conn, &stuffer)); + }; + + /* Test: client_verify does not match */ + { + const uint8_t bad_client_verify[] = "not correct"; + + EXPECT_MEMCPY_SUCCESS(server_conn->handshake.client_finished, + bad_client_verify, sizeof(bad_client_verify)); + server_conn->handshake.finished_len = sizeof(bad_client_verify); + + EXPECT_SUCCESS(s2n_client_renegotiation_info_extension.send(client_conn, &stuffer)); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_renegotiation_info_extension.recv(server_conn, &stuffer), + S2N_ERR_BAD_MESSAGE); + }; + + /* Test: no secure_renegotiation */ + { + server_conn->secure_renegotiation = false; + + EXPECT_MEMCPY_SUCCESS(server_conn->handshake.client_finished, + client_verify_data, sizeof(client_verify_data)); + server_conn->handshake.finished_len = sizeof(client_verify_data); + + EXPECT_SUCCESS(s2n_client_renegotiation_info_extension.send(client_conn, &stuffer)); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_renegotiation_info_extension.recv(server_conn, &stuffer), + S2N_ERR_NO_RENEGOTIATION); + }; + }; + + /* Test: if_missing during renegotiation handshake + * + *= https://tools.ietf.org/rfc/rfc5746#3.7 + *= type=test + *# o The server MUST verify that the "renegotiation_info" extension is + *# present; if it is not, the server MUST abort the handshake. + */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + client_conn->handshake.renegotiation = false; + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + server_conn->handshake.renegotiation = true; + server_conn->secure_renegotiation = true; + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Verify if_missing throws error */ + EXPECT_FAILURE_WITH_ERRNO(s2n_client_renegotiation_info_extension.if_missing(server_conn), + S2N_ERR_MISSING_EXTENSION); + + /* Verify server marks extension as missing when processing client hello */ + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_MISSING_EXTENSION); + }; + + /* Test: receiving SCSV during renegotiation is an error + * + *= https://tools.ietf.org/rfc/rfc5746#3.7 + *= type=test + *# o When a ClientHello is received, the server MUST verify that it + *# does not contain the TLS_EMPTY_RENEGOTIATION_INFO_SCSV SCSV. If + *# the SCSV is present, the server MUST abort the handshake. + */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + client_conn->secure_renegotiation = true; + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + server_conn->secure_renegotiation = true; + + /* Construct a security policy that will write the SCSV like a regular cipher suite */ + struct s2n_cipher_suite forced_scsv = { + .available = true, + .iana_value = { TLS_EMPTY_RENEGOTIATION_INFO_SCSV }, + }; + struct s2n_cipher_suite *cipher_suites[] = { + &forced_scsv, + &s2n_ecdhe_rsa_with_aes_128_gcm_sha256 + }; + struct s2n_cipher_preferences cipher_preferences = { .suites = cipher_suites, .count = s2n_array_len(cipher_suites) }; + struct s2n_security_policy security_policy = *config->security_policy; + security_policy.cipher_preferences = &cipher_preferences; + client_conn->security_policy_override = &security_policy; + + /* Succeeds if server expects an initial handshake */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + client_conn->handshake.renegotiation = true; + EXPECT_MEMCPY_SUCCESS(client_conn->handshake.client_finished, + client_verify_data, sizeof(client_verify_data)); + client_conn->handshake.finished_len = sizeof(client_verify_data); + + server_conn->handshake.renegotiation = true; + EXPECT_MEMCPY_SUCCESS(server_conn->handshake.client_finished, + client_verify_data, sizeof(client_verify_data)); + server_conn->handshake.finished_len = sizeof(client_verify_data); + + /* Fails if server expects renegotiation */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_hello_recv(server_conn), S2N_ERR_BAD_MESSAGE); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_client_sct_list_extension_test.c b/tests/unit/s2n_client_sct_list_extension_test.c new file mode 100644 index 00000000000..26e0e0442c0 --- /dev/null +++ b/tests/unit/s2n_client_sct_list_extension_test.c @@ -0,0 +1,88 @@ +/* + * 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 "s2n_test.h" +#include "tls/extensions/s2n_client_sct_list.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Test should_send */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Default configs use S2N_CT_SUPPORT_NONE */ + EXPECT_FALSE(s2n_client_sct_list_extension.should_send(conn)); + + /* Use cipher preferences that do include PQ */ + EXPECT_SUCCESS(s2n_config_set_ct_support_level(config, S2N_CT_SUPPORT_REQUEST)); + EXPECT_TRUE(s2n_client_sct_list_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test send */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_client_sct_list_extension.send(conn, &stuffer)); + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test receive */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_client_sct_list_extension.send(conn, &stuffer)); + + EXPECT_NOT_EQUAL(conn->ct_level_requested, S2N_CT_SUPPORT_REQUEST); + EXPECT_SUCCESS(s2n_client_sct_list_extension.recv(conn, &stuffer)); + EXPECT_EQUAL(conn->ct_level_requested, S2N_CT_SUPPORT_REQUEST); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_client_secure_renegotiation_test.c b/tests/unit/s2n_client_secure_renegotiation_test.c new file mode 100644 index 00000000000..1a5d348026c --- /dev/null +++ b/tests/unit/s2n_client_secure_renegotiation_test.c @@ -0,0 +1,309 @@ +/* + * 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 +#include +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls_parameters.h" + +#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F + +int main(int argc, char **argv) +{ + char *cert_chain; + char *private_key; + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); + + /* Success: server sends an empty initial renegotiation_info */ + { + struct s2n_connection *client_conn; + struct s2n_config *client_config; + s2n_blocked_status client_blocked; + + uint8_t server_extensions[] = { + /* Extension type TLS_EXTENSION_RENEGOTIATION_INFO */ + 0xff, + 0x01, + /* Extension size */ + 0x00, + 0x01, + /* renegotiated_connection len */ + 0x00, + }; + int server_extensions_len = sizeof(server_extensions); + uint8_t server_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Server random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, + 0x3C, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (server_extensions_len >> 8) & 0xff, + (server_extensions_len & 0xff), + }; + int body_len = sizeof(server_hello_message) + server_extensions_len; + uint8_t message_header[] = { + /* Handshake message type SERVER HELLO */ + 0x02, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + /* Security policy must allow cipher suite hard coded into server hello */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + /* Send the client hello */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Write the server hello */ + EXPECT_EQUAL(write(io_pair.server, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.server, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.server, server_hello_message, sizeof(server_hello_message)), sizeof(server_hello_message)); + EXPECT_EQUAL(write(io_pair.server, server_extensions, sizeof(server_extensions)), sizeof(server_extensions)); + + /* Verify that we proceed with handshake */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Secure renegotiation is set */ + EXPECT_EQUAL(client_conn->secure_renegotiation, 1); + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Success: server doesn't send an renegotiation_info extension */ + { + struct s2n_connection *client_conn; + struct s2n_config *client_config; + s2n_blocked_status client_blocked; + + uint8_t server_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, 0x03, + /* Server random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, 0x3C, + /* Compression method - none */ + 0x00, + /* Extensions len */ + 0x00, 0x00 + }; + int body_len = sizeof(server_hello_message); + uint8_t message_header[] = { + /* Handshake message type SERVER HELLO */ + 0x02, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + /* Security policy must allow cipher suite hard coded into server hello */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + /* Send the client hello */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Write the server hello */ + EXPECT_EQUAL(write(io_pair.server, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.server, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.server, server_hello_message, sizeof(server_hello_message)), sizeof(server_hello_message)); + + /* Verify that we proceed with handshake */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Secure renegotiation is not set, as server doesn't support it */ + EXPECT_EQUAL(client_conn->secure_renegotiation, 0); + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Failure: server sends a non-empty initial renegotiation_info */ + { + struct s2n_connection *client_conn; + struct s2n_config *client_config; + s2n_blocked_status client_blocked; + + uint8_t server_extensions[] = { + /* Extension type TLS_EXTENSION_RENEGOTIATION_INFO */ + 0xff, + 0x01, + /* Extension size */ + 0x00, + 0x21, + /* renegotiated_connection len */ + 0x20, + /* fake renegotiated_connection */ + ZERO_TO_THIRTY_ONE, + }; + int server_extensions_len = sizeof(server_extensions); + uint8_t server_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Server random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, + 0x3C, + /* Compression method - none */ + 0x00, + /* Extensions len */ + (server_extensions_len >> 8) & 0xff, + (server_extensions_len & 0xff), + }; + int body_len = sizeof(server_hello_message) + server_extensions_len; + uint8_t message_header[] = { + /* Handshake message type SERVER HELLO */ + 0x02, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + int message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + /* Send the client hello */ + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(client_blocked, S2N_BLOCKED_ON_READ); + + /* Write the server hello */ + EXPECT_EQUAL(write(io_pair.server, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.server, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.server, server_hello_message, sizeof(server_hello_message)), sizeof(server_hello_message)); + EXPECT_EQUAL(write(io_pair.server, server_extensions, sizeof(server_extensions)), sizeof(server_extensions)); + + /* Verify that we fail for non-empty renegotiated_connection */ + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_EQUAL(s2n_negotiate(client_conn, &client_blocked), -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_NON_EMPTY_RENEGOTIATION_INFO); + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + free(cert_chain); + free(private_key); + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_client_server_name_extension_test.c b/tests/unit/s2n_client_server_name_extension_test.c new file mode 100644 index 00000000000..94413f22531 --- /dev/null +++ b/tests/unit/s2n_client_server_name_extension_test.c @@ -0,0 +1,196 @@ +/* + * 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 "s2n_test.h" +#include "tls/extensions/s2n_client_server_name.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + const char *test_server_name = "github.com"; + + /* should_send */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + /* server_name not set -> don't send */ + EXPECT_FALSE(s2n_client_server_name_extension.should_send(conn)); + + /* server_name empty -> don't send */ + EXPECT_SUCCESS(s2n_set_server_name(conn, "")); + EXPECT_FALSE(s2n_client_server_name_extension.should_send(conn)); + + /* server_name set -> send */ + EXPECT_SUCCESS(s2n_set_server_name(conn, test_server_name)); + EXPECT_TRUE(s2n_client_server_name_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* send */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_set_server_name(conn, test_server_name)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_client_server_name_extension.send(conn, &stuffer)); + + uint16_t server_name_list_size; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &server_name_list_size)); + EXPECT_EQUAL(server_name_list_size, s2n_stuffer_data_available(&stuffer)); + + uint8_t name_type; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&stuffer, &name_type)); + EXPECT_EQUAL(name_type, 0); + + uint16_t server_name_size; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &server_name_size)); + EXPECT_EQUAL(server_name_size, s2n_stuffer_data_available(&stuffer)); + EXPECT_EQUAL(server_name_size, strlen(test_server_name)); + + char *server_name_data; + EXPECT_NOT_NULL(server_name_data = s2n_stuffer_raw_read(&stuffer, server_name_size)); + EXPECT_BYTEARRAY_EQUAL(server_name_data, test_server_name, strlen(test_server_name)); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* recv - basic */ + { + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_set_server_name(client_conn, test_server_name)); + EXPECT_SUCCESS(s2n_client_server_name_extension.send(client_conn, &stuffer)); + + EXPECT_STRING_EQUAL(server_conn->server_name, ""); + EXPECT_SUCCESS(s2n_client_server_name_extension.recv(server_conn, &stuffer)); + EXPECT_STRING_EQUAL(server_conn->server_name, test_server_name); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* recv - server name already set */ + { + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "DIFFERENT SERVER NAME")); + EXPECT_SUCCESS(s2n_client_server_name_extension.send(client_conn, &stuffer)); + + EXPECT_SUCCESS(s2n_set_server_name(server_conn, test_server_name)); + EXPECT_SUCCESS(s2n_client_server_name_extension.recv(server_conn, &stuffer)); + EXPECT_STRING_EQUAL(server_conn->server_name, test_server_name); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* recv - extra data ignored */ + { + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_set_server_name(client_conn, test_server_name)); + EXPECT_SUCCESS(s2n_client_server_name_extension.send(client_conn, &stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 'a')); + + EXPECT_STRING_EQUAL(server_conn->server_name, ""); + EXPECT_SUCCESS(s2n_client_server_name_extension.recv(server_conn, &stuffer)); + EXPECT_STRING_EQUAL(server_conn->server_name, test_server_name); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* recv - malformed */ + { + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_set_server_name(client_conn, test_server_name)); + EXPECT_SUCCESS(s2n_client_server_name_extension.send(client_conn, &stuffer)); + + uint8_t extension_size = s2n_stuffer_data_available(&stuffer); + uint8_t test_bytes = extension_size - strlen(test_server_name); + + /* Check that inverting any byte in the sizes / name type causes us to skip the extension */ + for (int i = 0; i < test_bytes; i++) { + /* Mess something up! */ + stuffer.blob.data[i] = ~stuffer.blob.data[i]; + + EXPECT_STRING_EQUAL(server_conn->server_name, ""); + EXPECT_SUCCESS(s2n_client_server_name_extension.recv(server_conn, &stuffer)); + EXPECT_STRING_EQUAL(server_conn->server_name, ""); + + EXPECT_SUCCESS(s2n_stuffer_rewrite(&stuffer)); + EXPECT_SUCCESS(s2n_client_server_name_extension.send(client_conn, &stuffer)); + } + + /* Check that inverting a byte in the server name itself is fine-- there are + * no real rules about the server name! */ + stuffer.blob.data[test_bytes] = ~stuffer.blob.data[test_bytes]; + + EXPECT_STRING_EQUAL(server_conn->server_name, ""); + EXPECT_SUCCESS(s2n_client_server_name_extension.recv(server_conn, &stuffer)); + EXPECT_STRING_NOT_EQUAL(server_conn->server_name, ""); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_client_session_ticket_extension_test.c b/tests/unit/s2n_client_session_ticket_extension_test.c new file mode 100644 index 00000000000..4fbefc86300 --- /dev/null +++ b/tests/unit/s2n_client_session_ticket_extension_test.c @@ -0,0 +1,190 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_client_session_ticket.h" +#include "tls/s2n_resume.h" + +/* Normally the session ticket is set by receiving a + * new_session_ticket message. We'll just set it manually. + */ +static void s2n_set_test_ticket(struct s2n_connection *conn, const uint8_t *ticket_data, uint8_t ticket_data_len) +{ + EXPECT_SUCCESS(s2n_realloc(&conn->client_ticket, ticket_data_len)); + EXPECT_MEMCPY_SUCCESS(conn->client_ticket.data, ticket_data, ticket_data_len); +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + /* should_send */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* session ticket should NOT be sent if turned off */ + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, false)); + EXPECT_FALSE(s2n_client_session_ticket_extension.should_send(conn)); + + /* session ticket should be sent if turned on */ + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, true)); + EXPECT_TRUE(s2n_client_session_ticket_extension.should_send(conn)); + + /* session ticket should not be sent if TLS1.3 PSKs are being used */ + DEFER_CLEANUP(struct s2n_psk *test_psk = s2n_test_psk_new(conn), s2n_psk_free); + EXPECT_SUCCESS(s2n_connection_append_psk(conn, test_psk)); + EXPECT_FALSE(s2n_client_session_ticket_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, true)); + + const uint8_t test_ticket[S2N_TLS12_TICKET_SIZE_IN_BYTES] = "TICKET"; + + /* send */ + { + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + s2n_set_test_ticket(client_conn, test_ticket, s2n_array_len(test_ticket)); + + EXPECT_SUCCESS(s2n_client_session_ticket_extension.send(client_conn, &stuffer)); + + EXPECT_BYTEARRAY_EQUAL(stuffer.blob.data, test_ticket, s2n_array_len(test_ticket)); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + /* recv - decrypt ticket */ + { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + s2n_set_test_ticket(client_conn, test_ticket, S2N_TLS12_TICKET_SIZE_IN_BYTES); + EXPECT_SUCCESS(s2n_client_session_ticket_extension.send(client_conn, &stuffer)); + + EXPECT_EQUAL(server_conn->session_ticket_status, S2N_NO_TICKET); + EXPECT_SUCCESS(s2n_client_session_ticket_extension.recv(server_conn, &stuffer)); + EXPECT_EQUAL(server_conn->session_ticket_status, S2N_DECRYPT_TICKET); + EXPECT_BYTEARRAY_EQUAL(server_conn->client_ticket_to_decrypt.blob.data, + test_ticket, s2n_array_len(test_ticket)); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* recv - ignore extension if TLS1.3 */ + { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + s2n_set_test_ticket(client_conn, test_ticket, S2N_TLS12_TICKET_SIZE_IN_BYTES); + EXPECT_SUCCESS(s2n_client_session_ticket_extension.send(client_conn, &stuffer)); + + server_conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_client_session_ticket_extension.recv(server_conn, &stuffer)); + EXPECT_EQUAL(server_conn->session_ticket_status, S2N_NO_TICKET); + EXPECT_EQUAL(s2n_stuffer_data_available(&server_conn->client_ticket_to_decrypt), 0); + + server_conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_client_session_ticket_extension.recv(server_conn, &stuffer)); + EXPECT_EQUAL(server_conn->session_ticket_status, S2N_DECRYPT_TICKET); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* recv - ignore extension if not correct size */ + { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + s2n_set_test_ticket(client_conn, test_ticket, S2N_TLS12_TICKET_SIZE_IN_BYTES - 1); + EXPECT_SUCCESS(s2n_client_session_ticket_extension.send(client_conn, &stuffer)); + uint8_t extension_data = s2n_stuffer_data_available(&stuffer); + + EXPECT_SUCCESS(s2n_client_session_ticket_extension.recv(server_conn, &stuffer)); + EXPECT_EQUAL(server_conn->session_ticket_status, S2N_NO_TICKET); + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), extension_data); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* recv - ignore extension if tickets not allowed */ + { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + s2n_set_test_ticket(client_conn, test_ticket, S2N_TLS12_TICKET_SIZE_IN_BYTES); + EXPECT_SUCCESS(s2n_client_session_ticket_extension.send(client_conn, &stuffer)); + uint8_t extension_data = s2n_stuffer_data_available(&stuffer); + + /* ignore if tickets not on */ + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, false)); + EXPECT_SUCCESS(s2n_client_session_ticket_extension.recv(server_conn, &stuffer)); + EXPECT_EQUAL(server_conn->session_ticket_status, S2N_NO_TICKET); + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), extension_data); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, true)); + + /* ignore if client auth in use */ + EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_client_session_ticket_extension.recv(server_conn, &stuffer)); + EXPECT_EQUAL(server_conn->session_ticket_status, S2N_NO_TICKET); + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), extension_data); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_NONE)); + + /* When tickets on and client auth not in use, don't ignore */ + EXPECT_SUCCESS(s2n_client_session_ticket_extension.recv(server_conn, &stuffer)); + EXPECT_NOT_EQUAL(server_conn->session_ticket_status, S2N_NO_TICKET); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_client_signature_algorithms_extension_test.c b/tests/unit/s2n_client_signature_algorithms_extension_test.c new file mode 100644 index 00000000000..817e66b3188 --- /dev/null +++ b/tests/unit/s2n_client_signature_algorithms_extension_test.c @@ -0,0 +1,135 @@ +/* + * 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 "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_client_signature_algorithms.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_safety.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + + struct s2n_cert_chain_and_key *chain_and_key; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + /* Test should_send */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + + conn->actual_protocol_version = S2N_TLS10; + EXPECT_FALSE(s2n_client_signature_algorithms_extension.should_send(conn)); + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_TRUE(s2n_client_signature_algorithms_extension.should_send(conn)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_TRUE(s2n_client_signature_algorithms_extension.should_send(conn)); + + s2n_connection_free(conn); + }; + + /* Test that recv can parse send */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + + struct s2n_stuffer io = { 0 }; + s2n_stuffer_growable_alloc(&io, 0); + + EXPECT_SUCCESS(s2n_client_signature_algorithms_extension.send(client_conn, &io)); + EXPECT_SUCCESS(s2n_client_signature_algorithms_extension.recv(server_conn, &io)); + EXPECT_EQUAL(s2n_stuffer_data_available(&io), 0); + + EXPECT_TRUE(server_conn->handshake_params.client_sig_hash_algs.len > 0); + + s2n_stuffer_free(&io); + s2n_connection_free(client_conn); + s2n_connection_free(server_conn); + }; + + /* Test that unknown TLS_EXTENSION_SIGNATURE_ALGORITHMS values are ignored and negotiation fails */ + { + struct s2n_sig_scheme_list sig_hash_algs = { + .iana_list = { 0xFF01, 0xFFFF }, + .len = 2, + }; + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + struct s2n_stuffer signature_algorithms_extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_alloc(&signature_algorithms_extension, 2 + (sig_hash_algs.len * 2))); + POSIX_GUARD(s2n_stuffer_write_uint16(&signature_algorithms_extension, sig_hash_algs.len * 2)); + for (size_t i = 0; i < sig_hash_algs.len; i++) { + POSIX_GUARD(s2n_stuffer_write_uint16(&signature_algorithms_extension, sig_hash_algs.iana_list[i])); + } + + /* If only unknown algorithms are offered, expect choosing a scheme to fail for TLS1.3 */ + conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_client_signature_algorithms_extension.recv(conn, &signature_algorithms_extension)); + EXPECT_EQUAL(conn->handshake_params.client_sig_hash_algs.len, sig_hash_algs.len); + EXPECT_FAILURE(s2n_choose_sig_scheme_from_peer_preference_list(conn, &conn->handshake_params.client_sig_hash_algs, + &conn->handshake_params.server_cert_sig_scheme)); + + EXPECT_SUCCESS(s2n_stuffer_free(&signature_algorithms_extension)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test that a valid algorithm is chosen when it is offered among unknown algorithms */ + { + struct s2n_sig_scheme_list sig_hash_algs = { + .iana_list = { 0xFF01, 0xFFFF, TLS_SIGNATURE_SCHEME_RSA_PKCS1_SHA384 }, + .len = 3, + }; + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + POSIX_GUARD(s2n_connection_set_config(conn, config)); + conn->actual_protocol_version = S2N_TLS12; + + struct s2n_stuffer signature_algorithms_extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_alloc(&signature_algorithms_extension, 2 + (sig_hash_algs.len * 2))); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&signature_algorithms_extension, sig_hash_algs.len * 2)); + for (size_t i = 0; i < sig_hash_algs.len; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&signature_algorithms_extension, sig_hash_algs.iana_list[i])); + } + + /* If a valid algorithm is offered among unknown algorithms, the valid one should be chosen */ + EXPECT_SUCCESS(s2n_client_signature_algorithms_extension.recv(conn, &signature_algorithms_extension)); + EXPECT_EQUAL(conn->handshake_params.client_sig_hash_algs.len, sig_hash_algs.len); + EXPECT_SUCCESS(s2n_choose_sig_scheme_from_peer_preference_list(conn, &conn->handshake_params.client_sig_hash_algs, + &conn->handshake_params.server_cert_sig_scheme)); + EXPECT_EQUAL(conn->handshake_params.server_cert_sig_scheme->iana_value, TLS_SIGNATURE_SCHEME_RSA_PKCS1_SHA384); + + EXPECT_SUCCESS(s2n_stuffer_free(&signature_algorithms_extension)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_client_supported_groups_extension_test.c b/tests/unit/s2n_client_supported_groups_extension_test.c new file mode 100644 index 00000000000..d56921373be --- /dev/null +++ b/tests/unit/s2n_client_supported_groups_extension_test.c @@ -0,0 +1,536 @@ +/* + * 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 "pq-crypto/s2n_pq.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/extensions/s2n_client_supported_groups.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +int main() +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Test s2n_extension_should_send_if_ecc_enabled */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + /* ecc extensions are required for the default config */ + EXPECT_TRUE(s2n_client_supported_groups_extension.should_send(conn)); + + /* ecc extensions are NOT required for 20140601 */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "20140601")); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test send (with default KEM prefs = kem_preferences_null) */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + EXPECT_EQUAL(kem_pref, &kem_preferences_null); + + EXPECT_SUCCESS(s2n_client_supported_groups_extension.send(conn, &stuffer)); + + uint16_t length; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &length)); + EXPECT_EQUAL(length, s2n_stuffer_data_available(&stuffer)); + EXPECT_EQUAL(length, ecc_pref->count * sizeof(uint16_t)); + + uint16_t curve_id; + for (size_t i = 0; i < ecc_pref->count; i++) { + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &curve_id)); + EXPECT_EQUAL(curve_id, ecc_pref->ecc_curves[i]->iana_id); + } + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + { + /* Define various PQ security policies to test different configurations */ + + /* Kyber */ + const struct s2n_kem_group *test_kem_groups_kyber[] = { + &s2n_secp256r1_kyber_512_r3, + }; + const struct s2n_kem_preferences test_kem_prefs_kyber = { + .kem_count = 0, + .kems = NULL, + .tls13_kem_group_count = s2n_array_len(test_kem_groups_kyber), + .tls13_kem_groups = test_kem_groups_kyber, + }; + const struct s2n_security_policy test_pq_security_policy_kyber = { + .minimum_protocol_version = S2N_SSLv3, + .cipher_preferences = &cipher_preferences_test_all_tls13, + .kem_preferences = &test_kem_prefs_kyber, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20200310, + }; + + /* Test send with TLS 1.3 KEM groups */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + conn->security_policy_override = &test_pq_security_policy_kyber; + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + + EXPECT_SUCCESS(s2n_client_supported_groups_extension.send(conn, &stuffer)); + + uint16_t length; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &length)); + uint16_t expected_length = ecc_pref->count * sizeof(uint16_t); + if (s2n_pq_is_enabled()) { + expected_length += kem_pref->tls13_kem_group_count * sizeof(uint16_t); + } + EXPECT_EQUAL(length, s2n_stuffer_data_available(&stuffer)); + EXPECT_EQUAL(length, expected_length); + + if (s2n_pq_is_enabled()) { + uint16_t kem_id; + for (size_t i = 0; i < kem_pref->tls13_kem_group_count; i++) { + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &kem_id)); + EXPECT_EQUAL(kem_id, kem_pref->tls13_kem_groups[i]->iana_id); + } + } + + uint16_t curve_id; + for (size_t i = 0; i < ecc_pref->count; i++) { + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &curve_id)); + EXPECT_EQUAL(curve_id, ecc_pref->ecc_curves[i]->iana_id); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + /* Test that send does not send KEM group IDs for versions != TLS 1.3 */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_EQUAL(s2n_connection_get_protocol_version(conn), S2N_TLS12); + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + conn->security_policy_override = &test_pq_security_policy_kyber; + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + + EXPECT_SUCCESS(s2n_client_supported_groups_extension.send(conn, &stuffer)); + + uint16_t length; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &length)); + EXPECT_EQUAL(length, s2n_stuffer_data_available(&stuffer)); + EXPECT_EQUAL(length, ecc_pref->count * sizeof(uint16_t)); + + uint16_t curve_id; + for (size_t i = 0; i < ecc_pref->count; i++) { + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &curve_id)); + EXPECT_EQUAL(curve_id, ecc_pref->ecc_curves[i]->iana_id); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + /* Test recv - in each case, the security policy overrides allow for a successful PQ handshake */ + { +#define NUM_PQ_TEST_POLICY_OVERRIDES 1 + /* Security policy overrides: {client_policy, server_policy} */ + const struct s2n_security_policy *test_policy_overrides[NUM_PQ_TEST_POLICY_OVERRIDES][2] = { + /* Client sends Kyber; server supports Kyber */ + { &test_pq_security_policy_kyber, &test_pq_security_policy_kyber }, + + }; + /* Expected KEM group to be negotiated - corresponds to test_policy_overrides array */ + const struct s2n_kem_group *expected_negotiated_kem_group[NUM_PQ_TEST_POLICY_OVERRIDES] = { + &s2n_secp256r1_kyber_512_r3, + }; + + for (size_t i = 0; i < NUM_PQ_TEST_POLICY_OVERRIDES; i++) { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->security_policy_override = test_policy_overrides[i][0]; + + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_CLIENT)); + server_conn->security_policy_override = test_policy_overrides[i][1]; + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + const struct s2n_ecc_preferences *server_ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &server_ecc_pref)); + EXPECT_NOT_NULL(server_ecc_pref); + + const struct s2n_kem_preferences *server_kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(server_conn, &server_kem_pref)); + EXPECT_NOT_NULL(server_kem_pref); + + EXPECT_SUCCESS(s2n_client_supported_groups_extension.send(client_conn, &stuffer)); + + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_params.kem); + + EXPECT_SUCCESS(s2n_client_supported_groups_extension.recv(server_conn, &stuffer)); + + /* If PQ is disabled, s2n_client_supported_groups_extension.send will not have sent PQ IDs */ + if (!s2n_pq_is_enabled()) { + EXPECT_EQUAL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve, server_ecc_pref->ecc_curves[0]); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_params.kem); + + for (size_t j = 0; j < server_kem_pref->tls13_kem_group_count; j++) { + EXPECT_NULL(server_conn->kex_params.mutually_supported_kem_groups[j]); + } + } else { + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_EQUAL(server_conn->kex_params.server_kem_group_params.kem_group, expected_negotiated_kem_group[i]); + EXPECT_EQUAL(server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve, expected_negotiated_kem_group[i]->curve); + EXPECT_EQUAL(server_conn->kex_params.server_kem_group_params.kem_params.kem, expected_negotiated_kem_group[i]->kem); + } + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + }; + /* Test recv - in each case, the security policy overrides do not allow for a successful PQ handshake, + * so ECC should be chosen */ + { +#define NUM_MISMATCH_PQ_TEST_POLICY_OVERRIDES 3 + /* Security policy overrides: {client_policy, server_policy} */ + const struct s2n_security_policy *test_policy_overrides[NUM_MISMATCH_PQ_TEST_POLICY_OVERRIDES][2] = { + /* Client sends Kyber; server supports only ECC */ + { &test_pq_security_policy_kyber, NULL }, + /* Client sends only ECC ; server supports ECC and Kyber */ + { NULL, &test_pq_security_policy_kyber }, + /* Client sends only ECC; server supports only ECC */ + { NULL, NULL } + }; + + for (size_t i = 0; i < NUM_MISMATCH_PQ_TEST_POLICY_OVERRIDES; i++) { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->security_policy_override = test_policy_overrides[i][0]; + + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_CLIENT)); + server_conn->security_policy_override = test_policy_overrides[i][1]; + + const struct s2n_ecc_preferences *server_ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &server_ecc_pref)); + EXPECT_NOT_NULL(server_ecc_pref); + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_client_supported_groups_extension.send(client_conn, &stuffer)); + + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_params.kem); + + EXPECT_SUCCESS(s2n_client_supported_groups_extension.recv(server_conn, &stuffer)); + + EXPECT_EQUAL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve, server_ecc_pref->ecc_curves[0]); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_params.kem); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + } + + /* Test recv - client sends exclusively unrecognized groups */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_CLIENT)); + server_conn->security_policy_override = &test_pq_security_policy_kyber; + + /* Manually craft a supported_groups extension with bogus IDs */ + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + struct s2n_stuffer_reservation group_list_len = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&stuffer, &group_list_len)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, 100)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, 101)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, 102)); + POSIX_GUARD(s2n_stuffer_write_vector_size(&group_list_len)); + + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_params.kem); + + EXPECT_SUCCESS(s2n_client_supported_groups_extension.recv(server_conn, &stuffer)); + + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_params.kem); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* Test recv - server doesn't recognize PQ group IDs when TLS 1.3 is disabled */ + { + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_EQUAL(s2n_connection_get_protocol_version(client_conn), S2N_TLS12); + client_conn->security_policy_override = &test_pq_security_policy_kyber; + + const struct s2n_ecc_preferences *client_ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(client_conn, &client_ecc_pref)); + EXPECT_NOT_NULL(client_ecc_pref); + + const struct s2n_kem_preferences *client_kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(client_conn, &client_kem_pref)); + EXPECT_NOT_NULL(client_kem_pref); + + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_CLIENT)); + server_conn->security_policy_override = &test_pq_security_policy_kyber; + + /* Manually craft a supported_groups extension with one PQ ID and one ECC ID, because + * s2n_client_supported_groups_extension.send will ignore PQ IDs when TLS 1.3 is disabled */ + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + struct s2n_stuffer_reservation group_list_len = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&stuffer, &group_list_len)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, client_kem_pref->tls13_kem_groups[0]->iana_id)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, client_ecc_pref->ecc_curves[0]->iana_id)); + POSIX_GUARD(s2n_stuffer_write_vector_size(&group_list_len)); + + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_params.kem); + + EXPECT_SUCCESS(s2n_client_supported_groups_extension.recv(server_conn, &stuffer)); + + EXPECT_EQUAL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve, client_ecc_pref->ecc_curves[0]); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_params.kem); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test recv - server doesn't recognize PQ group IDs when PQ is disabled */ + { + if (!s2n_pq_is_enabled()) { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->security_policy_override = &test_pq_security_policy_kyber; + + const struct s2n_ecc_preferences *client_ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(client_conn, &client_ecc_pref)); + EXPECT_NOT_NULL(client_ecc_pref); + + const struct s2n_kem_preferences *client_kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(client_conn, &client_kem_pref)); + EXPECT_NOT_NULL(client_kem_pref); + + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_CLIENT)); + server_conn->security_policy_override = &test_pq_security_policy_kyber; + + /* Manually craft a supported_groups extension with one PQ ID and one ECC ID, because + * s2n_client_supported_groups_extension.send will ignore PQ IDs when PQ is disabled */ + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + struct s2n_stuffer_reservation group_list_len = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&stuffer, &group_list_len)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, client_kem_pref->tls13_kem_groups[0]->iana_id)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, client_ecc_pref->ecc_curves[0]->iana_id)); + POSIX_GUARD(s2n_stuffer_write_vector_size(&group_list_len)); + + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_params.kem); + + EXPECT_SUCCESS(s2n_client_supported_groups_extension.recv(server_conn, &stuffer)); + + EXPECT_EQUAL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve, client_ecc_pref->ecc_curves[0]); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_params.kem); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + }; + }; + + /* Test recv */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + EXPECT_SUCCESS(s2n_client_supported_groups_extension.send(conn, &stuffer)); + + EXPECT_NULL(conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_SUCCESS(s2n_client_supported_groups_extension.recv(conn, &stuffer)); + EXPECT_EQUAL(conn->kex_params.server_ecc_evp_params.negotiated_curve, ecc_pref->ecc_curves[0]); + EXPECT_NULL(conn->kex_params.server_kem_group_params.kem_group); + EXPECT_NULL(conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(conn->kex_params.server_kem_group_params.kem_params.kem); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test recv - no common curve */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + EXPECT_SUCCESS(s2n_client_supported_groups_extension.send(conn, &stuffer)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "null")); + + EXPECT_NULL(conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_SUCCESS(s2n_client_supported_groups_extension.recv(conn, &stuffer)); + EXPECT_NULL(conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_NULL(conn->kex_params.server_kem_group_params.kem_group); + EXPECT_NULL(conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(conn->kex_params.server_kem_group_params.kem_params.kem); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test recv - malformed extension */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + EXPECT_SUCCESS(s2n_client_supported_groups_extension.send(conn, &stuffer)); + EXPECT_SUCCESS(s2n_stuffer_wipe_n(&stuffer, 1)); + + EXPECT_NULL(conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_SUCCESS(s2n_client_supported_groups_extension.recv(conn, &stuffer)); + EXPECT_NULL(conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_NULL(conn->kex_params.server_kem_group_params.kem_group); + EXPECT_NULL(conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(conn->kex_params.server_kem_group_params.kem_params.kem); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + { + /* Test that unknown TLS_EXTENSION_SUPPORTED_GROUPS values are ignored */ + struct s2n_ecc_named_curve unsupported_curves[2] = { + { .iana_id = 0x0, .libcrypto_nid = 0, .name = 0x0, .share_size = 0 }, + { .iana_id = 0xFF01, .libcrypto_nid = 0, .name = 0x0, .share_size = 0 }, + }; + int ec_curves_count = s2n_array_len(unsupported_curves); + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + struct s2n_stuffer supported_groups_extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_alloc(&supported_groups_extension, 2 + ec_curves_count * 2)); + POSIX_GUARD(s2n_stuffer_write_uint16(&supported_groups_extension, ec_curves_count * 2)); + for (size_t i = 0; i < ec_curves_count; i++) { + POSIX_GUARD(s2n_stuffer_write_uint16(&supported_groups_extension, unsupported_curves[i].iana_id)); + } + + /* Force a bad value for the negotiated curve so we know extension was parsed and the curve was set to NULL */ + struct s2n_ecc_named_curve invalid_curve = { 0 }; + conn->kex_params.server_ecc_evp_params.negotiated_curve = &invalid_curve; + EXPECT_SUCCESS(s2n_client_supported_groups_extension.recv(conn, &supported_groups_extension)); + EXPECT_NULL(conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_NULL(conn->kex_params.server_kem_group_params.kem_group); + EXPECT_NULL(conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(conn->kex_params.server_kem_group_params.kem_params.kem); + + EXPECT_SUCCESS(s2n_stuffer_free(&supported_groups_extension)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_client_supported_versions_extension_test.c b/tests/unit/s2n_client_supported_versions_extension_test.c new file mode 100644 index 00000000000..cd4d7b6a4b0 --- /dev/null +++ b/tests/unit/s2n_client_supported_versions_extension_test.c @@ -0,0 +1,450 @@ +/* + * 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 "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_client_supported_versions.h" +#include "tls/s2n_alerts.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +#define PROTOCOL_VERSION_ALERT 70 +#define GREASED_SUPPORTED_VERSION_EXTENSION_VALUES 0x0A0A, 0x1A1A, 0x2A2A, 0x3A3A, 0x4A4A, 0x5A5A, 0x6A6A, 0x7A7A, 0x8A8A, 0x9A9A, 0xAAAA, 0xBABA, 0xCACA, 0xDADA, 0xEAEA, 0xFAFA + +int write_test_supported_versions_list(struct s2n_stuffer *list, uint8_t *supported_versions, uint8_t length) +{ + POSIX_GUARD(s2n_stuffer_write_uint8(list, length * S2N_TLS_PROTOCOL_VERSION_LEN)); + + for (size_t i = 0; i < length; i++) { + POSIX_GUARD(s2n_stuffer_write_uint8(list, supported_versions[i] / 10)); + POSIX_GUARD(s2n_stuffer_write_uint8(list, supported_versions[i] % 10)); + } + + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + uint8_t latest_version = S2N_TLS13; + + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + + const struct s2n_security_policy *security_policy_with_tls13_and_earlier = &security_policy_20190801; + EXPECT_TRUE(s2n_security_policy_supports_tls13(security_policy_with_tls13_and_earlier)); + EXPECT_EQUAL(security_policy_with_tls13_and_earlier->minimum_protocol_version, S2N_TLS10); + + /* Client offers all supported versions in version list */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + conn->security_policy_override = security_policy_with_tls13_and_earlier; + + struct s2n_stuffer extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension, 0)); + EXPECT_SUCCESS(s2n_client_supported_versions_extension.send(conn, &extension)); + + /* Total supported versions. + * If the "+1" looks wrong, consider what would happen if latest_version == S2N_TLS10. */ + size_t supported_versions = (latest_version - S2N_TLS10) + 1; + + /* Check extension contains enough versions */ + uint8_t version_list_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&extension, &version_list_size)); + EXPECT_EQUAL(version_list_size, S2N_TLS_PROTOCOL_VERSION_LEN * supported_versions); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + }; + + /* Client doesn't offer security_policy_override = security_policy_with_tls13_and_earlier; + + struct s2n_stuffer extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension, 0)); + EXPECT_SUCCESS(s2n_client_supported_versions_extension.send(conn, &extension)); + + /* Check extension contains only one version */ + uint8_t version_list_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&extension, &version_list_size)); + EXPECT_EQUAL(version_list_size, S2N_TLS_PROTOCOL_VERSION_LEN); + + /* Check single version is TLS1.3 */ + uint16_t version = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&extension, &version)); + EXPECT_EQUAL(version, 0x0304); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + } + + /* Client produces a version list that the server can parse */ + { + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + int size_result = s2n_extensions_client_supported_versions_size(client_conn); + EXPECT_NOT_EQUAL(size_result, -1); + uint16_t expected_length = size_result - S2N_EXTENSION_TYPE_FIELD_LENGTH - S2N_EXTENSION_LENGTH_FIELD_LENGTH; + + struct s2n_stuffer extension = { 0 }; + s2n_stuffer_alloc(&extension, expected_length); + + EXPECT_SUCCESS(s2n_client_supported_versions_extension.send(client_conn, &extension)); + + /* Check that the size is correct */ + EXPECT_EQUAL(expected_length, s2n_stuffer_data_available(&extension)); + + /* Check that the server can process the version list */ + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* Server should negotiate the most recent version */ + EXPECT_SUCCESS(s2n_client_supported_versions_extension.recv(server_conn, &extension)); + EXPECT_EQUAL(server_conn->client_protocol_version, latest_version); + EXPECT_EQUAL(server_conn->server_protocol_version, latest_version); + EXPECT_EQUAL(server_conn->actual_protocol_version, latest_version); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + }; + + /* Server selects highest supported version shared by client */ + { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + uint8_t unsupported_client_version = 255; + uint8_t supported_version_list[] = { S2N_TLS11, S2N_TLS12, S2N_TLS13, unsupported_client_version }; + uint8_t supported_version_list_length = sizeof(supported_version_list); + + struct s2n_stuffer extension = { 0 }; + s2n_stuffer_alloc(&extension, supported_version_list_length * 2 + 1); + + EXPECT_SUCCESS(write_test_supported_versions_list(&extension, supported_version_list, + supported_version_list_length)); + + EXPECT_SUCCESS(s2n_client_supported_versions_extension.recv(server_conn, &extension)); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->server_protocol_version, latest_version); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + }; + + /* Server does not process the extension if using TLS1.2. */ + { + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + uint8_t unsupported_client_version = 255; + uint8_t supported_version_list[] = { S2N_TLS11, S2N_TLS12, S2N_TLS13, unsupported_client_version }; + uint8_t supported_version_list_length = sizeof(supported_version_list); + + struct s2n_stuffer extension = { 0 }; + s2n_stuffer_alloc(&extension, supported_version_list_length * 2 + 1); + + EXPECT_SUCCESS(write_test_supported_versions_list(&extension, supported_version_list, + supported_version_list_length)); + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + EXPECT_SUCCESS(s2n_client_supported_versions_extension.recv(server_conn, &extension)); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_UNKNOWN_PROTOCOL_VERSION); + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_UNKNOWN_PROTOCOL_VERSION); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + }; + + /* Server terminates connection if there are no supported version in the list */ + { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + uint16_t invalid_version_list[] = { 0x0020, 0x0021, 0x0403, 0x0305, 0x7a7a, 0x0201 }; + uint8_t invalid_version_list_length = s2n_array_len(invalid_version_list); + + struct s2n_stuffer extension = { 0 }; + s2n_stuffer_alloc(&extension, invalid_version_list_length * S2N_TLS_PROTOCOL_VERSION_LEN + 1); + + POSIX_GUARD(s2n_stuffer_write_uint8(&extension, invalid_version_list_length * S2N_TLS_PROTOCOL_VERSION_LEN)); + + for (size_t i = 0; i < invalid_version_list_length; i++) { + POSIX_GUARD(s2n_stuffer_write_uint16(&extension, invalid_version_list[i])); + } + + EXPECT_FAILURE_WITH_ERRNO(s2n_client_supported_versions_extension.recv(server_conn, &extension), + S2N_ERR_UNKNOWN_PROTOCOL_VERSION); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + }; + + /* Check grease values for the supported versions */ + { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + uint16_t grease_version_list[] = { 0x0304, GREASED_SUPPORTED_VERSION_EXTENSION_VALUES }; + uint8_t grease_version_list_length = s2n_array_len(grease_version_list); + + struct s2n_stuffer extension = { 0 }; + s2n_stuffer_alloc(&extension, grease_version_list_length * S2N_TLS_PROTOCOL_VERSION_LEN + 1); + + POSIX_GUARD(s2n_stuffer_write_uint8(&extension, grease_version_list_length * S2N_TLS_PROTOCOL_VERSION_LEN)); + + for (size_t i = 0; i < grease_version_list_length; i++) { + POSIX_GUARD(s2n_stuffer_write_uint16(&extension, grease_version_list[i])); + } + + EXPECT_SUCCESS(s2n_client_supported_versions_extension.recv(server_conn, &extension)); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + }; + + /* Server selects highest supported protocol among list of invalid protocols (that purposefully test our conversion methods) */ + { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + uint16_t invalid_version_list[] = { 0x0020, 0x0200, 0x0201, 0x0304, 0x0021, 0x0305, 0x0403, 0x7a7a }; + uint8_t invalid_version_list_length = s2n_array_len(invalid_version_list); + + struct s2n_stuffer extension = { 0 }; + s2n_stuffer_alloc(&extension, invalid_version_list_length * S2N_TLS_PROTOCOL_VERSION_LEN + 1); + + POSIX_GUARD(s2n_stuffer_write_uint8(&extension, invalid_version_list_length * S2N_TLS_PROTOCOL_VERSION_LEN)); + + for (size_t i = 0; i < invalid_version_list_length; i++) { + POSIX_GUARD(s2n_stuffer_write_uint16(&extension, invalid_version_list[i])); + } + + EXPECT_SUCCESS(s2n_client_supported_versions_extension.recv(server_conn, &extension)); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->server_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + }; + + /* Server alerts if no shared supported version found */ + { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + uint8_t supported_version_list[] = { S2N_SSLv3 }; + uint8_t supported_version_list_length = sizeof(supported_version_list); + + struct s2n_stuffer extension = { 0 }; + s2n_stuffer_alloc(&extension, supported_version_list_length * 2 + 1); + + EXPECT_SUCCESS(write_test_supported_versions_list(&extension, supported_version_list, + supported_version_list_length)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_client_supported_versions_extension.recv(server_conn, &extension), + S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + EXPECT_EQUAL(server_conn->reader_alert_out, PROTOCOL_VERSION_ALERT); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + }; + + /* Server alerts if supported version list is empty */ + { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + struct s2n_stuffer extension = { 0 }; + s2n_stuffer_alloc(&extension, 1); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension, 0)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_client_supported_versions_extension.recv(server_conn, &extension), + S2N_ERR_UNKNOWN_PROTOCOL_VERSION); + EXPECT_EQUAL(server_conn->reader_alert_out, PROTOCOL_VERSION_ALERT); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + }; + + /* Server alerts if version list size exceeds the extension size */ + { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + struct s2n_stuffer extension = { 0 }; + s2n_stuffer_alloc(&extension, 1); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension, 13)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_client_supported_versions_extension.recv(server_conn, &extension), S2N_ERR_BAD_MESSAGE); + EXPECT_EQUAL(server_conn->reader_alert_out, PROTOCOL_VERSION_ALERT); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + }; + + /* Server alerts if version list size is less than extension size */ + { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + struct s2n_stuffer extension = { 0 }; + s2n_stuffer_alloc(&extension, 5); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension, 2)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&extension, 0x0302)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&extension, 0x0303)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_client_supported_versions_extension.recv(server_conn, &extension), S2N_ERR_BAD_MESSAGE); + EXPECT_EQUAL(server_conn->reader_alert_out, PROTOCOL_VERSION_ALERT); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + }; + + /* Server alerts if version list size is odd */ + { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + struct s2n_stuffer extension = { 0 }; + s2n_stuffer_alloc(&extension, 4); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension, 3)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&extension, 0x0302)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension, 0x03)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_client_supported_versions_extension.recv(server_conn, &extension), S2N_ERR_BAD_MESSAGE); + EXPECT_EQUAL(server_conn->reader_alert_out, PROTOCOL_VERSION_ALERT); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + }; + + /* Since the supported_version extension replaces the version field + * in the client hello, for backwards compatibility the version field + * should be set to 1.2 even when a higher version is supported. */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_client_hello_send(conn)); + + struct s2n_stuffer client_hello = conn->handshake.io; + uint8_t version[2]; + s2n_stuffer_read_bytes(&client_hello, version, 2); + + EXPECT_EQUAL(version[0], 0x03); + EXPECT_EQUAL(version[1], 0x03); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /** + *= https://tools.ietf.org/rfc/rfc8446#appendix-D.2 + *= type=test + *# A TLS server can also receive a ClientHello indicating a version number smaller than its highest supported + *# version. If the "supported_versions" extension is present, the server MUST negotiate using that extension as + *# described in Section 4.2.1. + */ + if (s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, + S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config_with_cert = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config_with_cert, chain_and_key)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_with_cert)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_cert)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + struct s2n_stuffer *hello_stuffer = NULL; + hello_stuffer = &client_conn->handshake.io; + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + + /* Overwrite the Client Hello protocol version to TLS10 */ + uint8_t small_protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN] = { 0 }; + small_protocol_version[0] = S2N_TLS10 / 10; + small_protocol_version[1] = S2N_TLS10 % 10; + + EXPECT_SUCCESS(s2n_stuffer_rewrite(hello_stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(hello_stuffer, small_protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN)); + EXPECT_SUCCESS(s2n_stuffer_write(&server_conn->handshake.io, &hello_stuffer->blob)); + + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + /* The server does not use the protocol version in the Client Hello to set the actual protocol version. */ + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->client_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->client_hello_version, S2N_TLS10); + } + + EXPECT_SUCCESS(s2n_config_free(config)); + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_config_test.c b/tests/unit/s2n_config_test.c new file mode 100644 index 00000000000..9b97451f201 --- /dev/null +++ b/tests/unit/s2n_config_test.c @@ -0,0 +1,982 @@ +/* + * 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 "tls/s2n_config.h" + +#include + +#include "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_internal.h" +#include "tls/s2n_record.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls13.h" +#include "unstable/npn.h" + +static int s2n_test_select_psk_identity_callback(struct s2n_connection *conn, void *context, + struct s2n_offered_psk_list *psk_identity_list) +{ + return S2N_SUCCESS; +} + +static int s2n_test_reneg_req_cb(struct s2n_connection *conn, void *context, s2n_renegotiate_response *response) +{ + return S2N_SUCCESS; +} + +static int s2n_test_crl_lookup_cb(struct s2n_crl_lookup *lookup, void *context) +{ + return S2N_SUCCESS; +} + +static int s2n_test_cert_validation_cb(struct s2n_connection *conn, struct s2n_cert_validation_info *info, void *context) +{ + return S2N_SUCCESS; +} + +static int s2n_test_async_pkey_fn(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + const s2n_mode modes[] = { S2N_CLIENT, S2N_SERVER }; + + const struct s2n_security_policy *default_security_policy, *tls13_security_policy, *fips_security_policy; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("default_tls13", &tls13_security_policy)); + EXPECT_SUCCESS(s2n_find_security_policy_from_version("default_fips", &fips_security_policy)); + EXPECT_SUCCESS(s2n_find_security_policy_from_version("default", &default_security_policy)); + + char cert[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert, S2N_MAX_TEST_PEM_SIZE)); + char key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, key, S2N_MAX_TEST_PEM_SIZE)); + + /* Test: s2n_config_new and tls13_default_config match */ + { + struct s2n_config *config, *default_config; + + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_NOT_NULL(default_config = s2n_fetch_default_config()); + + /* s2n_config_new() matches s2n_fetch_default_config() */ + EXPECT_EQUAL(default_config->security_policy, config->security_policy); + EXPECT_EQUAL(default_config->security_policy->signature_preferences, config->security_policy->signature_preferences); + EXPECT_EQUAL(default_config->client_cert_auth_type, config->client_cert_auth_type); + + /* Calling s2n_fetch_default_config() repeatedly returns the same object */ + EXPECT_EQUAL(default_config, s2n_fetch_default_config()); + + /* TLS1.3 default does not match non-TLS1.3 default */ + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + EXPECT_NOT_EQUAL(default_config, s2n_fetch_default_config()); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Connections created with default configs */ + { + /* For TLS1.2 */ + if (!s2n_is_in_fips_mode()) { + struct s2n_connection *conn; + const struct s2n_security_policy *security_policy; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_EQUAL(conn->config, s2n_fetch_default_config()); + + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_EQUAL(security_policy, default_security_policy); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* For TLS1.3 */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + struct s2n_connection *conn; + const struct s2n_security_policy *security_policy; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_EQUAL(conn->config, s2n_fetch_default_config()); + + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_EQUAL(security_policy, tls13_security_policy); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* For fips */ + if (s2n_is_in_fips_mode()) { + struct s2n_connection *conn; + const struct s2n_security_policy *security_policy; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_EQUAL(conn->config, s2n_fetch_default_config()); + + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_EQUAL(security_policy, fips_security_policy); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + }; + + /* Test for s2n_config_new() and tls 1.3 behavior */ + { + if (!s2n_is_in_fips_mode()) { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_EQUAL(config->security_policy, default_security_policy); + EXPECT_EQUAL(config->security_policy->cipher_preferences, &cipher_preferences_20170210); + EXPECT_EQUAL(config->security_policy->kem_preferences, &kem_preferences_null); + EXPECT_EQUAL(config->security_policy->signature_preferences, &s2n_signature_preferences_20140601); + EXPECT_EQUAL(config->security_policy->ecc_preferences, &s2n_ecc_preferences_20140601); + EXPECT_SUCCESS(s2n_config_free(config)); + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_EQUAL(config->security_policy, tls13_security_policy); + EXPECT_EQUAL(config->security_policy->cipher_preferences, &cipher_preferences_20210831); + EXPECT_EQUAL(config->security_policy->kem_preferences, &kem_preferences_null); + EXPECT_EQUAL(config->security_policy->signature_preferences, &s2n_signature_preferences_20200207); + EXPECT_EQUAL(config->security_policy->ecc_preferences, &s2n_ecc_preferences_20200310); + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + }; + + /* Test setting the callback to select PSK identity */ + { + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + uint8_t context = 13; + + /* Safety check */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_psk_selection_callback( + NULL, s2n_test_select_psk_identity_callback, &context), + S2N_ERR_NULL); + EXPECT_NULL(config->psk_selection_cb); + EXPECT_NULL(config->psk_selection_ctx); + + EXPECT_SUCCESS(s2n_config_set_psk_selection_callback(config, s2n_test_select_psk_identity_callback, &context)); + EXPECT_EQUAL(config->psk_selection_cb, s2n_test_select_psk_identity_callback); + EXPECT_EQUAL(config->psk_selection_ctx, &context); + + EXPECT_SUCCESS(s2n_config_set_psk_selection_callback(config, NULL, NULL)); + EXPECT_NULL(config->psk_selection_cb); + EXPECT_NULL(config->psk_selection_ctx); + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /*Test s2n_connection_set_config */ + { + /* Test that tickets_to_send is set correctly */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + struct s2n_config *config; + uint8_t num_tickets = 1; + + EXPECT_NOT_NULL(config = s2n_config_new()); + + config->initial_tickets_to_send = num_tickets; + + EXPECT_EQUAL(conn->tickets_to_send, 0); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_EQUAL(conn->tickets_to_send, num_tickets); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test that PSK type is set correctly */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_EQUAL(config->psk_mode, S2N_PSK_MODE_RESUMPTION); + + /* Overrides connection value */ + { + conn->config = NULL; + conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_EQUAL(conn->psk_params.type, S2N_PSK_TYPE_RESUMPTION); + EXPECT_FALSE(conn->psk_mode_overridden); + }; + + /* Does not override connection value if conn->override_psk_mode set */ + { + conn->config = NULL; + conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; + conn->psk_mode_overridden = true; + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_EQUAL(conn->psk_params.type, S2N_PSK_TYPE_EXTERNAL); + EXPECT_TRUE(conn->psk_mode_overridden); + conn->psk_mode_overridden = false; + }; + + /* Does not override connection value if PSKs already set */ + { + conn->config = NULL; + DEFER_CLEANUP(struct s2n_psk *test_external_psk = s2n_test_psk_new(conn), s2n_psk_free); + EXPECT_SUCCESS(s2n_connection_append_psk(conn, test_external_psk)); + EXPECT_EQUAL(conn->psk_params.type, S2N_PSK_TYPE_EXTERNAL); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_EQUAL(conn->psk_params.type, S2N_PSK_TYPE_EXTERNAL); + EXPECT_FALSE(conn->psk_mode_overridden); + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + }; + + /* s2n_config_set_session_tickets_onoff */ + { + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_session_tickets_onoff(NULL, true), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_session_tickets_onoff(NULL, false), S2N_ERR_NULL); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, true)); + EXPECT_TRUE(config->use_tickets); + EXPECT_EQUAL(config->initial_tickets_to_send, 1); + + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, false)); + EXPECT_FALSE(config->use_tickets); + EXPECT_EQUAL(config->initial_tickets_to_send, 1); + + config->initial_tickets_to_send = 10; + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, true)); + EXPECT_TRUE(config->use_tickets); + EXPECT_EQUAL(config->initial_tickets_to_send, 10); + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* s2n_config_set_context */ + /* s2n_config_get_context */ + { + uint8_t context = 42; + uint8_t other = 123; + void *returned_context = NULL; + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + + EXPECT_SUCCESS(s2n_config_get_ctx(config, &returned_context)); + EXPECT_NULL(returned_context); + + EXPECT_SUCCESS(s2n_config_set_ctx(config, &context)); + EXPECT_SUCCESS(s2n_config_get_ctx(config, &returned_context)); + EXPECT_NOT_NULL(returned_context); + + EXPECT_EQUAL(*((uint8_t *) returned_context), context); + EXPECT_NOT_EQUAL(*((uint8_t *) returned_context), other); + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test s2n_config_set_extension_data */ + { + uint8_t extension_data[] = "extension data"; + + /* Test s2n_config_set_extension_data can be called for owned cert chains */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(config, cert, key)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + + EXPECT_SUCCESS(s2n_config_set_extension_data(config, S2N_EXTENSION_OCSP_STAPLING, + extension_data, sizeof(extension_data))); + EXPECT_EQUAL(s2n_config_get_single_default_cert(config)->ocsp_status.size, sizeof(extension_data)); + }; + + /* Test s2n_config_set_extension_data can't be called for unowned cert chains */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_extension_data(config, S2N_EXTENSION_OCSP_STAPLING, + extension_data, sizeof(extension_data)), + S2N_ERR_CERT_OWNERSHIP); + EXPECT_EQUAL(s2n_config_get_single_default_cert(config)->ocsp_status.size, 0); + EXPECT_EQUAL(chain->ocsp_status.size, 0); + }; + }; + + /* Test s2n_config_free_cert_chain_and_key */ + { + /* Chain owned by application */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + + /* No-op for application-owned chains */ + EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + + /* Still no-op if called again */ + EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + }; + + /* Chain owned by application and freed too early: + * This is arguably incorrect behavior, but did not cause errors in the past. + * We should continue to ensure it doesn't cause any errors. + */ + { + struct s2n_cert_chain_and_key *chain = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + + /* Free the chain early */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain)); + + /* No-op for application-owned chains */ + EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + + /* No-op if called again */ + EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + }; + + /* Chain owned by library */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(config, cert, key)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); + + EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); + EXPECT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_NOT_OWNED); + + /* No-op if called again */ + EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); + EXPECT_NULL(s2n_config_get_single_default_cert(config)); + }; + + /* Switch from library-owned certs to application-owned certs */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(config, cert, key)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); + + EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); + EXPECT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_NOT_OWNED); + + /* Now add an application-owned chain */ + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain)); + EXPECT_SUCCESS(s2n_config_free_cert_chain_and_key(config)); + }; + }; + + /* Test s2n_config_set_cert_chain_and_key_defaults */ + { + /* Succeeds if chains owned by app */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_1 = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_1, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_2 = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_2, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_1)); + EXPECT_EQUAL(s2n_config_get_single_default_cert(config), chain_1); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + + EXPECT_SUCCESS(s2n_config_set_cert_chain_and_key_defaults(config, &chain_2, 1)); + EXPECT_EQUAL(s2n_config_get_single_default_cert(config), chain_2); + EXPECT_EQUAL(config->cert_ownership, S2N_APP_OWNED); + }; + + /* Fails if chains owned by library */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key(config, cert, key)); + EXPECT_NOT_NULL(s2n_config_get_single_default_cert(config)); + EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); + + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_cert_chain_and_key_defaults( + config, &chain, 1), + S2N_ERR_CERT_OWNERSHIP); + }; + }; + + /* Test s2n_config_set_send_buffer_size */ + { + /* Safety */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + EXPECT_EQUAL(config->send_buffer_size_override, 0); + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_send_buffer_size(NULL, S2N_MIN_SEND_BUFFER_SIZE), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_send_buffer_size(config, 0), S2N_ERR_INVALID_ARGUMENT); + EXPECT_EQUAL(config->send_buffer_size_override, 0); + }; + + /* Default applied to connection */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_EQUAL(config->send_buffer_size_override, 0); + EXPECT_FALSE(conn->multirecord_send); + }; + + /* Custom applied to connection */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, S2N_MIN_SEND_BUFFER_SIZE)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_EQUAL(config->send_buffer_size_override, S2N_MIN_SEND_BUFFER_SIZE); + EXPECT_TRUE(conn->multirecord_send); + }; + }; + + /* Test s2n_config_set_verify_after_sign */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_FALSE(config->verify_after_sign); + + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_verify_after_sign(NULL, S2N_VERIFY_AFTER_SIGN_ENABLED), S2N_ERR_NULL); + + /* Invalid mode */ + config->verify_after_sign = true; + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_verify_after_sign(config, UINT8_MAX), S2N_ERR_INVALID_ARGUMENT); + EXPECT_TRUE(config->verify_after_sign); + config->verify_after_sign = false; + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_verify_after_sign(config, UINT8_MAX), S2N_ERR_INVALID_ARGUMENT); + EXPECT_FALSE(config->verify_after_sign); + + /* Set and unset */ + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(config, S2N_VERIFY_AFTER_SIGN_ENABLED)); + EXPECT_TRUE(config->verify_after_sign); + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(config, S2N_VERIFY_AFTER_SIGN_DISABLED)); + EXPECT_FALSE(config->verify_after_sign); + }; + + /* Test s2n_config_set_renegotiate_request_cb */ + { + uint8_t context = 0; + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + /* Unset by default */ + EXPECT_EQUAL(config->renegotiate_request_cb, NULL); + EXPECT_EQUAL(config->renegotiate_request_ctx, NULL); + + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_renegotiate_request_cb(NULL, s2n_test_reneg_req_cb, &context), S2N_ERR_NULL); + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, NULL, &context)); + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, s2n_test_reneg_req_cb, NULL)); + + /* Set */ + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, s2n_test_reneg_req_cb, &context)); + EXPECT_EQUAL(config->renegotiate_request_cb, s2n_test_reneg_req_cb); + EXPECT_EQUAL(config->renegotiate_request_ctx, &context); + + /* Unset */ + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, NULL, NULL)); + EXPECT_EQUAL(config->renegotiate_request_cb, NULL); + EXPECT_EQUAL(config->renegotiate_request_ctx, NULL); + }; + + /* Test s2n_config_set_npn */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_FALSE(config->npn_supported); + + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_npn(NULL, true), S2N_ERR_NULL); + + /* Set and unset */ + EXPECT_SUCCESS(s2n_config_set_npn(config, true)); + EXPECT_TRUE(config->npn_supported); + EXPECT_SUCCESS(s2n_config_set_npn(config, false)); + EXPECT_FALSE(config->npn_supported); + }; + + /* Test s2n_config_set_crl_lookup_cb */ + { + uint8_t context = 0; + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + /* Unset by default */ + EXPECT_EQUAL(config->crl_lookup_cb, NULL); + EXPECT_EQUAL(config->crl_lookup_ctx, NULL); + + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_crl_lookup_cb(NULL, s2n_test_crl_lookup_cb, &context), S2N_ERR_NULL); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, NULL, &context)); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, s2n_test_crl_lookup_cb, NULL)); + + /* Set */ + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, s2n_test_crl_lookup_cb, &context)); + EXPECT_EQUAL(config->crl_lookup_cb, s2n_test_crl_lookup_cb); + EXPECT_EQUAL(config->crl_lookup_ctx, &context); + + /* Unset */ + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, NULL, NULL)); + EXPECT_EQUAL(config->crl_lookup_cb, NULL); + EXPECT_EQUAL(config->crl_lookup_ctx, NULL); + }; + + /* Test s2n_config_set_cert_validation_cb */ + { + uint8_t context = 0; + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + /* Unset by default */ + EXPECT_EQUAL(config->cert_validation_cb, NULL); + EXPECT_EQUAL(config->cert_validation_ctx, NULL); + + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_cert_validation_cb(NULL, s2n_test_cert_validation_cb, &context), + S2N_ERR_NULL); + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, NULL, &context)); + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_cb, NULL)); + + /* Set */ + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, s2n_test_cert_validation_cb, &context)); + EXPECT_EQUAL(config->cert_validation_cb, s2n_test_cert_validation_cb); + EXPECT_EQUAL(config->cert_validation_ctx, &context); + + /* Unset */ + EXPECT_SUCCESS(s2n_config_set_cert_validation_cb(config, NULL, NULL)); + EXPECT_EQUAL(config->cert_validation_cb, NULL); + EXPECT_EQUAL(config->cert_validation_ctx, NULL); + }; + + /* Test s2n_config_set_status_request_type */ + for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { + s2n_mode mode = modes[mode_i]; + + if (!s2n_x509_ocsp_stapling_supported()) { + break; + } + + /* request_ocsp_status should be false by default */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_FALSE(config->ocsp_status_requested_by_user); + EXPECT_FALSE(config->ocsp_status_requested_by_s2n); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(mode), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_FALSE(conn->request_ocsp_status); + }; + + /* request_ocsp_status should be true if set via s2n_config_set_status_request_type */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_OCSP)); + EXPECT_TRUE(config->ocsp_status_requested_by_user); + EXPECT_FALSE(config->ocsp_status_requested_by_s2n); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(mode), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_TRUE(conn->request_ocsp_status); + }; + + /* ocsp_status_requested_by_s2n can be set in s2n_config_set_verification_ca_location. For + * backwards compatibility, this should tell clients to request OCSP stapling. However, this + * API should not tell servers to request OCSP stapling. + */ + for (int api_configuration_i = 0; api_configuration_i < 3; api_configuration_i++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + switch (api_configuration_i) { + case 0: + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + break; + case 1: + /* If a user intentionally disables OCSP stapling, s2n_config_set_verification_ca_location + * should not re-enable it for servers. + */ + EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_NONE)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + break; + default: + EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_OCSP)); + EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_NONE)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + break; + } + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(mode), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + if (mode == S2N_CLIENT) { + EXPECT_TRUE(conn->request_ocsp_status); + } else { + EXPECT_FALSE(conn->request_ocsp_status); + } + }; + + /* Calling s2n_config_set_status_request_type with S2N_STATUS_REQUEST_OCSP should enable OCSP + * status requests, regardless of s2n_config_set_verification_ca_location. + */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_OCSP)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(mode), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_TRUE(conn->request_ocsp_status); + }; + + /* Calling s2n_config_set_status_request_type with S2N_STATUS_REQUEST_NONE should disable OCSP + * status requests, regardless of s2n_config_set_verification_ca_location. + */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_NONE)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(mode), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_FALSE(conn->request_ocsp_status); + }; + }; + + /* Test s2n_config_add_cert_chain */ + { + uint32_t pem_len = 0; + uint8_t pem_bytes[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, + pem_bytes, &pem_len, sizeof(pem_bytes))); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain(config, pem_bytes, pem_len)); + EXPECT_TRUE(config->no_signing_key); + EXPECT_EQUAL(config->cert_ownership, S2N_LIB_OWNED); + + struct s2n_cert_chain_and_key *chain = s2n_config_get_single_default_cert(config); + POSIX_ENSURE_REF(chain); + EXPECT_FAILURE(s2n_pkey_check_key_exists(chain->private_key)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_set_config(conn, config), S2N_ERR_NO_PRIVATE_KEY); + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(config, s2n_test_async_pkey_fn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + }; + + /* Test loading system certs */ + { + /* s2n_config_load_system_certs safety */ + { + EXPECT_FAILURE_WITH_ERRNO(s2n_config_load_system_certs(NULL), S2N_ERR_NULL); + } + + /* s2n_config_new_minimal should not load system certs */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_NULL(config->trust_store.trust_store); + EXPECT_FALSE(config->trust_store.loaded_system_certs); + + /* System certs can be loaded onto the minimal config */ + EXPECT_SUCCESS(s2n_config_load_system_certs(config)); + EXPECT_NOT_NULL(config->trust_store.trust_store); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + + /* Attempting to load system certs multiple times on the same config should error */ + for (int i = 0; i < 20; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_config_load_system_certs(config), S2N_ERR_X509_TRUST_STORE); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + } + } + + /* s2n_config_new should load system certs */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_NOT_NULL(config->trust_store.trust_store); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + + /* Attempting to load system certs multiple times on the same config should error */ + for (int i = 0; i < 20; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_config_load_system_certs(config), S2N_ERR_X509_TRUST_STORE); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + } + } + + /* The default config should load system certs */ + { + struct s2n_config *config = s2n_fetch_default_config(); + EXPECT_NOT_NULL(config); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + } + + /* System certs can be loaded again after wiping the trust store */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + for (int i = 0; i < 20; i++) { + /* System certs were already loaded, so an attempt to load them should fail */ + EXPECT_NOT_NULL(config->trust_store.trust_store); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + EXPECT_FAILURE_WITH_ERRNO(s2n_config_load_system_certs(config), S2N_ERR_X509_TRUST_STORE); + + EXPECT_SUCCESS(s2n_config_wipe_trust_store(config)); + + /* The trust store is cleared after a wipe, so it should be possible to load system certs again */ + EXPECT_FALSE(config->trust_store.loaded_system_certs); + EXPECT_SUCCESS(s2n_config_load_system_certs(config)); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + } + } + + /* Ensure that system certs are properly loaded into the X509_STORE. + * + * The API used to retrieve the contents of an X509_STORE, X509_STORE_get0_objects, + * wasn't added until OpenSSL 1.1.0. + */ +#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_NULL(config->trust_store.trust_store); + EXPECT_FALSE(config->trust_store.loaded_system_certs); + + /* Initialize the X509_STORE by adding a cert */ + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&config->trust_store, S2N_RSA_PSS_2048_SHA256_CA_CERT, NULL)); + EXPECT_NOT_NULL(config->trust_store.trust_store); + EXPECT_FALSE(config->trust_store.loaded_system_certs); + + /* The X509_STORE should only contain the single cert that was added. */ + STACK_OF(X509_OBJECT) *x509_store_contents = X509_STORE_get0_objects(config->trust_store.trust_store); + EXPECT_NOT_NULL(x509_store_contents); + int initial_contents_count = sk_X509_OBJECT_num(x509_store_contents); + EXPECT_EQUAL(initial_contents_count, 1); + + /* Override the system cert file to guarantee that a system cert will be loaded */ + EXPECT_SUCCESS(setenv("SSL_CERT_FILE", S2N_SHA1_ROOT_SIGNATURE_CA_CERT, 1)); + + /* Load the system cert into the store */ + EXPECT_SUCCESS(s2n_config_load_system_certs(config)); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + int system_certs_contents_count = sk_X509_OBJECT_num(x509_store_contents); + + /* LibreSSL doesn't use the SSL_CERT_FILE environment variable to set the system cert location, + * so we don't know how many system certs will be loaded, if any. + */ + if (!s2n_libcrypto_is_libressl()) { + EXPECT_EQUAL(system_certs_contents_count, initial_contents_count + 1); + } + + /* Additional calls to s2n_config_load_default_certs should not add additional certs to the store */ + for (int i = 0; i < 20; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_config_load_system_certs(config), S2N_ERR_X509_TRUST_STORE); + EXPECT_TRUE(config->trust_store.loaded_system_certs); + int additional_call_contents_count = sk_X509_OBJECT_num(x509_store_contents); + EXPECT_TRUE(additional_call_contents_count == system_certs_contents_count); + } + + EXPECT_SUCCESS(unsetenv("SSL_CERT_FILE")); + } +#endif + + /* Self-talk tests */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* Ensure a handshake succeeds with a minimal server config and no mutual auth */ + { + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default")); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "s2nTestServer")); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_FALSE(server_config->trust_store.loaded_system_certs); + EXPECT_NULL(server_config->trust_store.trust_store); + + EXPECT_FALSE(client_config->trust_store.loaded_system_certs); + EXPECT_NOT_NULL(client_config->trust_store.trust_store); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } + + /* Ensure a handshake fails gracefully with an uninitialized trust store */ + { + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new_minimal(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default")); + EXPECT_NOT_NULL(client_config); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_FALSE(server_config->trust_store.loaded_system_certs); + EXPECT_NULL(server_config->trust_store.trust_store); + + EXPECT_FALSE(client_config->trust_store.loaded_system_certs); + EXPECT_NULL(client_config->trust_store.trust_store); + + /* The client should fail to validate the server's certificate without an initialized trust store */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_CERT_UNTRUSTED); + } + } + } + + /* s2n_config_disable_x509_time_verification tests */ + { + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_disable_x509_time_verification(NULL), S2N_ERR_NULL); + + /* Ensure s2n_config_disable_x509_time_verification sets the proper state */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_EQUAL(config->disable_x509_time_validation, false); + + EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(config)); + EXPECT_EQUAL(config->disable_x509_time_validation, true); + } + } + + END_TEST(); +} diff --git a/tests/unit/s2n_connection_context_test.c b/tests/unit/s2n_connection_context_test.c new file mode 100644 index 00000000000..5bc04d4003c --- /dev/null +++ b/tests/unit/s2n_connection_context_test.c @@ -0,0 +1,47 @@ +/* + * 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 "api/s2n.h" +#include "s2n_test.h" + +int main(int argc, char **argv) +{ + struct s2n_connection *conn; + int ctx; + + struct s2n_connection *conn_null = NULL; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + /* Verify that we can set and get ctx */ + EXPECT_SUCCESS(s2n_connection_set_ctx(conn, &ctx)); + EXPECT_EQUAL(s2n_connection_get_ctx(conn), &ctx); + + /* Verify that conext is cleaned up after wipe */ + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_EQUAL(s2n_connection_get_ctx(conn), NULL); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + + /* Verify that we don't assume nonnull input and seg fault */ + EXPECT_NULL(s2n_connection_get_cipher(conn_null)); + + END_TEST(); +} diff --git a/tests/unit/s2n_connection_preferences_test.c b/tests/unit/s2n_connection_preferences_test.c new file mode 100644 index 00000000000..d56c15ddf05 --- /dev/null +++ b/tests/unit/s2n_connection_preferences_test.c @@ -0,0 +1,296 @@ +/* + * 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 "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls13.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + const struct s2n_security_policy *default_security_policy, *tls13_security_policy, *fips_security_policy; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("default_tls13", &tls13_security_policy)); + EXPECT_SUCCESS(s2n_find_security_policy_from_version("default_fips", &fips_security_policy)); + EXPECT_SUCCESS(s2n_find_security_policy_from_version("default", &default_security_policy)); + + /* Test default TLS1.2 */ + if (!s2n_is_in_fips_mode()) { + struct s2n_connection *conn = NULL; + const struct s2n_cipher_preferences *cipher_preferences = NULL; + const struct s2n_security_policy *security_policy = NULL; + const struct s2n_kem_preferences *kem_preferences = NULL; + const struct s2n_signature_preferences *signature_preferences = NULL; + const struct s2n_ecc_preferences *ecc_preferences = NULL; + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NULL(conn->security_policy_override); + + EXPECT_SUCCESS(s2n_connection_get_cipher_preferences(conn, &cipher_preferences)); + EXPECT_EQUAL(cipher_preferences, default_security_policy->cipher_preferences); + + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_EQUAL(security_policy, default_security_policy); + + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(conn, &kem_preferences)); + EXPECT_EQUAL(kem_preferences, default_security_policy->kem_preferences); + + EXPECT_SUCCESS(s2n_connection_get_signature_preferences(conn, &signature_preferences)); + EXPECT_EQUAL(signature_preferences, default_security_policy->signature_preferences); + + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + EXPECT_EQUAL(ecc_preferences, default_security_policy->ecc_preferences); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "20170328")); + EXPECT_NOT_NULL(conn->security_policy_override); + + cipher_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_cipher_preferences(conn, &cipher_preferences)); + EXPECT_EQUAL(cipher_preferences, security_policy_20170328.cipher_preferences); + + security_policy = NULL; + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_EQUAL(security_policy, &security_policy_20170328); + + kem_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(conn, &kem_preferences)); + EXPECT_EQUAL(kem_preferences, security_policy_20170328.kem_preferences); + + signature_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_signature_preferences(conn, &signature_preferences)); + EXPECT_EQUAL(signature_preferences, security_policy_20170328.signature_preferences); + + ecc_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + EXPECT_EQUAL(ecc_preferences, security_policy_20170328.ecc_preferences); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Test TLS1.3 */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + struct s2n_connection *conn = NULL; + const struct s2n_cipher_preferences *cipher_preferences = NULL; + const struct s2n_security_policy *security_policy = NULL; + const struct s2n_kem_preferences *kem_preferences = NULL; + const struct s2n_signature_preferences *signature_preferences = NULL; + const struct s2n_ecc_preferences *ecc_preferences = NULL; + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NULL(conn->security_policy_override); + + EXPECT_SUCCESS(s2n_connection_get_cipher_preferences(conn, &cipher_preferences)); + EXPECT_EQUAL(cipher_preferences, tls13_security_policy->cipher_preferences); + + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_EQUAL(security_policy, tls13_security_policy); + + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(conn, &kem_preferences)); + EXPECT_EQUAL(kem_preferences, tls13_security_policy->kem_preferences); + + EXPECT_SUCCESS(s2n_connection_get_signature_preferences(conn, &signature_preferences)); + EXPECT_EQUAL(signature_preferences, tls13_security_policy->signature_preferences); + + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + EXPECT_EQUAL(ecc_preferences, tls13_security_policy->ecc_preferences); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all_tls13")); + EXPECT_NOT_NULL(conn->security_policy_override); + + cipher_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_cipher_preferences(conn, &cipher_preferences)); + EXPECT_EQUAL(cipher_preferences, security_policy_test_all_tls13.cipher_preferences); + + security_policy = NULL; + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_EQUAL(security_policy, &security_policy_test_all_tls13); + + kem_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(conn, &kem_preferences)); + EXPECT_EQUAL(kem_preferences, security_policy_test_all_tls13.kem_preferences); + + signature_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_signature_preferences(conn, &signature_preferences)); + EXPECT_EQUAL(signature_preferences, security_policy_test_all_tls13.signature_preferences); + + ecc_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + EXPECT_EQUAL(ecc_preferences, security_policy_test_all_tls13.ecc_preferences); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* Test default fips */ + + if (s2n_is_in_fips_mode()) { + struct s2n_connection *conn = NULL; + const struct s2n_cipher_preferences *cipher_preferences = NULL; + const struct s2n_security_policy *security_policy = NULL; + const struct s2n_kem_preferences *kem_preferences = NULL; + const struct s2n_signature_preferences *signature_preferences = NULL; + const struct s2n_ecc_preferences *ecc_preferences = NULL; + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NULL(conn->security_policy_override); + + EXPECT_SUCCESS(s2n_connection_get_cipher_preferences(conn, &cipher_preferences)); + EXPECT_EQUAL(cipher_preferences, fips_security_policy->cipher_preferences); + + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_EQUAL(security_policy, fips_security_policy); + + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(conn, &kem_preferences)); + EXPECT_EQUAL(kem_preferences, fips_security_policy->kem_preferences); + + EXPECT_SUCCESS(s2n_connection_get_signature_preferences(conn, &signature_preferences)); + EXPECT_EQUAL(signature_preferences, fips_security_policy->signature_preferences); + + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + EXPECT_EQUAL(ecc_preferences, fips_security_policy->ecc_preferences); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all_fips")); + EXPECT_NOT_NULL(conn->security_policy_override); + + cipher_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_cipher_preferences(conn, &cipher_preferences)); + EXPECT_EQUAL(cipher_preferences, security_policy_test_all_fips.cipher_preferences); + + security_policy = NULL; + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_EQUAL(security_policy, &security_policy_test_all_fips); + + kem_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(conn, &kem_preferences)); + EXPECT_EQUAL(kem_preferences, security_policy_test_all_fips.kem_preferences); + + signature_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_signature_preferences(conn, &signature_preferences)); + EXPECT_EQUAL(signature_preferences, security_policy_test_all_fips.signature_preferences); + + ecc_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + EXPECT_EQUAL(ecc_preferences, security_policy_test_all_fips.ecc_preferences); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Test for NULL */ + { + struct s2n_connection *conn = NULL; + const struct s2n_cipher_preferences *cipher_preferences = NULL; + const struct s2n_security_policy *security_policy = NULL; + const struct s2n_kem_preferences *kem_preferences = NULL; + const struct s2n_signature_preferences *signature_preferences = NULL; + const struct s2n_ecc_preferences *ecc_preferences = NULL; + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NULL(conn->security_policy_override); + EXPECT_FAILURE(s2n_connection_set_cipher_preferences(conn, NULL)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "null")); + EXPECT_NOT_NULL(conn->security_policy_override); + + EXPECT_SUCCESS(s2n_connection_get_cipher_preferences(conn, &cipher_preferences)); + EXPECT_EQUAL(cipher_preferences, &cipher_preferences_null); + + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_EQUAL(security_policy, &security_policy_null); + + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(conn, &kem_preferences)); + EXPECT_EQUAL(kem_preferences, &kem_preferences_null); + + EXPECT_SUCCESS(s2n_connection_get_signature_preferences(conn, &signature_preferences)); + EXPECT_EQUAL(signature_preferences, &s2n_signature_preferences_null); + + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + EXPECT_EQUAL(ecc_preferences, &s2n_ecc_preferences_null); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test Error Case */ + { + struct s2n_connection *conn = NULL; + const struct s2n_cipher_preferences *cipher_preferences = NULL; + const struct s2n_security_policy *security_policy = NULL; + const struct s2n_kem_preferences *kem_preferences = NULL; + const struct s2n_signature_preferences *signature_preferences = NULL; + const struct s2n_ecc_preferences *ecc_preferences = NULL; + + EXPECT_FAILURE(s2n_connection_get_cipher_preferences(conn, &cipher_preferences)); + EXPECT_FAILURE(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_FAILURE(s2n_connection_get_kem_preferences(conn, &kem_preferences)); + EXPECT_FAILURE(s2n_connection_get_signature_preferences(conn, &signature_preferences)); + EXPECT_FAILURE(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(conn->config->security_policy); + EXPECT_NULL(conn->security_policy_override); + + conn->config->security_policy = NULL; + + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_cipher_preferences(conn, &cipher_preferences), S2N_ERR_INVALID_CIPHER_PREFERENCES); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_security_policy(conn, &security_policy), S2N_ERR_INVALID_SECURITY_POLICY); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_kem_preferences(conn, &kem_preferences), S2N_ERR_INVALID_KEM_PREFERENCES); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_signature_preferences(conn, &signature_preferences), S2N_ERR_INVALID_SIGNATURE_ALGORITHMS_PREFERENCES); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_ecc_preferences(conn, &ecc_preferences), S2N_ERR_INVALID_ECC_PREFERENCES); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* s2n_connection_get_curve */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + const char *curve_name = NULL; + char no_curve[] = { "NONE" }; + + /* No curve negotiated yet */ + EXPECT_NOT_NULL(curve_name = s2n_connection_get_curve(conn)); + EXPECT_BYTEARRAY_EQUAL(curve_name, no_curve, strlen(no_curve)); + + /* TLS1.3 always returns a curve */ + conn->actual_protocol_version = S2N_TLS13; + conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp256r1; + EXPECT_NOT_NULL(curve_name = s2n_connection_get_curve(conn)); + EXPECT_BYTEARRAY_EQUAL(curve_name, s2n_ecc_curve_secp256r1.name, strlen(s2n_ecc_curve_secp256r1.name)); + + /* TLS1.2 returns a curve if ECDHE cipher negotiated */ + conn->actual_protocol_version = S2N_TLS12; + conn->secure->cipher_suite = &s2n_ecdhe_rsa_with_aes_128_cbc_sha256; + conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp256r1; + EXPECT_NOT_NULL(curve_name = s2n_connection_get_curve(conn)); + EXPECT_BYTEARRAY_EQUAL(curve_name, s2n_ecc_curve_secp256r1.name, strlen(s2n_ecc_curve_secp256r1.name)); + + /* TLS1.2 does not return a curve if ECDHE cipher was not negotiated */ + conn->actual_protocol_version = S2N_TLS12; + conn->secure->cipher_suite = &s2n_rsa_with_aes_256_gcm_sha384; + conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp256r1; + EXPECT_NOT_NULL(curve_name = s2n_connection_get_curve(conn)); + EXPECT_BYTEARRAY_EQUAL(curve_name, no_curve, strlen(no_curve)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_connection_protocol_versions_test.c b/tests/unit/s2n_connection_protocol_versions_test.c new file mode 100644 index 00000000000..ecd58666140 --- /dev/null +++ b/tests/unit/s2n_connection_protocol_versions_test.c @@ -0,0 +1,390 @@ +/* +* 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 "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_tls.h" + +static S2N_RESULT s2n_write_test_supported_versions_extension(struct s2n_blob *supported_versions_blob, uint8_t version, + uint8_t extension_length) +{ + RESULT_ENSURE_REF(supported_versions_blob); + + struct s2n_stuffer supported_versions_stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&supported_versions_stuffer, supported_versions_blob)); + + /* Write the length byte. */ + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&supported_versions_stuffer, extension_length)); + /* Write the supported version. */ + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&supported_versions_stuffer, version / 10)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&supported_versions_stuffer, version % 10)); + + return S2N_RESULT_OK; +} + +struct s2n_overwrite_client_hello_ctx { + uint8_t client_hello_version; + uint8_t client_supported_version; + uint8_t extension_length; + + uint8_t supported_versions_data[3]; + int invoked_count; +}; + +static int s2n_overwrite_client_hello_cb(struct s2n_connection *conn, void *ctx) +{ + EXPECT_NOT_NULL(conn); + EXPECT_NOT_NULL(ctx); + + struct s2n_overwrite_client_hello_ctx *context = (struct s2n_overwrite_client_hello_ctx *) ctx; + context->invoked_count += 1; + + if (context->extension_length) { + struct s2n_blob supported_versions_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&supported_versions_blob, context->supported_versions_data, + sizeof(context->supported_versions_data))); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(conn); + EXPECT_NOT_NULL(client_hello); + + s2n_extension_type_id supported_versions_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(S2N_EXTENSION_SUPPORTED_VERSIONS, &supported_versions_id)); + s2n_parsed_extension *extension = &client_hello->extensions.parsed_extensions[supported_versions_id]; + + EXPECT_OK(s2n_write_test_supported_versions_extension(&supported_versions_blob, + context->client_supported_version, context->extension_length)); + + extension->extension_type = S2N_EXTENSION_SUPPORTED_VERSIONS; + extension->extension = supported_versions_blob; + } + + /* The client version fields are set when parsing the client hello before the client hello + * callback is invoked. The version fields are overridden to emulate receiving a client hello + * with a different version. + */ + if (context->client_hello_version) { + conn->client_hello_version = context->client_hello_version; + conn->client_protocol_version = context->client_hello_version; + } + + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* Safety */ + { + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_client_protocol_version(NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_client_hello_version(NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_server_protocol_version(NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_actual_protocol_version(NULL), S2N_ERR_NULL); + } + + /* Test protocol version getters on the server when a supported versions extension is received */ + for (uint8_t server_version = S2N_TLS12; server_version <= S2N_TLS13; server_version++) { + for (uint8_t client_hello_version = S2N_SSLv3; client_hello_version <= S2N_TLS12; client_hello_version++) { + for (uint8_t client_supported_version = S2N_SSLv3; client_supported_version <= S2N_TLS13; client_supported_version++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + if (server_version == S2N_TLS12) { + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all_tls12")); + } else if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + } else { + continue; + } + + struct s2n_overwrite_client_hello_ctx context = { + .client_hello_version = client_hello_version, + .client_supported_version = client_supported_version, + .extension_length = 2, + }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_overwrite_client_hello_cb, &context)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); + EXPECT_EQUAL(context.invoked_count, 1); + + /* Ensure that a supported versions extension was received. */ + bool supported_versions_received = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, + &supported_versions_received)); + EXPECT_TRUE(supported_versions_received); + + EXPECT_EQUAL(s2n_connection_get_server_protocol_version(server), server_version); + EXPECT_EQUAL(s2n_connection_get_client_hello_version(server), client_hello_version); + + /* The reported client protocol version should always match the version specified + * in the supported versions extension, even for TLS 1.2 servers which don't + * process the extension for version selection. + */ + EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), client_supported_version); + + uint8_t actual_protocol_version = s2n_connection_get_actual_protocol_version(server); + if (server_version == S2N_TLS12) { + /* For backwards compatibility, TLS 1.2 servers always use the client hello + * version to determine the client's maximum version, even if a supported + * versions extension was received. + */ + EXPECT_EQUAL(actual_protocol_version, MIN(server_version, client_hello_version)); + } else { + /* TLS 1.3 servers always use the version in the supported versions extension, + * regardless of the client hello version. + */ + EXPECT_EQUAL(actual_protocol_version, MIN(server_version, client_supported_version)); + } + } + } + } + + /* Test protocol version getters on the server when a supported versions extension isn't received */ + for (uint8_t server_version = S2N_TLS12; server_version <= S2N_TLS13; server_version++) { + for (uint8_t client_hello_version = S2N_SSLv3; client_hello_version <= S2N_TLS12; client_hello_version++) { + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + + /* A TLS 1.2 security policy is set to prevent the client from sending a supported + * versions extension. + */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all_tls12")); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + if (server_version == S2N_TLS12) { + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all_tls12")); + } else if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); + } else { + continue; + } + + struct s2n_overwrite_client_hello_ctx context = { + .client_hello_version = client_hello_version, + }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(server_config, s2n_overwrite_client_hello_cb, &context)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, client_config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, server_config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); + EXPECT_EQUAL(context.invoked_count, 1); + + /* Ensure that a supported versions extension wasn't received. */ + bool supported_versions_received = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, + &supported_versions_received)); + EXPECT_FALSE(supported_versions_received); + + EXPECT_EQUAL(s2n_connection_get_server_protocol_version(server), server_version); + EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), client_hello_version); + EXPECT_EQUAL(s2n_connection_get_client_hello_version(server), client_hello_version); + EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server), client_hello_version); + } + } + + /* Test protocol version getters on the client */ + for (uint8_t server_version = S2N_SSLv3; server_version <= S2N_TLS13; server_version++) { + if (server_version == S2N_TLS13 && !s2n_is_tls13_fully_supported()) { + continue; + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + server->server_protocol_version = server_version; + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_CERT)); + + EXPECT_EQUAL(s2n_connection_get_server_protocol_version(client), server_version); + EXPECT_EQUAL(s2n_connection_get_client_protocol_version(client), s2n_get_highest_fully_supported_tls_version()); + EXPECT_EQUAL(s2n_connection_get_client_hello_version(client), S2N_TLS12); + EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(client), server_version); + } + + /* Ensure that TLS 1.2 servers report the client hello version as the client protocol version + * if a malformed supported versions extension was received + */ + for (uint8_t client_hello_version = S2N_SSLv3; client_hello_version <= S2N_TLS12; client_hello_version++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all_tls12")); + + struct s2n_overwrite_client_hello_ctx context = { + .client_hello_version = client_hello_version, + .client_supported_version = S2N_TLS13, + /* Write an invalid length */ + .extension_length = 11, + }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_overwrite_client_hello_cb, &context)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); + EXPECT_EQUAL(context.invoked_count, 1); + + /* Ensure that a supported versions extension was received. */ + bool supported_versions_received = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, + &supported_versions_received)); + EXPECT_TRUE(supported_versions_received); + + EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), client_hello_version); + } + + /* Ensure that TLS 1.2 servers report the client hello version as the client protocol version + * if an invalid supported version is received + */ + for (uint8_t client_hello_version = S2N_SSLv3; client_hello_version <= S2N_TLS12; client_hello_version++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all_tls12")); + + struct s2n_overwrite_client_hello_ctx context = { + .client_hello_version = client_hello_version, + /* Write an invalid version */ + .client_supported_version = S2N_TLS13 + 10, + .extension_length = 2, + }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_overwrite_client_hello_cb, &context)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO)); + EXPECT_EQUAL(context.invoked_count, 1); + + /* Ensure that a supported versions extension was received. */ + bool supported_versions_received = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, + &supported_versions_received)); + EXPECT_TRUE(supported_versions_received); + + EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), client_hello_version); + } + + /* Ensure that TLS 1.3 servers report an unknown protocol version if a supported versions + * extension can't be processed + */ + if (s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + + struct s2n_overwrite_client_hello_ctx context = { + .client_supported_version = S2N_TLS13, + /* Write an invalid length */ + .extension_length = 11, + }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_overwrite_client_hello_cb, &context)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_test_server_and_client_until_message(server, client, SERVER_HELLO), + S2N_ERR_BAD_MESSAGE); + EXPECT_EQUAL(context.invoked_count, 1); + + /* Ensure that a supported versions extension was received. */ + bool supported_versions_received = false; + EXPECT_SUCCESS(s2n_client_hello_has_extension(&server->client_hello, S2N_EXTENSION_SUPPORTED_VERSIONS, + &supported_versions_received)); + EXPECT_TRUE(supported_versions_received); + + EXPECT_EQUAL(s2n_connection_get_server_protocol_version(server), S2N_TLS13); + EXPECT_EQUAL(s2n_connection_get_client_protocol_version(server), s2n_unknown_protocol_version); + EXPECT_EQUAL(s2n_connection_get_actual_protocol_version(server), s2n_unknown_protocol_version); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_connection_size_test.c b/tests/unit/s2n_connection_size_test.c new file mode 100644 index 00000000000..8364a67012f --- /dev/null +++ b/tests/unit/s2n_connection_size_test.c @@ -0,0 +1,64 @@ +/* + * 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 "s2n_test.h" +#include "tls/s2n_connection.h" + +bool is_32_bit_platform() +{ + return (sizeof(void *) == 4); +} + +/* Test s2n_connection does not grow too much. + * s2n_connection is a very large structure. We should be working to reduce its + * size, not increasing it. + * This test documents changes to its size for reviewers so that we can + * make very deliberate choices about increasing memory usage. + * + * We can't easily enforce an exact size for s2n_connection because it varies + * based on some settings (like how many KEM groups are supported). + */ +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* We don't run this test on 32 bit platforms. The goal of this test is + * accomplished as long as it is running on any platform in our CI, and + * just running it on a single platform keeps us from having to maintain + * multiple static constants. + */ + if (is_32_bit_platform()) { + END_TEST(); + return 0; + } + + /* Carefully consider any increases to this number. */ + const uint16_t max_connection_size = 4274; + const uint16_t min_connection_size = max_connection_size * 0.9; + + size_t connection_size = sizeof(struct s2n_connection); + + if (connection_size > max_connection_size || connection_size < min_connection_size) { + const char message[] = "s2n_connection size (%zu) no longer in (%i, %i). " + "Please verify that this change was intentional and then update this test."; + char message_buffer[sizeof(message) + 100] = { 0 }; + int r = snprintf(message_buffer, sizeof(message_buffer), message, + connection_size, min_connection_size, max_connection_size); + EXPECT_TRUE(r < sizeof(message_buffer)); + FAIL_MSG(message_buffer); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_connection_test.c b/tests/unit/s2n_connection_test.c new file mode 100644 index 00000000000..9e331586071 --- /dev/null +++ b/tests/unit/s2n_connection_test.c @@ -0,0 +1,884 @@ +/* + * 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 "tls/s2n_connection.h" + +#include "crypto/s2n_hash.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_client_server_name.h" +#include "tls/extensions/s2n_extension_list.h" +#include "tls/s2n_internal.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_socket.h" + +const uint8_t actual_version = 1, client_version = 2, server_version = 3; +static int s2n_set_test_protocol_versions(struct s2n_connection *conn) +{ + conn->actual_protocol_version = actual_version; + conn->client_protocol_version = client_version; + conn->server_protocol_version = server_version; + return S2N_SUCCESS; +} + +bool s2n_server_name_test_callback_flag = false; +static int s2n_server_name_test_callback(struct s2n_connection *conn, void *ctx) +{ + const char *expected_server_name = *(const char **) ctx; + + const char *actual_server_name = NULL; + EXPECT_NOT_NULL(actual_server_name = s2n_get_server_name(conn)); + EXPECT_STRING_EQUAL(actual_server_name, expected_server_name); + + s2n_server_name_test_callback_flag = true; + return S2N_SUCCESS; +} + +S2N_RESULT s2n_test_signature_scheme_valid(s2n_tls_signature_algorithm expected_sig_alg, + s2n_tls_signature_algorithm server_sig_alg, s2n_tls_signature_algorithm client_sig_alg, + s2n_tls_hash_algorithm server_hash_alg, s2n_tls_hash_algorithm client_hash_alg) +{ + /* The server and client should agree */ + RESULT_ENSURE_EQ(server_sig_alg, client_sig_alg); + RESULT_ENSURE_EQ(server_hash_alg, client_hash_alg); + + /* The certificate dictates the signature algorithm, so we know the correct algorithm */ + RESULT_ENSURE_EQ(server_sig_alg, expected_sig_alg); + + /* The security policy dictates the hash algorithm, + * but we used a default policy so we just expect a sane, non-legacy hash. + */ + RESULT_ENSURE_NE(server_hash_alg, S2N_TLS_HASH_NONE); + RESULT_ENSURE_NE(server_hash_alg, S2N_TLS_HASH_MD5); + RESULT_ENSURE_NE(server_hash_alg, S2N_TLS_HASH_SHA1); + RESULT_ENSURE_NE(server_hash_alg, S2N_TLS_HASH_MD5_SHA1); + + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_test_all_signature_schemes_valid(s2n_tls_signature_algorithm expected_sig_alg, + struct s2n_connection *server_conn, struct s2n_connection *client_conn) +{ + s2n_tls_signature_algorithm server_sig_alg = 0, client_sig_alg = 0; + s2n_tls_hash_algorithm server_hash_alg = 0, client_hash_alg = 0; + + RESULT_GUARD_POSIX(s2n_connection_get_selected_signature_algorithm(client_conn, &client_sig_alg)); + RESULT_GUARD_POSIX(s2n_connection_get_selected_signature_algorithm(server_conn, &server_sig_alg)); + RESULT_GUARD_POSIX(s2n_connection_get_selected_digest_algorithm(client_conn, &client_hash_alg)); + RESULT_GUARD_POSIX(s2n_connection_get_selected_digest_algorithm(server_conn, &server_hash_alg)); + RESULT_GUARD(s2n_test_signature_scheme_valid(expected_sig_alg, + server_sig_alg, client_sig_alg, server_hash_alg, client_hash_alg)); + + RESULT_GUARD_POSIX(s2n_connection_get_selected_client_cert_signature_algorithm(client_conn, &client_sig_alg)); + RESULT_GUARD_POSIX(s2n_connection_get_selected_client_cert_signature_algorithm(server_conn, &server_sig_alg)); + RESULT_GUARD_POSIX(s2n_connection_get_selected_client_cert_digest_algorithm(client_conn, &client_hash_alg)); + RESULT_GUARD_POSIX(s2n_connection_get_selected_client_cert_digest_algorithm(server_conn, &server_hash_alg)); + RESULT_GUARD(s2n_test_signature_scheme_valid(expected_sig_alg, + server_sig_alg, client_sig_alg, server_hash_alg, client_hash_alg)); + + return S2N_RESULT_OK; +} + +int s2n_noop_recv_cb(void *io_context, uint8_t *buf, uint32_t len) +{ + return 0; +} + +int s2n_noop_send_cb(void *io_context, const uint8_t *buf, uint32_t len) +{ + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + struct s2n_cert_chain_and_key *rsa_chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&rsa_chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* s2n_get_server_name */ + { + const char *test_server_name = "A server name"; + + /* Safety check */ + EXPECT_NULL(s2n_get_server_name(NULL)); + + /* Return NULL by default / for new connection */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_NULL(s2n_get_server_name(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Return server_name if set */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_set_server_name(conn, test_server_name)); + + const char *actual_server_name = NULL; + EXPECT_NOT_NULL(actual_server_name = s2n_get_server_name(conn)); + EXPECT_STRING_EQUAL(actual_server_name, test_server_name); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Return server_name if server_name extension parsed, but not yet processed */ + { + struct s2n_connection *client_conn, *server_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + DEFER_CLEANUP(struct s2n_stuffer stuffer, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, test_server_name)); + EXPECT_SUCCESS(s2n_client_server_name_extension.send(client_conn, &stuffer)); + + s2n_extension_type_id extension_id; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_SERVER_NAME, &extension_id)); + server_conn->client_hello.extensions.parsed_extensions[extension_id].extension_type = TLS_EXTENSION_SERVER_NAME; + server_conn->client_hello.extensions.parsed_extensions[extension_id].extension = stuffer.blob; + + const char *actual_server_name = NULL; + EXPECT_NOT_NULL(actual_server_name = s2n_get_server_name(server_conn)); + EXPECT_STRING_EQUAL(actual_server_name, test_server_name); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test retrieving server_name via ClientHello callback, + * which is when we expect this API to be called. */ + { + s2n_server_name_test_callback_flag = false; + + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_server_name_test_callback, &test_server_name)); + + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, test_server_name)); + + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* This function can succeed or fail -- it doesn't affect the test. */ + s2n_client_hello_recv(server_conn); + + /* Make sure the callback actually fired. If it did, + * then the actual test ran and we have verified the server name. */ + EXPECT_TRUE(s2n_server_name_test_callback_flag); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + }; + + /* s2n_connection_get_protocol_version */ + { + struct s2n_connection *client_conn, *server_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_set_test_protocol_versions(client_conn)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_set_test_protocol_versions(server_conn)); + + /* Handle null */ + EXPECT_EQUAL(s2n_connection_get_protocol_version(NULL), S2N_UNKNOWN_PROTOCOL_VERSION); + + /* Return actual if set */ + EXPECT_EQUAL(s2n_connection_get_protocol_version(client_conn), actual_version); + EXPECT_EQUAL(s2n_connection_get_protocol_version(server_conn), actual_version); + + /* If actual version not set, result version for mode */ + client_conn->actual_protocol_version = S2N_UNKNOWN_PROTOCOL_VERSION; + EXPECT_EQUAL(s2n_connection_get_protocol_version(client_conn), client_version); + server_conn->actual_protocol_version = S2N_UNKNOWN_PROTOCOL_VERSION; + EXPECT_EQUAL(s2n_connection_get_protocol_version(server_conn), server_version); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test: get selected digest alg */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + s2n_tls_hash_algorithm output = { 0 }; + + EXPECT_FAILURE(s2n_connection_get_selected_digest_algorithm(NULL, &output)); + EXPECT_FAILURE(s2n_connection_get_selected_digest_algorithm(conn, NULL)); + EXPECT_FAILURE(s2n_connection_get_selected_client_cert_digest_algorithm(NULL, &output)); + EXPECT_FAILURE(s2n_connection_get_selected_client_cert_digest_algorithm(conn, NULL)); + + EXPECT_SUCCESS(s2n_connection_get_selected_client_cert_digest_algorithm(conn, &output)); + EXPECT_EQUAL(S2N_TLS_HASH_NONE, output); + + EXPECT_SUCCESS(s2n_connection_get_selected_digest_algorithm(conn, &output)); + EXPECT_EQUAL(S2N_TLS_HASH_NONE, output); + + s2n_tls_hash_algorithm expected_output[] = { + S2N_TLS_HASH_NONE, S2N_TLS_HASH_MD5, + S2N_TLS_HASH_SHA1, S2N_TLS_HASH_SHA224, + S2N_TLS_HASH_SHA256, S2N_TLS_HASH_SHA384, + S2N_TLS_HASH_SHA512, S2N_TLS_HASH_MD5_SHA1, + S2N_TLS_HASH_NONE + }; + + for (size_t i = S2N_TLS_HASH_NONE; i <= UINT16_MAX; i++) { + struct s2n_signature_scheme test_scheme = *conn->handshake_params.client_cert_sig_scheme; + test_scheme.hash_alg = i; + conn->handshake_params.client_cert_sig_scheme = &test_scheme; + conn->handshake_params.server_cert_sig_scheme = &test_scheme; + if (i <= S2N_HASH_SENTINEL) { + EXPECT_SUCCESS(s2n_connection_get_selected_client_cert_digest_algorithm(conn, &output)); + EXPECT_EQUAL(expected_output[i], output); + + EXPECT_SUCCESS(s2n_connection_get_selected_digest_algorithm(conn, &output)); + EXPECT_EQUAL(expected_output[i], output); + } else { + EXPECT_SUCCESS(s2n_connection_get_selected_client_cert_digest_algorithm(conn, &output)); + EXPECT_EQUAL(S2N_TLS_HASH_NONE, output); + + EXPECT_SUCCESS(s2n_connection_get_selected_digest_algorithm(conn, &output)); + EXPECT_EQUAL(S2N_TLS_HASH_NONE, output); + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: get selected signature alg */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + s2n_tls_signature_algorithm output = { 0 }; + + EXPECT_FAILURE(s2n_connection_get_selected_signature_algorithm(NULL, &output)); + EXPECT_FAILURE(s2n_connection_get_selected_signature_algorithm(conn, NULL)); + EXPECT_FAILURE(s2n_connection_get_selected_client_cert_signature_algorithm(NULL, &output)); + EXPECT_FAILURE(s2n_connection_get_selected_client_cert_signature_algorithm(conn, NULL)); + + EXPECT_SUCCESS(s2n_connection_get_selected_client_cert_signature_algorithm(conn, &output)); + EXPECT_EQUAL(S2N_TLS_SIGNATURE_ANONYMOUS, output); + + EXPECT_SUCCESS(s2n_connection_get_selected_signature_algorithm(conn, &output)); + EXPECT_EQUAL(S2N_TLS_SIGNATURE_ANONYMOUS, output); + + s2n_tls_signature_algorithm expected_output[] = { + [S2N_SIGNATURE_ANONYMOUS] = S2N_TLS_SIGNATURE_ANONYMOUS, + [S2N_SIGNATURE_RSA] = S2N_TLS_SIGNATURE_RSA, + [S2N_SIGNATURE_ECDSA] = S2N_TLS_SIGNATURE_ECDSA, + [S2N_SIGNATURE_RSA_PSS_RSAE] = S2N_TLS_SIGNATURE_RSA_PSS_RSAE, + [S2N_SIGNATURE_RSA_PSS_PSS] = S2N_TLS_SIGNATURE_RSA_PSS_PSS, + }; + + for (size_t i = 0; i <= UINT16_MAX; i++) { + struct s2n_signature_scheme test_scheme = *conn->handshake_params.client_cert_sig_scheme; + test_scheme.sig_alg = i; + conn->handshake_params.client_cert_sig_scheme = &test_scheme; + conn->handshake_params.server_cert_sig_scheme = &test_scheme; + + if (i < s2n_array_len(expected_output)) { + EXPECT_SUCCESS(s2n_connection_get_selected_client_cert_signature_algorithm(conn, &output)); + EXPECT_EQUAL(expected_output[i], output); + + EXPECT_SUCCESS(s2n_connection_get_selected_signature_algorithm(conn, &output)); + EXPECT_EQUAL(expected_output[i], output); + } else { + EXPECT_SUCCESS(s2n_connection_get_selected_client_cert_signature_algorithm(conn, &output)); + EXPECT_EQUAL(S2N_TLS_SIGNATURE_ANONYMOUS, output); + + EXPECT_SUCCESS(s2n_connection_get_selected_signature_algorithm(conn, &output)); + EXPECT_EQUAL(S2N_TLS_SIGNATURE_ANONYMOUS, output); + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: signature algorithm and hash can be retrieved after the handshake. + * Check both TLS1.2 and TLS1.3, because they use different signature negotiation logic. + * Check for both the server and client certificates, because they use different negotiation logic. + */ + { + /* TLS1.3 */ + if (s2n_is_tls13_fully_supported()) { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_OK(s2n_test_all_signature_schemes_valid(S2N_TLS_SIGNATURE_ECDSA, server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_config_free(config)); + } + + /* TLS1.2 */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, rsa_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_OK(s2n_test_all_signature_schemes_valid(S2N_TLS_SIGNATURE_RSA, server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + }; + + /* s2n_connection_set_max_fragment_length */ + { + const uint8_t mfl_code = S2N_TLS_MAX_FRAG_LEN_1024; + const uint16_t mfl_code_value = 1024; + const uint16_t low_mfl = 10; + const uint16_t high_mfl = UINT16_MAX; + + /* Safety check */ + EXPECT_ERROR_WITH_ERRNO(s2n_connection_set_max_fragment_length(NULL, 1), S2N_ERR_NULL); + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + /* Default behavior - set high mfl */ + { + conn->max_outgoing_fragment_length = 1; + EXPECT_OK(s2n_connection_set_max_fragment_length(conn, high_mfl)); + EXPECT_EQUAL(conn->max_outgoing_fragment_length, high_mfl); + EXPECT_EQUAL(conn->out.blob.size, 0); + }; + + /* Default behavior - set low mfl */ + { + conn->max_outgoing_fragment_length = 1; + EXPECT_OK(s2n_connection_set_max_fragment_length(conn, low_mfl)); + EXPECT_EQUAL(conn->max_outgoing_fragment_length, low_mfl); + EXPECT_EQUAL(conn->out.blob.size, 0); + }; + + /* After extension - don't set mfl higher than agreed with peer */ + { + conn->negotiated_mfl_code = mfl_code; + conn->max_outgoing_fragment_length = 1; + EXPECT_OK(s2n_connection_set_max_fragment_length(conn, high_mfl)); + EXPECT_EQUAL(conn->max_outgoing_fragment_length, mfl_code_value); + EXPECT_EQUAL(conn->out.blob.size, 0); + }; + + /* After extension - set mfl lower than agreed with peer */ + { + conn->negotiated_mfl_code = mfl_code; + conn->max_outgoing_fragment_length = 1; + EXPECT_OK(s2n_connection_set_max_fragment_length(conn, low_mfl)); + EXPECT_EQUAL(conn->max_outgoing_fragment_length, low_mfl); + EXPECT_EQUAL(conn->out.blob.size, 0); + }; + + /* After extension - invalid negotiated mfl */ + { + conn->negotiated_mfl_code = UINT8_MAX; + EXPECT_ERROR_WITH_ERRNO(s2n_connection_set_max_fragment_length(conn, low_mfl), S2N_ERR_SAFETY); + conn->negotiated_mfl_code = 0; + }; + + /* output IO buffer already allocated: resize for higher mfl */ + { + EXPECT_SUCCESS(s2n_realloc(&conn->out.blob, 1)); + EXPECT_OK(s2n_connection_set_max_fragment_length(conn, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH)); + EXPECT_EQUAL(conn->max_outgoing_fragment_length, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH); + EXPECT_EQUAL(conn->out.blob.size, S2N_TLS_MAXIMUM_RECORD_LENGTH); + EXPECT_SUCCESS(s2n_free(&conn->out.blob)); + }; + + /* output IO buffer already allocated: do nothing for lower mfl */ + { + EXPECT_SUCCESS(s2n_realloc(&conn->out.blob, UINT16_MAX)); + EXPECT_OK(s2n_connection_set_max_fragment_length(conn, low_mfl)); + EXPECT_EQUAL(conn->max_outgoing_fragment_length, low_mfl); + EXPECT_EQUAL(conn->out.blob.size, UINT16_MAX); + EXPECT_SUCCESS(s2n_free(&conn->out.blob)); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* s2n_connection set fd functionality */ + { + static const int READFD = 1; + static const int WRITEFD = 2; + static int getReadFd, getWriteFd; + + /* Safety checks */ + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_set_fd(NULL, READFD), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_set_read_fd(NULL, READFD), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_set_write_fd(NULL, WRITEFD), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_write_fd(NULL, &getWriteFd), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_read_fd(NULL, &getReadFd), S2N_ERR_NULL); + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + /* check getter API after s2n_connection_set_fd */ + EXPECT_SUCCESS(s2n_connection_set_fd(conn, READFD)); + EXPECT_SUCCESS(s2n_connection_get_write_fd(conn, &getWriteFd)); + EXPECT_SUCCESS(s2n_connection_get_read_fd(conn, &getReadFd)); + EXPECT_EQUAL(getReadFd, READFD); + EXPECT_EQUAL(getWriteFd, READFD); + + /* check getter API after s2n_connection_set_read_fd */ + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_SUCCESS(s2n_connection_set_read_fd(conn, READFD)); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_write_fd(conn, &getWriteFd), S2N_ERR_INVALID_STATE); + EXPECT_SUCCESS(s2n_connection_get_read_fd(conn, &getReadFd)); + EXPECT_EQUAL(getReadFd, READFD); + + /* check getter API after s2n_connection_set_write_fd */ + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_SUCCESS(s2n_connection_set_write_fd(conn, WRITEFD)); + EXPECT_SUCCESS(s2n_connection_get_write_fd(conn, &getWriteFd)); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_read_fd(conn, &getReadFd), S2N_ERR_INVALID_STATE); + EXPECT_EQUAL(getWriteFd, WRITEFD); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* s2n_connection_set_fd can be called twice in a row */ + { + static const int OLDFD = 1; + static const int NEWFD = 2; + static int getReadFd, getWriteFd; + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + EXPECT_SUCCESS(s2n_connection_set_fd(conn, OLDFD)); + EXPECT_SUCCESS(s2n_connection_set_fd(conn, NEWFD)); + + EXPECT_SUCCESS(s2n_connection_get_write_fd(conn, &getWriteFd)); + EXPECT_SUCCESS(s2n_connection_get_read_fd(conn, &getReadFd)); + EXPECT_EQUAL(getReadFd, NEWFD); + EXPECT_EQUAL(getWriteFd, NEWFD); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* s2n_connection_set_read_fd and s2n_connection_set_write_fd can be called + * after s2n_connection_set_fd */ + { + static const int OLDFD = 1; + static const int NEWFD = 2; + static int getReadFd, getWriteFd; + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + EXPECT_SUCCESS(s2n_connection_set_fd(conn, OLDFD)); + EXPECT_SUCCESS(s2n_connection_set_read_fd(conn, NEWFD)); + EXPECT_SUCCESS(s2n_connection_set_write_fd(conn, NEWFD)); + + EXPECT_SUCCESS(s2n_connection_get_write_fd(conn, &getWriteFd)); + EXPECT_SUCCESS(s2n_connection_get_read_fd(conn, &getReadFd)); + EXPECT_EQUAL(getReadFd, NEWFD); + EXPECT_EQUAL(getWriteFd, NEWFD); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* The default s2n socket read/write setup can be used with a user-defined send/recv setup */ + { + static const int READFD = 1; + static const int WRITEFD = 2; + uint8_t socket_ctx[] = { "Some test context" }; + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + EXPECT_SUCCESS(s2n_connection_set_read_fd(conn, READFD)); + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_noop_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, socket_ctx)); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + EXPECT_SUCCESS(s2n_connection_set_write_fd(conn, WRITEFD)); + EXPECT_SUCCESS(s2n_connection_set_recv_cb(conn, s2n_noop_recv_cb)); + EXPECT_SUCCESS(s2n_connection_set_recv_ctx(conn, socket_ctx)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* The default s2n socket read/write setup can be overwritten by custom socket setup */ + { + static const int READFD = 1; + uint8_t socket_ctx[] = { "Some test context" }; + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + /* Connection sets up the default socket functions */ + EXPECT_SUCCESS(s2n_connection_set_fd(conn, READFD)); + EXPECT_NOT_NULL(conn->send); + EXPECT_NOT_NULL(conn->recv); + + /* Setting up custom socket contexts will remove default socket functions */ + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, socket_ctx)); + EXPECT_SUCCESS(s2n_connection_set_recv_ctx(conn, socket_ctx)); + EXPECT_NULL(conn->send); + EXPECT_NULL(conn->recv); + + /* Setup default socket functions again */ + EXPECT_SUCCESS(s2n_connection_set_fd(conn, READFD)); + EXPECT_NOT_NULL(conn->send_io_context); + EXPECT_NOT_NULL(conn->recv_io_context); + + /* Setting up custom socket functions will remove default socket contexts */ + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_noop_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_recv_cb(conn, s2n_noop_recv_cb)); + EXPECT_NULL(conn->send_io_context); + EXPECT_NULL(conn->recv_io_context); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_connection_get_config */ + { + struct s2n_config *returned_config = NULL; + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_config(conn, &returned_config), S2N_ERR_NULL); + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_connection_get_config(conn, &returned_config)); + EXPECT_EQUAL(returned_config, config); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test s2n_connection_get_wire_bytes_out */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_EQUAL(0, s2n_connection_get_wire_bytes_out(conn)); + + uint64_t magic_number = 123456; + conn->wire_bytes_out = magic_number; + EXPECT_EQUAL(magic_number, s2n_connection_get_wire_bytes_out(conn)); + }; + + /* Test connection reuse when memory freed */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + uint8_t app_data[100] = "hello world"; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + for (size_t i = 0; i < 10; i++) { + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Free handshake memory */ + EXPECT_SUCCESS(s2n_connection_free_handshake(client_conn)); + EXPECT_SUCCESS(s2n_connection_free_handshake(server_conn)); + + /* Send and recv data */ + EXPECT_EQUAL(s2n_send(client_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_EQUAL(s2n_recv(server_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_EQUAL(s2n_send(server_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_EQUAL(s2n_recv(client_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + + /* Free io buffers */ + EXPECT_SUCCESS(s2n_connection_release_buffers(client_conn)); + EXPECT_SUCCESS(s2n_connection_release_buffers(server_conn)); + + /* Reuse connections */ + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + } + }; + + /* Test post-handshake buffer lifecycle */ + { + const uint32_t size = 10; + + /* Test s2n_connection_wipe */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + /* Test uninitialized */ + for (size_t i = 0; i < 3; i++) { + EXPECT_FALSE(conn->post_handshake.in.growable); + EXPECT_FALSE(conn->post_handshake.in.alloced); + EXPECT_EQUAL(conn->post_handshake.in.blob.size, 0); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_EQUAL(conn->post_handshake.in.blob.size, 0); + } + + /* Test with dynamic buffer */ + for (size_t i = 0; i < 3; i++) { + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&conn->post_handshake.in, size)); + EXPECT_TRUE(conn->post_handshake.in.growable); + EXPECT_TRUE(conn->post_handshake.in.alloced); + EXPECT_EQUAL(conn->post_handshake.in.blob.size, size); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_EQUAL(conn->post_handshake.in.blob.size, 0); + } + + /* Test with static buffer */ + for (size_t i = 0; i < 3; i++) { + struct s2n_blob static_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&static_blob, conn->post_handshake.header_in, + sizeof(conn->post_handshake.header_in))); + EXPECT_SUCCESS(s2n_stuffer_init(&conn->post_handshake.in, &static_blob)); + EXPECT_FALSE(conn->post_handshake.in.growable); + EXPECT_FALSE(conn->post_handshake.in.alloced); + EXPECT_NOT_EQUAL(conn->post_handshake.in.blob.size, 0); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_EQUAL(conn->post_handshake.in.blob.size, 0); + } + }; + + /* Test s2n_connection_release_buffers */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + /* Test uninitialized */ + for (size_t i = 0; i < 3; i++) { + EXPECT_FALSE(conn->post_handshake.in.growable); + EXPECT_FALSE(conn->post_handshake.in.alloced); + EXPECT_EQUAL(conn->post_handshake.in.blob.size, 0); + + EXPECT_SUCCESS(s2n_connection_release_buffers(conn)); + EXPECT_EQUAL(conn->post_handshake.in.blob.size, 0); + } + + /* Test with dynamic buffer */ + for (size_t i = 0; i < 3; i++) { + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&conn->post_handshake.in, size)); + EXPECT_TRUE(conn->post_handshake.in.growable); + EXPECT_TRUE(conn->post_handshake.in.alloced); + EXPECT_EQUAL(conn->post_handshake.in.blob.size, size); + + EXPECT_SUCCESS(s2n_connection_release_buffers(conn)); + EXPECT_EQUAL(conn->post_handshake.in.blob.size, 0); + } + + /* Test with static memory */ + for (size_t i = 0; i < 3; i++) { + struct s2n_blob static_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&static_blob, conn->post_handshake.header_in, + sizeof(conn->post_handshake.header_in))); + EXPECT_SUCCESS(s2n_stuffer_init(&conn->post_handshake.in, &static_blob)); + EXPECT_FALSE(conn->post_handshake.in.growable); + EXPECT_FALSE(conn->post_handshake.in.alloced); + EXPECT_NOT_EQUAL(conn->post_handshake.in.blob.size, 0); + + EXPECT_SUCCESS(s2n_connection_release_buffers(conn)); + EXPECT_EQUAL(conn->post_handshake.in.blob.size, 0); + } + + /* Fails to release if in use */ + { + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&conn->post_handshake.in, size)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&conn->post_handshake.in, 1)); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_release_buffers(conn), + S2N_ERR_STUFFER_HAS_UNPROCESSED_DATA); + EXPECT_NOT_EQUAL(conn->post_handshake.in.blob.size, 0); + }; + }; + }; + + /* Test: s2n_connection_check_io_status */ + { + /* Safety */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + EXPECT_FALSE(s2n_connection_check_io_status(NULL, S2N_IO_WRITABLE)); + EXPECT_FALSE(s2n_connection_check_io_status(NULL, S2N_IO_READABLE)); + EXPECT_FALSE(s2n_connection_check_io_status(NULL, S2N_IO_FULL_DUPLEX)); + EXPECT_FALSE(s2n_connection_check_io_status(NULL, S2N_IO_CLOSED)); + EXPECT_FALSE(s2n_connection_check_io_status(NULL, 10)); + EXPECT_FALSE(s2n_connection_check_io_status(conn, 10)); + } + + /* TLS1.2 */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS12; + + /* Full duplex by default */ + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_WRITABLE)); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_READABLE)); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_FULL_DUPLEX)); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + + /* Close write */ + s2n_atomic_flag_set(&conn->write_closed); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_WRITABLE)); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_READABLE)); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_FULL_DUPLEX)); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + s2n_atomic_flag_clear(&conn->write_closed); + + /* Close read */ + s2n_atomic_flag_set(&conn->read_closed); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_WRITABLE)); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_READABLE)); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_FULL_DUPLEX)); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + s2n_atomic_flag_clear(&conn->read_closed); + + /* Close both */ + s2n_atomic_flag_set(&conn->read_closed); + s2n_atomic_flag_set(&conn->write_closed); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_WRITABLE)); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_READABLE)); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_FULL_DUPLEX)); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + }; + + /* TLS1.3 */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS13; + + /* Full duplex by default */ + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_WRITABLE)); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_READABLE)); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_FULL_DUPLEX)); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + + /* Close write */ + s2n_atomic_flag_set(&conn->write_closed); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_WRITABLE)); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_READABLE)); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_FULL_DUPLEX)); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + s2n_atomic_flag_clear(&conn->write_closed); + + /* Close read */ + s2n_atomic_flag_set(&conn->read_closed); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_WRITABLE)); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_READABLE)); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_FULL_DUPLEX)); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + s2n_atomic_flag_clear(&conn->read_closed); + + /* Close both */ + s2n_atomic_flag_set(&conn->read_closed); + s2n_atomic_flag_set(&conn->write_closed); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_WRITABLE)); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_READABLE)); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_FULL_DUPLEX)); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + }; + }; + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_chain_and_key)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(rsa_chain_and_key)); + END_TEST(); +} diff --git a/tests/unit/s2n_cookie_test.c b/tests/unit/s2n_cookie_test.c new file mode 100644 index 00000000000..d3ede10c3b2 --- /dev/null +++ b/tests/unit/s2n_cookie_test.c @@ -0,0 +1,378 @@ +/* + * 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 "tls/extensions/s2n_cookie.h" + +#include + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_handshake_type.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" + +#define TEST_COOKIE_COUNT 5 + +int main() +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + uint16_t test_cookie_sizes[TEST_COOKIE_COUNT] = { 1, UINT8_MAX, UINT8_MAX + 1, UINT16_MAX - 1, UINT16_MAX }; + struct s2n_blob test_cookies[TEST_COOKIE_COUNT] = { 0 }; + for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { + EXPECT_SUCCESS(s2n_alloc(&test_cookies[i], test_cookie_sizes[i])); + EXPECT_OK(s2n_get_public_random_data(&test_cookies[i])); + } + + /** + * Test: client only sends extension if cookie present + * + *= https://tools.ietf.org/rfc/rfc8446#4.1.2 + *= type=test + *# - Including a "cookie" extension if one was provided in the + *# HelloRetryRequest. + **/ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + + /* Not sent without a cookie */ + EXPECT_FALSE(s2n_client_cookie_extension.should_send(client_conn)); + + /* Sent with a cookie */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[0], &client_conn->cookie)); + EXPECT_TRUE(s2n_client_cookie_extension.should_send(client_conn)); + }; + + /* Test: server only sends extension if cookie present + * (cookie will never be present in production) + */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + /* Not sent without a cookie */ + EXPECT_FALSE(s2n_server_cookie_extension.should_send(server_conn)); + + /* Sent with a cookie */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[0], &server_conn->cookie)); + EXPECT_TRUE(s2n_server_cookie_extension.should_send(server_conn)); + }; + + /* Test: client can parse server cookie extension */ + for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + DEFER_CLEANUP(struct s2n_stuffer server_extension = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_extension, 0)); + + /* Server sends extension with test cookie */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &server_conn->cookie)); + EXPECT_SUCCESS(s2n_server_cookie_extension.send(server_conn, &server_extension)); + + /* Client doesn't parse extension if no retry */ + EXPECT_FAILURE_WITH_ERRNO(s2n_server_cookie_extension.recv(client_conn, &server_extension), + S2N_ERR_UNSUPPORTED_EXTENSION); + + /* Client parses extension if retry */ + client_conn->handshake.handshake_type = HELLO_RETRY_REQUEST; + EXPECT_SUCCESS(s2n_server_cookie_extension.recv(client_conn, &server_extension)); + S2N_BLOB_EXPECT_EQUAL(test_cookies[i], client_conn->cookie); + } + + /* Test: client sends correctly formatted extension */ + for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + DEFER_CLEANUP(struct s2n_stuffer client_extension = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_extension, 0)); + + /* Client sends extension with test cookie */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &client_conn->cookie)); + EXPECT_SUCCESS(s2n_client_cookie_extension.send(client_conn, &client_extension)); + + /* Sanity check: Server rejects incorrectly sized cookie */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &server_conn->cookie)); + server_conn->cookie.size--; + EXPECT_FAILURE_WITH_ERRNO(s2n_client_cookie_extension.recv(server_conn, &client_extension), + S2N_ERR_BAD_MESSAGE); + EXPECT_SUCCESS(s2n_free(&server_conn->cookie)); + EXPECT_SUCCESS(s2n_stuffer_reread(&client_extension)); + + /* Sanity check: Server rejects incorrect cookie data */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &server_conn->cookie)); + server_conn->cookie.data[0] = server_conn->cookie.data[0] + 1; + EXPECT_FAILURE_WITH_ERRNO(s2n_client_cookie_extension.recv(server_conn, &client_extension), + S2N_ERR_BAD_MESSAGE); + EXPECT_SUCCESS(s2n_free(&server_conn->cookie)); + EXPECT_SUCCESS(s2n_stuffer_reread(&client_extension)); + + /* Server accepts correct cookie data */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &server_conn->cookie)); + EXPECT_SUCCESS(s2n_client_cookie_extension.recv(server_conn, &client_extension)); + } + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + /* Although the cookie is *technically* allowed to be UINT16_MAX, + * in reality it has to share a uint16_t extensions list length + * with other extensions. + * + * So for the self-talk tests, reduce the size of any large test cookies. + */ + for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { + test_cookies[i].size = MIN(test_cookies[i].size, UINT16_MAX / 2); + } + + /* Sanity check: server fails if client does not provide expected cookie */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Force the server to send a cookie */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[0], &server_conn->cookie)); + + /* Begin negotiating handshake. + * The first negotiate_until blocks because the client is looking for a SERVER_HELLO, + * not the HELLO_RETRY_MESSAGE. This is fine; it's in the right place in the handshake. + */ + EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, HELLO_RETRY_MSG), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), HELLO_RETRY_MSG); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, CLIENT_HELLO)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); + + /* Verify HRR path */ + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + + /* At this point, the server has already sent its HRR request with a cookie. + * The client has stored the server's cookie, but not responded. + * Wipe the cookie on the client, preventing it from sending the response. + */ + EXPECT_NOT_EQUAL(client_conn->cookie.size, 0); + EXPECT_SUCCESS(s2n_free(&client_conn->cookie)); + + /* Continue negotiating. We should fail because of the "missing" cookie. */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_MISSING_EXTENSION); + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /* Self-Talk: Server does NOT use cookies */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify HRR path */ + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + + /* Verify no cookies */ + EXPECT_EQUAL(client_conn->cookie.size, 0); + EXPECT_EQUAL(server_conn->cookie.size, 0); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /* Self-Talk: Server does use cookies + * + *= https://tools.ietf.org/rfc/rfc8446#section-4.2.2 + *= type=test + *# When sending a HelloRetryRequest, the server MAY provide a "cookie" + *# extension to the client (this is an exception to the usual rule that + *# the only extensions that may be sent are those that appear in the + *# ClientHello). When sending the new ClientHello, the client MUST copy + *# the contents of the extension received in the HelloRetryRequest into + *# a "cookie" extension in the new ClientHello. + */ + for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Force the server to send a cookie */ + EXPECT_SUCCESS(s2n_dup(&test_cookies[i], &server_conn->cookie)); + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify HRR path */ + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + + /* Verify cookies */ + S2N_BLOB_EXPECT_EQUAL(test_cookies[i], client_conn->cookie); + S2N_BLOB_EXPECT_EQUAL(test_cookies[i], server_conn->cookie); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + /* Self-Talk: Full connection lifecycle with cookies + * We try the handshake multiple times with different possible call patterns. + */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + /* We need an arbitrary combination of conditions, + * but consistent across test runs. + */ + srand(0); + + for (size_t i = 0; i < 250; i++) { + int r = rand(); + bool hrr = (r % 2) == 0; + bool cookie = (r % 3) == 0; + size_t cookie_i = i % TEST_COOKIE_COUNT; + bool free_handshake = (r % 7) == 0; + + /* Verify calls to s2n_connection_wipe are safe */ + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the HRR path */ + if (hrr) { + client_conn->security_policy_override = &security_policy_test_tls13_retry; + } + + /* Force the server to send a cookie */ + if (cookie) { + EXPECT_SUCCESS(s2n_dup(&test_cookies[cookie_i], &server_conn->cookie)); + } + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify HRR path */ + if (hrr) { + EXPECT_TRUE(s2n_is_hello_retry_handshake(server_conn)); + EXPECT_TRUE(s2n_is_hello_retry_handshake(client_conn)); + } else { + EXPECT_FALSE(s2n_is_hello_retry_handshake(server_conn)); + EXPECT_FALSE(s2n_is_hello_retry_handshake(client_conn)); + } + + /* Verify cookie data */ + if (hrr && cookie) { + S2N_BLOB_EXPECT_EQUAL(test_cookies[cookie_i], client_conn->cookie); + } else { + EXPECT_EQUAL(client_conn->cookie.size, 0); + } + + if (free_handshake) { + EXPECT_SUCCESS(s2n_connection_free_handshake(client_conn)); + EXPECT_SUCCESS(s2n_connection_free_handshake(server_conn)); + } + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + }; + + for (size_t i = 0; i < TEST_COOKIE_COUNT; i++) { + EXPECT_SUCCESS(s2n_free(&test_cookies[i])); + } + END_TEST(); +} diff --git a/tests/unit/s2n_crl_test.c b/tests/unit/s2n_crl_test.c new file mode 100644 index 00000000000..54cc3ce1c09 --- /dev/null +++ b/tests/unit/s2n_crl_test.c @@ -0,0 +1,930 @@ +/* +* 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 "tls/s2n_crl.h" + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +#define S2N_CRL_ROOT_CERT "../pems/crl/root_cert.pem" +#define S2N_CRL_NONE_REVOKED_CERT_CHAIN "../pems/crl/none_revoked_cert_chain.pem" +#define S2N_CRL_NONE_REVOKED_KEY "../pems/crl/none_revoked_key.pem" +#define S2N_CRL_INTERMEDIATE_REVOKED_CERT_CHAIN "../pems/crl/intermediate_revoked_cert_chain.pem" +#define S2N_CRL_INTERMEDIATE_REVOKED_KEY "../pems/crl/intermediate_revoked_key.pem" +#define S2N_CRL_LEAF_REVOKED_CERT_CHAIN "../pems/crl/leaf_revoked_cert_chain.pem" +#define S2N_CRL_LEAF_REVOKED_KEY "../pems/crl/leaf_revoked_key.pem" +#define S2N_CRL_ALL_REVOKED_CERT_CHAIN "../pems/crl/all_revoked_cert_chain.pem" +#define S2N_CRL_ALL_REVOKED_KEY "../pems/crl/all_revoked_key.pem" +#define S2N_CRL_ROOT_CRL "../pems/crl/root_crl.pem" +#define S2N_CRL_INTERMEDIATE_CRL "../pems/crl/intermediate_crl.pem" +#define S2N_CRL_INTERMEDIATE_REVOKED_CRL "../pems/crl/intermediate_revoked_crl.pem" +#define S2N_CRL_INTERMEDIATE_INVALID_THIS_UPDATE_CRL "../pems/crl/intermediate_invalid_this_update_crl.pem" +#define S2N_CRL_INTERMEDIATE_INVALID_NEXT_UPDATE_CRL "../pems/crl/intermediate_invalid_next_update_crl.pem" + +#define CRL_TEST_CHAIN_LEN 2 + +struct crl_lookup_data { + struct s2n_crl *crls[5]; + X509 *certs[5]; + uint8_t callback_invoked_count; +}; + +static int crl_lookup_test_callback(struct s2n_crl_lookup *lookup, void *context) +{ + struct crl_lookup_data *crl_data = (struct crl_lookup_data *) context; + crl_data->callback_invoked_count += 1; + crl_data->certs[lookup->cert_idx] = lookup->cert; + + struct s2n_crl *crl = crl_data->crls[lookup->cert_idx]; + if (crl == NULL) { + POSIX_GUARD(s2n_crl_lookup_ignore(lookup)); + } else { + POSIX_GUARD(s2n_crl_lookup_set(lookup, crl)); + } + + return 0; +} + +static int crl_lookup_noop(struct s2n_crl_lookup *lookup, void *context) +{ + return 0; +} + +static int crl_lookup_callback_fail(struct s2n_crl_lookup *lookup, void *context) +{ + return 1; +} + +static uint8_t verify_host_always_allow(const char *host_name, size_t host_name_len, void *data) +{ + return 1; +} + +static struct s2n_crl *load_test_crl(const char *pem_path) +{ + uint8_t crl_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint32_t pem_len = 0; + PTR_GUARD_POSIX(s2n_read_test_pem_and_len(pem_path, crl_pem, &pem_len, S2N_MAX_TEST_PEM_SIZE)); + DEFER_CLEANUP(struct s2n_crl *crl = s2n_crl_new(), s2n_crl_free); + PTR_ENSURE_REF(crl); + PTR_GUARD_POSIX(s2n_crl_load_pem(crl, crl_pem, pem_len)); + + struct s2n_crl *crl_ret = crl; + ZERO_TO_DISABLE_DEFER_CLEANUP(crl); + + return crl_ret; +} + +int main(int argc, char *argv[]) +{ + BEGIN_TEST(); + + /* s2n_crl_new allocates and frees a s2n_crl */ + { + struct s2n_crl *crl = s2n_crl_new(); + EXPECT_NOT_NULL(crl); + + EXPECT_SUCCESS(s2n_crl_free(&crl)); + EXPECT_NULL(crl); + + /* Multiple calls to free succeed */ + EXPECT_SUCCESS(s2n_crl_free(&crl)); + EXPECT_NULL(crl); + }; + + /* s2n_crl_new allocates and frees a s2n_crl with an internal X509_CRL set */ + { + struct s2n_crl *crl = load_test_crl(S2N_CRL_ROOT_CRL); + EXPECT_NOT_NULL(crl); + EXPECT_NOT_NULL(crl->crl); + + EXPECT_SUCCESS(s2n_crl_free(&crl)); + EXPECT_NULL(crl); + + /* Multiple calls to free succeed */ + EXPECT_SUCCESS(s2n_crl_free(&crl)); + EXPECT_NULL(crl); + }; + + /* Ensure s2n_crl_load_pem produces a valid X509_CRL internally */ + { + DEFER_CLEANUP(struct s2n_crl *crl = load_test_crl(S2N_CRL_ROOT_CRL), s2n_crl_free); + EXPECT_NOT_NULL(crl); + EXPECT_NOT_NULL(crl->crl); + + /* Make sure an OpenSSL operation succeeds on the internal X509_CRL */ + X509_NAME *crl_name = X509_CRL_get_issuer(crl->crl); + POSIX_ENSURE_REF(crl_name); + }; + + /* s2n_crl_load_pem fails if provided a bad pem */ + { + uint8_t crl_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint32_t crl_pem_len = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_CRL_ROOT_CRL, crl_pem, &crl_pem_len, S2N_MAX_TEST_PEM_SIZE)); + DEFER_CLEANUP(struct s2n_crl *crl = s2n_crl_new(), s2n_crl_free); + EXPECT_NOT_NULL(crl); + EXPECT_SUCCESS(s2n_crl_load_pem(crl, crl_pem, crl_pem_len)); + + /* Change a random byte in the pem to make it invalid */ + crl_pem[50] = 1; + + DEFER_CLEANUP(struct s2n_crl *invalid_crl = s2n_crl_new(), s2n_crl_free); + EXPECT_NOT_NULL(invalid_crl); + EXPECT_FAILURE_WITH_ERRNO(s2n_crl_load_pem(invalid_crl, crl_pem, crl_pem_len), + S2N_ERR_INVALID_PEM); + }; + + /* CRL issuer hash is retrieved successfully */ + { + DEFER_CLEANUP(struct s2n_crl *crl = load_test_crl(S2N_CRL_ROOT_CRL), s2n_crl_free); + EXPECT_NOT_NULL(crl); + + uint64_t hash = 0; + EXPECT_SUCCESS(s2n_crl_get_issuer_hash(crl, &hash)); + EXPECT_TRUE(hash != 0); + }; + + DEFER_CLEANUP(struct s2n_crl *root_crl = load_test_crl(S2N_CRL_ROOT_CRL), s2n_crl_free); + EXPECT_NOT_NULL(root_crl); + + DEFER_CLEANUP(struct s2n_crl *intermediate_crl = load_test_crl(S2N_CRL_INTERMEDIATE_CRL), s2n_crl_free); + EXPECT_NOT_NULL(intermediate_crl); + + DEFER_CLEANUP(struct s2n_crl *intermediate_revoked_crl = load_test_crl(S2N_CRL_INTERMEDIATE_REVOKED_CRL), s2n_crl_free); + EXPECT_NOT_NULL(intermediate_revoked_crl); + + DEFER_CLEANUP(struct s2n_crl *intermediate_invalid_this_update_crl = + load_test_crl(S2N_CRL_INTERMEDIATE_INVALID_THIS_UPDATE_CRL), + s2n_crl_free); + EXPECT_NOT_NULL(intermediate_invalid_this_update_crl); + + DEFER_CLEANUP(struct s2n_crl *intermediate_invalid_next_update_crl = + load_test_crl(S2N_CRL_INTERMEDIATE_INVALID_NEXT_UPDATE_CRL), + s2n_crl_free); + EXPECT_NOT_NULL(intermediate_invalid_next_update_crl); + + /* Save a list of received X509s for s2n_crl_lookup tests */ + struct crl_lookup_data received_lookup_data = { 0 }; + DEFER_CLEANUP(struct s2n_x509_validator received_lookup_data_validator, s2n_x509_validator_wipe); + + /* CRL validation succeeds for unrevoked certificate chain */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + EXPECT_SUCCESS(s2n_x509_validator_init(&received_lookup_data_validator, &trust_store, 0)); + + received_lookup_data.crls[0] = intermediate_crl; + received_lookup_data.crls[1] = root_crl; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &received_lookup_data)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&received_lookup_data_validator, connection, chain_data, + chain_len, &pkey_type, &public_key_out)); + EXPECT_TRUE(received_lookup_data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + + /* Ensure all certificates were received in the callback */ + for (int i = 0; i < CRL_TEST_CHAIN_LEN; i++) { + EXPECT_NOT_NULL(received_lookup_data.certs[i]); + } + }; + + /* CRL validation errors when a leaf certificate is revoked */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_crl; + data.crls[1] = root_crl; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_LEAF_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, + chain_len, &pkey_type, &public_key_out), + S2N_ERR_CERT_REVOKED); + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* CRL validation errors when an intermediate certificate is revoked */ + for (int i = 0; i < 2; i++) { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_revoked_crl; + data.crls[1] = root_crl; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + if (i == 0) { + /* Ensure CRL validation fails when only the intermediate certificate is revoked */ + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_INTERMEDIATE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + } else if (i == 1) { + /* Ensure CRL validation fails when both the intermediate and leaf certificates are revoked */ + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_ALL_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + } + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, + chain_len, &pkey_type, &public_key_out), + S2N_ERR_CERT_REVOKED); + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + } + + /* CRL validation fails when a certificate is rejected from the callback */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + struct crl_lookup_data data = { 0 }; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, + chain_len, &pkey_type, &public_key_out), + S2N_ERR_CRL_LOOKUP_FAILED); + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* CRL validation succeeds for unrevoked certificate chain when extraneous certificate is rejected */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_crl; + data.crls[1] = root_crl; + + /* Reject the extraneous cert */ + data.crls[2] = NULL; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + uint8_t cert_chain_pem[S2N_MAX_TEST_PEM_SIZE * 2]; + uint32_t pem_len_1 = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_CRL_NONE_REVOKED_CERT_CHAIN, cert_chain_pem, &pem_len_1, + S2N_MAX_TEST_PEM_SIZE)); + + /* Add an arbitrary cert to the chain that won't be included in the chain of trust */ + uint32_t pem_len_2 = 0; + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_2048_SHA256_CLIENT_CERT, cert_chain_pem + pem_len_1, + &pem_len_2, S2N_MAX_TEST_PEM_SIZE)); + + uint32_t cert_chain_len = pem_len_1 + pem_len_2; + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem_data(connection, cert_chain_pem, cert_chain_len, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, + &public_key_out)); + EXPECT_TRUE(data.callback_invoked_count == 3); + }; + + /* s2n_x509_validator_validate_cert_chain blocks until all CRL callbacks respond */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_noop, NULL)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + /* Blocks if no response received from callbacks */ + for (int i = 0; i < 10; i++) { + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_ASYNC_BLOCKED); + } + + /* Continues to block if only one callback has sent a response */ + struct s2n_crl_lookup *lookup = NULL; + EXPECT_OK(s2n_array_get(validator.crl_lookup_list, 0, (void **) &lookup)); + EXPECT_NOT_NULL(lookup); + EXPECT_SUCCESS(s2n_crl_lookup_set(lookup, root_crl)); + for (int i = 0; i < 10; ++i) { + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_ASYNC_BLOCKED); + } + + /* Unblocks when all callbacks send a response */ + lookup = NULL; + EXPECT_OK(s2n_array_get(validator.crl_lookup_list, 1, (void **) &lookup)); + EXPECT_NOT_NULL(lookup); + EXPECT_SUCCESS(s2n_crl_lookup_set(lookup, intermediate_crl)); + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, + &public_key_out)); + }; + + /* CRL validation fails when a callback returns unsuccessfully */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_callback_fail, NULL)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, + chain_len, &pkey_type, &public_key_out), + S2N_ERR_CANCELLED); + }; + + /* CRL validation succeeds for a CRL with an invalid thisUpdate date */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_invalid_this_update_crl; + data.crls[1] = root_crl; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, + &public_key_out)); + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* CRL validation fails for a revoked leaf certificate, with a CRL that has an invalid thisUpdate date */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_invalid_this_update_crl; + data.crls[1] = root_crl; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_LEAF_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_REVOKED); + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* CRL validation succeeds for a CRL with an invalid nextUpdate date */ + for (int disable_time_validation = 0; disable_time_validation <= 1; disable_time_validation += 1) { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_invalid_next_update_crl; + data.crls[1] = root_crl; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + /* Ensure that validation succeeds for a CRL with an invalid nextUpdate field when time + * validation is disabled. + */ + if (disable_time_validation) { + EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(config)); + } + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_NONE_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, + &public_key_out)); + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* CRL validation fails for a revoked leaf certificate, with a CRL that has an invalid nextUpdate date */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char root_cert[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_CRL_ROOT_CERT, root_cert, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, root_cert)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_invalid_next_update_crl; + data.crls[1] = root_crl; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_CRL_LEAF_REVOKED_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_REVOKED); + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* Self-talk: server certificate is not revoked */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_CRL_NONE_REVOKED_CERT_CHAIN, S2N_CRL_NONE_REVOKED_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_CRL_ROOT_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_crl; + data.crls[1] = root_crl; + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* Self-talk: server certificate is revoked */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_CRL_LEAF_REVOKED_CERT_CHAIN, S2N_CRL_LEAF_REVOKED_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_CRL_ROOT_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_crl; + data.crls[1] = root_crl; + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_CERT_REVOKED); + + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* Self-talk: client certificate is not revoked */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *server_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&server_chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, server_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_CRL_ROOT_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *client_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&client_chain_and_key, + S2N_CRL_NONE_REVOKED_CERT_CHAIN, S2N_CRL_NONE_REVOKED_KEY)); + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, client_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_REQUIRED)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_crl; + data.crls[1] = root_crl; + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(server_config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(server_conn, verify_host_always_allow, NULL)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "S2nTestServer")); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* Self-talk: client certificate is revoked */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *server_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&server_chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, server_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, S2N_CRL_ROOT_CERT, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *client_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&client_chain_and_key, + S2N_CRL_LEAF_REVOKED_CERT_CHAIN, S2N_CRL_LEAF_REVOKED_KEY)); + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, client_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_REQUIRED)); + + struct crl_lookup_data data = { 0 }; + data.crls[0] = intermediate_crl; + data.crls[1] = root_crl; + EXPECT_SUCCESS(s2n_config_set_crl_lookup_cb(server_config, crl_lookup_test_callback, &data)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(server_conn, verify_host_always_allow, NULL)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "S2nTestServer")); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_CERT_REVOKED); + + EXPECT_TRUE(data.callback_invoked_count == CRL_TEST_CHAIN_LEN); + }; + + /* Calling s2n_crl_lookup return functions correctly set context fields */ + { + struct s2n_crl_lookup lookup = { 0 }; + + lookup.status = AWAITING_RESPONSE; + EXPECT_SUCCESS(s2n_crl_lookup_set(&lookup, root_crl)); + EXPECT_TRUE(lookup.status == FINISHED); + EXPECT_NOT_NULL(lookup.crl); + + lookup.status = AWAITING_RESPONSE; + EXPECT_SUCCESS(s2n_crl_lookup_ignore(&lookup)); + EXPECT_TRUE(lookup.status == FINISHED); + EXPECT_NULL(lookup.crl); + }; + + /* Certificate issuer hash is retrieved successfully */ + { + struct s2n_crl_lookup lookup = { 0 }; + EXPECT_NOT_NULL(received_lookup_data.certs[0]); + lookup.cert = received_lookup_data.certs[0]; + + uint64_t hash = 0; + EXPECT_SUCCESS(s2n_crl_lookup_get_cert_issuer_hash(&lookup, &hash)); + EXPECT_TRUE(hash != 0); + }; + + /* Retrieved hash values for certificates match CRL hashes */ + { + /* The hash of the leaf certificate matches the hash of the intermediate CRL */ + + struct s2n_crl_lookup leaf_lookup = { 0 }; + EXPECT_NOT_NULL(received_lookup_data.certs[0]); + leaf_lookup.cert = received_lookup_data.certs[0]; + + uint64_t leaf_cert_hash = 0; + EXPECT_SUCCESS(s2n_crl_lookup_get_cert_issuer_hash(&leaf_lookup, &leaf_cert_hash)); + EXPECT_TRUE(leaf_cert_hash != 0); + + uint64_t intermediate_crl_hash = 0; + EXPECT_SUCCESS(s2n_crl_get_issuer_hash(intermediate_crl, &intermediate_crl_hash)); + EXPECT_TRUE(intermediate_crl_hash != 0); + + EXPECT_TRUE(leaf_cert_hash == intermediate_crl_hash); + + /* The hash of the intermediate certificate matches the hash of the root CRL */ + + struct s2n_crl_lookup intermediate_lookup = { 0 }; + EXPECT_NOT_NULL(received_lookup_data.certs[1]); + intermediate_lookup.cert = received_lookup_data.certs[1]; + + uint64_t intermediate_cert_hash = 0; + EXPECT_SUCCESS(s2n_crl_lookup_get_cert_issuer_hash(&intermediate_lookup, &intermediate_cert_hash)); + EXPECT_TRUE(intermediate_cert_hash != 0); + + uint64_t root_crl_hash = 0; + EXPECT_SUCCESS(s2n_crl_get_issuer_hash(root_crl, &root_crl_hash)); + EXPECT_TRUE(root_crl_hash != 0); + + EXPECT_TRUE(intermediate_cert_hash == root_crl_hash); + + /* If the certificate and CRL were issued by different CAs, their hashes should not match */ + EXPECT_TRUE(leaf_cert_hash != root_crl_hash); + }; + + /* s2n_crl_validate_active tests */ + { + /* Succeeds for valid CRL */ + EXPECT_SUCCESS(s2n_crl_validate_active(intermediate_crl)); + + /* Succeeds for expired CRL */ + EXPECT_SUCCESS(s2n_crl_validate_active(intermediate_invalid_next_update_crl)); + + /* Fails for CRL that is not yet valid */ + EXPECT_FAILURE_WITH_ERRNO(s2n_crl_validate_active(intermediate_invalid_this_update_crl), + S2N_ERR_CRL_NOT_YET_VALID); + }; + + /* s2n_crl_validate_not_expired tests */ + { + /* Succeeds for valid CRL */ + EXPECT_SUCCESS(s2n_crl_validate_not_expired(intermediate_crl)); + + /* Succeeds for CRL that is not yet valid */ + EXPECT_SUCCESS(s2n_crl_validate_not_expired(intermediate_invalid_this_update_crl)); + + /* Fails for expired CRL */ + EXPECT_FAILURE_WITH_ERRNO(s2n_crl_validate_not_expired(intermediate_invalid_next_update_crl), + S2N_ERR_CRL_EXPIRED); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_drain_alert_test.c b/tests/unit/s2n_drain_alert_test.c new file mode 100644 index 00000000000..90356bf0842 --- /dev/null +++ b/tests/unit/s2n_drain_alert_test.c @@ -0,0 +1,140 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" + +#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F + +#define INTERNAL_ERROR_ALERT_HEX 0x50 + +/* This test simulates a client sends a TLS alert record and closes its socket immediately after the ClientHello. + * We want to validate that s2n informs the caller of the alert instead of an I/O error. Both errors result + * in a failed handshake, but the alert is generally more useful. + */ + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + uint8_t client_hello_message[] = { + /* Protocol version TLS 1.2 */ + 0x03, 0x03, + /* Client random */ + ZERO_TO_THIRTY_ONE, + /* SessionID len - 32 bytes */ + 0x20, + /* Session ID */ + ZERO_TO_THIRTY_ONE, + /* Cipher suites len */ + 0x00, 0x02, + /* Cipher suite - TLS_RSA_WITH_AES_128_CBC_SHA256 */ + 0x00, 0x3C, + /* Compression methods len */ + 0x01, + /* Compression method - none */ + 0x00, + /* Extensions len */ + 0x00, 0x00 + }; + size_t body_len = sizeof(client_hello_message); + uint8_t message_header[] = { + /* Handshake message type CLIENT HELLO */ + 0x01, + /* Body len */ + (body_len >> 16) & 0xff, + (body_len >> 8) & 0xff, + (body_len & 0xff), + }; + size_t message_len = sizeof(message_header) + body_len; + uint8_t record_header[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Message len */ + (message_len >> 8) & 0xff, + (message_len & 0xff), + }; + + uint8_t alert_record[] = { + /* Record type ALERT */ + 0x15, + /* Protocol version TLS 1.2 */ + 0x03, + 0x03, + /* Length */ + 0x00, + 0x02, + /* Fatal alert "internal_error" */ + 0x02, + INTERNAL_ERROR_ALERT_HEX, + }; + + struct s2n_connection *server_conn; + struct s2n_config *server_config; + s2n_blocked_status server_blocked; + char *cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE); + char *private_key = malloc(S2N_MAX_TEST_PEM_SIZE); + struct s2n_cert_chain_and_key *chain_and_key; + + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + /* Security policy must allow cipher suite hard coded into client hello */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all")); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Send the client hello */ + EXPECT_EQUAL(write(io_pair.client, record_header, sizeof(record_header)), sizeof(record_header)); + EXPECT_EQUAL(write(io_pair.client, message_header, sizeof(message_header)), sizeof(message_header)); + EXPECT_EQUAL(write(io_pair.client, client_hello_message, sizeof(client_hello_message)), sizeof(client_hello_message)); + + /* Send an alert from client to server */ + EXPECT_EQUAL(write(io_pair.client, alert_record, sizeof(alert_record)), sizeof(alert_record)); + + /* Close the client read/write end */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + /* Expect the server to fail due to an incoming alert. We should not fail due to an I/O error(EPIPE). */ + s2n_negotiate(server_conn, &server_blocked); + EXPECT_EQUAL(s2n_error_get_type(s2n_errno), S2N_ERR_T_ALERT); + EXPECT_EQUAL(s2n_connection_get_alert(server_conn), INTERNAL_ERROR_ALERT_HEX); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + free(cert_chain); + free(private_key); + + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + END_TEST(); +} diff --git a/tests/unit/s2n_drbg_test.c b/tests/unit/s2n_drbg_test.c new file mode 100644 index 00000000000..91a8d8a9d99 --- /dev/null +++ b/tests/unit/s2n_drbg_test.c @@ -0,0 +1,456 @@ +/* + * 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 "crypto/s2n_drbg.h" + +#include +#include +#include +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_config.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" +#include "utils/s2n_timer.h" + +/* Test vectors are taken from https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/drbg/drbgtestvectors.zip + * - drbgvectors_pr_true/CTR_DRBG.txt : + * [AES-128 no df] + * [PredictionResistance = True] + * [EntropyInputLen = 256] + * [NonceLen = 0] + * [PersonalizationStringLen = 256] + * [AdditionalInputLen = 0] + * [ReturnedBitsLen = 512] + */ + +struct s2n_stuffer nist_aes128_reference_entropy = { 0 }; +const char nist_aes128_reference_entropy_hex[] = + "528ccd2d6c143800a34ad33e7f153cfaceaa2411abbaf4bfcfe9796898d0ece6" + "478fd1eaa7ed293294d370979b0f0f1948d5a3161b12eeebf2cf6bd1bf059adf" + "036bf1173977b323f60c6f0f603d9c50835b2afca8347dfb24e8f66604444951" + "53e5783fd27cda264189ff0aa5513dc5903c28e3e260b77b4b4a0b9b76e4aa46" + "e04bade7636e72397f8303437cb22be52810dadd768d5cfc9d3269e7ad4bc8bc" + "e20dabb9fcaf3aed0ec40c7ef68d78f2bcb5675db2d62ee40d8184b7046f0e0e" + "f3b247d0400740d18b795e8e9c04cc43eb138cb9eb6c46862030517d8b3679db" + "0603b481bb88d77d6bacf03a64cf82248420a95cca10e3efc012bd770dd5621c" + "233b1e550a0d1249a5bd09ccb9a88be9119258feadfc309a6ae3c340918545b9" + "9d97ce8452474bc19a4be64efd43484874074779e05ac221f19db3955ce29bc1" + "5c0ef27b53eb7903cc9e2bc709f9e885eec139ec687a3444450f0cb079ca5171" + "0e26d3e8d2fd18fe0ce2538c2ede5d61574c8227b3f82b333ac42dbdd78b8e19" + "12f68f049216e3987860651f6fd13efef184fc8fe05cac459eb3cf97dd957e6c" + "ccfabc0f847801b342774ce50d80c600af2d1f2016e1aea412fc4c60b2a041b1" + "07e192e576bb031bb8baa60b9374dcc1c40cb43ab42035719588e2afd75fc8f0" + "6129a163efde5f14067e075c4790029652a56eedf8ea4877a0ffcedd32866166" + "356ade73b8ffb38408be0537ca539b633d0afffcf2962b717ac37eb2a06ddf50" + "0b857fe137cab149e9195019732a714e25e62c946eba58dd2fa8e6c797172bcb" + "310d2e7b249ca8fdf1d4b15612f6e1f5428241c7ad777281171fdcecf7f5ed44" + "241e8fe2d6de5efb6509144c8dba00a582d1aebfabd306052747fc311d7c5a56" + "a2c100da70d8659d94a4d6a0f10594c8412c977168df242a35d5c2600a47084b" + "f5771eeacddef0c6cbfac5ac4285205f110fd6a8174ad45748a8633a28c85ec2" + "0621babc5c0ea527ca7c8d42338e16b6113b387ba888a6c12779cc127561a148" + "672df1d58510c016913fc6c42771273f7cbaeef1993dbabcd6cca26758bdb7df" + "7a9365ebb48fdd83809167cb2bfebc37b69b34595b2f8d81cff2924a70e300d5" + "954e64b6f4f1d7e0135eb2a9174612d23111329c00bded4cecf6b2b2fdfad145" + "e6495728cb28b8f64b208afa9e1012f061c790d285663f5cf0760c9f7c4bd32f" + "bd10e4b282c98b6c2e0451272a332c24f54a2a45244e9889420a48786f662aed" + "5a1cf5b5212ae2a776ae4f688ea9e6124f624c817b5e429b8cc2ad730cf29f6e" + "fcca996bf011ba139b7bd05e31c594d459685aa143e0ee46d6db0ba8582a6fd6" + "9009b2d3b9ef7fd1e925d75d6ad07d42e165310153ed1ef9e1284894e0d16575" + "13f8641a1d9cd50966b4fff6f2a5a9f67aefa8d7b5cb527d3b73338aea773ee1" + "4645aa5ebb41f27380328161864cca36055c871356316bf3773d35687fe72874" + "87e58c3c91ae0935b7935d2d0a855e795327062238180f20b02f9c9c861cbc33" + "28c5691d0c0ff0d834cb0c85a82fe561f40803f63beb3e740215e5d5089fc554" + "fd22324369fd30e2f47553c012ce2c46fd40df5fcdd737bf266cd20bcdc20a98" + "f3291f1d5fa47abf1918416a2cb78b243e614f883bda2afc10ffc1cc7f7f06a8" + "c9cbe9f41e2d81f99ca6327ca617caf836b73512d96b1edc5ae5ed56c056cade" + "47ebfc4437f769ac299f0d87069e5d6074420113761a9f9e053866581e836701" + "bcb51bc0dfae0fb04bf08face7369981d9a78bbcc0d3da2a1442d8fef756c1c0" + "b74a392be091fe00eb6a6576f027073a838308f5a5aed7fd0db47d6616cbec98" + "d39a307b956d1183f718be7d91a11891dc3fee45652edb89449d9766fc85bb32" + "5565457cf60a3aff9ea6f3dc6254a76a085ff87f1dd19127b2aeb3ac50b8c31e" + "9dfd31e3adc822b675c0a9c8702df12de4c3354ccdb5389ed481d910b3dbb51a" + "2a1b1519d22d40ef4ec23c6d97dc148cfe171fb5f8b1c305ec6d8e83a1ef9064"; + +const char nist_aes128_reference_personalization_strings_hex[] = + "07ca7a007b53f014d7f10461d6c97b5c8b0c502d3461d583f99efec69f121cd2" + "99f2d501dd183f5657d8b8061bf4f0988de3bc2719f41281cf08d9bcc99f0dcb" + "617d6463eaf3849e13b89a7f9805edff3ea1f0aa7ad71e7de7af3481bfeba7f5" + "7f7d81a5c72f9e3498de183329d6b2291792ac8c81e690200387305aba8987c7" + "0c70a20b690bf5d586b284aa752ec73055e039233089a30efdd6218a1e49f548" + "1fe2b0ce3a1896017e30753064e1c0e1341b673569c739a199cd218fe64665d0" + "2703e1dca9926995705229cc8a8c9d958bcdbfd5f3e09eeb349b5135686aeed1" + "9ca4b8e6cdec1ecd9546508542c84936ff2aa1a4dab969613139c01e35f36d71" + "0f533937857dc9f97fefa142c95445b484e1f259a488e9fa38a46e49d693c4b6" + "950472ba4b3b6979ba4d2a1ad51db7732eb3337b28270fdb7f99018144734f72" + "06bd36977e09cc13d7c9edc9a285043f7c00575610e9b95fd3754a4b7c561fb7" + "6dacec09cc407ddf545c4d54c77efd712f65be8ba5ecbc9fa6bdf29243eed72e" + "85facc571d2c6eb257437e1d3b77b5fba4a125606440181b8387f9a4fb1a7fbf" + "2c4e73ad2597b027dd04c6056db1189b7a0d8e9fece52237cb7fc2511509c4eb" + "717c4de30c0ac7b831807cb1fcf4ab37f1734683a723d183fcfe5fc8a4c6c7e5"; + +/* clang-format off */ +const char nist_aes128_reference_values_hex[] = +"462eaef2ff6d82aec55f451776700e4c" "010cd7c293306adbe9798f2f65bdfb01" "f6f808dd7e199b3ce8497d63515092df" +"1e574e0a9b220668776a109ecec959f5" "f47380b9e6d8ce485a5bb9f890331f89" "f2b475b29ed8aca7f3a69477212153e3" +"d63aa6ddf10dfb6934b7a745456f2056" "29c05402d0dd6ff1d171c523e6066b3d" "fb15e2dfd607439797e29f9ea9a24788" +"601d313b010af1930132417697d9e27e" "b17422b4a74cc83de34c7196cd232355" "b63cf4e0894e185eb7ef572c2adacf98" +"a7ec1f62b063acd9904d2ca4b26e755c" "b98e1a708027be7d5f0ff46f775ceece" "5b5adf71f7d8c85a011acc778c4e3d6f" +"6536d316f19bd244ca1a2deba572face" "0ce6ba1487b38261f306c84642338b53" "a19af0020f46085ca3caf34b2cb4f1f5" +"cac724dc3e214ff8d0ac4f60ee2dfded" "67138c2c7a488b2f4449b03192aa54f8" "d10073624d73847284c91b46e28b83c3" +"edadadc2ad451ea48ab9619d6c89cdcb" "24f64c09499b4803a03aadf0e34240ca" "8ac5b02284cac91bb39e14bd3d38ae2b" +"31f21cce9f11c7e9047e3ebad7c23a1b" "6b59014f0e7e8df5f7456017841e9c63" "4cfc026d6bc19e9674ec7002bf0baf62" +"d871c3f06cdf34c0cebb8b405aa79be7" "6af76a8c51f305b88249af9db4eacfe4" "ff77327ab3761736e40f79ca0a6660e2" +"9eedbc9923b20434c175c066ed3584ba" "06ebe4bd2ea20c39ef8f4f66796816bb" "497b437274f3d7cee04b163c10233aec" +"7fca6267fd42102de5baacb7b4409565" "1310719710bd2c3468f8000fad5b6f4c" "34e5d8e0f7e6c28718a5f27ac086516b" +"9948b0263f2c91756050fad1f5d7876f" "9aff44fbc0f539186d13e81a9b233786" "0a86ad5766efe6d3fa01a9ac4ec9090d" +"a022dfed4c805b8f2c15d81693edfb53" "9d4f527ab03ff0c4abe7e442572fc0bb" "5fc9cb9e3cd694c6b08bb99256062814" +"faa46432da44e336bd782edd85ccfa83" "c17b86c7fd8e5a2131f515fb0ab62c9b" "619ff6d15579a1cb4bc9c96f6be8f4e5"; +/* clang-format on */ +const char nist_aes128_reference_returned_bits_hex[] = + "bba1ef50b4bad288897f02ac2706ee1e01488dcbe9b3d8a637921f5e788faed3b23db63d590ceaa7607a2179192bea9aeaa85d048e7e108fee666dc646af5f0e" + "b9ecf5521d38f9212578f9dd32af38089b45812ca96e661fc8e1be0b2f4b54215f52c9c93a6f76efee4521cb316378403108a6bf2724fdc93bc4d2db60a2de83" + "ce4bec8241dd1ce12d7fa18bd1a3188b43892392b7dcae7228a851afef9c9728c587167b6df28c895a67af35b6fd84ad076efcd70d1c59c49fa7c0f7ed87bc82" + "94001b011830bc6a911fa23bc1fd0dac8c0acd0e856ab4497ad072e54fe3b47bc06890e7e7babd4912af1e7da96e90523b75dd86d9a4ba4d88107de607e52534" + "d0619996c88423a740f04dfdad3bfc131bf3601fc43022e89957bf8b9a1ad8702bc8fbb2004e7ab02550777818e11f03fab48458424ae5c8cc8cb73436657292" + "a2613ddb75107ef3fe9fc392061a52ea2304af7aed6dedec3aee626e1ab2bf989ce7d6484555941e9a4013036c4605c564f17d4d1369e1e12d28b877d2125d99" + "48460543d08b3331f0c7bcf786be06a276efc757bedce0f428323d54206931f18c3c7c606c0ff8e673f3d1367c7d50db2ed002120ff05623d2deddd037c125a4" + "037a0305532b6bd2a51057962f23dbfd4660be8e5b7a448b1168be3bf8598a301fb517a4714b7826162fc0fcdae08800e967f638ff1ad1da39282d3454d93075" + "035c19396d603b52222c16af6c6bc1c07a1a518f578b59943ffad73ca14948b7a8dde117e5c571506d57fd08e3a067a2ae3bde2240c2399f160c5cc5a2f5d582" + "e17674172397369025d6b7811b69b6e62dfb8ab94852cc96bde1371fcedbe5fe31a11589f4e57183fb46883d93c647e36f70f8d5a536f8fb0d428dcfd7722e4e" + "cbedccaa88b22e39fb0e14bf15dc05e4e1b7002fa0cffe7803e3f6bcf6a03f3faa51ce5b3ccb0d341533d335e20f9f71e90f83fef06ecfe93ed056f5d6851306" + "116bd7ab4030379cd6f50e85a04182775292fa9619c38b7418a19b8e7855f29efffdf7a2b8b1d9d3d96ea85a6d56302014dd3bcbe401ada5b0e3cf2f66dce9cd" + "51e0b54c109884baecd1884f7bafc846ace216b6fd97eb1ca70b563e62c4a2f22b55561152f379326ef2999e9746f25043a02402d3e47b4a58e747c222b7a081" + "7c41adf941656cfb9f24409d6cc4d578d43930b3e23ec801a59c53d999401bc0cb3e5b8797b2770a8a8f51ff594b7b17d9e694d5e36644508d16cb2554057adc" + "ac054570b081cf53b39b0a2faa21ee9b554c05ff9055843ac0eb9031d1de324701ad4cf2875623e0bf4184de4aea20070be1cb586880ac87fbb7e414b4b128d0"; + +/* Test vectors are taken from https://csrc.nist.gov/CSRC/media/Projects/Cryptographic-Algorithm-Validation-Program/documents/drbg/drbgtestvectors.zip + * - drbgvectors_pr_true/CTR_DRBG.txt : + * [AES-256 no df] + * [PredictionResistance = True] + * [EntropyInputLen = 384] + * [NonceLen = 0] + * [PersonalizationStringLen = 384] + * [AdditionalInputLen = 0] + * [ReturnedBitsLen = 512] + */ + +struct s2n_stuffer nist_aes256_reference_entropy = { 0 }; +const char nist_aes256_reference_entropy_hex_part1[] = + "c8f0c7b9bdf7e7d524c998aedeabb3b7dd4fa8f95c51b582010a5e09d0b4b1ad510302422df738fbef002a051543b4cc" + "c1a7b160c8e33a01fbd49743dc1161539390d9ba6b876fe63b58e8fd605b98617322578b17aca9db71e858b154f97910" + "d98b25eda41d15eacdecba586609d8c743704fa099ac37f9185bcb19652723d1648431a73c4399773c85caf12fcdd842" + "5fb3f302c36d0d0e28efb065df595ea69a6b3eb45622f9d14038ebcc0d4a8b0d6aa45e681edd5c506e00f0dc12775ac4" + "6440cc8c735ca9b05f261358e51b8b4f8c4bbdee9e2681c76fb552bb6b62334038bd8e0394a4d50f6af7a7a920379799" + "2ed7e6118394ce10a842d609baabfaef9d1c1fea914be4b725822f943fda023244f806a47b944ea0c1b853f36bfa24f7" + "f3fb24592e88aa2bb3921b46e198310fd269883eb68a7831e5627a5a6d21543864ede3fbc8badc1888b0daf37a97dd40" + "deda2a043d035744b5d0e696e496382ff0c01cc79c9f8f588b40d934237fdf7d0f9ea389cf28a352917c724106417d92" + "dccffb5e3cbf5e36eb1ca92db6247e1d6dc777e591893e220b5cf55c50c74bae2995affb003a8306197771737aaddf64" + "8a0a52b1e228c26be6497b5cad0677cfaa2c42a4fc429c4bade7a468ce681a732f92dcebdf5b3d9a0dcfdb5c11a830d4" + "51a2f38792fb87570747a54b7a3f5473583faaceacd70cd5b5336e8ecbbcc286627943999210573ee7f8f29437f74b02" + "77efd90bf6faab8ed0bb32f138fec4fe8afd7e1a55b2e60d5d500ebd57ed382af97d9ea89375fc56b511cb674e76e4f4" + "4256c1c6dcb9463f751d97c2b9a1df629d2f6db19dfce80da4624e5ceb2403ae6f1906f2d8e34cb015525b27bb25c262" + "8ba03c2283b97a37063a4597f7ca15fb2df7b0de1e2236b8338f14539c059d1a47a947bc9a3ef954c833fffaff8fa8ff" + "c5d3a88de9db79dd707187dfe5d35443f7eada1669d5daf39af4d78fff3fd35f0e81922a207e640cba2c9fb18280f62d" + "6e92427a94816c838ef57f9d98941ffc3cd070c76fcc19e765f9b1f9d390ec99f0b5193fd72a3d700deaeffea6c2282e" + "8e7159350759fe73e6c51ab224eda1cd769c703a42a79d3c4ce407aa99c8427f872b7c3e09559c789cb75c546ddeb988" + "db12fb9c2f1cc022768f824dfd05edc6d57184ee0e51ce8decc74c390adee63ee401a7d3bb0a91c1d50fd2239dc3fa57" + "14932f8ee3ec2119e03b65dde53c2c4156b8013c4d1c260227b240af61bbd0efedcdcd4fbc828dc688cef0b25bb12374" + "a5d063e966ebfa8bb05fc0c0625a30d0a408e5e31ec9667374ccccbcc07f6c289ecdf3deb3f784925e7e72c75105bf54" + "31d8ab37201b55662101ad235883c28ecfb5c9e3cf0498f6cd563db96f622e5656967256c7c3efc2ca3c577be02de3ce" + "a7d7b56bdf38b57d7418ae3f3a90375d7aafc3839a2956f6824d1c55d615966207a5ef31e2bf8359db1070612ad6d775"; +const char nist_aes256_reference_entropy_hex_part2[] = + "f480e7c970c02a5a64b09f0473d04b658e19332634043569e24996981f7d515d326ad1129661638753d117e8d31a540f" + "0854d1a0aa78e3e60b6825d143eaa97ec7ddd1b8b85c26b625fe4da7973d643cd0629186c42928c8e46857a603e5ac0c" + "89dfbb4a10bad1c269341b7815ac1388a7d28bff8393e078d99a0cdf12d0f28a24e45441c86584829ce37d4eb3b28873" + "6d14b671eaa66e217dd973047b6180b7dbd2c29b709fce014bcfda25e269bf5a736d7154308aa0597c5f5adc3eb4cdd9" + "7c0a71a8f56ac0b5766525b0f63afe48f4a0d282adf300f9da47ac62693f0d74f6739128c61024a22f7469d822382cfb" + "d0daca073b0c1517b9d6643c518130580f254b482d4c44b735590e167b06b304e0664b8e12d9c8dd083f4b758e7b3d81" + "4441277388baf0ae4d69b0c1db2b4d767647730e10cf1191cd10fd2eba570d348e544cea0641af89418c900ccbf2ca20" + "2180dc1f5f17f5f8cbb8499e155de4bd50e0f2175041c24d1c72f654af8a08563b1c42a84c5ae126ef33e1909bcee38f" + "48e6177813ad0dd8da8506a91fe0d4e615bdd068b423050a1dfc94e1cbaa2bbeb2f92561391a4f54143ac46a74d57c03" + "f9d761f589d8e466123257d4b144e40106be6a063643547b8549733134b3375715157246a9bf9a91f854a37ce7efb3e1" + "b774616e89a2297ce5feea2f5708eb0fd21f3ae188ae25c64c8bcee73be40db2af5d8e640ccd48b8cdc7375e5c53cad7" + "3507214c359f4ab75904dd4e430ccfd484b853b40b7253d430111588410f1c9fc41e757f669d3de45a3c0efdeb250bd3" + "8364b816f4c64359e54415082fd5b04cb896bd5de5527b0930fc6a855d2a31b55cba81ab25b019adc7cc7c7a6f308c4c" + "ab2a1d12b1997f5a1b74d6608e60f09682924fcc4270fd5fab8e84f2be19306e49537a40add14ba9b1472f80a97ade7d" + "e6a8dd29ea2f9f2a4595aa1b9600831d1647f53f366724517569e2848f5a7ccb4bd2f553f83f91c14c2361cbc3ece926" + "ac25bc0603a569b90efc796aea338684d84c03240d1bb3c9d3b24627fb24235d6fb837fc382c6c949ebc911abadfe9b5" + "60fe48c071073a815ee75a6b66e0cec21fa510eb721a10cea64eed977efe89af922f8bedade893212e6a253169821860" + "7f1a2afae1e642c3348a0ee56a0f4ebf136d68f8f04de54b3beece7828aa43171d09570bb70fca53775e4770e3d4cb84" + "edab61da555eeff611c0428a25200003a5ac1c4978a766514e9222c80adda7194b8414835f0fae94264d3d81d49bec13" + "7810ed74bdb095e1a3a8a2b1fd885e21c3a23317b5a4514a9bae2d0072826bfc292a231500703ab0a1f04ce35bd3d808" + "f72592e0626539903bfe7926f2df89428ffe4eceeb57f0be46f5d4bb0ab8c70633431c70056e051b2a44a99fb2decc80" + "2b3877ed4d1b3174d36e784ad7b6b7991dd52979ca5b1c4cc5f415ea56aa78f2486e2b033570089aa0e8f5d4be3ee140" + "91d421c3eb04de94099a7467254bad70e236d5c27616f766e85b4de3965001db854e61a80bfec2eacb4ff93ecafb8b83"; + +const char nist_aes256_reference_personalization_strings_hex[] = + "3b8f2ed03e4c4a857ebaf4730124ae5b3ffa67e294381fe085e1d5b25bb79c228f39b9912d8bf9e84cdb43fcefb552e0" + "e4afd48a08355ccbd097bf5c87e9aa6cca5bce84b211ef0c783d7e05cd52caae4b4d83de53a84200a556b5aff5047b13" + "a1a853ffa798f93a604874d4c2c7fcf2ac4a6ed1e07e03336b7e3ddbf603c19f9d8b3041cbe3ac09e96c86c8bdebaad6" + "9367c213593e3a82059d3c80513c301f0c840c900ae8f18b5be558d1fb8bd528dbf93e6efb7f3cd8fed577a3e299bc76" + "88d9b7c50d99c926f035085a7a8f02698fde845db85088e866187407bdf75b2c6cb628d5081c859a5d738826d2e8c1ee" + "6d7621ae460ae10c8c8d0e11c80aa444e279f2cef7da2a0e52fe43edfa025c67ba0111b6a2774b0620eae65aa2bc292c" + "3dc52487f866eef27a6aaa0f3da87c09c859babbb73e1771626ca434534a66b02e561363db90798edb55cced2746767a" + "015c5a1d1f9e2aeb34713767a58d01b0055a27151bafbd0afc31fc22d765e57f65ffc282aba726a2ec5a7c36a33c0ecc" + "5235d1b8ea0e83618a755c598fa8e74d6e425f946249e80f12d85d3e1c547d5048f235687dc93c86cbb691384f889408" + "06170a3a66308cea0ab1f44e688e7a4f69a4fe435cc325dc554771b4156614c77d3c21377ca552e6d5ac9b7ffb52f1a3" + "767fa94128aa6e71dfbfb4c16be33c48941c5ccbf96e8094365f581810ce94b3feb8960b99eb925b0c6184db40343849" + "806f8ff25ac8c4d61fe8cc895f5c5d2e153701b5c2cc8ef6b4ff7ec5f6273b285695000016e7d5a6145f4f98f4a42bf9" + "dfa7445d60ac908bb031bcb4f9feed626afab1e4aef021862e1fcdb34fddf9028b6226dfb11f677dca420ceb11930587" + "b1805d90d9e43a9e509f5f71bad514b548808b993cfaef7a69279239e687f4cbe3a6ba579e11a4fa2b2450f82c2fd31a" + "27b9250c01189a8a675d597cd2e476d11f4feaa29ce2d9e729eeede42d9b78521937d240b12db5493aee96b84102cd9c"; + +/* clang-format off */ +const char nist_aes256_reference_values_hex[] = +"ac5ab81937daeb6772799c778ff0d3a2" "0eebb4180d8410794daeaafc1847c923" "70c7d3292e845bdf82fd274edd68a653" +"5389de7c7ad334241af4b0fd92751459" "75c74d6c01f589d3ad73a3a5b623e7bb" "d6c396bf4a3e833cd2d606fce7d81981" +"8b06d07034ff5a65b07ea9b5b27a4218" "0ee02cd9df5f2bd1ada6f1f9ba7f6056" "8965850a9a42cb15dd40acdc5a9fb677" +"860be14f13822b3622b859718637b92c" "9d344fb6d571418dbbfe63bed5bf948d" "3f86d6e5294dd831d702c43b8ef27b48" +"71cf2dede759e35e9983268f1ccb3602" "4a656c7375adb348950a73236fd94f3c" "157ecd447d923208706e95561b3d882e" +"38d40b4342fb5c02fca2fc2a7178348c" "65a6f202aec5a1fcdbdcf1cc456e1ec5" "1271bbfb300f823f19fedd138118fc60" +"b1fbdde650b4de3c8239c9d109f16080" "78151d8dba8d945a0feea6b2e1adec27" "a3da7d1591fa63595a3c4cf7c4671bff" +"103a2e797ebe8f8fe6e8f9d9fcecec37" "8067a5626c79baec12ba054032d6c5a7" "1f7232216822ed37039b6e46db4d81d5" +"1e7662e3820a927086f719f8893c29f5" "3d8195aa8d54d162d3b8c7dd2194c9aa" "8b3b81b54c750c1f795dd6f009b9f0f9" +"ef3a697359dab04f0c312584002ff9ac" "aba26d5d26ee7f035d3f87bf5886ebc5" "a14740caa86474ad9e2b843f49dcc482" +"3e21b0a09757f77bc9f9b53f41e771c4" "bbb03998195593d46bdbe7b1be8533e4" "41ccfeb0a667ee27ae8370efb2afbd9f" +"e0eb76b547dcc2369fc1b4eb6a8715a4" "1c4a394a564ce025ecd6278a7a314403" "3e37edd1aeb247aa4a6116b54ba29dae" +"b2d0d0467e86dcc857c398aea779d92f" "695104cd3099d7f3320ee44505729da5" "55b9cb44f0a6d2b4a5ee6a8aa7d5c7c1" +"8ccfee961eb844dd8dd8e206bafd2d10" "ea3a81303d10eb79c8b26cbb42969a4d" "cad6784d017fedabef3bbf374b06ac2b" +"5814cdfa83e59a26c108caa986da3492" "503e3029d555fb6bcd4be394d8ed4638" "969f3ea8c1ab6986630149eff81d2162"; +/* clang-format on */ +const char nist_aes256_reference_returned_bits_hex[] = + "338da59350a72bba94b217b99ff813fafa85f31fa7e991d4350d6fce39471aa249c6a9e7d189649ea6770a15be302b943ad403728b7387f5984c9bfb82597771" + "be9333140428553231505e8ce3f789f2860f44647d97239c0d34cee98e0e4f5e5a5eb296d4cec508624913080a1b44990993a2ae63f61124c4d8184f191ca8ae" + "cdecc083b5c4bd38b573c5e109fc1cbf2dbc92982ed6d24cf4d2cb06383ba27d51dc064a8cf42205420d1c62505b987c774502b93e837485284a137cf2380872" + "ab1478cc75bbfdc930fefeff1e6f8e7b822213976fc556ee7f71550d128620766d8353b3c54c59934e7fa57e8b857c7daef8e9d82705fd1fec7b19d5c3b5af4f" + "f64d6ac3fd9beda9ea4b3fc2921e967d126ab4e747eb4f29bfd949f94119271052ff52501cb45af6bf9499ef5926423f61bd4dac0b28fc95aa886c8226ea27b4" + "45e3bfafaa00941459b373aa00e6096b7527110f4c6e4e7d6661829dfe4ad83ab23a5779dc7f8116b73b929fc3da43ad346a3e11364e1b453e66335b5cbf59b1" + "8bc98d27050e713d19551eadae19d14f3b1304aa6f736d9bec6c7cc90c85412a4e6a7218b8a7ce46b1cb93fa1b296eee789aae0cb2a58fe7a80e26f0a1018dc8" + "794128d681d355336148eeb4dd882d76ef984f2a8ceb4b451e9b37beac0fb50247bd595ede2de000f4397988d3f52e0b3b293208c16b5f45d6032bc5ef20b3c8" + "3571383b971291e37616f1c6af623a1d576cc5f95090563654420f184fc6663242aeffb06052a29e84380db38d9f3ed09823dfef1774c14120fb010600b7b274" + "b7723a01d35011a9a8e0e82a8ade37d5e992d8cc5390e2409e185a1bcede39b742fa1fa541572aa36b16be410f3ca0ca853ffd7cea2996e852b888fe9b1263f9" + "0d42ad75a67008674be7d81bb2ca9f0625b55ea37e1985d3a27cac38e347824ff03caaaeae9b646a220a8a672cb28ba869d0f53e3317be00128fa965a8ea7b39" + "d5be1698e011122874ea19c562859e552c68d2734a39f72d6f8eca8ce30916504bf224670f9a7b6afb57830a28fec0fe818df449f5e6eb8b49a3397df4bd3506" + "c834a90e72a5ec0148942c9fbde8b90f058866ac3ff8cc854bda3d33f6072bc0e5a22c7d0cd801f091276ed7b7e3d6495df9546b9fc45e46aa6b89d3dda207cc" + "6553c630a07ddf7dc2110c3aa3c5ac0c488a0345e07571cd71df115ba37ea4676935be72a6033aeca7ac6fcce5654dae38f5777b5cff34b156539b42ed6dc93c" + "ed703d9273bb9462ac400ee8d587ea3c4d6c27aa014defcb6ca6fe885272bcb4b6ba0822f42941071bf635b41d997c631b680d91b23ee48351041dc274900821"; + +int nist_fake_entropy_init_cleanup(void) +{ + return 0; +} + +int nist_fake_128_entropy_data(void *data, uint32_t size) +{ + struct s2n_blob blob = { 0 }; + POSIX_GUARD(s2n_blob_init(&blob, data, size)); + + POSIX_GUARD(s2n_stuffer_read(&nist_aes128_reference_entropy, &blob)); + + return 0; +} + +int nist_fake_256_entropy_data(void *data, uint32_t size) +{ + struct s2n_blob blob = { 0 }; + POSIX_GUARD(s2n_blob_init(&blob, data, size)); + + POSIX_GUARD(s2n_stuffer_read(&nist_aes256_reference_entropy, &blob)); + + return 0; +} + +int check_drgb_version(s2n_drbg_mode mode, int (*generator)(void *, uint32_t), int personalization_size, + const char personalization_hex[], const char reference_values_hex[], const char returned_bits_hex[]) +{ + DEFER_CLEANUP(struct s2n_stuffer personalization = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer returned_bits = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer reference_values = { 0 }, s2n_stuffer_free); + POSIX_GUARD(s2n_stuffer_alloc_ro_from_hex_string(&personalization, personalization_hex)); + POSIX_GUARD(s2n_stuffer_alloc_ro_from_hex_string(&returned_bits, returned_bits_hex)); + POSIX_GUARD(s2n_stuffer_alloc_ro_from_hex_string(&reference_values, reference_values_hex)); + + for (int i = 0; i < 14; i++) { + uint8_t ps[S2N_DRBG_MAX_SEED_SIZE] = { 0 }; + struct s2n_drbg nist_drbg = { 0 }; + struct s2n_blob personalization_string = { 0 }; + POSIX_GUARD(s2n_blob_init(&personalization_string, ps, personalization_size)); + + /* Read the next personalization string */ + POSIX_GUARD(s2n_stuffer_read(&personalization, &personalization_string)); + + /* Over-ride the entropy sources */ + POSIX_GUARD(s2n_rand_set_callbacks(nist_fake_entropy_init_cleanup, nist_fake_entropy_init_cleanup, generator, generator)); + + /* Instantiate the DRBG */ + POSIX_GUARD_RESULT(s2n_drbg_instantiate(&nist_drbg, &personalization_string, mode)); + + uint8_t nist_v[16]; + + POSIX_GUARD(s2n_stuffer_read_bytes(&reference_values, nist_v, sizeof(nist_v))); + POSIX_ENSURE_EQ(memcmp(nist_v, nist_drbg.v, sizeof(nist_drbg.v)), 0); + + /* Generate 512 bits (FIRST CALL) */ + uint8_t out[64]; + struct s2n_blob generated = { 0 }; + POSIX_GUARD(s2n_blob_init(&generated, out, 64)); + POSIX_GUARD_RESULT(s2n_drbg_generate(&nist_drbg, &generated)); + + POSIX_GUARD(s2n_stuffer_read_bytes(&reference_values, nist_v, sizeof(nist_v))); + POSIX_ENSURE_EQ(memcmp(nist_v, nist_drbg.v, sizeof(nist_drbg.v)), 0); + + /* Generate another 512 bits (SECOND CALL) */ + POSIX_GUARD_RESULT(s2n_drbg_generate(&nist_drbg, &generated)); + + POSIX_GUARD(s2n_stuffer_read_bytes(&reference_values, nist_v, sizeof(nist_v))); + POSIX_ENSURE_EQ(memcmp(nist_v, nist_drbg.v, sizeof(nist_drbg.v)), 0); + + uint8_t nist_returned_bits[64]; + POSIX_GUARD(s2n_stuffer_read_bytes(&returned_bits, nist_returned_bits, + sizeof(nist_returned_bits))); + POSIX_ENSURE_EQ(memcmp(nist_returned_bits, out, sizeof(nist_returned_bits)), 0); + + if (mode == S2N_AES_128_CTR_NO_DF_PR || mode == S2N_AES_256_CTR_NO_DF_PR) { + POSIX_ENSURE_EQ(nist_drbg.mixes, 2); + } else { + POSIX_BAIL(S2N_ERR_DRBG); + } + + POSIX_GUARD_RESULT(s2n_drbg_wipe(&nist_drbg)); + } + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + uint8_t data[256] = { 0 }; + struct s2n_drbg aes128_drbg = { 0 }; + struct s2n_drbg aes256_pr_drbg = { 0 }; + struct s2n_blob blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&blob, data, 64)); + + EXPECT_OK(s2n_drbg_instantiate(&aes128_drbg, &blob, S2N_AES_128_CTR_NO_DF_PR)); + EXPECT_OK(s2n_drbg_instantiate(&aes256_pr_drbg, &blob, S2N_AES_256_CTR_NO_DF_PR)); + + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + + /* Use the AES128 DRBG for 32MB of data */ + for (int i = 0; i < 500000; i++) { + EXPECT_OK(s2n_drbg_generate(&aes128_drbg, &blob)); + } + EXPECT_EQUAL(aes128_drbg.mixes, 500000); + + /* Use the AES256 DRBG with prediction resistance for 32MB of data */ + for (int i = 0; i < 500000; i++) { + EXPECT_OK(s2n_drbg_generate(&aes256_pr_drbg, &blob)); + } + EXPECT_EQUAL(aes256_pr_drbg.mixes, 500000); + + /* NOTE: s2n_random_test also includes monobit tests for this DRBG */ + /* the DRBG state is 128 bytes, test that we can get more than that */ + blob.size = 129; + for (int i = 0; i < 10; i++) { + EXPECT_OK(s2n_drbg_generate(&aes128_drbg, &blob)); + EXPECT_OK(s2n_drbg_generate(&aes256_pr_drbg, &blob)); + } + EXPECT_EQUAL(aes128_drbg.mixes, 500010); + EXPECT_EQUAL(aes256_pr_drbg.mixes, 500010); + + /* Check that ignoring prediction resistance works */ + EXPECT_OK(s2n_ignore_prediction_resistance_for_testing(true)); + uint64_t aes128_drbg_mixes_start = aes128_drbg.mixes; + uint64_t aes256_pr_drbg_mixes_start = aes256_pr_drbg.mixes; + for (int i = 0; i < 10; i++) { + EXPECT_OK(s2n_drbg_generate(&aes128_drbg, &blob)); + EXPECT_OK(s2n_drbg_generate(&aes256_pr_drbg, &blob)); + } + EXPECT_EQUAL(aes128_drbg.mixes, aes128_drbg_mixes_start); + EXPECT_EQUAL(aes256_pr_drbg.mixes, aes256_pr_drbg_mixes_start); + + /* Check that we can enable prediction resistance again */ + EXPECT_OK(s2n_ignore_prediction_resistance_for_testing(false)); + for (int i = 0; i < 10; i++) { + EXPECT_OK(s2n_drbg_generate(&aes128_drbg, &blob)); + EXPECT_OK(s2n_drbg_generate(&aes256_pr_drbg, &blob)); + } + EXPECT_EQUAL(aes128_drbg.mixes, aes128_drbg_mixes_start + 10); + EXPECT_EQUAL(aes256_pr_drbg.mixes, aes256_pr_drbg_mixes_start + 10); + + /* Generate 31 (= 16 + 15) bytes. Since the DRBG generates 16 bytes at a time, + * a common error is to incorrectly fill the last (not-aligned) bytes. Sometimes + * they are left unchanged and sometimes a single byte is copied in. We ensure + * that the last 15 bytes are not all equal to guard against this. */ + POSIX_CHECKED_MEMSET((void *) data, 0, 31); + blob.size = 31; + EXPECT_OK(s2n_drbg_generate(&aes128_drbg, &blob)); + bool bytes_are_all_equal = true; + for (size_t i = 17; i < 31; i++) { + if (data[16] != data[i]) { + bytes_are_all_equal = false; + break; + } + } + EXPECT_FALSE(bytes_are_all_equal); + + POSIX_CHECKED_MEMSET((void *) data, 0, 31); + blob.size = 31; + EXPECT_OK(s2n_drbg_generate(&aes256_pr_drbg, &blob)); + bytes_are_all_equal = true; + for (size_t i = 17; i < 31; i++) { + if (data[16] != data[i]) { + bytes_are_all_equal = false; + break; + } + } + EXPECT_FALSE(bytes_are_all_equal); + + EXPECT_OK(s2n_drbg_wipe(&aes128_drbg)); + EXPECT_OK(s2n_drbg_wipe(&aes256_pr_drbg)); + + /* Check everything against the NIST AES 128 vectors with prediction resistance */ + EXPECT_SUCCESS(s2n_stuffer_alloc_ro_from_hex_string(&nist_aes128_reference_entropy, nist_aes128_reference_entropy_hex)); + EXPECT_SUCCESS(check_drgb_version(S2N_AES_128_CTR_NO_DF_PR, &nist_fake_128_entropy_data, 32, nist_aes128_reference_personalization_strings_hex, + nist_aes128_reference_values_hex, nist_aes128_reference_returned_bits_hex)); + + /* Check everything against the NIST AES 256 vectors with prediction resistance */ + DEFER_CLEANUP(struct s2n_stuffer temp1 = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer temp2 = { 0 }, s2n_stuffer_free); + + /* Combine nist_aes256_reference_entropy_hex_part1 and nist_aes256_reference_entropy_hex_part2 to avoid C99 + * string length limit. */ + EXPECT_SUCCESS(s2n_stuffer_alloc_ro_from_hex_string(&temp1, nist_aes256_reference_entropy_hex_part1)); + EXPECT_SUCCESS(s2n_stuffer_alloc_ro_from_hex_string(&temp2, nist_aes256_reference_entropy_hex_part2)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&nist_aes256_reference_entropy, temp1.write_cursor + temp2.write_cursor)); + EXPECT_SUCCESS(s2n_stuffer_copy(&temp1, &nist_aes256_reference_entropy, temp1.write_cursor)); + EXPECT_SUCCESS(s2n_stuffer_copy(&temp2, &nist_aes256_reference_entropy, temp2.write_cursor)); + + EXPECT_SUCCESS(check_drgb_version(S2N_AES_256_CTR_NO_DF_PR, &nist_fake_256_entropy_data, 48, nist_aes256_reference_personalization_strings_hex, + nist_aes256_reference_values_hex, nist_aes256_reference_returned_bits_hex)); + + EXPECT_SUCCESS(s2n_stuffer_free(&nist_aes128_reference_entropy)); + EXPECT_SUCCESS(s2n_stuffer_free(&nist_aes256_reference_entropy)); + + EXPECT_SUCCESS(s2n_config_free(config)); + + END_TEST(); +} diff --git a/tests/unit/s2n_early_data_io_api_test.c b/tests/unit/s2n_early_data_io_api_test.c new file mode 100644 index 00000000000..966da872a56 --- /dev/null +++ b/tests/unit/s2n_early_data_io_api_test.c @@ -0,0 +1,1096 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_early_data.h" + +#define BUFFER_SIZE 100 + +#define EXPECT_NOT_BLOCKED(conn, blocked, expected_msg) \ + EXPECT_EQUAL((blocked), S2N_NOT_BLOCKED); \ + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), (expected_msg)) +#define EXPECT_BLOCKED_ON_EARLY_DATA(result) EXPECT_FAILURE_WITH_ERRNO((result), S2N_ERR_EARLY_DATA_BLOCKED) +#define EXPECT_BLOCKED_ON_IO(result) EXPECT_FAILURE_WITH_ERRNO((result), S2N_ERR_IO_BLOCKED) +#define EXPECT_BLOCKED_ON(conn, blocked, expected_blocked, expected_msg) \ + EXPECT_EQUAL((blocked), (expected_blocked)); \ + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), (expected_msg)) + +static S2N_RESULT s2n_test_client_and_server_new(struct s2n_connection **client_conn, struct s2n_connection **server_conn) +{ + *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(*client_conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(*client_conn, "default_tls13")); + + *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(*server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(*server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(*server_conn, "default_tls13")); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(*client_conn, *server_conn, &io_pair)); + + return S2N_RESULT_OK; +} + +uint8_t s2n_allowed_reads = 0; +static int s2n_blocking_buffer_read(void *io_context, uint8_t *buf, uint32_t len) +{ + struct s2n_stuffer *in = (struct s2n_stuffer *) io_context; + + bool would_block = s2n_stuffer_data_available(in) < len; + if (would_block || !s2n_allowed_reads) { + errno = EAGAIN; + return -1; + } + s2n_allowed_reads--; + POSIX_GUARD(s2n_stuffer_read_bytes(in, buf, len)); + return len; +} + +uint8_t s2n_allowed_writes = 0; +static int s2n_blocking_buffer_write(void *io_context, const uint8_t *buf, uint32_t len) +{ + struct s2n_stuffer *out = (struct s2n_stuffer *) io_context; + + bool would_block = !out->growable && s2n_stuffer_space_remaining(out) < len; + if (would_block || !s2n_allowed_writes) { + errno = EAGAIN; + return -1; + } + s2n_allowed_writes--; + POSIX_GUARD(s2n_stuffer_write_bytes(out, buf, len)); + return len; +} + +static S2N_RESULT s2n_test_set_blocking_stuffer_io( + struct s2n_connection *client_conn, struct s2n_connection *server_conn, + struct s2n_stuffer *client_in, struct s2n_stuffer *server_in) +{ + RESULT_GUARD_POSIX(s2n_stuffer_alloc(client_in, S2N_TLS_MAXIMUM_RECORD_LENGTH)); + RESULT_GUARD_POSIX(s2n_stuffer_alloc(server_in, S2N_TLS_MAXIMUM_RECORD_LENGTH)); + + RESULT_GUARD_POSIX(s2n_connection_set_recv_cb(client_conn, &s2n_blocking_buffer_read)); + RESULT_GUARD_POSIX(s2n_connection_set_recv_ctx(client_conn, client_in)); + RESULT_GUARD_POSIX(s2n_connection_set_recv_cb(server_conn, &s2n_blocking_buffer_read)); + RESULT_GUARD_POSIX(s2n_connection_set_recv_ctx(server_conn, server_in)); + + RESULT_GUARD_POSIX(s2n_connection_set_send_cb(client_conn, &s2n_blocking_buffer_write)); + RESULT_GUARD_POSIX(s2n_connection_set_send_ctx(client_conn, server_in)); + RESULT_GUARD_POSIX(s2n_connection_set_send_cb(server_conn, &s2n_blocking_buffer_write)); + RESULT_GUARD_POSIX(s2n_connection_set_send_ctx(server_conn, client_in)); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + const uint8_t test_data[] = "hello world"; + + /* Malformed record: empty handshake record */ + uint8_t malformed_record[] = { + TLS_HANDSHAKE, 0x03, 0x03, 0x00, 0x04, + TLS_FINISHED, 0x00, 0x00, 0x00 + }; + + DEFER_CLEANUP(struct s2n_psk *test_psk = s2n_external_psk_new(), s2n_psk_free); + EXPECT_SUCCESS(s2n_psk_set_identity(test_psk, test_data, sizeof(test_data))); + EXPECT_SUCCESS(s2n_psk_set_secret(test_psk, test_data, sizeof(test_data))); + EXPECT_SUCCESS(s2n_psk_configure_early_data(test_psk, UINT32_MAX, 0x13, 0x01)); + + DEFER_CLEANUP(struct s2n_psk *test_psk_without_early_data = s2n_external_psk_new(), s2n_psk_free); + EXPECT_SUCCESS(s2n_psk_set_identity(test_psk_without_early_data, test_data, sizeof(test_data))); + EXPECT_SUCCESS(s2n_psk_set_secret(test_psk_without_early_data, test_data, sizeof(test_data))); + + DEFER_CLEANUP(struct s2n_psk *test_psk_with_wrong_early_data = s2n_external_psk_new(), s2n_psk_free); + EXPECT_SUCCESS(s2n_psk_set_identity(test_psk_with_wrong_early_data, test_data, sizeof(test_data))); + EXPECT_SUCCESS(s2n_psk_set_secret(test_psk_with_wrong_early_data, test_data, sizeof(test_data))); + EXPECT_SUCCESS(s2n_psk_configure_early_data(test_psk_with_wrong_early_data, UINT32_MAX, 0x13, 0x03)); + EXPECT_SUCCESS(s2n_psk_set_application_protocol(test_psk_with_wrong_early_data, test_data, sizeof(test_data))); + + /* Test s2n_send_early_data */ + { + /* Safety checks */ + { + struct s2n_connection conn = { 0 }; + conn.mode = S2N_CLIENT; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t data_size = 0; + uint8_t data = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_send_early_data(NULL, &data, 1, &data_size, &blocked), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_send_early_data(&conn, &data, 1, NULL, &blocked), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_send_early_data(&conn, &data, 1, &data_size, NULL), S2N_ERR_NULL); + + conn.mode = S2N_SERVER; + EXPECT_FAILURE_WITH_ERRNO(s2n_send_early_data(&conn, &data, 1, &data_size, &blocked), S2N_ERR_SERVER_MODE); + }; + + /* Propagate errors from s2n_negotiate */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, S2N_DEFAULT_RECORD_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, S2N_DEFAULT_RECORD_LENGTH)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, client_conn)); + + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&input, malformed_record, sizeof(malformed_record))); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t data_size = 0; + + EXPECT_FAILURE_WITH_ERRNO(s2n_send_early_data(client_conn, test_data, sizeof(test_data), &data_size, &blocked), + S2N_ERR_BAD_MESSAGE); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + EXPECT_EQUAL(data_size, 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_stuffer_free(&output)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* Propagate errors from s2n_send */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, S2N_DEFAULT_RECORD_LENGTH)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, client_conn)); + + /* Indicate that we're already sending. That will cause an error. */ + client_conn->send_in_use = true; + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t data_size = 0; + + EXPECT_FAILURE_WITH_ERRNO(s2n_send_early_data(client_conn, test_data, sizeof(test_data), &data_size, &blocked), + S2N_ERR_REENTRANCY); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + EXPECT_EQUAL(data_size, 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&output)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + }; + + /* s2n_recv_early_data */ + { + /* Safety checks */ + { + struct s2n_connection conn = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t data_size = 0; + uint8_t data = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_recv_early_data(NULL, &data, 1, &data_size, &blocked), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_recv_early_data(&conn, &data, 1, NULL, &blocked), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_recv_early_data(&conn, &data, 1, &data_size, NULL), S2N_ERR_NULL); + + conn.mode = S2N_CLIENT; + EXPECT_FAILURE_WITH_ERRNO(s2n_recv_early_data(&conn, &data, 1, &data_size, &blocked), S2N_ERR_CLIENT_MODE); + }; + + /* Propagate errors from s2n_negotiate */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk)); + + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, S2N_DEFAULT_RECORD_LENGTH)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&input, malformed_record, sizeof(malformed_record))); + + uint8_t payload[BUFFER_SIZE] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t data_size = 0; + + EXPECT_FAILURE_WITH_ERRNO(s2n_recv_early_data(server_conn, payload, sizeof(payload), &data_size, &blocked), + S2N_ERR_BAD_MESSAGE); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); + EXPECT_EQUAL(data_size, 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Propagate errors from s2n_recv */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_test_client_and_server_new(&client_conn, &server_conn)); + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk)); + + /* Indicate that we're already receiving. That will cause an error. */ + server_conn->recv_in_use = true; + + uint8_t payload[BUFFER_SIZE] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t data_size = 0; + + EXPECT_BLOCKED_ON_IO(s2n_send_early_data(client_conn, test_data, sizeof(test_data), &data_size, &blocked)); + EXPECT_BLOCKED_ON(client_conn, blocked, S2N_BLOCKED_ON_READ, SERVER_HELLO); + + EXPECT_FAILURE_WITH_ERRNO(s2n_recv_early_data(server_conn, payload, sizeof(payload), &data_size, &blocked), + S2N_ERR_REENTRANCY); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), END_OF_EARLY_DATA); + EXPECT_EQUAL(data_size, 0); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + }; + + /* Test sending and receiving early data */ + { + /* Send zero-length early data */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_test_client_and_server_new(&client_conn, &server_conn)); + + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t data_size = 0; + s2n_early_data_status_t status = 0; + + EXPECT_BLOCKED_ON_IO(s2n_send_early_data(client_conn, NULL, 0, &data_size, &blocked)); + EXPECT_BLOCKED_ON(client_conn, blocked, S2N_BLOCKED_ON_READ, SERVER_HELLO); + EXPECT_EQUAL(data_size, 0); + + EXPECT_SUCCESS(s2n_connection_get_early_data_status(client_conn, &status)); + EXPECT_EQUAL(status, S2N_EARLY_DATA_STATUS_OK); + + EXPECT_BLOCKED_ON_IO(s2n_recv_early_data(server_conn, NULL, 0, &data_size, &blocked)); + EXPECT_BLOCKED_ON(server_conn, blocked, S2N_BLOCKED_ON_READ, END_OF_EARLY_DATA); + EXPECT_EQUAL(data_size, 0); + + EXPECT_SUCCESS(s2n_connection_get_early_data_status(server_conn, &status)); + EXPECT_EQUAL(status, S2N_EARLY_DATA_STATUS_OK); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Send early data once */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_test_client_and_server_new(&client_conn, &server_conn)); + + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk)); + + uint8_t actual_payload[BUFFER_SIZE] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t data_size = 0; + s2n_early_data_status_t status = 0; + + EXPECT_BLOCKED_ON_IO(s2n_send_early_data(client_conn, test_data, sizeof(test_data), &data_size, &blocked)); + EXPECT_BLOCKED_ON(client_conn, blocked, S2N_BLOCKED_ON_READ, SERVER_HELLO); + EXPECT_EQUAL(data_size, sizeof(test_data)); + + EXPECT_SUCCESS(s2n_connection_get_early_data_status(client_conn, &status)); + EXPECT_EQUAL(status, S2N_EARLY_DATA_STATUS_OK); + + EXPECT_BLOCKED_ON_IO(s2n_recv_early_data(server_conn, actual_payload, sizeof(actual_payload), &data_size, &blocked)); + EXPECT_BLOCKED_ON(server_conn, blocked, S2N_BLOCKED_ON_READ, END_OF_EARLY_DATA); + EXPECT_EQUAL(data_size, sizeof(test_data)); + EXPECT_BYTEARRAY_EQUAL(actual_payload, test_data, sizeof(test_data)); + + EXPECT_SUCCESS(s2n_connection_get_early_data_status(server_conn, &status)); + EXPECT_EQUAL(status, S2N_EARLY_DATA_STATUS_OK); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Receive early data too large for buffer */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_test_client_and_server_new(&client_conn, &server_conn)); + + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk)); + + uint8_t actual_payload[BUFFER_SIZE] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t data_size = 0; + + EXPECT_BLOCKED_ON_IO(s2n_send_early_data(client_conn, test_data, sizeof(test_data), &data_size, &blocked)); + EXPECT_BLOCKED_ON(client_conn, blocked, S2N_BLOCKED_ON_READ, SERVER_HELLO); + EXPECT_EQUAL(data_size, sizeof(test_data)); + + EXPECT_BLOCKED_ON_EARLY_DATA(s2n_recv_early_data(server_conn, NULL, 0, &data_size, &blocked)); + EXPECT_BLOCKED_ON(server_conn, blocked, S2N_BLOCKED_ON_EARLY_DATA, END_OF_EARLY_DATA); + EXPECT_EQUAL(data_size, 0); + + EXPECT_BLOCKED_ON_EARLY_DATA(s2n_recv_early_data(server_conn, actual_payload, 1, &data_size, &blocked)); + EXPECT_BLOCKED_ON(server_conn, blocked, S2N_BLOCKED_ON_EARLY_DATA, END_OF_EARLY_DATA); + EXPECT_EQUAL(data_size, 1); + EXPECT_BYTEARRAY_EQUAL(actual_payload, test_data, 1); + + /* Remaining early data should block handshake. + * We can't successfully call s2n_negotiate again until we've drained all the early data + * via s2n_recv_early_data. For safety, we are not allowed to arbitrarily discard any early data. + */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + + /* Pretend we didn't test the above error condition. + * The S2N_ERR_BAD_MESSAGE error triggered S2N to close the connection. */ + s2n_atomic_flag_clear(&server_conn->write_closed); + s2n_atomic_flag_clear(&client_conn->write_closed); + + /* Read the remaining early data properly */ + EXPECT_SUCCESS(s2n_recv_early_data(server_conn, actual_payload, sizeof(actual_payload), + &data_size, &blocked)); + + EXPECT_NOT_BLOCKED(server_conn, blocked, APPLICATION_DATA); + EXPECT_EQUAL(data_size, sizeof(test_data) - 1); + EXPECT_BYTEARRAY_EQUAL(actual_payload, test_data + 1, sizeof(test_data) - 1); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Send multiple early data messages */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_test_client_and_server_new(&client_conn, &server_conn)); + + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk)); + + uint8_t actual_payload[BUFFER_SIZE] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t data_size = 0; + + EXPECT_BLOCKED_ON_IO(s2n_send_early_data(client_conn, test_data, sizeof(test_data), &data_size, &blocked)); + EXPECT_BLOCKED_ON(client_conn, blocked, S2N_BLOCKED_ON_READ, SERVER_HELLO); + EXPECT_EQUAL(data_size, sizeof(test_data)); + + EXPECT_BLOCKED_ON_IO(s2n_recv_early_data(server_conn, actual_payload, sizeof(actual_payload), &data_size, &blocked)); + EXPECT_BLOCKED_ON(server_conn, blocked, S2N_BLOCKED_ON_READ, END_OF_EARLY_DATA); + EXPECT_EQUAL(data_size, sizeof(test_data)); + EXPECT_BYTEARRAY_EQUAL(actual_payload, test_data, sizeof(test_data)); + + for (size_t i = 0; i < 10; i++) { + EXPECT_SUCCESS(s2n_send_early_data(client_conn, test_data, sizeof(test_data), + &data_size, &blocked)); + EXPECT_NOT_BLOCKED(client_conn, blocked, END_OF_EARLY_DATA); + EXPECT_EQUAL(data_size, sizeof(test_data)); + + EXPECT_BLOCKED_ON_IO(s2n_recv_early_data(server_conn, actual_payload, sizeof(actual_payload), &data_size, &blocked)); + EXPECT_BLOCKED_ON(client_conn, blocked, S2N_BLOCKED_ON_READ, END_OF_EARLY_DATA); + EXPECT_EQUAL(data_size, sizeof(test_data)); + EXPECT_BYTEARRAY_EQUAL(actual_payload, test_data, sizeof(test_data)); + } + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Receive and combine multiple early data records */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_test_client_and_server_new(&client_conn, &server_conn)); + + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk)); + + uint8_t actual_payload[BUFFER_SIZE] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t data_size = 0; + + const size_t send_count = 5; + for (size_t i = 0; i < send_count; i++) { + EXPECT_BLOCKED_ON_IO(s2n_send_early_data(client_conn, test_data, sizeof(test_data), &data_size, &blocked)); + EXPECT_BLOCKED_ON(client_conn, blocked, S2N_BLOCKED_ON_READ, SERVER_HELLO); + EXPECT_EQUAL(data_size, sizeof(test_data)); + } + + EXPECT_BLOCKED_ON_IO(s2n_recv_early_data(server_conn, actual_payload, sizeof(actual_payload), &data_size, &blocked)); + EXPECT_BLOCKED_ON(server_conn, blocked, S2N_BLOCKED_ON_READ, END_OF_EARLY_DATA); + EXPECT_EQUAL(data_size, sizeof(test_data) * send_count); + + struct s2n_blob payload_blob = { 0 }; + struct s2n_stuffer payload_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&payload_blob, actual_payload, sizeof(actual_payload))); + EXPECT_SUCCESS(s2n_stuffer_init(&payload_stuffer, &payload_blob)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&payload_stuffer, data_size)); + + uint8_t payload_chunk[sizeof(test_data)] = { 0 }; + for (size_t i = 0; i < send_count; i++) { + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&payload_stuffer, payload_chunk, sizeof(test_data))); + EXPECT_BYTEARRAY_EQUAL(payload_chunk, test_data, sizeof(test_data)); + } + EXPECT_EQUAL(s2n_stuffer_data_available(&payload_stuffer), 0); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Early data not requested */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_test_client_and_server_new(&client_conn, &server_conn)); + + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk_with_wrong_early_data)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk)); + + uint8_t actual_payload[BUFFER_SIZE] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t data_size = 0; + s2n_early_data_status_t status = 0; + + EXPECT_SUCCESS(s2n_send_early_data(client_conn, test_data, sizeof(test_data), + &data_size, &blocked)); + EXPECT_NOT_BLOCKED(client_conn, blocked, SERVER_HELLO); + EXPECT_EQUAL(data_size, 0); + + EXPECT_SUCCESS(s2n_connection_get_early_data_status(client_conn, &status)); + EXPECT_EQUAL(status, S2N_EARLY_DATA_STATUS_NOT_REQUESTED); + + EXPECT_SUCCESS(s2n_recv_early_data(server_conn, actual_payload, sizeof(actual_payload), + &data_size, &blocked)); + EXPECT_NOT_BLOCKED(server_conn, blocked, CLIENT_FINISHED); + EXPECT_EQUAL(data_size, 0); + + EXPECT_SUCCESS(s2n_connection_get_early_data_status(server_conn, &status)); + EXPECT_EQUAL(status, S2N_EARLY_DATA_STATUS_NOT_REQUESTED); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Early data rejected */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_test_client_and_server_new(&client_conn, &server_conn)); + + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk_with_wrong_early_data)); + + uint8_t actual_payload[BUFFER_SIZE] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t data_size = 0; + s2n_early_data_status_t status = 0; + + EXPECT_BLOCKED_ON_IO(s2n_send_early_data(client_conn, test_data, sizeof(test_data), &data_size, &blocked)); + EXPECT_BLOCKED_ON(client_conn, blocked, S2N_BLOCKED_ON_READ, SERVER_HELLO); + EXPECT_EQUAL(data_size, sizeof(test_data)); + + EXPECT_SUCCESS(s2n_connection_get_early_data_status(client_conn, &status)); + EXPECT_EQUAL(status, S2N_EARLY_DATA_STATUS_OK); + + EXPECT_SUCCESS(s2n_recv_early_data(server_conn, actual_payload, sizeof(actual_payload), + &data_size, &blocked)); + EXPECT_NOT_BLOCKED(server_conn, blocked, CLIENT_FINISHED); + EXPECT_EQUAL(data_size, 0); + + EXPECT_SUCCESS(s2n_connection_get_early_data_status(server_conn, &status)); + EXPECT_EQUAL(status, S2N_EARLY_DATA_STATUS_REJECTED); + + EXPECT_SUCCESS(s2n_send_early_data(client_conn, test_data, sizeof(test_data), + &data_size, &blocked)); + EXPECT_NOT_BLOCKED(client_conn, blocked, APPLICATION_DATA); + EXPECT_EQUAL(data_size, 0); + + EXPECT_SUCCESS(s2n_connection_get_early_data_status(client_conn, &status)); + EXPECT_EQUAL(status, S2N_EARLY_DATA_STATUS_REJECTED); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + }; + + /* Test blocking behavior when sending and receiving early data. + * + * We override the send and recv callbacks to allow us to block on every + * call to s2n_negotiate, s2n_send, and s2n_recv. This lets us exercise all + * possible blocking paths. + */ + { + /* To read a record, we need to both read its header and read its data */ + const uint8_t full_record_reads = 2; + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk)); + + DEFER_CLEANUP(struct s2n_stuffer client_in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer server_in = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_set_blocking_stuffer_io(client_conn, server_conn, &client_in, &server_in)); + + uint8_t actual_payload[BUFFER_SIZE] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t data_size = 0; + + /* Block writing the ClientHello */ + s2n_allowed_writes = 0; + EXPECT_BLOCKED_ON_IO(s2n_send_early_data(client_conn, test_data, sizeof(test_data), &data_size, &blocked)); + EXPECT_BLOCKED_ON(client_conn, blocked, S2N_BLOCKED_ON_WRITE, CLIENT_HELLO); + EXPECT_EQUAL(data_size, 0); + + /* Write the ClientHello, but block on writing the Client CCS message */ + s2n_allowed_writes = 1; + EXPECT_BLOCKED_ON_IO(s2n_send_early_data(client_conn, test_data, sizeof(test_data), &data_size, &blocked)); + EXPECT_BLOCKED_ON(client_conn, blocked, S2N_BLOCKED_ON_WRITE, CLIENT_CHANGE_CIPHER_SPEC); + EXPECT_EQUAL(data_size, 0); + + /* Write the Client CCS message, but block on writing the early data */ + s2n_allowed_writes = 1; + EXPECT_BLOCKED_ON_IO(s2n_send_early_data(client_conn, test_data, sizeof(test_data), &data_size, &blocked)); + EXPECT_BLOCKED_ON(client_conn, blocked, S2N_BLOCKED_ON_WRITE, SERVER_HELLO); + EXPECT_EQUAL(data_size, 0); + + /* Write the early data, but block on reading the ServerHello */ + s2n_allowed_writes = 1; + s2n_allowed_reads = 0; + EXPECT_BLOCKED_ON_IO(s2n_send_early_data(client_conn, test_data, sizeof(test_data), &data_size, &blocked)); + EXPECT_BLOCKED_ON(client_conn, blocked, S2N_BLOCKED_ON_READ, SERVER_HELLO); + EXPECT_EQUAL(data_size, sizeof(test_data)); + + /* Block reading the ClientHello */ + s2n_allowed_reads = 0; + EXPECT_BLOCKED_ON_IO(s2n_recv_early_data(server_conn, actual_payload, sizeof(actual_payload), &data_size, &blocked)); + EXPECT_BLOCKED_ON(server_conn, blocked, S2N_BLOCKED_ON_READ, CLIENT_HELLO); + EXPECT_EQUAL(data_size, 0); + + /* Read the ClientHello, but block on writing the ServerHello */ + s2n_allowed_reads = full_record_reads; + s2n_allowed_writes = 0; + EXPECT_BLOCKED_ON_IO(s2n_recv_early_data(server_conn, actual_payload, sizeof(actual_payload), &data_size, &blocked)); + EXPECT_BLOCKED_ON(server_conn, blocked, S2N_BLOCKED_ON_WRITE, SERVER_HELLO); + EXPECT_EQUAL(data_size, 0); + + /* Write the server messages */ + while (s2n_conn_get_current_message_type(server_conn) != SERVER_FINISHED) { + s2n_allowed_writes = 1; + EXPECT_FAILURE_WITH_ERRNO(s2n_recv_early_data(server_conn, actual_payload, sizeof(actual_payload), &data_size, &blocked), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(data_size, 0); + }; + + /* Write the last server message, but block on reading the early data */ + s2n_allowed_writes = 1; + s2n_allowed_reads = 0; + EXPECT_BLOCKED_ON_IO(s2n_recv_early_data(server_conn, actual_payload, sizeof(actual_payload), &data_size, &blocked)); + EXPECT_BLOCKED_ON(server_conn, blocked, S2N_BLOCKED_ON_READ, END_OF_EARLY_DATA); + EXPECT_EQUAL(data_size, 0); + + /* Read the Client CCS message */ + s2n_allowed_reads = full_record_reads; + EXPECT_BLOCKED_ON_IO(s2n_recv_early_data(server_conn, actual_payload, sizeof(actual_payload), &data_size, &blocked)); + EXPECT_BLOCKED_ON(server_conn, blocked, S2N_BLOCKED_ON_READ, END_OF_EARLY_DATA); + EXPECT_EQUAL(data_size, 0); + + /* Read the early data */ + s2n_allowed_reads = full_record_reads; + EXPECT_BLOCKED_ON_IO(s2n_recv_early_data(server_conn, actual_payload, sizeof(actual_payload), &data_size, &blocked)); + EXPECT_BLOCKED_ON(server_conn, blocked, S2N_BLOCKED_ON_READ, END_OF_EARLY_DATA); + EXPECT_EQUAL(data_size, sizeof(test_data)); + + /* Read the ServerHello, but block on writing more early data */ + s2n_allowed_reads = full_record_reads; + s2n_allowed_writes = 0; + EXPECT_BLOCKED_ON_IO(s2n_send_early_data(client_conn, test_data, sizeof(test_data), &data_size, &blocked)); + EXPECT_BLOCKED_ON(client_conn, blocked, S2N_BLOCKED_ON_WRITE, ENCRYPTED_EXTENSIONS); + EXPECT_EQUAL(data_size, 0); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test blocking behavior with partial successes when sending early data. + * + * Parital successes should be reported to the application. + * Partial successes should count towards the early data limit. + */ + { + const uint32_t max_early_data = UINT16_MAX; + DEFER_CLEANUP(struct s2n_psk *psk_with_early_data_limit = s2n_external_psk_new(), s2n_psk_free); + EXPECT_SUCCESS(s2n_psk_set_identity(psk_with_early_data_limit, test_data, sizeof(test_data))); + EXPECT_SUCCESS(s2n_psk_set_secret(psk_with_early_data_limit, test_data, sizeof(test_data))); + EXPECT_SUCCESS(s2n_psk_configure_early_data(psk_with_early_data_limit, max_early_data, 0x13, 0x01)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, psk_with_early_data_limit)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, psk_with_early_data_limit)); + + DEFER_CLEANUP(struct s2n_stuffer client_in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer server_in = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_set_blocking_stuffer_io(client_conn, server_conn, &client_in, &server_in)); + + /* We will send more than one record worth of data, + * so that we can block after one record worth. + */ + uint8_t large_data[S2N_DEFAULT_FRAGMENT_LENGTH * 2] = "hello world"; + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t data_size = 0; + + /* Block after each record write. + * Eventually, we expect a partial success when we write one record of early data. + * That partial success should correctly report the number of bytes written, + * and record the correct number of early data bytes consumed. + */ + size_t max_attempts = 100; + size_t attempts = 0; + while (true) { + s2n_allowed_writes = 1; + EXPECT_FAILURE_WITH_ERRNO(s2n_send_early_data(client_conn, large_data, sizeof(large_data), &data_size, &blocked), + S2N_ERR_IO_BLOCKED); + + if (data_size != 0) { + /* We blocked on both reading the next handshake message (S2N_BLOCKED_ON_READ) + * and writing the early data (S2N_BLOCKED_ON_WRITE). + * We prefer the negotiate result. + */ + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + /* Part of the early data sent */ + EXPECT_EQUAL(data_size, S2N_DEFAULT_FRAGMENT_LENGTH); + EXPECT_EQUAL(client_conn->early_data_bytes, S2N_DEFAULT_FRAGMENT_LENGTH); + break; + } else { + /* We blocked sending a handshake message, + * since we haven't gotten to early data yet. + */ + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + + /* No early data sent yet */ + EXPECT_EQUAL(client_conn->early_data_bytes, 0); + } + + attempts++; + EXPECT_TRUE(attempts < max_attempts); + } + }; + + /* Known-value early data tests. + * The RFC8848s ClientHello uses x25519, which is only available if evp APIs are supported. + * Otherwise, skip these tests. */ + if (s2n_is_evp_apis_supported()) { + DEFER_CLEANUP(struct s2n_psk resumption_psk = { 0 }, s2n_psk_wipe); + EXPECT_OK(s2n_psk_init(&resumption_psk, S2N_PSK_TYPE_RESUMPTION)); + struct s2n_psk *known_psk = &resumption_psk; + + /** + *= https://tools.ietf.org/rfc/rfc8448#section-3 + *= type=test + *# {server} generate resumption secret "tls13 resumption": + *# + *# PRK (32 octets): 7d f2 35 f2 03 1d 2a 05 12 87 d0 2b 02 41 b0 bf + *# da f8 6c c8 56 23 1f 2d 5a ba 46 c4 34 ec 19 6c + *# + *# hash (2 octets): 00 00 + *# + *# info (22 octets): 00 20 10 74 6c 73 31 33 20 72 65 73 75 6d 70 74 + *# 69 6f 6e 02 00 00 + *# + *# expanded (32 octets): 4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c + *# a4 c5 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3 + */ + S2N_BLOB_FROM_HEX(psk_secret, "4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c \ + a4 c5 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3"); + + EXPECT_SUCCESS(s2n_psk_set_secret(known_psk, psk_secret.data, psk_secret.size)); + + /** + *= https://tools.ietf.org/rfc/rfc8448#section-3 + *= type=test + *# {server} construct a NewSessionTicket handshake message: + *# + *# NewSessionTicket (205 octets): 04 00 00 c9 00 00 00 1e fa d6 aa + *# c5 02 00 00 00 b2 2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00 00 00 + *# 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70 ad 3c + *# 49 88 83 c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9 82 11 + *# 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6 1d 28 + *# 27 db 27 9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0 37 25 + *# a6 a4 da fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5 90 6c + *# 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5 ae a6 + *# 17 64 6f ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d e6 50 + *# 5e 5b fb c3 88 e9 33 43 69 40 93 93 4a e4 d3 57 00 08 00 2a 00 + *# 04 00 00 04 00 + */ + /* Skip past the message type, message size, ticket lifetime, + * ticket age add, nonce, and ticket size: + * 04 00 00 c9 00 00 00 1e fa d6 aa + * c5 02 00 00 00 b2 + */ + S2N_BLOB_FROM_HEX(psk_identity, "2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00 00 00 \ + 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70 ad 3c \ + 49 88 83 c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9 82 11 \ + 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6 1d 28 \ + 27 db 27 9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0 37 25 \ + a6 a4 da fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5 90 6c \ + 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5 ae a6 \ + 17 64 6f ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d e6 50 \ + 5e 5b fb c3 88 e9 33 43 69 40 93 93 4a e4 d3 57"); + EXPECT_SUCCESS(s2n_psk_set_identity(known_psk, psk_identity.data, psk_identity.size)); + /* Skip past the total extensions size, early data extension type, + * and early data extension size: 00 08 00 2a 00 + * 04 + */ + const uint32_t max_early_data = 0x00000400; + EXPECT_SUCCESS(s2n_psk_configure_early_data(known_psk, max_early_data, 0x13, 0x01)); + + /** ClientHello record + * + *= https://tools.ietf.org/rfc/rfc8448#section-4 + *= type=test + *# + *# complete record (517 octets): 16 03 01 02 00 01 00 01 fc 03 03 1b + *# c3 ce b6 bb e3 9c ff 93 83 55 b5 a5 0a db 6d b2 1b 7a 6a f6 49 + *# d7 b4 bc 41 9d 78 76 48 7d 95 00 00 06 13 01 13 03 13 02 01 00 + *# 01 cd 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 + *# 00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 + *# 01 03 01 04 00 33 00 26 00 24 00 1d 00 20 e4 ff b6 8a c0 5f 8d + *# 96 c9 9d a2 66 98 34 6c 6b e1 64 82 ba dd da fe 05 1a 66 b4 f1 + *# 8d 66 8f 0b 00 2a 00 00 00 2b 00 03 02 03 04 00 0d 00 20 00 1e + *# 04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 + *# 01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 + *# 00 15 00 57 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 29 00 dd 00 b8 00 b2 2c 03 5d 82 93 59 + *# ee 5f f7 af 4e c9 00 00 00 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb + *# 33 fa 90 bf 1b 00 70 ad 3c 49 88 83 c9 36 7c 09 a2 be 78 5a bc + *# 55 cd 22 60 97 a3 a9 82 11 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 + *# 6d 64 e8 61 be 7f d6 1d 28 27 db 27 9c ce 14 50 77 d4 54 a3 66 + *# 4d 4e 6d a4 d2 9e e0 37 25 a6 a4 da fc d0 fc 67 d2 ae a7 05 29 + *# 51 3e 3d a2 67 7f a5 90 6c 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 + *# 14 70 f9 fb f2 97 b5 ae a6 17 64 6f ac 5c 03 27 2e 97 07 27 c6 + *# 21 a7 91 41 ef 5f 7d e6 50 5e 5b fb c3 88 e9 33 43 69 40 93 93 + *# 4a e4 d3 57 fa d6 aa cb 00 21 20 3a dd 4f b2 d8 fd f8 22 a0 ca + *# 3c f7 67 8e f5 e8 8d ae 99 01 41 c5 92 4d 57 bb 6f a3 1b 9e 5f + *# 9d + */ + S2N_BLOB_FROM_HEX(ch_record, "16 03 01 02 00 01 00 01 fc 03 03 1b \ + c3 ce b6 bb e3 9c ff 93 83 55 b5 a5 0a db 6d b2 1b 7a 6a f6 49 \ + d7 b4 bc 41 9d 78 76 48 7d 95 00 00 06 13 01 13 03 13 02 01 00 \ + 01 cd 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 \ + 00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 \ + 01 03 01 04 00 33 00 26 00 24 00 1d 00 20 e4 ff b6 8a c0 5f 8d \ + 96 c9 9d a2 66 98 34 6c 6b e1 64 82 ba dd da fe 05 1a 66 b4 f1 \ + 8d 66 8f 0b 00 2a 00 00 00 2b 00 03 02 03 04 00 0d 00 20 00 1e \ + 04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 \ + 01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 \ + 00 15 00 57 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 00 00 00 00 00 00 00 29 00 dd 00 b8 00 b2 2c 03 5d 82 93 59 \ + ee 5f f7 af 4e c9 00 00 00 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb \ + 33 fa 90 bf 1b 00 70 ad 3c 49 88 83 c9 36 7c 09 a2 be 78 5a bc \ + 55 cd 22 60 97 a3 a9 82 11 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 \ + 6d 64 e8 61 be 7f d6 1d 28 27 db 27 9c ce 14 50 77 d4 54 a3 66 \ + 4d 4e 6d a4 d2 9e e0 37 25 a6 a4 da fc d0 fc 67 d2 ae a7 05 29 \ + 51 3e 3d a2 67 7f a5 90 6c 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 \ + 14 70 f9 fb f2 97 b5 ae a6 17 64 6f ac 5c 03 27 2e 97 07 27 c6 \ + 21 a7 91 41 ef 5f 7d e6 50 5e 5b fb c3 88 e9 33 43 69 40 93 93 \ + 4a e4 d3 57 fa d6 aa cb 00 21 20 3a dd 4f b2 d8 fd f8 22 a0 ca \ + 3c f7 67 8e f5 e8 8d ae 99 01 41 c5 92 4d 57 bb 6f a3 1b 9e 5f \ + 9d"); + + /* ApplicationData record containing early data + * + *= https://tools.ietf.org/rfc/rfc8448#section-4 + *= type=test + *# {client} send application_data record: + *# + *# payload (6 octets): 41 42 43 44 45 46 + *# + *# complete record (28 octets): 17 03 03 00 17 ab 1d f4 20 e7 5c 45 + *# 7a 7c c5 d2 84 4f 76 d5 ae e4 b4 ed bf 04 9b e0 + */ + S2N_BLOB_FROM_HEX(payload, "41 42 43 44 45 46"); + S2N_BLOB_FROM_HEX(early_record, "17 03 03 00 17 ab 1d f4 20 e7 5c 45 \ + 7a 7c c5 d2 84 4f 76 d5 ae e4 b4 ed bf 04 9b e0") + + /* EndOfEarlyData record + * + *= https://tools.ietf.org/rfc/rfc8448#section-4 + *= type=test + *# + *# complete record (26 octets): 17 03 03 00 15 ac a6 fc 94 48 41 29 + *# 8d f9 95 93 72 5f 9b f9 75 44 29 b1 2f 09 + */ + S2N_BLOB_FROM_HEX(end_record, "17 03 03 00 15 ac a6 fc 94 48 41 29 \ + 8d f9 95 93 72 5f 9b f9 75 44 29 b1 2f 09"); + + /* Test s2n_recv_early_data without any blocking */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, known_psk)); + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(server_conn, max_early_data)); + /* We need to explicitly set the psk_params type to skip our stateless session resumption recv + * code because the handshake traces we're using are meant for stateful session resumption. + * TODO: https://github.com/aws/s2n-tls/issues/2742 */ + server_conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; + + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, S2N_DEFAULT_RECORD_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, S2N_DEFAULT_RECORD_LENGTH)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_write(&input, &ch_record)); + EXPECT_SUCCESS(s2n_stuffer_write(&input, &early_record)); + EXPECT_SUCCESS(s2n_stuffer_write(&input, &end_record)); + + uint8_t actual_payload[BUFFER_SIZE] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t data_read = 0; + + EXPECT_SUCCESS(s2n_recv_early_data(server_conn, actual_payload, sizeof(actual_payload), + &data_read, &blocked)); + EXPECT_NOT_BLOCKED(server_conn, blocked, CLIENT_FINISHED); + EXPECT_EQUAL(data_read, payload.size); + EXPECT_BYTEARRAY_EQUAL(actual_payload, payload.data, payload.size); + EXPECT_EQUAL(server_conn->early_data_state, S2N_END_OF_EARLY_DATA); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_stuffer_free(&output)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test s2n_recv_early_data with blocking */ + { + /* When we block, we should continue to block regardless of how many times the API is called. + * Let's choose an arbitrary "retry" test value > 1. + */ + const size_t repeat_count = 5; + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, known_psk)); + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(server_conn, max_early_data)); + /* We need to explicitly set the psk_params type to skip our stateless session resumption recv + * code because the handshake traces we're using are meant for stateful session resumption. + * TODO: https://github.com/aws/s2n-tls/issues/2742 */ + server_conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; + + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, S2N_DEFAULT_RECORD_LENGTH)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, server_conn)); + + uint8_t actual_payload[BUFFER_SIZE] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t data_read = 0; + + for (size_t i = 0; i < repeat_count; i++) { + EXPECT_BLOCKED_ON_IO(s2n_recv_early_data(server_conn, actual_payload, sizeof(actual_payload), &data_read, &blocked)); + EXPECT_BLOCKED_ON(server_conn, blocked, S2N_BLOCKED_ON_READ, CLIENT_HELLO); + EXPECT_EQUAL(data_read, 0); + } + + EXPECT_SUCCESS(s2n_stuffer_write(&input, &ch_record)); + + for (size_t i = 0; i < repeat_count; i++) { + EXPECT_BLOCKED_ON_IO(s2n_recv_early_data(server_conn, actual_payload, sizeof(actual_payload), &data_read, &blocked)); + EXPECT_BLOCKED_ON(server_conn, blocked, S2N_BLOCKED_ON_WRITE, SERVER_HELLO); + EXPECT_EQUAL(data_read, 0); + } + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, S2N_DEFAULT_RECORD_LENGTH)); + + for (size_t i = 0; i < repeat_count; i++) { + EXPECT_BLOCKED_ON_IO(s2n_recv_early_data(server_conn, actual_payload, sizeof(actual_payload), &data_read, &blocked)); + EXPECT_BLOCKED_ON(server_conn, blocked, S2N_BLOCKED_ON_READ, END_OF_EARLY_DATA); + EXPECT_EQUAL(data_read, 0); + } + EXPECT_EQUAL(server_conn->early_data_state, S2N_EARLY_DATA_ACCEPTED); + + EXPECT_SUCCESS(s2n_stuffer_write(&input, &early_record)); + + EXPECT_BLOCKED_ON_IO(s2n_recv_early_data(server_conn, actual_payload, sizeof(actual_payload), &data_read, &blocked)); + EXPECT_BLOCKED_ON(server_conn, blocked, S2N_BLOCKED_ON_READ, END_OF_EARLY_DATA); + EXPECT_EQUAL(data_read, payload.size); + EXPECT_BYTEARRAY_EQUAL(actual_payload, payload.data, payload.size); + + for (size_t i = 0; i < repeat_count; i++) { + EXPECT_BLOCKED_ON_IO(s2n_recv_early_data(server_conn, actual_payload, sizeof(actual_payload), &data_read, &blocked)); + EXPECT_BLOCKED_ON(server_conn, blocked, S2N_BLOCKED_ON_READ, END_OF_EARLY_DATA); + EXPECT_EQUAL(data_read, 0); + } + EXPECT_EQUAL(server_conn->early_data_state, S2N_EARLY_DATA_ACCEPTED); + + EXPECT_SUCCESS(s2n_stuffer_write(&input, &end_record)); + + for (size_t i = 0; i < repeat_count; i++) { + EXPECT_SUCCESS(s2n_recv_early_data(server_conn, actual_payload, sizeof(actual_payload), + &data_read, &blocked)); + EXPECT_NOT_BLOCKED(server_conn, blocked, CLIENT_FINISHED); + EXPECT_EQUAL(data_read, 0); + EXPECT_EQUAL(server_conn->early_data_state, S2N_END_OF_EARLY_DATA); + } + + EXPECT_BLOCKED_ON_IO(s2n_negotiate(server_conn, &blocked)); + EXPECT_BLOCKED_ON(server_conn, blocked, S2N_BLOCKED_ON_READ, CLIENT_FINISHED); + EXPECT_EQUAL(server_conn->early_data_state, S2N_END_OF_EARLY_DATA); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_stuffer_free(&output)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test s2n_recv_early_data when early data not allowed for PSK */ + { + struct s2n_psk psk_copy = *known_psk; + EXPECT_SUCCESS(s2n_psk_configure_early_data(&psk_copy, 0, 0x13, 0x01)); + struct s2n_psk *known_psk_without_early_data = &psk_copy; + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, known_psk_without_early_data)); + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(server_conn, max_early_data)); + /* We need to explicitly set the psk_params type to skip our stateless session resumption recv + * code because the handshake traces we're using are meant for stateful session resumption. + * TODO: https://github.com/aws/s2n-tls/issues/2742 */ + server_conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; + + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, S2N_DEFAULT_RECORD_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, S2N_DEFAULT_RECORD_LENGTH)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_write(&input, &ch_record)); + EXPECT_SUCCESS(s2n_stuffer_write(&input, &early_record)); + EXPECT_SUCCESS(s2n_stuffer_write(&input, &end_record)); + + uint8_t actual_payload[BUFFER_SIZE] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t data_read = 0; + + EXPECT_SUCCESS(s2n_recv_early_data(server_conn, actual_payload, sizeof(actual_payload), + &data_read, &blocked)); + EXPECT_NOT_BLOCKED(server_conn, blocked, CLIENT_HELLO); + EXPECT_EQUAL(data_read, 0); + + EXPECT_BLOCKED_ON_IO(s2n_negotiate(server_conn, &blocked)); + EXPECT_BLOCKED_ON(server_conn, blocked, S2N_BLOCKED_ON_READ, CLIENT_FINISHED); + EXPECT_EQUAL(server_conn->early_data_state, S2N_EARLY_DATA_REJECTED); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_stuffer_free(&output)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test s2n_recv_early_data when early data rejected */ + { + struct s2n_psk psk_copy = *known_psk; + EXPECT_SUCCESS(s2n_psk_configure_early_data(&psk_copy, max_early_data, 0x13, 0x03)); + struct s2n_psk *known_psk_with_wrong_cipher_suite = &psk_copy; + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, known_psk_with_wrong_cipher_suite)); + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(server_conn, max_early_data)); + /* We need to explicitly set the psk_params type to skip our stateless session resumption recv + * code because the handshake traces we're using are meant for stateful session resumption. + * TODO: https://github.com/aws/s2n-tls/issues/2742 */ + server_conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; + + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, S2N_DEFAULT_RECORD_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, S2N_DEFAULT_RECORD_LENGTH)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_write(&input, &ch_record)); + EXPECT_SUCCESS(s2n_stuffer_write(&input, &early_record)); + EXPECT_SUCCESS(s2n_stuffer_write(&input, &end_record)); + + uint8_t actual_payload[BUFFER_SIZE] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t data_read = 0; + + EXPECT_SUCCESS(s2n_recv_early_data(server_conn, actual_payload, sizeof(actual_payload), + &data_read, &blocked)); + EXPECT_NOT_BLOCKED(server_conn, blocked, CLIENT_FINISHED); + EXPECT_EQUAL(data_read, 0); + EXPECT_EQUAL(server_conn->early_data_state, S2N_EARLY_DATA_REJECTED); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_stuffer_free(&output)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + } + + END_TEST(); +} diff --git a/tests/unit/s2n_early_data_io_test.c b/tests/unit/s2n_early_data_io_test.c new file mode 100644 index 00000000000..75c133cc033 --- /dev/null +++ b/tests/unit/s2n_early_data_io_test.c @@ -0,0 +1,814 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_early_data.h" + +#define TEST_MAX_EARLY_DATA_SIZE 1000 + +#define EXPECT_SUCCESS_S2N_SEND(conn, data, data_len, blocked) \ + EXPECT_EQUAL(s2n_send(conn, data, data_len, blocked), data_len) +#define EXPECT_SUCCESS_S2N_RECV(conn, data_buffer, data_buffer_size, blocked, data, data_len) \ + EXPECT_EQUAL(s2n_recv(conn, data_buffer, data_buffer_size, blocked), data_len); \ + EXPECT_BYTEARRAY_EQUAL(data_buffer, data, data_len) + +static S2N_RESULT s2n_test_client_and_server_new(struct s2n_connection **client_conn, struct s2n_connection **server_conn) +{ + *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(*client_conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(*client_conn, "default_tls13")); + + *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(*server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(*server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(*server_conn, "default_tls13")); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(*client_conn, *server_conn, &io_pair)); + + return S2N_RESULT_OK; +} + +struct s2n_offered_early_data *async_early_data = NULL; +static int s2n_test_async_early_data_cb(struct s2n_connection *conn, struct s2n_offered_early_data *early_data) +{ + POSIX_ENSURE_REF(conn); + async_early_data = early_data; + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + const uint8_t test_data[] = "hello world"; + + DEFER_CLEANUP(struct s2n_psk *test_psk = s2n_external_psk_new(), s2n_psk_free); + EXPECT_SUCCESS(s2n_psk_set_identity(test_psk, test_data, sizeof(test_data))); + EXPECT_SUCCESS(s2n_psk_set_secret(test_psk, test_data, sizeof(test_data))); + EXPECT_SUCCESS(s2n_psk_configure_early_data(test_psk, TEST_MAX_EARLY_DATA_SIZE, 0x13, 0x01)); + + DEFER_CLEANUP(struct s2n_psk *test_psk_without_early_data = s2n_external_psk_new(), s2n_psk_free); + EXPECT_SUCCESS(s2n_psk_set_identity(test_psk_without_early_data, test_data, sizeof(test_data))); + EXPECT_SUCCESS(s2n_psk_set_secret(test_psk_without_early_data, test_data, sizeof(test_data))); + + DEFER_CLEANUP(struct s2n_psk *test_psk_to_reject = s2n_external_psk_new(), s2n_psk_free); + EXPECT_SUCCESS(s2n_psk_set_identity(test_psk_to_reject, test_data, sizeof(test_data))); + EXPECT_SUCCESS(s2n_psk_set_secret(test_psk_to_reject, test_data, sizeof(test_data))); + EXPECT_SUCCESS(s2n_psk_configure_early_data(test_psk_to_reject, TEST_MAX_EARLY_DATA_SIZE, 0x13, 0x03)); + + struct s2n_cert_chain_and_key *cert_chain = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&cert_chain, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + struct s2n_config *config_with_cert = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config_with_cert, cert_chain)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config_with_cert)); + + /* Test s2n_negotiate with early data */ + { + /* Early data not supported by server */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_test_client_and_server_new(&client_conn, &server_conn)); + + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_FALSE(WITH_EARLY_DATA(client_conn)); + EXPECT_FALSE(WITH_EARLY_DATA(server_conn)); + EXPECT_TRUE(WITH_EARLY_CLIENT_CCS(client_conn)); + EXPECT_TRUE(WITH_EARLY_CLIENT_CCS(server_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Early data not supported by client */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_test_client_and_server_new(&client_conn, &server_conn)); + + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_FALSE(WITH_EARLY_DATA(client_conn)); + EXPECT_FALSE(WITH_EARLY_DATA(server_conn)); + EXPECT_FALSE(WITH_EARLY_CLIENT_CCS(client_conn)); + EXPECT_FALSE(WITH_EARLY_CLIENT_CCS(server_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Server does not support TLS1.3 */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_test_client_and_server_new(&client_conn, &server_conn)); + + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all_tls12")); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_with_cert)); + + /* Stop the TLS1.2 server before the CLIENT_KEY message. At that point, it will attempt to read + * a CLIENT_KEY message but instead receive CLIENT_CHANGE_CIPHER_SPEC. But this error is + * irrelevant to the test: TLS1.2 servers aren't expected to successfully handle early data. + * + * What we really care about is how the client reacts to the TLS1.2 SERVER_HELLO. + */ + EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, CLIENT_KEY), + S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Early data accepted */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_test_client_and_server_new(&client_conn, &server_conn)); + + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + END_OF_EARLY_DATA)); + + /* Blocked indefinitely */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + for (size_t i = 0; i < 10; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(client_conn, &blocked), S2N_ERR_EARLY_DATA_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_EARLY_DATA); + } + + EXPECT_SUCCESS(s2n_connection_set_end_of_early_data(client_conn)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_TRUE(WITH_EARLY_DATA(client_conn)); + EXPECT_TRUE(WITH_EARLY_DATA(server_conn)); + EXPECT_TRUE(WITH_EARLY_CLIENT_CCS(client_conn)); + EXPECT_TRUE(WITH_EARLY_CLIENT_CCS(server_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Early data accepted asynchronously */ + { + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + struct s2n_config *config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_set_early_data_cb(config, s2n_test_async_early_data_cb)); + EXPECT_NOT_NULL(config); + + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_test_client_and_server_new(&client_conn, &server_conn)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + + /* Blocks on processing the ClientHello */ + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &blocked), S2N_ERR_ASYNC_BLOCKED); + + /* Still blocks if called again */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &blocked), S2N_ERR_ASYNC_BLOCKED); + + /* Accept early data */ + EXPECT_SUCCESS(s2n_offered_early_data_accept(async_early_data)); + + /* Finish early data */ + EXPECT_SUCCESS(s2n_connection_set_end_of_early_data(client_conn)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_TRUE(WITH_EARLY_DATA(client_conn)); + EXPECT_TRUE(WITH_EARLY_DATA(server_conn)); + EXPECT_TRUE(WITH_EARLY_CLIENT_CCS(client_conn)); + EXPECT_TRUE(WITH_EARLY_CLIENT_CCS(server_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Early data rejected */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_test_client_and_server_new(&client_conn, &server_conn)); + + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk_without_early_data)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_FALSE(WITH_EARLY_DATA(client_conn)); + EXPECT_FALSE(WITH_EARLY_DATA(server_conn)); + EXPECT_TRUE(WITH_EARLY_CLIENT_CCS(client_conn)); + EXPECT_TRUE(WITH_EARLY_CLIENT_CCS(server_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Early data rejected asynchronously */ + { + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + struct s2n_config *config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_set_early_data_cb(config, s2n_test_async_early_data_cb)); + EXPECT_NOT_NULL(config); + + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_test_client_and_server_new(&client_conn, &server_conn)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + + /* Blocks on processing the ClientHello */ + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &blocked), S2N_ERR_ASYNC_BLOCKED); + + /* Still blocks if called again */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &blocked), S2N_ERR_ASYNC_BLOCKED); + + EXPECT_SUCCESS(s2n_offered_early_data_reject(async_early_data)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_FALSE(WITH_EARLY_DATA(client_conn)); + EXPECT_FALSE(WITH_EARLY_DATA(server_conn)); + EXPECT_TRUE(WITH_EARLY_CLIENT_CCS(client_conn)); + EXPECT_TRUE(WITH_EARLY_CLIENT_CCS(server_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Early data rejected and ignored */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_test_client_and_server_new(&client_conn, &server_conn)); + + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk_to_reject)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + SERVER_HELLO)); + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_REQUESTED); + + /* Send some early data to verify the server ignores it */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_SUCCESS_S2N_SEND(client_conn, test_data, sizeof(test_data), &blocked); + EXPECT_SUCCESS_S2N_SEND(client_conn, test_data, sizeof(test_data), &blocked); + EXPECT_SUCCESS_S2N_SEND(client_conn, test_data, sizeof(test_data), &blocked); + + EXPECT_SUCCESS(s2n_connection_set_end_of_early_data(client_conn)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_FALSE(WITH_EARLY_DATA(client_conn)); + EXPECT_FALSE(WITH_EARLY_DATA(server_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Early data rejected, but too much early data received */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_test_client_and_server_new(&client_conn, &server_conn)); + + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk_to_reject)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + SERVER_HELLO)); + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_REQUESTED); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_TRUE((TEST_MAX_EARLY_DATA_SIZE / sizeof(test_data)) > 0); + for (size_t i = 0; i < (TEST_MAX_EARLY_DATA_SIZE / sizeof(test_data)); i++) { + EXPECT_SUCCESS_S2N_SEND(client_conn, test_data, sizeof(test_data), &blocked); + } + + /* Trick the client into sending more early data than allowed */ + client_conn->early_data_bytes = 0; + EXPECT_SUCCESS_S2N_SEND(client_conn, test_data, sizeof(test_data), &blocked); + + EXPECT_SUCCESS(s2n_connection_set_end_of_early_data(client_conn)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_MAX_EARLY_DATA_SIZE); + + EXPECT_FALSE(WITH_EARLY_DATA(client_conn)); + EXPECT_FALSE(WITH_EARLY_DATA(server_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Early data rejected due to HRR */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_test_client_and_server_new(&client_conn, &server_conn)); + + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_FALSE(WITH_EARLY_DATA(client_conn)); + EXPECT_FALSE(WITH_EARLY_DATA(server_conn)); + EXPECT_TRUE(WITH_EARLY_CLIENT_CCS(client_conn)); + EXPECT_TRUE(WITH_EARLY_CLIENT_CCS(server_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Early data rejected due to HRR, but received anyway and ignored. */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + /* We run this multiple times because if the early data is incorrectly processed as a normal, + * unencrypted record, it is usually just ignored anyway because the record type is unknown. + * An error will only occur if the encryption happens to produce a known record type as the + * last non-padding byte. + * + * We handle 4 record types (HANDSHAKE, APPLICATION_DATA, ALERT, and CHANGE_CIPHER_SPEC). + * So the chance this test produces a false negative (succeeds when it should fail): + * (((256 - 4) / 256) ^ 450) = 0.0008, < 0.1% + * + * (This calculation ignores the case where the encryption produces an apparently padded record. + * That would increase the number of records not ignored, making a false negative even less likely) + */ + const size_t repetitions = 450; + for (size_t i = 0; i < repetitions; i++) { + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_REQUESTED); + + EXPECT_SUCCESS_S2N_SEND(client_conn, test_data, sizeof(test_data), &blocked); + + EXPECT_SUCCESS(s2n_connection_set_end_of_early_data(client_conn)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_FALSE(WITH_EARLY_DATA(client_conn)); + EXPECT_FALSE(WITH_EARLY_DATA(server_conn)); + EXPECT_TRUE(WITH_EARLY_CLIENT_CCS(client_conn)); + EXPECT_TRUE(WITH_EARLY_CLIENT_CCS(server_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + } + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /* PSK rejected altogether */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_test_client_and_server_new(&client_conn, &server_conn)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_cert)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_with_cert)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_FALSE(WITH_EARLY_DATA(client_conn)); + EXPECT_FALSE(WITH_EARLY_DATA(server_conn)); + EXPECT_TRUE(WITH_EARLY_CLIENT_CCS(client_conn)); + EXPECT_TRUE(WITH_EARLY_CLIENT_CCS(server_conn)); + EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn)); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + }; + + /* Test s2n_send with early data */ + { + /* End early data after the server accepts the early data request */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_test_client_and_server_new(&client_conn, &server_conn)); + + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + /* Can't send early data before negotiation begins */ + EXPECT_EQUAL(client_conn->early_data_state, S2N_UNKNOWN_EARLY_DATA_STATE); + EXPECT_FAILURE_WITH_ERRNO(s2n_send(client_conn, test_data, sizeof(test_data), &blocked), + S2N_ERR_EARLY_DATA_NOT_ALLOWED); + EXPECT_FAILURE_WITH_ERRNO(s2n_send(client_conn, test_data, 0, &blocked), + S2N_ERR_EARLY_DATA_NOT_ALLOWED); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + SERVER_HELLO)); + + /* Can send after early data requested */ + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_REQUESTED); + EXPECT_SUCCESS_S2N_SEND(client_conn, test_data, sizeof(test_data), &blocked); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + SERVER_FINISHED)); + + /* Can send after early data accepted */ + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_ACCEPTED); + EXPECT_SUCCESS_S2N_SEND(client_conn, test_data, sizeof(test_data), &blocked); + + /* Can send zero-length */ + EXPECT_SUCCESS_S2N_SEND(client_conn, NULL, 0, &blocked); + + /* Can send more */ + EXPECT_SUCCESS_S2N_SEND(client_conn, test_data, sizeof(test_data), &blocked); + + /* Can't send too much early data */ + EXPECT_FAILURE_WITH_ERRNO(s2n_send(client_conn, test_data, TEST_MAX_EARLY_DATA_SIZE, &blocked), + S2N_ERR_MAX_EARLY_DATA_SIZE); + + /* Can continue sending, even if a send failed */ + EXPECT_SUCCESS_S2N_SEND(client_conn, test_data, sizeof(test_data), &blocked); + + /* Can't send early data after end of early data indicated */ + EXPECT_SUCCESS(s2n_connection_set_end_of_early_data(client_conn)); + EXPECT_FAILURE_WITH_ERRNO(s2n_send(client_conn, NULL, 0, &blocked), S2N_ERR_EARLY_DATA_NOT_ALLOWED); + + /* Continue the handshake */ + EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, CLIENT_FINISHED), + S2N_ERR_EARLY_DATA_BLOCKED); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_FINISHED); + + /* Can't send early data after END_OF_EARLY_DATA sent */ + EXPECT_EQUAL(client_conn->early_data_state, S2N_END_OF_EARLY_DATA); + EXPECT_FAILURE_WITH_ERRNO(s2n_send(client_conn, NULL, 0, &blocked), S2N_ERR_EARLY_DATA_NOT_ALLOWED); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* End early data before the server accepts the early data request. */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_test_client_and_server_new(&client_conn, &server_conn)); + + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + SERVER_HELLO)); + + /* Can send after early data requested */ + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_REQUESTED); + EXPECT_SUCCESS_S2N_SEND(client_conn, test_data, sizeof(test_data), &blocked); + + /* Can't send early data after end of early data indicated */ + EXPECT_SUCCESS(s2n_connection_set_end_of_early_data(client_conn)); + EXPECT_FAILURE_WITH_ERRNO(s2n_send(client_conn, NULL, 0, &blocked), S2N_ERR_EARLY_DATA_NOT_ALLOWED); + + /* Continue the handshake */ + EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, CLIENT_FINISHED), + S2N_ERR_EARLY_DATA_BLOCKED); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_FINISHED); + + /* Still can't send early data after END_OF_EARLY_DATA sent */ + EXPECT_EQUAL(client_conn->early_data_state, S2N_END_OF_EARLY_DATA); + EXPECT_FAILURE_WITH_ERRNO(s2n_send(client_conn, NULL, 0, &blocked), S2N_ERR_EARLY_DATA_NOT_ALLOWED); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* s2n_send reports early data bytes on partial writes */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = NULL, s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *server_conn = NULL, s2n_connection_ptr_free); + EXPECT_OK(s2n_test_client_and_server_new(&client_conn, &server_conn)); + + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + + /* Configure the connection to use stuffers instead of fds. + * This will let us block the send. + */ + DEFER_CLEANUP(struct s2n_stuffer client_in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer client_out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_in, &client_out, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_out, &client_in, server_conn)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + const uint8_t large_test_data[TEST_MAX_EARLY_DATA_SIZE] = "hello"; + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + END_OF_EARLY_DATA)); + + /* Only allocate space for one record to be written */ + size_t fragment_len = 100; + EXPECT_TRUE(fragment_len < TEST_MAX_EARLY_DATA_SIZE); + client_conn->max_outgoing_fragment_length = fragment_len; + EXPECT_SUCCESS(s2n_stuffer_free(&client_out)); + /* This is just an estimate: the record overhead means we need more + * than fragment_len space, but we need less than fragment_len * 2 + * so that we only write one record. */ + size_t out_size = fragment_len * 1.5; + EXPECT_SUCCESS(s2n_stuffer_alloc(&client_out, out_size)); + + /* Try to send more than one record of data. + * s2n_send should block, but report the early data that was sent before it blocked. + */ + size_t actual_send_size = s2n_send(client_conn, large_test_data, fragment_len * 2, &blocked); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(actual_send_size, fragment_len); + EXPECT_EQUAL(client_conn->early_data_bytes, fragment_len); + }; + }; + + /* Test s2n_recv with early data */ + { + /* s2n_recv can read early data sent with s2n_send */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_test_client_and_server_new(&client_conn, &server_conn)); + + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + uint8_t test_buffer[TEST_MAX_EARLY_DATA_SIZE] = { 0 }; + + /* Can't recv early data before negotiation begins */ + EXPECT_EQUAL(server_conn->early_data_state, S2N_UNKNOWN_EARLY_DATA_STATE); + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, test_buffer, sizeof(test_buffer), &blocked), + S2N_ERR_EARLY_DATA_NOT_ALLOWED); + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, test_buffer, 0, &blocked), + S2N_ERR_EARLY_DATA_NOT_ALLOWED); + + /* Can't recv before END_OF_EARLY_DATA */ + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + SERVER_HELLO)); + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, test_buffer, 0, &blocked), S2N_ERR_EARLY_DATA_NOT_ALLOWED); + + /* Can recv on END_OF_EARLY_DATA */ + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + END_OF_EARLY_DATA)); + EXPECT_SUCCESS_S2N_RECV(server_conn, test_buffer, 0, &blocked, test_data, 0); + + /* Can recv after s2n_negotiate decrypts the record */ + EXPECT_SUCCESS_S2N_SEND(client_conn, test_data, sizeof(test_data), &blocked); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &blocked), S2N_ERR_EARLY_DATA_BLOCKED); + EXPECT_SUCCESS_S2N_RECV(server_conn, test_buffer, sizeof(test_buffer), &blocked, + test_data, sizeof(test_data)); + EXPECT_BYTEARRAY_EQUAL(test_buffer, test_data, sizeof(test_data)); + + /* Can spread recv over multiple calls */ + EXPECT_SUCCESS_S2N_SEND(client_conn, test_data, sizeof(test_data), &blocked); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &blocked), S2N_ERR_EARLY_DATA_BLOCKED); + EXPECT_SUCCESS_S2N_RECV(server_conn, test_buffer, 0, &blocked, test_data, 0); + EXPECT_SUCCESS_S2N_RECV(server_conn, test_buffer, 2, &blocked, test_data, 2); + EXPECT_SUCCESS_S2N_RECV(server_conn, test_buffer, (sizeof(test_data) - 2), &blocked, + (test_data + 2), (sizeof(test_data) - 2)); + + /* Can block + recv repeatedly on the same record */ + EXPECT_SUCCESS_S2N_SEND(client_conn, test_data, sizeof(test_data), &blocked); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &blocked), S2N_ERR_EARLY_DATA_BLOCKED); + EXPECT_SUCCESS_S2N_RECV(server_conn, test_buffer, 2, &blocked, test_data, 2); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &blocked), S2N_ERR_EARLY_DATA_BLOCKED); + EXPECT_SUCCESS_S2N_RECV(server_conn, test_buffer, 0, &blocked, test_data, 0); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &blocked), S2N_ERR_EARLY_DATA_BLOCKED); + EXPECT_SUCCESS_S2N_RECV(server_conn, test_buffer, (sizeof(test_data) - 2), &blocked, + (test_data + 2), (sizeof(test_data) - 2)); + + /* Can't recv after END_OF_EARLY_DATA */ + EXPECT_SUCCESS(s2n_connection_set_end_of_early_data(client_conn)); + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, CLIENT_FINISHED)); + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, test_buffer, 0, &blocked), S2N_ERR_EARLY_DATA_NOT_ALLOWED); + + /* Can finish handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* s2n_recv fails if it encounters a handshake message instead of early data. */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_test_client_and_server_new(&client_conn, &server_conn)); + + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + uint8_t test_buffer[TEST_MAX_EARLY_DATA_SIZE] = { 0 }; + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + END_OF_EARLY_DATA)); + + /* Client sends the EndOfEarlyData handshake message */ + EXPECT_SUCCESS(s2n_connection_set_end_of_early_data(client_conn)); + EXPECT_SUCCESS(s2n_negotiate(client_conn, &blocked)); + + /* Server fails to read the EndOfEarlyData handshake message via s2n_recv. + * s2n_recv can only process post-handshake messages, and EndOfEarlyData is not a post-handshake + * message. + * We should have read the EndOfEarlyData handshake message via s2n_negotiate. + */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), END_OF_EARLY_DATA); + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, test_buffer, sizeof(test_buffer), &blocked), + S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* s2n_recv fails on too much early data */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_test_client_and_server_new(&client_conn, &server_conn)); + + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + const uint8_t large_test_data[TEST_MAX_EARLY_DATA_SIZE] = "hello"; + uint8_t test_buffer[TEST_MAX_EARLY_DATA_SIZE] = { 0 }; + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + END_OF_EARLY_DATA)); + + /* Send the maximum allowed data */ + EXPECT_SUCCESS_S2N_SEND(client_conn, large_test_data, sizeof(large_test_data), &blocked); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &blocked), S2N_ERR_EARLY_DATA_BLOCKED); + EXPECT_SUCCESS_S2N_RECV(server_conn, test_buffer, sizeof(test_buffer), &blocked, + large_test_data, TEST_MAX_EARLY_DATA_SIZE); + + /* Trick the client into sending more */ + client_conn->early_data_bytes = 0; + EXPECT_SUCCESS_S2N_SEND(client_conn, test_data, sizeof(test_data), &blocked); + + /* The server should fail to accept the data */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &blocked), S2N_ERR_EARLY_DATA_BLOCKED); + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, test_buffer, sizeof(test_buffer), &blocked), + S2N_ERR_MAX_EARLY_DATA_SIZE); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + }; + + EXPECT_SUCCESS(s2n_config_free(config_with_cert)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(cert_chain)); + END_TEST(); +} diff --git a/tests/unit/s2n_early_data_test.c b/tests/unit/s2n_early_data_test.c new file mode 100644 index 00000000000..f2c9460733f --- /dev/null +++ b/tests/unit/s2n_early_data_test.c @@ -0,0 +1,1432 @@ +/* + * 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 "tls/s2n_early_data.h" + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +#define TEST_SIZE 10 + +typedef struct { + const s2n_early_data_state *states; + size_t len; +} s2n_early_state_sequence; + +/* We want to verify that applying s2n_connection_set_early_data_state to the current state + * can only produce valid state sequences. + * + * We check every possible next state and verify that the only transitions that + * s2n_connection_set_early_data_state allows are those in the valid state sequences. Then, for + * every valid transition, we call this method again recursively. The recursion ends when we either + * reach the end of a valid state sequence or encounter an invalid state sequence. + */ +static S2N_RESULT s2n_test_all_early_data_sequences(struct s2n_connection *conn, size_t i, + const s2n_early_state_sequence *valid_early_state_sequences, size_t valid_early_state_sequences_len) +{ + s2n_early_data_state current_state = conn->early_data_state; + for (s2n_early_data_state next_state = 0; next_state < S2N_EARLY_DATA_STATES_COUNT; next_state++) { + /* We always allow no-op transitions, so ignore them */ + if (next_state == current_state) { + continue; + } + + conn->early_data_state = current_state; + + bool actual_valid = s2n_result_is_ok(s2n_connection_set_early_data_state(conn, next_state)); + bool expected_valid = false; + + size_t next_i = i + 1; + for (size_t j = 0; j < valid_early_state_sequences_len; j++) { + if (next_i < valid_early_state_sequences[j].len) { + expected_valid |= (valid_early_state_sequences[j].states[i] == current_state) + && (valid_early_state_sequences[j].states[next_i] == next_state); + } + } + + RESULT_ENSURE_EQ(actual_valid, expected_valid); + + if (expected_valid) { + RESULT_GUARD(s2n_test_all_early_data_sequences(conn, i + 1, + valid_early_state_sequences, valid_early_state_sequences_len)); + } + } + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_alloc_test_config_buffers(struct s2n_early_data_config *config) +{ + RESULT_GUARD_POSIX(s2n_alloc(&config->application_protocol, TEST_SIZE)); + RESULT_ENSURE_NE(config->application_protocol.size, 0); + RESULT_GUARD_POSIX(s2n_alloc(&config->context, TEST_SIZE)); + RESULT_ENSURE_NE(config->context.size, 0); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_test_config_buffers_freed(struct s2n_early_data_config *config) +{ + RESULT_ENSURE_EQ(config->application_protocol.data, NULL); + RESULT_ENSURE_EQ(config->application_protocol.size, 0); + RESULT_ENSURE_EQ(config->context.data, NULL); + RESULT_ENSURE_EQ(config->context.size, 0); + return S2N_RESULT_OK; +} + +static int s2n_test_early_data_cb(struct s2n_connection *conn, struct s2n_offered_early_data *early_data) +{ + POSIX_ENSURE_REF(conn); + + uint16_t context_len = 0; + POSIX_GUARD(s2n_offered_early_data_get_context_length(early_data, &context_len)); + POSIX_ENSURE_EQ(context_len, 1); + + uint8_t context = 0; + POSIX_GUARD(s2n_offered_early_data_get_context(early_data, &context, 1)); + + if (context) { + POSIX_GUARD(s2n_offered_early_data_accept(early_data)); + } else { + POSIX_GUARD(s2n_offered_early_data_reject(early_data)); + } + return S2N_SUCCESS; +} + +struct s2n_offered_early_data *async_early_data = NULL; +static int s2n_test_async_early_data_cb(struct s2n_connection *conn, struct s2n_offered_early_data *early_data) +{ + POSIX_ENSURE_REF(conn); + async_early_data = early_data; + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + const uint8_t test_value[] = "test value"; + const uint8_t test_value_2[] = "more test data"; + + const uint32_t nonzero_max_early_data = 10; + + /* Test s2n_connection_set_early_data_state */ + { + /* Safety check */ + EXPECT_ERROR_WITH_ERRNO(s2n_connection_set_early_data_state(NULL, 0), S2N_ERR_NULL); + + const s2n_early_data_state early_data_not_requested_seq[] = { + S2N_UNKNOWN_EARLY_DATA_STATE, S2N_EARLY_DATA_NOT_REQUESTED + }; + const s2n_early_data_state early_data_rejected_seq[] = { + S2N_UNKNOWN_EARLY_DATA_STATE, S2N_EARLY_DATA_REQUESTED, S2N_EARLY_DATA_REJECTED + }; + const s2n_early_data_state early_data_success_seq[] = { + S2N_UNKNOWN_EARLY_DATA_STATE, S2N_EARLY_DATA_REQUESTED, S2N_EARLY_DATA_ACCEPTED, S2N_END_OF_EARLY_DATA + }; + + /* Test known valid / invalid transitions */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + + conn->early_data_state = 0; + EXPECT_OK(s2n_connection_set_early_data_state(conn, S2N_UNKNOWN_EARLY_DATA_STATE)); + + conn->early_data_state = 0; + EXPECT_ERROR_WITH_ERRNO(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_STATES_COUNT), + S2N_ERR_INVALID_EARLY_DATA_STATE); + + conn->early_data_state = 0; + EXPECT_ERROR_WITH_ERRNO(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_STATES_COUNT + 1), + S2N_ERR_INVALID_EARLY_DATA_STATE); + + conn->early_data_state = 0; + EXPECT_OK(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_REQUESTED)); + + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + EXPECT_OK(s2n_connection_set_early_data_state(conn, S2N_EARLY_DATA_REJECTED)); + + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + EXPECT_ERROR_WITH_ERRNO(s2n_connection_set_early_data_state(conn, S2N_END_OF_EARLY_DATA), + S2N_ERR_INVALID_EARLY_DATA_STATE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test that only the expected sequences of states are possible. + * Given every possible sequence of early data states, test that s2n_connection_set_early_data_state + * can only be used to iterate through the known valid sequences. */ + { + /* Test with the correct expected sequences */ + { + const s2n_early_state_sequence valid_early_state_sequences[] = { + { .states = early_data_not_requested_seq, .len = s2n_array_len(early_data_not_requested_seq) }, + { .states = early_data_rejected_seq, .len = s2n_array_len(early_data_rejected_seq) }, + { .states = early_data_success_seq, .len = s2n_array_len(early_data_success_seq) }, + }; + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_test_all_early_data_sequences(conn, 0, + valid_early_state_sequences, s2n_array_len(valid_early_state_sequences))); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Sanity check: adding an invalid expected sequence causes test to fail */ + { + const s2n_early_data_state invalid_seq[] = { + S2N_UNKNOWN_EARLY_DATA_STATE, S2N_EARLY_DATA_ACCEPTED, S2N_END_OF_EARLY_DATA + }; + const s2n_early_state_sequence test_early_state_sequences[] = { + { .states = early_data_not_requested_seq, .len = s2n_array_len(early_data_not_requested_seq) }, + { .states = early_data_rejected_seq, .len = s2n_array_len(early_data_rejected_seq) }, + { .states = early_data_success_seq, .len = s2n_array_len(early_data_success_seq) }, + { .states = invalid_seq, .len = s2n_array_len(invalid_seq) }, + }; + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_ERROR(s2n_test_all_early_data_sequences(conn, 0, + test_early_state_sequences, s2n_array_len(test_early_state_sequences))); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Sanity check: removing one of the expected sequences causes test to fail */ + { + const s2n_early_state_sequence test_early_state_sequences[] = { + { .states = early_data_not_requested_seq, .len = s2n_array_len(early_data_not_requested_seq) }, + { .states = early_data_success_seq, .len = s2n_array_len(early_data_success_seq) }, + }; + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_ERROR(s2n_test_all_early_data_sequences(conn, 0, + test_early_state_sequences, s2n_array_len(test_early_state_sequences))); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + }; + + /* Test s2n_early_data_config_free */ + { + /* Safety check */ + EXPECT_OK(s2n_early_data_config_free(NULL)); + + /* Resets everything */ + { + struct s2n_early_data_config config = { 0 }; + EXPECT_OK(s2n_alloc_test_config_buffers(&config)); + + EXPECT_OK(s2n_early_data_config_free(&config)); + EXPECT_OK(s2n_test_config_buffers_freed(&config)); + }; + + /* Called by s2n_psk_wipe */ + { + struct s2n_psk psk = { 0 }; + EXPECT_OK(s2n_alloc_test_config_buffers(&psk.early_data_config)); + + EXPECT_OK(s2n_psk_wipe(&psk)); + EXPECT_OK(s2n_test_config_buffers_freed(&psk.early_data_config)); + }; + + /* Called by s2n_psk_free */ + { + struct s2n_psk *psk = s2n_external_psk_new(); + EXPECT_OK(s2n_alloc_test_config_buffers(&psk->early_data_config)); + + EXPECT_SUCCESS(s2n_psk_free(&psk)); + /* A memory leak in this test would indicate that s2n_psk_free isn't freeing the buffers. */ + }; + }; + + /* Test s2n_psk_configure_early_data */ + { + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_psk_configure_early_data(NULL, nonzero_max_early_data, 1, 1), S2N_ERR_NULL); + + /* Set valid configuration */ + { + uint32_t expected_max_early_data_size = 1000; + const struct s2n_cipher_suite *expected_cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + DEFER_CLEANUP(struct s2n_psk *psk = s2n_external_psk_new(), s2n_psk_free); + EXPECT_SUCCESS(s2n_psk_configure_early_data(psk, expected_max_early_data_size, + expected_cipher_suite->iana_value[0], expected_cipher_suite->iana_value[1])); + + EXPECT_EQUAL(psk->early_data_config.max_early_data_size, expected_max_early_data_size); + EXPECT_EQUAL(psk->early_data_config.protocol_version, S2N_TLS13); + EXPECT_EQUAL(psk->early_data_config.cipher_suite, expected_cipher_suite); + }; + + /* Set cipher suite must match hmac algorithm */ + { + DEFER_CLEANUP(struct s2n_psk *psk = s2n_external_psk_new(), s2n_psk_free); + const struct s2n_cipher_suite *cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + + psk->hmac_alg = cipher_suite->prf_alg + 1; + EXPECT_FAILURE_WITH_ERRNO(s2n_psk_configure_early_data(psk, nonzero_max_early_data, + cipher_suite->iana_value[0], cipher_suite->iana_value[1]), + S2N_ERR_INVALID_ARGUMENT); + + psk->hmac_alg = cipher_suite->prf_alg; + EXPECT_SUCCESS(s2n_psk_configure_early_data(psk, nonzero_max_early_data, + cipher_suite->iana_value[0], cipher_suite->iana_value[1])); + EXPECT_EQUAL(psk->early_data_config.cipher_suite, cipher_suite); + }; + }; + + /* Test s2n_psk_set_application_protocol */ + { + /* Safety checks */ + { + struct s2n_psk psk = { 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_psk_set_application_protocol(&psk, NULL, 1), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_psk_set_application_protocol(NULL, test_value, 1), S2N_ERR_NULL); + }; + + DEFER_CLEANUP(struct s2n_psk *psk = s2n_external_psk_new(), s2n_psk_free); + EXPECT_EQUAL(psk->early_data_config.application_protocol.size, 0); + EXPECT_EQUAL(psk->early_data_config.application_protocol.allocated, 0); + + /* Set empty value as no-op */ + EXPECT_SUCCESS(s2n_psk_set_application_protocol(psk, test_value, 0)); + EXPECT_EQUAL(psk->early_data_config.application_protocol.size, 0); + EXPECT_EQUAL(psk->early_data_config.application_protocol.allocated, 0); + + /* Set valid value */ + EXPECT_SUCCESS(s2n_psk_set_application_protocol(psk, test_value, sizeof(test_value))); + EXPECT_EQUAL(psk->early_data_config.application_protocol.size, sizeof(test_value)); + EXPECT_BYTEARRAY_EQUAL(psk->early_data_config.application_protocol.data, test_value, sizeof(test_value)); + + /* Replace previous value */ + EXPECT_SUCCESS(s2n_psk_set_application_protocol(psk, test_value_2, sizeof(test_value_2))); + EXPECT_EQUAL(psk->early_data_config.application_protocol.size, sizeof(test_value_2)); + EXPECT_BYTEARRAY_EQUAL(psk->early_data_config.application_protocol.data, test_value_2, sizeof(test_value_2)); + + /* Clear with empty value */ + EXPECT_SUCCESS(s2n_psk_set_application_protocol(psk, test_value, 0)); + EXPECT_EQUAL(psk->early_data_config.application_protocol.size, 0); + EXPECT_EQUAL(psk->early_data_config.application_protocol.allocated, 0); + + /* Repeat clear */ + EXPECT_SUCCESS(s2n_psk_set_application_protocol(psk, test_value, 0)); + EXPECT_EQUAL(psk->early_data_config.application_protocol.size, 0); + EXPECT_EQUAL(psk->early_data_config.application_protocol.allocated, 0); + }; + + /* Test s2n_psk_set_early_data_context */ + { + /* Safety checks */ + { + struct s2n_psk psk = { 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_psk_set_early_data_context(&psk, NULL, 1), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_psk_set_early_data_context(NULL, test_value, 1), S2N_ERR_NULL); + }; + + DEFER_CLEANUP(struct s2n_psk *psk = s2n_external_psk_new(), s2n_psk_free); + EXPECT_EQUAL(psk->early_data_config.context.size, 0); + EXPECT_EQUAL(psk->early_data_config.context.allocated, 0); + + /* Set empty value as no-op */ + EXPECT_SUCCESS(s2n_psk_set_early_data_context(psk, test_value, 0)); + EXPECT_EQUAL(psk->early_data_config.context.size, 0); + EXPECT_EQUAL(psk->early_data_config.context.allocated, 0); + + /* Set valid value */ + EXPECT_SUCCESS(s2n_psk_set_early_data_context(psk, test_value, sizeof(test_value))); + EXPECT_EQUAL(psk->early_data_config.context.size, sizeof(test_value)); + EXPECT_BYTEARRAY_EQUAL(psk->early_data_config.context.data, test_value, sizeof(test_value)); + + /* Replace previous value */ + EXPECT_SUCCESS(s2n_psk_set_early_data_context(psk, test_value_2, sizeof(test_value_2))); + EXPECT_EQUAL(psk->early_data_config.context.size, sizeof(test_value_2)); + EXPECT_BYTEARRAY_EQUAL(psk->early_data_config.context.data, test_value_2, sizeof(test_value_2)); + + /* Clear with empty value */ + EXPECT_SUCCESS(s2n_psk_set_early_data_context(psk, test_value, 0)); + EXPECT_EQUAL(psk->early_data_config.context.size, 0); + EXPECT_EQUAL(psk->early_data_config.context.allocated, 0); + + /* Repeat clear */ + EXPECT_SUCCESS(s2n_psk_set_early_data_context(psk, test_value, 0)); + EXPECT_EQUAL(psk->early_data_config.context.size, 0); + EXPECT_EQUAL(psk->early_data_config.context.allocated, 0); + }; + + /* Test s2n_early_data_config_clone */ + { + const uint8_t test_bad_value[] = "wrong"; + const uint8_t test_apln[] = "protocol"; + const uint8_t test_context[] = "context"; + const uint8_t test_version = UINT8_MAX; + const uint32_t test_max_early_data = 10; + const struct s2n_cipher_suite *test_cipher_suite = &s2n_tls13_chacha20_poly1305_sha256; + + for (size_t called_directly = 0; called_directly < 2; called_directly++) { + struct s2n_psk *original = s2n_external_psk_new(); + EXPECT_NOT_NULL(original); + EXPECT_SUCCESS(s2n_psk_configure_early_data(original, test_max_early_data, + test_cipher_suite->iana_value[0], test_cipher_suite->iana_value[1])); + EXPECT_SUCCESS(s2n_psk_set_application_protocol(original, test_apln, sizeof(test_apln))); + EXPECT_SUCCESS(s2n_psk_set_early_data_context(original, test_context, sizeof(test_context))); + original->early_data_config.protocol_version = test_version; + + DEFER_CLEANUP(struct s2n_psk *clone = s2n_external_psk_new(), s2n_psk_free); + EXPECT_SUCCESS(s2n_psk_set_application_protocol(clone, test_bad_value, sizeof(test_bad_value))); + EXPECT_NOT_NULL(clone); + + if (called_directly) { + EXPECT_OK(s2n_early_data_config_clone(clone, &original->early_data_config)); + } else { + EXPECT_SUCCESS(s2n_psk_set_identity(original, test_bad_value, sizeof(test_bad_value))); + EXPECT_SUCCESS(s2n_psk_set_secret(original, test_bad_value, sizeof(test_bad_value))); + EXPECT_OK(s2n_psk_clone(clone, original)); + } + + /* Check that the blobs weren't shallow copied */ + EXPECT_NOT_EQUAL(original->early_data_config.application_protocol.data, + clone->early_data_config.application_protocol.data); + EXPECT_NOT_EQUAL(original->early_data_config.context.data, clone->early_data_config.context.data); + + /* Free the original to ensure they share no memory */ + EXPECT_SUCCESS(s2n_psk_free(&original)); + + /* existing alpn is replaced by original's alpn */ + EXPECT_EQUAL(clone->early_data_config.application_protocol.size, sizeof(test_apln)); + EXPECT_BYTEARRAY_EQUAL(clone->early_data_config.application_protocol.data, test_apln, sizeof(test_apln)); + + /* new context is allocated for original's context */ + EXPECT_EQUAL(clone->early_data_config.context.size, sizeof(test_context)); + EXPECT_BYTEARRAY_EQUAL(clone->early_data_config.context.data, test_context, sizeof(test_context)); + + /* other values are copied */ + EXPECT_EQUAL(clone->early_data_config.max_early_data_size, test_max_early_data); + EXPECT_EQUAL(clone->early_data_config.cipher_suite, test_cipher_suite); + EXPECT_EQUAL(clone->early_data_config.protocol_version, test_version); + } + }; + + /* Test s2n_early_data_is_valid_for_connection */ + { + /* Safety check */ + EXPECT_FALSE(s2n_early_data_is_valid_for_connection(NULL)); + + /* Not valid if the first wire PSK was not chosen + * + *= https://tools.ietf.org/rfc/rfc8446#section-4.2.10 + *= type=test + *# In order to accept early data, the server MUST have accepted a PSK + *# cipher suite and selected the first key offered in the client's + *# "pre_shared_key" extension. + **/ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + conn->actual_protocol_version = S2N_TLS13; + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + + /* No chosen PSK */ + EXPECT_NULL(conn->psk_params.chosen_psk); + EXPECT_FALSE(s2n_early_data_is_valid_for_connection(conn)); + + EXPECT_OK(s2n_append_test_chosen_psk_with_early_data(conn, nonzero_max_early_data, &s2n_tls13_aes_256_gcm_sha384)); + + /* PSK chosen, but not first PSK */ + conn->psk_params.chosen_psk_wire_index = 5; + EXPECT_FALSE(s2n_early_data_is_valid_for_connection(conn)); + + /* First PSK chosen */ + conn->psk_params.chosen_psk_wire_index = 0; + EXPECT_TRUE(s2n_early_data_is_valid_for_connection(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /** + *= https://tools.ietf.org/rfc/rfc8446#section-4.2.10 + *= type=test + *# In addition, it MUST verify that the + *# following values are the same as those associated with the + *# selected PSK: + **/ + { + /** + *= https://tools.ietf.org/rfc/rfc8446#section-4.2.10 + *= type=test + *# - The TLS version number + **/ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); + EXPECT_OK(s2n_append_test_chosen_psk_with_early_data(conn, nonzero_max_early_data, &s2n_tls13_aes_256_gcm_sha384)); + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS12)); + EXPECT_FALSE(s2n_early_data_is_valid_for_connection(conn)); + + /* Reset state machine */ + conn->handshake.state_machine = S2N_STATE_MACHINE_INITIAL; + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + EXPECT_TRUE(s2n_early_data_is_valid_for_connection(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /** + *= https://tools.ietf.org/rfc/rfc8446#section-4.2.10 + *= type=test + *# - The selected cipher suite + **/ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); + EXPECT_OK(s2n_append_test_chosen_psk_with_early_data(conn, nonzero_max_early_data, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + conn->actual_protocol_version = S2N_TLS13; + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + + conn->secure->cipher_suite = &s2n_tls13_chacha20_poly1305_sha256; + EXPECT_FALSE(s2n_early_data_is_valid_for_connection(conn)); + + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + EXPECT_TRUE(s2n_early_data_is_valid_for_connection(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /** + *= https://tools.ietf.org/rfc/rfc8446#section-4.2.10 + *= type=test + *# - The selected ALPN [RFC7301] protocol, if any + **/ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); + EXPECT_OK(s2n_append_test_chosen_psk_with_early_data(conn, nonzero_max_early_data, &s2n_tls13_aes_256_gcm_sha384)); + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + + const uint8_t empty_protocol[] = ""; + const uint8_t required_protocol[] = "required_protocol"; + const uint8_t wrong_protocol[] = "wrong protocol"; + + /* No early data alpn set, no alpn negotiated */ + EXPECT_TRUE(s2n_early_data_is_valid_for_connection(conn)); + + /* No early data alpn set, but alpn negotiated */ + EXPECT_MEMCPY_SUCCESS(conn->application_protocol, required_protocol, sizeof(required_protocol)); + EXPECT_FALSE(s2n_early_data_is_valid_for_connection(conn)); + + EXPECT_SUCCESS(s2n_psk_set_application_protocol(conn->psk_params.chosen_psk, + required_protocol, sizeof(required_protocol))); + + /* Early data alpn set, but no alpn negotiated */ + EXPECT_MEMCPY_SUCCESS(conn->application_protocol, empty_protocol, sizeof(empty_protocol)); + EXPECT_FALSE(s2n_early_data_is_valid_for_connection(conn)); + + /* Early data alpn does NOT match negotiated alpn */ + EXPECT_MEMCPY_SUCCESS(conn->application_protocol, wrong_protocol, sizeof(wrong_protocol)); + EXPECT_FALSE(s2n_early_data_is_valid_for_connection(conn)); + + /* Early data alpn matches negotiated alpn */ + EXPECT_MEMCPY_SUCCESS(conn->application_protocol, required_protocol, sizeof(required_protocol)); + EXPECT_TRUE(s2n_early_data_is_valid_for_connection(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + }; + + /* Test s2n_early_data_accept_or_reject */ + { + /* Safety check */ + EXPECT_ERROR_WITH_ERRNO(s2n_early_data_accept_or_reject(NULL), S2N_ERR_NULL); + + /* Server */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); + EXPECT_OK(s2n_append_test_chosen_psk_with_early_data(conn, nonzero_max_early_data, &s2n_tls13_aes_256_gcm_sha384)); + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + + /* Early data not enabled */ + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_early_data_accept_or_reject(conn)); + EXPECT_EQUAL(conn->early_data_state, S2N_EARLY_DATA_REJECTED); + + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(conn)); + + /* Early data not requested */ + conn->early_data_state = S2N_EARLY_DATA_NOT_REQUESTED; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_early_data_accept_or_reject(conn)); + EXPECT_EQUAL(conn->early_data_state, S2N_EARLY_DATA_NOT_REQUESTED); + + /* Set wrong protocol version */ + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + conn->actual_protocol_version = S2N_TLS12; + EXPECT_OK(s2n_early_data_accept_or_reject(conn)); + EXPECT_EQUAL(conn->early_data_state, S2N_EARLY_DATA_REJECTED); + + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_early_data_accept_or_reject(conn)); + EXPECT_EQUAL(conn->early_data_state, S2N_EARLY_DATA_ACCEPTED); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Client */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); + EXPECT_OK(s2n_append_test_chosen_psk_with_early_data(conn, nonzero_max_early_data, &s2n_tls13_aes_256_gcm_sha384)); + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + + /* Early data not enabled */ + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_early_data_accept_or_reject(conn)); + EXPECT_EQUAL(conn->early_data_state, S2N_EARLY_DATA_REQUESTED); + + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(conn)); + + /* Early data not requested */ + conn->early_data_state = S2N_EARLY_DATA_NOT_REQUESTED; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_early_data_accept_or_reject(conn)); + EXPECT_EQUAL(conn->early_data_state, S2N_EARLY_DATA_NOT_REQUESTED); + + /* Set wrong protocol version */ + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + conn->actual_protocol_version = S2N_TLS12; + EXPECT_OK(s2n_early_data_accept_or_reject(conn)); + EXPECT_EQUAL(conn->early_data_state, S2N_EARLY_DATA_REJECTED); + + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_early_data_accept_or_reject(conn)); + EXPECT_EQUAL(conn->early_data_state, S2N_EARLY_DATA_REQUESTED); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Triggers callback to let application reject early data */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); + EXPECT_OK(s2n_append_test_chosen_psk_with_early_data(conn, nonzero_max_early_data, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(conn)); + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + conn->actual_protocol_version = S2N_TLS13; + + /* Without callback set, accepts early data */ + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + EXPECT_OK(s2n_early_data_accept_or_reject(conn)); + EXPECT_EQUAL(conn->early_data_state, S2N_EARLY_DATA_ACCEPTED); + + uint8_t accept_early_data = true; + EXPECT_SUCCESS(s2n_config_set_early_data_cb(config, s2n_test_early_data_cb)); + + /* With callback set, may still accept early data */ + conn->handshake.early_data_async_state = (struct s2n_offered_early_data){ 0 }; + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + EXPECT_SUCCESS(s2n_psk_set_early_data_context(conn->psk_params.chosen_psk, &accept_early_data, sizeof(accept_early_data))); + EXPECT_OK(s2n_early_data_accept_or_reject(conn)); + EXPECT_EQUAL(conn->early_data_state, S2N_EARLY_DATA_ACCEPTED); + + /* With callback set, may reject early data */ + accept_early_data = false; + conn->handshake.early_data_async_state = (struct s2n_offered_early_data){ 0 }; + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + EXPECT_SUCCESS(s2n_psk_set_early_data_context(conn->psk_params.chosen_psk, &accept_early_data, sizeof(accept_early_data))); + EXPECT_OK(s2n_early_data_accept_or_reject(conn)); + EXPECT_EQUAL(conn->early_data_state, S2N_EARLY_DATA_REJECTED); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Application rejects early data asynchronously */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_set_early_data_cb(config, s2n_test_async_early_data_cb)); + EXPECT_NOT_NULL(config); + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); + EXPECT_OK(s2n_append_test_chosen_psk_with_early_data(conn, nonzero_max_early_data, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(conn)); + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + conn->actual_protocol_version = S2N_TLS13; + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + + /* No decision yet: blocked */ + EXPECT_ERROR_WITH_ERRNO(s2n_early_data_accept_or_reject(conn), S2N_ERR_ASYNC_BLOCKED); + EXPECT_EQUAL(conn->early_data_state, S2N_EARLY_DATA_REQUESTED); + + /* If called again, still blocked */ + EXPECT_ERROR_WITH_ERRNO(s2n_early_data_accept_or_reject(conn), S2N_ERR_ASYNC_BLOCKED); + EXPECT_EQUAL(conn->early_data_state, S2N_EARLY_DATA_REQUESTED); + + /* Make decision */ + EXPECT_SUCCESS(s2n_offered_early_data_reject(async_early_data)); + EXPECT_EQUAL(conn->early_data_state, S2N_EARLY_DATA_REJECTED); + + /* Complete */ + EXPECT_OK(s2n_early_data_accept_or_reject(conn)); + EXPECT_EQUAL(conn->early_data_state, S2N_EARLY_DATA_REJECTED); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + }; + + /* Test s2n_connection_get_early_data_status */ + { + const uint32_t limit = 10; + + /* Safety */ + { + struct s2n_connection conn = { 0 }; + s2n_early_data_status_t status = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_early_data_status(NULL, &status), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_early_data_status(&conn, NULL), S2N_ERR_NULL); + + conn.early_data_state = S2N_EARLY_DATA_STATES_COUNT; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_early_data_status(&conn, &status), S2N_ERR_INVALID_EARLY_DATA_STATE); + }; + + /* Correct status returned for current early data state */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_append_test_psk_with_early_data(conn, limit, &s2n_tls13_aes_256_gcm_sha384)); + + s2n_early_data_status_t status = 0; + + EXPECT_SUCCESS(s2n_connection_get_early_data_status(conn, &status)); + EXPECT_EQUAL(status, S2N_EARLY_DATA_STATUS_OK); + + conn->early_data_state = S2N_EARLY_DATA_NOT_REQUESTED; + EXPECT_SUCCESS(s2n_connection_get_early_data_status(conn, &status)); + EXPECT_EQUAL(status, S2N_EARLY_DATA_STATUS_NOT_REQUESTED); + + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + EXPECT_SUCCESS(s2n_connection_get_early_data_status(conn, &status)); + EXPECT_EQUAL(status, S2N_EARLY_DATA_STATUS_OK); + + conn->early_data_state = S2N_EARLY_DATA_REJECTED; + EXPECT_SUCCESS(s2n_connection_get_early_data_status(conn, &status)); + EXPECT_EQUAL(status, S2N_EARLY_DATA_STATUS_REJECTED); + + conn->early_data_state = S2N_EARLY_DATA_ACCEPTED; + EXPECT_SUCCESS(s2n_connection_get_early_data_status(conn, &status)); + EXPECT_EQUAL(status, S2N_EARLY_DATA_STATUS_OK); + + conn->early_data_state = S2N_END_OF_EARLY_DATA; + EXPECT_SUCCESS(s2n_connection_get_early_data_status(conn, &status)); + EXPECT_EQUAL(status, S2N_EARLY_DATA_STATUS_END); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Sanity check that all valid early data states successfully report a status */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_append_test_psk_with_early_data(conn, limit, &s2n_tls13_aes_256_gcm_sha384)); + + s2n_early_data_status_t status = 0; + for (s2n_early_data_state state = 1; state < S2N_EARLY_DATA_STATES_COUNT; state++) { + conn->early_data_state = state; + EXPECT_SUCCESS(s2n_connection_get_early_data_status(conn, &status)); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test s2n_connection_get_remaining_early_data_size */ + { + const uint32_t limit = 10; + + /* Safety */ + { + struct s2n_connection conn = { 0 }; + uint32_t bytes = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_remaining_early_data_size(NULL, &bytes), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_remaining_early_data_size(&conn, NULL), S2N_ERR_NULL); + }; + + /* If early data allowed, return the remaining bytes allowed */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_append_test_psk_with_early_data(conn, limit, &s2n_tls13_aes_256_gcm_sha384)); + + uint32_t bytes = 0; + + EXPECT_SUCCESS(s2n_connection_get_remaining_early_data_size(conn, &bytes)); + EXPECT_EQUAL(bytes, limit); + + conn->early_data_state = S2N_EARLY_DATA_NOT_REQUESTED; + EXPECT_SUCCESS(s2n_connection_get_remaining_early_data_size(conn, &bytes)); + EXPECT_EQUAL(bytes, 0); + + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + EXPECT_SUCCESS(s2n_connection_get_remaining_early_data_size(conn, &bytes)); + EXPECT_EQUAL(bytes, limit); + + conn->early_data_bytes = limit - 1; + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + EXPECT_SUCCESS(s2n_connection_get_remaining_early_data_size(conn, &bytes)); + EXPECT_EQUAL(bytes, 1); + + conn->early_data_bytes = limit; + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + EXPECT_SUCCESS(s2n_connection_get_remaining_early_data_size(conn, &bytes)); + EXPECT_EQUAL(bytes, 0); + + conn->early_data_bytes = limit + 1; + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_remaining_early_data_size(conn, &bytes), + S2N_ERR_MAX_EARLY_DATA_SIZE); + EXPECT_EQUAL(bytes, 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test that all valid early data states successfully report a zero size */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + uint32_t size = 0; + for (s2n_early_data_state state = 0; state < S2N_EARLY_DATA_STATES_COUNT; state++) { + conn->early_data_state = state; + EXPECT_SUCCESS(s2n_connection_get_remaining_early_data_size(conn, &size)); + EXPECT_EQUAL(size, 0); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test that if S2N_EARLY_DATA_STATUS_OK, then non-zero size reported */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_append_test_psk_with_early_data(conn, limit, &s2n_tls13_aes_256_gcm_sha384)); + + s2n_early_data_status_t reason = 0; + uint32_t size = 0; + for (s2n_early_data_state state = 0; state < S2N_EARLY_DATA_STATES_COUNT; state++) { + conn->early_data_state = state; + + EXPECT_SUCCESS(s2n_connection_get_early_data_status(conn, &reason)); + EXPECT_SUCCESS(s2n_connection_get_remaining_early_data_size(conn, &size)); + + if (reason == S2N_EARLY_DATA_STATUS_OK) { + EXPECT_EQUAL(size, limit); + } else { + EXPECT_EQUAL(size, 0); + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test s2n_connection_get_max_early_data_size */ + { + /* Safety */ + { + struct s2n_connection conn = { 0 }; + uint32_t bytes = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_max_early_data_size(&conn, NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_max_early_data_size(NULL, &bytes), S2N_ERR_NULL); + EXPECT_EQUAL(bytes, 0); + }; + + /* Retrieve the limit from the first PSK, or return 0 */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + const uint32_t limit = 10; + uint32_t actual_bytes = limit; + + /* No PSKs: limit is zero */ + EXPECT_SUCCESS(s2n_connection_get_max_early_data_size(conn, &actual_bytes)); + EXPECT_EQUAL(actual_bytes, 0); + + /* PSK with zero limit: limit is zero */ + EXPECT_OK(s2n_append_test_psk_with_early_data(conn, 0, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_SUCCESS(s2n_connection_get_max_early_data_size(conn, &actual_bytes)); + EXPECT_EQUAL(actual_bytes, 0); + + /* Second PSK with non-zero limit: limit is still zero */ + EXPECT_OK(s2n_append_test_psk_with_early_data(conn, limit, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_SUCCESS(s2n_connection_get_max_early_data_size(conn, &actual_bytes)); + EXPECT_EQUAL(actual_bytes, 0); + + /* First PSK with non-zero limit: limit is non-zero */ + EXPECT_OK(s2n_psk_parameters_wipe(&conn->psk_params)); + EXPECT_OK(s2n_append_test_psk_with_early_data(conn, limit, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_SUCCESS(s2n_connection_get_max_early_data_size(conn, &actual_bytes)); + EXPECT_EQUAL(actual_bytes, limit); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* If in server mode, apply the server limit */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + + const uint32_t psk_limit = 10; + EXPECT_OK(s2n_append_test_chosen_psk_with_early_data(conn, psk_limit, &s2n_tls13_aes_256_gcm_sha384)); + + uint32_t actual_bytes = 0; + + /* server limit is lower, but PSK is external: use PSK limit */ + EXPECT_EQUAL(conn->psk_params.chosen_psk->type, S2N_PSK_TYPE_EXTERNAL); + uint32_t server_limit = psk_limit - 1; + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(conn, server_limit)); + EXPECT_SUCCESS(s2n_connection_get_max_early_data_size(conn, &actual_bytes)); + EXPECT_EQUAL(actual_bytes, psk_limit); + + conn->psk_params.chosen_psk->type = S2N_PSK_TYPE_RESUMPTION; + + /* server limit is higher: use PSK limit */ + server_limit = psk_limit + 1; + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(conn, server_limit)); + EXPECT_SUCCESS(s2n_connection_get_max_early_data_size(conn, &actual_bytes)); + EXPECT_EQUAL(actual_bytes, psk_limit); + + /* server limit is lower and PSK is resumption: use server limit */ + server_limit = psk_limit - 1; + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(conn, server_limit)); + EXPECT_SUCCESS(s2n_connection_get_max_early_data_size(conn, &actual_bytes)); + EXPECT_EQUAL(actual_bytes, server_limit); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* If in server mode, fall back to the server limit */ + { + const uint32_t server_limit = 15; + uint32_t actual_bytes = 0; + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + + /* No PSKs: use server limit for initial connection */ + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(server_conn, server_limit)); + EXPECT_SUCCESS(s2n_connection_get_max_early_data_size(server_conn, &actual_bytes)); + EXPECT_EQUAL(actual_bytes, server_limit); + + /* Client mode: don't use server limit for initial connection */ + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(client_conn, server_limit)); + EXPECT_SUCCESS(s2n_connection_get_max_early_data_size(client_conn, &actual_bytes)); + EXPECT_EQUAL(actual_bytes, 0); + + /* Negotiated connection: once a connection is negotiated, no PSKs means no early data */ + server_conn->handshake.handshake_type = NEGOTIATED; + EXPECT_SUCCESS(s2n_connection_get_max_early_data_size(server_conn, &actual_bytes)); + EXPECT_EQUAL(actual_bytes, 0); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + }; + + /* Test s2n_config_set_server_max_early_data_size */ + { + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_server_max_early_data_size(NULL, 1), S2N_ERR_NULL); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + + EXPECT_EQUAL(config->server_max_early_data_size, 0); + + EXPECT_SUCCESS(s2n_config_set_server_max_early_data_size(config, 1)); + EXPECT_EQUAL(config->server_max_early_data_size, 1); + + EXPECT_SUCCESS(s2n_config_set_server_max_early_data_size(config, UINT32_MAX)); + EXPECT_EQUAL(config->server_max_early_data_size, UINT32_MAX); + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test s2n_connection_set_server_max_early_data_size */ + { + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_set_server_max_early_data_size(NULL, 1), S2N_ERR_NULL); + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + EXPECT_EQUAL(conn->server_max_early_data_size, 0); + EXPECT_FALSE(conn->server_max_early_data_size_overridden); + + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(conn, 1)); + EXPECT_EQUAL(conn->server_max_early_data_size, 1); + EXPECT_TRUE(conn->server_max_early_data_size_overridden); + + conn->server_max_early_data_size_overridden = false; + + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(conn, UINT32_MAX)); + EXPECT_EQUAL(conn->server_max_early_data_size, UINT32_MAX); + EXPECT_TRUE(conn->server_max_early_data_size_overridden); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_early_data_get_server_max_size */ + { + uint32_t result_size = 0; + const uint32_t connection_value = 10; + const uint32_t config_value = 5; + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_server_max_early_data_size(config, config_value)); + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(conn, connection_value)); + + /* Safety */ + EXPECT_ERROR_WITH_ERRNO(s2n_early_data_get_server_max_size(NULL, &result_size), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_early_data_get_server_max_size(conn, NULL), S2N_ERR_NULL); + + /* No config */ + conn->config = NULL; + conn->server_max_early_data_size_overridden = false; + EXPECT_ERROR_WITH_ERRNO(s2n_early_data_get_server_max_size(conn, &result_size), S2N_ERR_NULL); + + /* No config, but connection override set */ + EXPECT_NULL(conn->config); + conn->server_max_early_data_size_overridden = true; + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(conn, connection_value)); + EXPECT_OK(s2n_early_data_get_server_max_size(conn, &result_size)); + EXPECT_EQUAL(result_size, connection_value); + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Return config version if override not set */ + conn->server_max_early_data_size_overridden = false; + EXPECT_OK(s2n_early_data_get_server_max_size(conn, &result_size)); + EXPECT_EQUAL(result_size, config_value); + + /* Return connection version if set */ + conn->server_max_early_data_size_overridden = true; + EXPECT_OK(s2n_early_data_get_server_max_size(conn, &result_size)); + EXPECT_EQUAL(result_size, connection_value); + + /* Connection can override with a zero value */ + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(conn, 0)); + EXPECT_OK(s2n_early_data_get_server_max_size(conn, &result_size)); + EXPECT_EQUAL(result_size, 0); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_connection_set_server_early_data_context */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + const uint8_t data[] = "hello world"; + + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_set_server_early_data_context(NULL, data, 1), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_set_server_early_data_context(conn, NULL, 1), S2N_ERR_NULL); + EXPECT_EQUAL(conn->server_early_data_context.size, 0); + EXPECT_EQUAL(conn->server_early_data_context.allocated, 0); + + /* Set context */ + EXPECT_SUCCESS(s2n_connection_set_server_early_data_context(conn, data, sizeof(data))); + EXPECT_EQUAL(conn->server_early_data_context.size, sizeof(data)); + EXPECT_BYTEARRAY_EQUAL(conn->server_early_data_context.data, data, sizeof(data)); + + /* Clear context */ + EXPECT_SUCCESS(s2n_connection_set_server_early_data_context(conn, NULL, 0)); + EXPECT_EQUAL(conn->server_early_data_context.size, 0); + + /* Set context again */ + EXPECT_SUCCESS(s2n_connection_set_server_early_data_context(conn, data, 1)); + EXPECT_EQUAL(conn->server_early_data_context.size, 1); + EXPECT_BYTEARRAY_EQUAL(conn->server_early_data_context.data, data, 1); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_early_data_record_bytes */ + { + /* Safety check */ + EXPECT_ERROR_WITH_ERRNO(s2n_early_data_record_bytes(NULL, 1), S2N_ERR_NULL); + + const uint32_t limit = 10; + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_append_test_psk_with_early_data(conn, limit, &s2n_tls13_aes_256_gcm_sha384)); + + /* If early data not expected, bytes are not recorded */ + conn->early_data_bytes = 0; + EXPECT_OK(s2n_early_data_record_bytes(conn, limit + 1)); + EXPECT_EQUAL(conn->early_data_bytes, 0); + + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(conn)); + + /* Bytes are recorded */ + { + conn->early_data_bytes = 0; + EXPECT_OK(s2n_early_data_record_bytes(conn, 1)); + EXPECT_EQUAL(conn->early_data_bytes, 1); + + conn->early_data_bytes = 1; + EXPECT_OK(s2n_early_data_record_bytes(conn, 1)); + EXPECT_EQUAL(conn->early_data_bytes, 2); + }; + + /* Error if exceeds max_early_data_size */ + { + conn->early_data_bytes = 0; + EXPECT_OK(s2n_early_data_record_bytes(conn, limit)); + EXPECT_EQUAL(conn->early_data_bytes, limit); + + conn->early_data_bytes = 1; + EXPECT_ERROR_WITH_ERRNO(s2n_early_data_record_bytes(conn, limit), S2N_ERR_MAX_EARLY_DATA_SIZE); + EXPECT_EQUAL(conn->early_data_bytes, limit + 1); + }; + + /* Prevents early_data_bytes from overflowing */ + { + conn->early_data_bytes = UINT64_MAX - 2; + EXPECT_ERROR_WITH_ERRNO(s2n_early_data_record_bytes(conn, 1), S2N_ERR_MAX_EARLY_DATA_SIZE); + EXPECT_EQUAL(conn->early_data_bytes, UINT64_MAX - 1); + + conn->early_data_bytes = UINT64_MAX - 1; + EXPECT_ERROR_WITH_ERRNO(s2n_early_data_record_bytes(conn, 1), S2N_ERR_MAX_EARLY_DATA_SIZE); + EXPECT_EQUAL(conn->early_data_bytes, UINT64_MAX); + + conn->early_data_bytes = UINT64_MAX; + EXPECT_ERROR_WITH_ERRNO(s2n_early_data_record_bytes(conn, 1), S2N_ERR_INTEGER_OVERFLOW); + EXPECT_EQUAL(conn->early_data_bytes, UINT64_MAX); + }; + + /* Zero bytes are "recorded" */ + { + conn->early_data_bytes = 0; + EXPECT_OK(s2n_early_data_record_bytes(conn, 0)); + EXPECT_EQUAL(conn->early_data_bytes, 0); + + conn->early_data_bytes = limit; + EXPECT_OK(s2n_early_data_record_bytes(conn, 0)); + EXPECT_EQUAL(conn->early_data_bytes, limit); + + conn->early_data_bytes = limit + 1; + EXPECT_ERROR_WITH_ERRNO(s2n_early_data_record_bytes(conn, 0), S2N_ERR_MAX_EARLY_DATA_SIZE); + EXPECT_EQUAL(conn->early_data_bytes, limit + 1); + }; + + /* Negative bytes are "recorded" */ + { + conn->early_data_bytes = 0; + EXPECT_OK(s2n_early_data_record_bytes(conn, -1)); + EXPECT_EQUAL(conn->early_data_bytes, 0); + + conn->early_data_bytes = limit / 2; + EXPECT_OK(s2n_early_data_record_bytes(conn, -1)); + EXPECT_EQUAL(conn->early_data_bytes, limit / 2); + + conn->early_data_bytes = limit; + EXPECT_OK(s2n_early_data_record_bytes(conn, -1)); + EXPECT_EQUAL(conn->early_data_bytes, limit); + + /* Unlike with other inputs, does not return an error and set S2N_ERR_MAX_EARLY_DATA_SIZE. + * That would overwrite whatever send error caused the -1 result. + */ + conn->early_data_bytes = limit + 1; + EXPECT_OK(s2n_early_data_record_bytes(conn, -1)); + EXPECT_EQUAL(conn->early_data_bytes, limit + 1); + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_early_data_validate_send */ + { + /* Safety check */ + EXPECT_ERROR_WITH_ERRNO(s2n_early_data_validate_send(NULL, 1), S2N_ERR_NULL); + + const uint32_t limit = 10; + struct s2n_connection *valid_connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(valid_connection); + EXPECT_OK(s2n_append_test_psk_with_early_data(valid_connection, limit, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(valid_connection)); + valid_connection->mode = S2N_CLIENT; + valid_connection->early_data_state = S2N_EARLY_DATA_REQUESTED; + valid_connection->early_data_bytes = 0; + + /* Fails if server */ + struct s2n_connection conn = *valid_connection; + conn.mode = S2N_SERVER; + EXPECT_ERROR_WITH_ERRNO(s2n_early_data_validate_send(&conn, 1), S2N_ERR_EARLY_DATA_NOT_ALLOWED); + + /* Fails if wrong state */ + conn = *valid_connection; + conn.early_data_state = S2N_END_OF_EARLY_DATA; + EXPECT_ERROR_WITH_ERRNO(s2n_early_data_validate_send(&conn, 1), S2N_ERR_EARLY_DATA_NOT_ALLOWED); + + /* Passes for S2N_EARLY_DATA_REQUESTED */ + conn = *valid_connection; + conn.early_data_state = S2N_EARLY_DATA_REQUESTED; + EXPECT_OK(s2n_early_data_validate_send(&conn, 1)); + + /* Passes for S2N_EARLY_DATA_ACCEPTED */ + conn = *valid_connection; + conn.early_data_state = S2N_EARLY_DATA_ACCEPTED; + EXPECT_OK(s2n_early_data_validate_send(&conn, 1)); + + /* Fails if too much data */ + conn = *valid_connection; + conn.early_data_bytes = limit; + EXPECT_ERROR_WITH_ERRNO(s2n_early_data_validate_send(&conn, 1), S2N_ERR_MAX_EARLY_DATA_SIZE); + + /* No-op if actually Application Data */ + conn = *valid_connection; + conn.early_data_bytes = limit; + conn.handshake.handshake_type = NEGOTIATED; + while (s2n_conn_get_current_message_type(&conn) != APPLICATION_DATA) { + conn.handshake.message_number++; + } + EXPECT_OK(s2n_early_data_validate_send(&conn, 1)); + + EXPECT_SUCCESS(s2n_connection_free(valid_connection)); + }; + + /* Test s2n_early_data_validate_recv */ + { + /* Safety check */ + EXPECT_ERROR_WITH_ERRNO(s2n_early_data_validate_recv(NULL), S2N_ERR_NULL); + + const uint32_t limit = 10; + struct s2n_connection *valid_connection = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(valid_connection); + EXPECT_OK(s2n_append_test_psk_with_early_data(valid_connection, limit, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(valid_connection)); + valid_connection->early_data_state = S2N_EARLY_DATA_ACCEPTED; + valid_connection->early_data_bytes = 0; + valid_connection->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(valid_connection, S2N_TLS13)); + valid_connection->handshake.handshake_type = NEGOTIATED | WITH_EARLY_DATA; + while (s2n_conn_get_current_message_type(valid_connection) != END_OF_EARLY_DATA) { + valid_connection->handshake.message_number++; + } + + /* Passes if everything valid */ + struct s2n_connection conn = *valid_connection; + EXPECT_OK(s2n_early_data_validate_recv(&conn)); + + /* Fails if client */ + conn = *valid_connection; + conn.mode = S2N_CLIENT; + EXPECT_ERROR_WITH_ERRNO(s2n_early_data_validate_recv(&conn), S2N_ERR_EARLY_DATA_NOT_ALLOWED); + + /* Fails if wrong state */ + conn = *valid_connection; + conn.early_data_state = S2N_END_OF_EARLY_DATA; + EXPECT_ERROR_WITH_ERRNO(s2n_early_data_validate_recv(&conn), S2N_ERR_EARLY_DATA_NOT_ALLOWED); + + /* Fails if wrong handshake message */ + conn = *valid_connection; + conn.handshake.message_number--; + EXPECT_ERROR_WITH_ERRNO(s2n_early_data_validate_recv(&conn), S2N_ERR_EARLY_DATA_NOT_ALLOWED); + + /* Fails for S2N_EARLY_DATA_REQUESTED */ + conn = *valid_connection; + conn.early_data_state = S2N_EARLY_DATA_REQUESTED; + EXPECT_ERROR_WITH_ERRNO(s2n_early_data_validate_recv(&conn), S2N_ERR_EARLY_DATA_NOT_ALLOWED); + + /* Passes for S2N_EARLY_DATA_ACCEPTED */ + conn = *valid_connection; + conn.early_data_state = S2N_EARLY_DATA_ACCEPTED; + EXPECT_OK(s2n_early_data_validate_recv(&conn)); + + /* No-op if actually Application Data */ + conn = *valid_connection; + conn.early_data_bytes = limit; + conn.handshake.handshake_type = NEGOTIATED; + while (s2n_conn_get_current_message_type(&conn) != APPLICATION_DATA) { + conn.handshake.message_number++; + } + EXPECT_OK(s2n_early_data_validate_recv(&conn)); + + EXPECT_SUCCESS(s2n_connection_free(valid_connection)); + }; + + /* Test s2n_config_set_early_data_cb */ + { + struct s2n_config *config = s2n_config_new(); + + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_early_data_cb(NULL, s2n_test_early_data_cb), S2N_ERR_NULL); + EXPECT_EQUAL(config->early_data_cb, 0); + + /* Set callback */ + EXPECT_SUCCESS(s2n_config_set_early_data_cb(config, s2n_test_early_data_cb)); + EXPECT_EQUAL(config->early_data_cb, s2n_test_early_data_cb); + + /* Clear callback */ + EXPECT_SUCCESS(s2n_config_set_early_data_cb(config, NULL)); + EXPECT_EQUAL(config->early_data_cb, NULL); + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test s2n_offered_early_data_get_context and s2n_offered_early_data_get_context_length */ + { + struct s2n_offered_early_data early_data = { 0 }; + const uint8_t context[] = "psk context"; + const uint8_t empty_context[sizeof(context)] = { 0 }; + uint8_t actual_context[sizeof(context)] = { 0 }; + uint16_t length = 1; + + /* Safety */ + { + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_early_data_get_context_length(NULL, &length), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_early_data_get_context(NULL, actual_context, 1), S2N_ERR_NULL); + + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_early_data_get_context_length(&early_data, NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_early_data_get_context(&early_data, NULL, 1), S2N_ERR_NULL); + + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_early_data_get_context_length(&early_data, &length), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_early_data_get_context(&early_data, actual_context, 1), S2N_ERR_NULL); + + early_data.conn = s2n_connection_new(S2N_SERVER); + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_early_data_get_context_length(&early_data, &length), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_early_data_get_context(&early_data, actual_context, 1), S2N_ERR_NULL); + EXPECT_SUCCESS(s2n_connection_free(early_data.conn)); + }; + + /* No context */ + { + early_data.conn = s2n_connection_new(S2N_SERVER); + DEFER_CLEANUP(struct s2n_psk *test_psk = s2n_test_psk_new(early_data.conn), s2n_psk_free); + early_data.conn->psk_params.chosen_psk = test_psk; + + EXPECT_SUCCESS(s2n_offered_early_data_get_context_length(&early_data, &length)); + EXPECT_EQUAL(length, 0); + + EXPECT_SUCCESS(s2n_offered_early_data_get_context(&early_data, actual_context, 0)); + EXPECT_BYTEARRAY_EQUAL(actual_context, empty_context, sizeof(empty_context)); + + EXPECT_SUCCESS(s2n_offered_early_data_get_context(&early_data, actual_context, sizeof(actual_context))); + EXPECT_BYTEARRAY_EQUAL(actual_context, empty_context, sizeof(empty_context)); + + EXPECT_SUCCESS(s2n_connection_free(early_data.conn)); + }; + + /* Context */ + { + early_data.conn = s2n_connection_new(S2N_SERVER); + DEFER_CLEANUP(struct s2n_psk *test_psk = s2n_test_psk_new(early_data.conn), s2n_psk_free); + EXPECT_SUCCESS(s2n_psk_set_early_data_context(test_psk, context, sizeof(context))); + early_data.conn->psk_params.chosen_psk = test_psk; + + EXPECT_SUCCESS(s2n_offered_early_data_get_context_length(&early_data, &length)); + EXPECT_EQUAL(length, sizeof(context)); + + EXPECT_SUCCESS(s2n_offered_early_data_get_context(&early_data, actual_context, sizeof(actual_context))); + EXPECT_BYTEARRAY_EQUAL(actual_context, context, sizeof(context)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_early_data_get_context(&early_data, actual_context, 1), + S2N_ERR_INSUFFICIENT_MEM_SIZE); + + EXPECT_SUCCESS(s2n_connection_free(early_data.conn)); + }; + }; + + /* Test s2n_offered_early_data_reject */ + { + struct s2n_offered_early_data early_data = { .conn = NULL }; + + /* Safety */ + { + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_early_data_reject(NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_early_data_reject(&early_data), S2N_ERR_NULL); + }; + + /* Reject early data */ + { + early_data.conn = s2n_connection_new(S2N_SERVER); + early_data.conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + EXPECT_SUCCESS(s2n_offered_early_data_reject(&early_data)); + EXPECT_EQUAL(early_data.conn->early_data_state, S2N_EARLY_DATA_REJECTED); + EXPECT_SUCCESS(s2n_connection_free(early_data.conn)); + }; + }; + + /* Test s2n_offered_early_data_accept */ + { + struct s2n_offered_early_data early_data = { .conn = NULL }; + + /* Safety */ + { + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_early_data_accept(NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_early_data_accept(&early_data), S2N_ERR_NULL); + }; + + /* Accept early data */ + { + early_data.conn = s2n_connection_new(S2N_SERVER); + early_data.conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + EXPECT_SUCCESS(s2n_offered_early_data_accept(&early_data)); + EXPECT_EQUAL(early_data.conn->early_data_state, S2N_EARLY_DATA_ACCEPTED); + EXPECT_SUCCESS(s2n_connection_free(early_data.conn)); + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_ecc_evp_test.c b/tests/unit/s2n_ecc_evp_test.c new file mode 100644 index 00000000000..a3074052439 --- /dev/null +++ b/tests/unit/s2n_ecc_evp_test.c @@ -0,0 +1,409 @@ +/* + * 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 "crypto/s2n_ecc_evp.h" + +#include "api/s2n.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_security_policies.h" +#include "utils/s2n_mem.h" + +#define ECDHE_PARAMS_LEGACY_FORM 4 + +extern const struct s2n_ecc_named_curve s2n_unsupported_curve; + +int main(int argc, char** argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + { + /* Test generate ephemeral keys for all supported curves */ + for (size_t i = 0; i < s2n_all_supported_curves_list_len; i++) { + struct s2n_ecc_evp_params evp_params = { 0 }; + /* Server generates a key */ + evp_params.negotiated_curve = s2n_all_supported_curves_list[i]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&evp_params)); + EXPECT_NOT_NULL(evp_params.evp_pkey); + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&evp_params)); + } + }; + { + /* Test failure case for generate ephemeral key when the negotiated curve is not set */ + for (size_t i = 0; i < s2n_all_supported_curves_list_len; i++) { + struct s2n_ecc_evp_params evp_params = { 0 }; + /* Server generates a key */ + evp_params.negotiated_curve = NULL; + EXPECT_FAILURE(s2n_ecc_evp_generate_ephemeral_key(&evp_params)); + EXPECT_NULL(evp_params.evp_pkey); + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&evp_params)); + } + }; + { + /* Test generate ephemeral key and compute shared key for all supported curves */ + for (size_t i = 0; i < s2n_all_supported_curves_list_len; i++) { + struct s2n_ecc_evp_params server_params = { 0 }; + struct s2n_ecc_evp_params client_params = { 0 }; + struct s2n_blob server_shared = { 0 }; + struct s2n_blob client_shared = { 0 }; + + /* Server generates a key */ + server_params.negotiated_curve = s2n_all_supported_curves_list[i]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&server_params)); + EXPECT_NOT_NULL(server_params.evp_pkey); + + /* Client generates a key */ + client_params.negotiated_curve = s2n_all_supported_curves_list[i]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_params)); + EXPECT_NOT_NULL(client_params.evp_pkey); + + /* Compute shared secret for server */ + EXPECT_SUCCESS( + s2n_ecc_evp_compute_shared_secret_from_params(&server_params, &client_params, &server_shared)); + + /* Compute shared secret for client */ + EXPECT_SUCCESS( + s2n_ecc_evp_compute_shared_secret_from_params(&client_params, &server_params, &client_shared)); + + /* Check if the shared secret computed is the same for the client + * and the server */ + EXPECT_EQUAL(client_shared.size, server_shared.size); + EXPECT_BYTEARRAY_EQUAL(client_shared.data, server_shared.data, client_shared.size); + + /* Clean up */ + EXPECT_SUCCESS(s2n_free(&server_shared)); + EXPECT_SUCCESS(s2n_free(&client_shared)); + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&server_params)); + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&client_params)); + } + }; + { + /* Test failure case for computing shared key for all supported curves when the server + and client curves do not match */ + for (size_t i = 0; i < s2n_all_supported_curves_list_len; i++) { + for (size_t j = 0; j < s2n_all_supported_curves_list_len; j++) { + struct s2n_ecc_evp_params server_params = { 0 }; + struct s2n_ecc_evp_params client_params = { 0 }; + struct s2n_blob server_shared = { 0 }; + struct s2n_blob client_shared = { 0 }; + if (i == j) { + continue; + } + + /* Server generates a key */ + server_params.negotiated_curve = s2n_all_supported_curves_list[j]; + + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&server_params)); + EXPECT_NOT_NULL(server_params.evp_pkey); + + /* Client generates a key */ + client_params.negotiated_curve = s2n_all_supported_curves_list[i]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_params)); + EXPECT_NOT_NULL(client_params.evp_pkey); + + /* Compute shared secret for server */ + EXPECT_FAILURE( + s2n_ecc_evp_compute_shared_secret_from_params(&server_params, &client_params, &server_shared)); + + /* Compute shared secret for client */ + EXPECT_FAILURE( + s2n_ecc_evp_compute_shared_secret_from_params(&client_params, &server_params, &client_shared)); + + /* Clean up */ + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&server_params)); + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&client_params)); + } + } + }; + { + /* Test s2n_ecc_evp_write_params_point for all supported curves */ + for (size_t i = 0; i < s2n_all_supported_curves_list_len; i++) { + struct s2n_ecc_evp_params test_params = { 0 }; + struct s2n_stuffer wire = { 0 }; + uint8_t legacy_form; + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&wire, 0)); + + /* Server generates a key for a given curve */ + test_params.negotiated_curve = s2n_all_supported_curves_list[i]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&test_params)); + EXPECT_NOT_NULL(test_params.evp_pkey); + EXPECT_SUCCESS(s2n_ecc_evp_write_params_point(&test_params, &wire)); + + /* Verify output is of the right length */ + uint32_t avail = s2n_stuffer_data_available(&wire); + EXPECT_EQUAL(avail, s2n_all_supported_curves_list[i]->share_size); + + /* Verify output starts with the known legacy form for curves secp256r1 + * and secp384r1*/ + if (s2n_all_supported_curves_list[i]->iana_id == TLS_EC_CURVE_SECP_256_R1 || s2n_all_supported_curves_list[i]->iana_id == TLS_EC_CURVE_SECP_384_R1) { + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&wire, &legacy_form)); + EXPECT_EQUAL(legacy_form, ECDHE_PARAMS_LEGACY_FORM); + } + + /* Clean up */ + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&test_params)); + EXPECT_SUCCESS(s2n_stuffer_free(&wire)); + } + }; + { + /* TEST s2n_ecc_evp_read_params_point for all supported curves */ + for (size_t i = 0; i < s2n_all_supported_curves_list_len; i++) { + struct s2n_ecc_evp_params write_params = { 0 }; + struct s2n_blob point_blob = { 0 }; + struct s2n_stuffer wire = { 0 }; + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&wire, 0)); + + /* Server generates a key for a given curve */ + write_params.negotiated_curve = s2n_all_supported_curves_list[i]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&write_params)); + EXPECT_NOT_NULL(write_params.evp_pkey); + EXPECT_SUCCESS(s2n_ecc_evp_write_params_point(&write_params, &wire)); + + /* Read point back in */ + EXPECT_SUCCESS( + s2n_ecc_evp_read_params_point(&wire, s2n_all_supported_curves_list[i]->share_size, &point_blob)); + + /* Check that the blob looks generally correct. */ + EXPECT_EQUAL(point_blob.size, s2n_all_supported_curves_list[i]->share_size); + EXPECT_NOT_NULL(point_blob.data); + + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&write_params)); + EXPECT_SUCCESS(s2n_stuffer_free(&wire)); + } + }; + { + /* TEST s2n_ecc_evp_parse_params_point for all supported curves */ + for (size_t i = 0; i < s2n_all_supported_curves_list_len; i++) { + struct s2n_ecc_evp_params write_params = { 0 }; + struct s2n_ecc_evp_params read_params = { 0 }; + struct s2n_blob point_blob = { 0 }; + struct s2n_stuffer wire = { 0 }; + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&wire, 0)); + + write_params.negotiated_curve = s2n_all_supported_curves_list[i]; + read_params.negotiated_curve = s2n_all_supported_curves_list[i]; + + /* Server generates a key for a given curve */ + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&write_params)); + EXPECT_NOT_NULL(write_params.evp_pkey); + EXPECT_SUCCESS(s2n_ecc_evp_write_params_point(&write_params, &wire)); + + /* Read point back in */ + EXPECT_SUCCESS( + s2n_ecc_evp_read_params_point(&wire, s2n_all_supported_curves_list[i]->share_size, &point_blob)); + EXPECT_SUCCESS(s2n_ecc_evp_parse_params_point(&point_blob, &read_params)); + /* Check that the point we read is the same we wrote */ + EXPECT_TRUE(EVP_PKEY_cmp(write_params.evp_pkey, read_params.evp_pkey)); + + /* Clean up */ + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&write_params)); + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&read_params)); + EXPECT_SUCCESS(s2n_stuffer_free(&wire)); + } + }; + { + DEFER_CLEANUP(struct s2n_connection* conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + /* Test read/write/parse params for all supported curves */ + for (size_t i = 0; i < s2n_all_supported_curves_list_len; i++) { + struct s2n_ecc_evp_params write_params = { 0 }; + struct s2n_ecc_evp_params read_params = { 0 }; + struct s2n_stuffer wire = { 0 }; + struct s2n_blob ecdh_params_sent, ecdh_params_received; + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&wire, 1024)); + + write_params.negotiated_curve = s2n_all_supported_curves_list[i]; + read_params.negotiated_curve = s2n_all_supported_curves_list[i]; + + /* Server generates a key for a given curve */ + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&write_params)); + EXPECT_NOT_NULL(write_params.evp_pkey); + + /* Write params points to wire */ + EXPECT_SUCCESS(s2n_ecc_evp_write_params(&write_params, &wire, &ecdh_params_sent)); + struct s2n_ecdhe_raw_server_params ecdhe_data = { 0 }; + + /* Read params points from the wire */ + EXPECT_SUCCESS(s2n_ecc_evp_read_params(&wire, &ecdh_params_received, &ecdhe_data)); + EXPECT_SUCCESS(s2n_ecc_evp_parse_params(conn, &ecdhe_data, &read_params)); + + /* Check that the point we read is the same we wrote */ + EXPECT_TRUE(EVP_PKEY_cmp(write_params.evp_pkey, read_params.evp_pkey)); + + /* Clean up */ + EXPECT_SUCCESS(s2n_stuffer_free(&wire)); + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&write_params)); + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&read_params)); + } + }; + { + DEFER_CLEANUP(struct s2n_connection* conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + /* Test generate/read/write/parse and compute shared secrets for all supported curves */ + for (size_t i = 0; i < s2n_all_supported_curves_list_len; i++) { + struct s2n_ecc_evp_params server_params = { 0 }; + struct s2n_ecc_evp_params read_params = { 0 }; + struct s2n_ecc_evp_params client_params = { 0 }; + struct s2n_stuffer wire = { 0 }; + struct s2n_blob ecdh_params_sent, ecdh_params_received; + struct s2n_blob server_shared_secret, client_shared_secret; + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&wire, 1024)); + + server_params.negotiated_curve = s2n_all_supported_curves_list[i]; + read_params.negotiated_curve = s2n_all_supported_curves_list[i]; + + /* Server generates a key for a given curve */ + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&server_params)); + EXPECT_NOT_NULL(server_params.evp_pkey); + + /* Server sends the public */ + EXPECT_SUCCESS(s2n_ecc_evp_write_params(&server_params, &wire, &ecdh_params_sent)); + + /* Client reads the public */ + struct s2n_ecdhe_raw_server_params ecdhe_data = { 0 }; + EXPECT_SUCCESS(s2n_ecc_evp_read_params(&wire, &ecdh_params_received, &ecdhe_data)); + EXPECT_SUCCESS(s2n_ecc_evp_parse_params(conn, &ecdhe_data, &read_params)); + + /* Verify if the client correctly read the server public */ + EXPECT_TRUE(EVP_PKEY_cmp(server_params.evp_pkey, read_params.evp_pkey)); + + /* Client generates its key for the given curve */ + client_params.negotiated_curve = s2n_all_supported_curves_list[i]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_params)); + EXPECT_NOT_NULL(client_params.evp_pkey); + + /* Compute shared secret for the server */ + EXPECT_SUCCESS( + s2n_ecc_evp_compute_shared_secret_from_params(&server_params, &client_params, &server_shared_secret)); + + /* Compute shared secret for the client */ + EXPECT_SUCCESS( + s2n_ecc_evp_compute_shared_secret_from_params(&client_params, &read_params, &client_shared_secret)); + + /* Verify that shared is the same for the client and the server */ + EXPECT_EQUAL(client_shared_secret.size, server_shared_secret.size); + EXPECT_BYTEARRAY_EQUAL(client_shared_secret.data, server_shared_secret.data, client_shared_secret.size); + + /* Clean up */ + EXPECT_SUCCESS(s2n_stuffer_free(&wire)); + EXPECT_SUCCESS(s2n_free(&server_shared_secret)); + EXPECT_SUCCESS(s2n_free(&client_shared_secret)); + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&server_params)); + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&read_params)); + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&client_params)); + } + }; + { + DEFER_CLEANUP(struct s2n_connection* conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + /* Test generate->write->read->compute_shared with all supported curves */ + for (size_t i = 0; i < s2n_all_supported_curves_list_len; i++) { + struct s2n_ecc_evp_params server_params = { 0 }, client_params = { 0 }; + struct s2n_stuffer wire = { 0 }; + struct s2n_blob server_shared, client_shared, ecdh_params_sent, ecdh_params_received; + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&wire, 1024)); + + /* Server generates a key for a given curve */ + server_params.negotiated_curve = s2n_all_supported_curves_list[i]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&server_params)); + EXPECT_NOT_NULL(server_params.evp_pkey); + /* Server sends the public */ + EXPECT_SUCCESS(s2n_ecc_evp_write_params(&server_params, &wire, &ecdh_params_sent)); + /* Client reads the public */ + struct s2n_ecdhe_raw_server_params ecdhe_data = { 0 }; + EXPECT_SUCCESS(s2n_ecc_evp_read_params(&wire, &ecdh_params_received, &ecdhe_data)); + EXPECT_SUCCESS(s2n_ecc_evp_parse_params(conn, &ecdhe_data, &client_params)); + + /* The client got the curve */ + EXPECT_EQUAL(client_params.negotiated_curve, server_params.negotiated_curve); + + /* Client sends its public */ + EXPECT_SUCCESS(s2n_ecc_evp_compute_shared_secret_as_client(&client_params, &wire, &client_shared)); + /* Server receives it */ + EXPECT_SUCCESS(s2n_ecc_evp_compute_shared_secret_as_server(&server_params, &wire, &server_shared)); + /* Shared is the same for the client and the server */ + EXPECT_EQUAL(client_shared.size, server_shared.size); + EXPECT_BYTEARRAY_EQUAL(client_shared.data, server_shared.data, client_shared.size); + + /* Clean up */ + EXPECT_SUCCESS(s2n_stuffer_free(&wire)); + EXPECT_SUCCESS(s2n_free(&server_shared)); + EXPECT_SUCCESS(s2n_free(&client_shared)); + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&server_params)); + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&client_params)); + } + }; + + /* Test that the client does not negotiate a group that was not + * offered in EC preferences */ + { + const struct s2n_security_policy* security_policy = NULL; + DEFER_CLEANUP(struct s2n_connection* conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + /* Version does not include the unsupported curve and secp521r1, which will be used by a malicious server */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "20190802")); + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + + /* Setup & verify invalid curves, which will be selected by a malicious server */ + const struct s2n_ecc_named_curve* const unrequested_curves[] = { + &s2n_unsupported_curve, + &s2n_ecc_curve_secp521r1, + }; + + /* Verify that the client errors when the server attempts to + * negotiate a curve that was never offered */ + for (size_t i = 0; i < s2n_array_len(unrequested_curves); i++) { + struct s2n_ecc_evp_params server_params = { 0 }; + struct s2n_ecc_evp_params client_params = { 0 }; + struct s2n_stuffer wire = { 0 }; + struct s2n_blob ecdh_params_sent = { 0 }, ecdh_params_received = { 0 }; + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&wire, 1024)); + + /* Server maliciously chooses an unsupported curve */ + server_params.negotiated_curve = unrequested_curves[i]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&server_params)); + EXPECT_NOT_NULL(server_params.evp_pkey); + /* Server sends the public */ + EXPECT_SUCCESS(s2n_ecc_evp_write_params(&server_params, &wire, &ecdh_params_sent)); + /* Client reads the public */ + struct s2n_ecdhe_raw_server_params ecdhe_data = { 0 }; + EXPECT_SUCCESS(s2n_ecc_evp_read_params(&wire, &ecdh_params_received, &ecdhe_data)); + EXPECT_FAILURE_WITH_ERRNO( + s2n_ecc_evp_parse_params(conn, &ecdhe_data, &client_params), S2N_ERR_ECDHE_UNSUPPORTED_CURVE); + + /* The client didn't agree on a curve */ + EXPECT_NULL(client_params.negotiated_curve); + + /* Clean up */ + EXPECT_SUCCESS(s2n_stuffer_free(&wire)); + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&server_params)); + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&client_params)); + } + }; + END_TEST(); +} diff --git a/tests/unit/s2n_ecc_point_format_extension_test.c b/tests/unit/s2n_ecc_point_format_extension_test.c new file mode 100644 index 00000000000..bf543e97b2a --- /dev/null +++ b/tests/unit/s2n_ecc_point_format_extension_test.c @@ -0,0 +1,106 @@ +/* + * 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 "s2n_test.h" +#include "tls/extensions/s2n_ec_point_format.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_resume.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + + /* Test server should_send */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + /* Do not send for null connection */ + EXPECT_FALSE(s2n_server_ec_point_format_extension.should_send(NULL)); + + /* Do not send for connection without chosen cipher */ + conn->secure->cipher_suite = NULL; + EXPECT_FALSE(s2n_server_ec_point_format_extension.should_send(conn)); + + /* Do not send for connection without ec kex */ + conn->secure->cipher_suite = &s2n_rsa_with_aes_128_cbc_sha; + EXPECT_FALSE(s2n_server_ec_point_format_extension.should_send(conn)); + conn->secure->cipher_suite = &s2n_dhe_rsa_with_chacha20_poly1305_sha256; + EXPECT_FALSE(s2n_server_ec_point_format_extension.should_send(conn)); + + /* Do send for connection with ec kex */ + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha; + EXPECT_TRUE(s2n_server_ec_point_format_extension.should_send(conn)); + + /* Do send for connection with hybrid ec kex */ + conn->secure->cipher_suite = &s2n_ecdhe_kyber_rsa_with_aes_256_gcm_sha384; + EXPECT_TRUE(s2n_server_ec_point_format_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test send */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_client_ec_point_format_extension.send(conn, &stuffer)); + + uint8_t length; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&stuffer, &length)); + EXPECT_EQUAL(length, s2n_stuffer_data_available(&stuffer)); + + uint8_t point_format; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&stuffer, &point_format)); + EXPECT_EQUAL(point_format, TLS_EC_POINT_FORMAT_UNCOMPRESSED); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test recv */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_client_ec_point_format_extension.send(conn, &stuffer)); + + EXPECT_FALSE(conn->ec_point_formats); + EXPECT_SUCCESS(s2n_client_ec_point_format_extension.recv(conn, &stuffer)); + EXPECT_TRUE(conn->ec_point_formats); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + EXPECT_SUCCESS(s2n_config_free(config)); + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_ecc_preferences_test.c b/tests/unit/s2n_ecc_preferences_test.c new file mode 100644 index 00000000000..ab649c496e0 --- /dev/null +++ b/tests/unit/s2n_ecc_preferences_test.c @@ -0,0 +1,49 @@ +/* + * 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 "tls/s2n_ecc_preferences.h" + +#include "s2n_test.h" +#include "tls/s2n_tls_parameters.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Tests for s2n_ecc_preferences_includes */ + { + EXPECT_FALSE(s2n_ecc_preferences_includes_curve(NULL, TLS_EC_CURVE_SECP_256_R1)); + + EXPECT_TRUE(s2n_ecc_preferences_includes_curve(&s2n_ecc_preferences_20140601, TLS_EC_CURVE_SECP_256_R1)); + EXPECT_TRUE(s2n_ecc_preferences_includes_curve(&s2n_ecc_preferences_20140601, TLS_EC_CURVE_SECP_384_R1)); + EXPECT_FALSE(s2n_ecc_preferences_includes_curve(&s2n_ecc_preferences_20140601, TLS_EC_CURVE_ECDH_X25519)); + + EXPECT_TRUE(s2n_ecc_preferences_includes_curve(&s2n_ecc_preferences_20200310, TLS_EC_CURVE_SECP_256_R1)); + EXPECT_TRUE(s2n_ecc_preferences_includes_curve(&s2n_ecc_preferences_20200310, TLS_EC_CURVE_SECP_384_R1)); +#if EVP_APIS_SUPPORTED + EXPECT_TRUE(s2n_ecc_preferences_includes_curve(&s2n_ecc_preferences_20200310, TLS_EC_CURVE_ECDH_X25519)); +#else + EXPECT_FALSE(s2n_ecc_preferences_includes_curve(&s2n_ecc_preferences_20200310, TLS_EC_CURVE_ECDH_X25519)); +#endif + + EXPECT_TRUE(s2n_ecc_preferences_includes_curve(&s2n_ecc_preferences_20201021, TLS_EC_CURVE_SECP_256_R1)); + EXPECT_TRUE(s2n_ecc_preferences_includes_curve(&s2n_ecc_preferences_20201021, TLS_EC_CURVE_SECP_384_R1)); + EXPECT_TRUE(s2n_ecc_preferences_includes_curve(&s2n_ecc_preferences_20201021, TLS_EC_CURVE_SECP_521_R1)); + EXPECT_FALSE(s2n_ecc_preferences_includes_curve(&s2n_ecc_preferences_20201021, TLS_EC_CURVE_ECDH_X25519)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_ecdsa_test.c b/tests/unit/s2n_ecdsa_test.c new file mode 100644 index 00000000000..2741a3e4b6c --- /dev/null +++ b/tests/unit/s2n_ecdsa_test.c @@ -0,0 +1,254 @@ +/* + * 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 "crypto/s2n_ecdsa.h" + +#include + +#include "crypto/s2n_ecc_evp.h" +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "utils/s2n_safety.h" + +static uint8_t s2n_test_noop_verify_host_fn(const char *host_name, size_t host_name_len, void *data) +{ + return true; +} + +static uint8_t unmatched_private_key[] = + "-----BEGIN EC PRIVATE KEY-----\n" + "MIIB+gIBAQQwuenHFMJsDm5tCQgthH8kGXQ1dHkKACmHH3ZqIGteoghhGow6vGmr\n" + "xzA8gAdD2bJ0oIIBWzCCAVcCAQEwPAYHKoZIzj0BAQIxAP//////////////////\n" + "///////////////////////+/////wAAAAAAAAAA/////zB7BDD/////////////\n" + "/////////////////////////////v////8AAAAAAAAAAP////wEMLMxL6fiPufk\n" + "mI4Fa+P4LRkYHZxu/oFBEgMUCI9QE4daxlY5jYou0Z0qhcjt0+wq7wMVAKM1kmqj\n" + "GaJ6HQCJamdzpIJ6zaxzBGEEqofKIr6LBTeOscce8yCtdG4dO2KLp5uYWfdB4IJU\n" + "KjhVAvJdv1UpbDpUXjhydgq3NhfeSpYmLG9dnpi/kpLcKfj0Hb0omhR86doxE7Xw\n" + "uMAKYLHOHX6BnXpDHXyQ6g5fAjEA////////////////////////////////x2NN\n" + "gfQ3Ld9YGg2ySLCneuzsGWrMxSlzAgEBoWQDYgAE8oYPSRINnKlr5ZBHWacYEq4Y\n" + "j18l5f9yoMhBhpl7qvzf7uNFQ1SHzgHu0/v662d8Z0Pc0ujIms3/9uYxXVUY73vm\n" + "iwVevOxBJ1GL0usqhWNqOKoNp048H4rCmfyMN97E\n" + "-----END EC PRIVATE KEY-----\n"; + +int main(int argc, char **argv) +{ + struct s2n_stuffer certificate_in = { 0 }, certificate_out = { 0 }; + struct s2n_stuffer ecdsa_key_in = { 0 }, ecdsa_key_out = { 0 }; + struct s2n_stuffer unmatched_ecdsa_key_in = { 0 }, unmatched_ecdsa_key_out = { 0 }; + struct s2n_blob b = { 0 }; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + + const int supported_hash_algorithms[] = { + S2N_HASH_NONE, + S2N_HASH_MD5, + S2N_HASH_SHA1, + S2N_HASH_SHA224, + S2N_HASH_SHA256, + S2N_HASH_SHA384, + S2N_HASH_SHA512, + S2N_HASH_MD5_SHA1 + }; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* s2n_ecdsa_pkey_matches_curve */ + { + struct s2n_ecdsa_key *p256_key = NULL, *p384_key = NULL; + struct s2n_cert_chain_and_key *p256_chain = NULL, *p384_chain = NULL; + + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&p256_chain, + S2N_ECDSA_P256_PKCS1_CERT_CHAIN, S2N_ECDSA_P256_PKCS1_KEY)); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&p384_chain, + S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); + + p256_key = &p256_chain->private_key->key.ecdsa_key; + p384_key = &p384_chain->private_key->key.ecdsa_key; + + EXPECT_SUCCESS(s2n_ecdsa_pkey_matches_curve(p256_key, &s2n_ecc_curve_secp256r1)); + EXPECT_SUCCESS(s2n_ecdsa_pkey_matches_curve(p384_key, &s2n_ecc_curve_secp384r1)); + + EXPECT_FAILURE(s2n_ecdsa_pkey_matches_curve(p256_key, &s2n_ecc_curve_secp384r1)); + EXPECT_FAILURE(s2n_ecdsa_pkey_matches_curve(p384_key, &s2n_ecc_curve_secp256r1)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(p256_chain)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(p384_chain)); + }; + + EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_in, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_out, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&ecdsa_key_in, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&ecdsa_key_out, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&unmatched_ecdsa_key_in, sizeof(unmatched_private_key))); + EXPECT_SUCCESS(s2n_stuffer_alloc(&unmatched_ecdsa_key_out, sizeof(unmatched_private_key))); + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + + EXPECT_SUCCESS(s2n_blob_init(&b, (uint8_t *) cert_chain_pem, strlen(cert_chain_pem) + 1)); + EXPECT_SUCCESS(s2n_stuffer_write(&certificate_in, &b)); + + EXPECT_SUCCESS(s2n_blob_init(&b, (uint8_t *) private_key_pem, strlen(private_key_pem) + 1)); + EXPECT_SUCCESS(s2n_stuffer_write(&ecdsa_key_in, &b)); + + EXPECT_SUCCESS(s2n_blob_init(&b, (uint8_t *) unmatched_private_key, sizeof(unmatched_private_key))); + EXPECT_SUCCESS(s2n_stuffer_write(&unmatched_ecdsa_key_in, &b)); + + int type = 0; + EXPECT_SUCCESS(s2n_stuffer_certificate_from_pem(&certificate_in, &certificate_out)); + EXPECT_SUCCESS(s2n_stuffer_private_key_from_pem(&ecdsa_key_in, &ecdsa_key_out, &type)); + EXPECT_EQUAL(type, EVP_PKEY_EC); + EXPECT_SUCCESS(s2n_stuffer_private_key_from_pem(&unmatched_ecdsa_key_in, &unmatched_ecdsa_key_out, &type)); + EXPECT_EQUAL(type, EVP_PKEY_EC); + + struct s2n_pkey pub_key = { 0 }; + struct s2n_pkey priv_key = { 0 }; + struct s2n_pkey unmatched_priv_key = { 0 }; + s2n_pkey_type pkey_type = { 0 }; + uint32_t available_size = 0; + + available_size = s2n_stuffer_data_available(&certificate_out); + EXPECT_SUCCESS(s2n_blob_init(&b, s2n_stuffer_raw_read(&certificate_out, available_size), available_size)); + EXPECT_SUCCESS(s2n_asn1der_to_public_key_and_type(&pub_key, &pkey_type, &b)); + + /* Test without a type hint */ + int wrong_type = 0; + EXPECT_NOT_EQUAL(wrong_type, EVP_PKEY_EC); + + available_size = s2n_stuffer_data_available(&ecdsa_key_out); + EXPECT_SUCCESS(s2n_blob_init(&b, s2n_stuffer_raw_read(&ecdsa_key_out, available_size), available_size)); + EXPECT_SUCCESS(s2n_asn1der_to_private_key(&priv_key, &b, wrong_type)); + + available_size = s2n_stuffer_data_available(&unmatched_ecdsa_key_out); + EXPECT_SUCCESS(s2n_blob_init(&b, s2n_stuffer_raw_read(&unmatched_ecdsa_key_out, available_size), available_size)); + EXPECT_SUCCESS(s2n_asn1der_to_private_key(&unmatched_priv_key, &b, wrong_type)); + + /* Verify that the public/private key pair match */ + EXPECT_SUCCESS(s2n_pkey_match(&pub_key, &priv_key)); + + /* Try signing and verification with ECDSA */ + uint8_t inputpad[] = "Hello world!"; + struct s2n_blob signature = { 0 }, bad_signature = { 0 }; + struct s2n_hash_state hash_one = { 0 }, hash_two = { 0 }; + + uint32_t maximum_signature_length = 0; + EXPECT_OK(s2n_pkey_size(&priv_key, &maximum_signature_length)); + EXPECT_SUCCESS(s2n_alloc(&signature, maximum_signature_length)); + + EXPECT_SUCCESS(s2n_hash_new(&hash_one)); + EXPECT_SUCCESS(s2n_hash_new(&hash_two)); + + for (size_t i = 0; i < s2n_array_len(supported_hash_algorithms); i++) { + int hash_alg = supported_hash_algorithms[i]; + + if (!s2n_hash_is_available(hash_alg) || hash_alg == S2N_HASH_NONE) { + /* Skip hash algorithms that are not available. */ + continue; + } + + EXPECT_SUCCESS(s2n_hash_init(&hash_one, hash_alg)); + EXPECT_SUCCESS(s2n_hash_init(&hash_two, hash_alg)); + + EXPECT_SUCCESS(s2n_hash_update(&hash_one, inputpad, sizeof(inputpad))); + EXPECT_SUCCESS(s2n_hash_update(&hash_two, inputpad, sizeof(inputpad))); + + /* Reset signature size when we compute a new signature */ + signature.size = maximum_signature_length; + + EXPECT_SUCCESS(s2n_pkey_sign(&priv_key, S2N_SIGNATURE_ECDSA, &hash_one, &signature)); + EXPECT_SUCCESS(s2n_pkey_verify(&pub_key, S2N_SIGNATURE_ECDSA, &hash_two, &signature)); + + EXPECT_SUCCESS(s2n_hash_reset(&hash_one)); + EXPECT_SUCCESS(s2n_hash_reset(&hash_two)); + } + + /* Mismatched public/private key should fail verification */ + EXPECT_OK(s2n_pkey_size(&unmatched_priv_key, &maximum_signature_length)); + EXPECT_SUCCESS(s2n_alloc(&bad_signature, maximum_signature_length)); + + EXPECT_FAILURE(s2n_pkey_match(&pub_key, &unmatched_priv_key)); + + EXPECT_SUCCESS(s2n_pkey_sign(&unmatched_priv_key, S2N_SIGNATURE_ECDSA, &hash_one, &bad_signature)); + EXPECT_FAILURE(s2n_pkey_verify(&pub_key, S2N_SIGNATURE_ECDSA, &hash_two, &bad_signature)); + + EXPECT_SUCCESS(s2n_free(&signature)); + EXPECT_SUCCESS(s2n_free(&bad_signature)); + + EXPECT_SUCCESS(s2n_hash_free(&hash_one)); + EXPECT_SUCCESS(s2n_hash_free(&hash_two)); + + EXPECT_SUCCESS(s2n_pkey_free(&pub_key)); + EXPECT_SUCCESS(s2n_pkey_free(&priv_key)); + EXPECT_SUCCESS(s2n_pkey_free(&unmatched_priv_key)); + + EXPECT_SUCCESS(s2n_stuffer_free(&certificate_in)); + EXPECT_SUCCESS(s2n_stuffer_free(&certificate_out)); + EXPECT_SUCCESS(s2n_stuffer_free(&ecdsa_key_in)); + EXPECT_SUCCESS(s2n_stuffer_free(&ecdsa_key_out)); + EXPECT_SUCCESS(s2n_stuffer_free(&unmatched_ecdsa_key_in)); + EXPECT_SUCCESS(s2n_stuffer_free(&unmatched_ecdsa_key_out)); + free(cert_chain_pem); + free(private_key_pem); + + EXPECT_SUCCESS(s2n_reset_tls13_in_test()); + + /* Self-Talk test */ + { + const char *ecdsa_certs[][2] = { + { S2N_ECDSA_P256_PKCS1_CERT_CHAIN, S2N_ECDSA_P256_PKCS1_KEY }, + { S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY }, + { S2N_ECDSA_P512_CERT_CHAIN, S2N_ECDSA_P512_KEY }, + }; + + for (size_t i = 0; i < s2n_array_len(ecdsa_certs); i++) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + ecdsa_certs[i][0], ecdsa_certs[i][1])); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, + ecdsa_certs[i][0], NULL)); + EXPECT_SUCCESS(s2n_config_set_verify_host_callback(config, + s2n_test_noop_verify_host_fn, NULL)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, + s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client)); + } + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_ems_extension_test.c b/tests/unit/s2n_ems_extension_test.c new file mode 100644 index 00000000000..d2bdf43a107 --- /dev/null +++ b/tests/unit/s2n_ems_extension_test.c @@ -0,0 +1,113 @@ +/* + * 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 "s2n_test.h" +#include "tls/extensions/s2n_ems.h" +#include "tls/extensions/s2n_extension_list.h" +#include "tls/s2n_connection.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* s2n_server_ems_should_send */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + + /* Protocol version is too high */ + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FALSE(s2n_server_ems_extension.should_send(conn)); + + /* Protocol version is less than TLS1.3 */ + conn->actual_protocol_version = S2N_TLS12; + EXPECT_TRUE(s2n_server_ems_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test that the ems_negotiated flag is set when the EMS extension is received */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + + /* This extension is only relevant for TLS1.2 */ + server_conn->actual_protocol_version = S2N_TLS12; + client_conn->actual_protocol_version = S2N_TLS12; + + DEFER_CLEANUP(struct s2n_stuffer stuffer, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_extension_list_send(S2N_EXTENSION_LIST_CLIENT_HELLO, client_conn, &stuffer)); + EXPECT_FALSE(client_conn->ems_negotiated); + + EXPECT_SUCCESS(s2n_extension_list_recv(S2N_EXTENSION_LIST_CLIENT_HELLO, server_conn, &stuffer)); + EXPECT_TRUE(server_conn->ems_negotiated); + + EXPECT_SUCCESS(s2n_extension_list_send(S2N_EXTENSION_LIST_SERVER_HELLO_DEFAULT, server_conn, &stuffer)); + EXPECT_SUCCESS(s2n_extension_list_recv(S2N_EXTENSION_LIST_SERVER_HELLO_DEFAULT, client_conn, &stuffer)); + EXPECT_TRUE(client_conn->ems_negotiated); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* s2n_server_ems_is_missing */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + /** + *= https://tools.ietf.org/rfc/rfc7627#section-5.3 + *= type=test + *# If the original session used the extension but the new ServerHello + *# does not contain the extension, the client MUST abort the + *# handshake. + **/ + conn->ems_negotiated = true; + EXPECT_FAILURE_WITH_ERRNO(s2n_server_ems_extension.if_missing(conn), S2N_ERR_MISSING_EXTENSION); + + conn->ems_negotiated = false; + EXPECT_SUCCESS(s2n_server_ems_extension.if_missing(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* s2n_client_ems_should_send */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + /* In the default case we should always be sending this extension */ + EXPECT_TRUE(s2n_client_ems_extension.should_send(conn)); + + conn->set_session = true; + conn->ems_negotiated = true; + /* If we have set a ticket on the connection we only send this extension + * if the previous session negotiated EMS. */ + EXPECT_TRUE(s2n_client_ems_extension.should_send(conn)); + + /* Don't send this extension if we have set a ticket on the connection + * and the previous session did not negotiate EMS */ + conn->ems_negotiated = false; + EXPECT_FALSE(s2n_client_ems_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_encrypted_extensions_test.c b/tests/unit/s2n_encrypted_extensions_test.c new file mode 100644 index 00000000000..3b1c6259f51 --- /dev/null +++ b/tests/unit/s2n_encrypted_extensions_test.c @@ -0,0 +1,257 @@ +/* + * 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 "crypto/s2n_rsa_signing.h" +#include "error/s2n_errno.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_extension_type.h" +#include "tls/extensions/s2n_server_alpn.h" +#include "tls/extensions/s2n_server_max_fragment_length.h" +#include "tls/extensions/s2n_server_server_name.h" +#include "tls/extensions/s2n_server_supported_versions.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* Test s2n_encrypted_extensions_send */ + { + /* Safety checks */ + EXPECT_FAILURE(s2n_encrypted_extensions_send(NULL)); + + /* Should fail for pre-TLS1.3 */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(conn)); + + /* Fails for TLS1.2 */ + conn->actual_protocol_version = S2N_TLS12; + EXPECT_FAILURE_WITH_ERRNO(s2n_encrypted_extensions_send(conn), S2N_ERR_BAD_MESSAGE); + + /* Succeeds for TLS1.3 */ + conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_encrypted_extensions_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Should send no extensions by default */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(conn)); + conn->actual_protocol_version = S2N_TLS13; + + struct s2n_stuffer *stuffer = &conn->handshake.io; + + EXPECT_SUCCESS(s2n_encrypted_extensions_send(conn)); + + uint16_t extension_list_size; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(stuffer, &extension_list_size)); + EXPECT_EQUAL(extension_list_size, 0); + EXPECT_EQUAL(s2n_stuffer_data_available(stuffer), 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Should send a requested extension */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(conn)); + conn->actual_protocol_version = S2N_TLS13; + + struct s2n_stuffer *stuffer = &conn->handshake.io; + + conn->server_name_used = 1; + EXPECT_SUCCESS(s2n_encrypted_extensions_send(conn)); + + uint16_t extension_list_size; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(stuffer, &extension_list_size)); + EXPECT_NOT_EQUAL(extension_list_size, 0); + EXPECT_EQUAL(s2n_stuffer_data_available(stuffer), extension_list_size); + + uint16_t extension_type; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(stuffer, &extension_type)); + EXPECT_EQUAL(extension_type, s2n_server_server_name_extension.iana_value); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test s2n_encrypted_extensions_recv */ + { + /* Safety checks */ + EXPECT_FAILURE(s2n_encrypted_extensions_recv(NULL)); + + /* Should fail for pre-TLS1.3 */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(conn)); + + /* Fails for TLS1.2 */ + conn->actual_protocol_version = S2N_TLS12; + EXPECT_FAILURE_WITH_ERRNO(s2n_encrypted_extensions_recv(conn), S2N_ERR_BAD_MESSAGE); + + /* Succeeds for TLS1.3 */ + conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_encrypted_extensions_recv(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Should parse an empty list */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(conn)); + conn->actual_protocol_version = S2N_TLS13; + + struct s2n_stuffer *stuffer = &conn->handshake.io; + + /* Parse no data */ + EXPECT_SUCCESS(s2n_encrypted_extensions_recv(conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(stuffer), 0); + + /* Parse explicitly empty list */ + EXPECT_SUCCESS(s2n_extension_list_send(S2N_EXTENSION_LIST_EMPTY, conn, stuffer)); + EXPECT_SUCCESS(s2n_encrypted_extensions_recv(conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(stuffer), 0); + + /* Parse empty result of default s2n_encrypted_extensions_send */ + EXPECT_SUCCESS(s2n_encrypted_extensions_send(conn)); + EXPECT_SUCCESS(s2n_encrypted_extensions_recv(conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(stuffer), 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Should parse a requested extension */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(conn)); + conn->actual_protocol_version = S2N_TLS13; + + struct s2n_stuffer *stuffer = &conn->handshake.io; + + conn->server_name_used = 1; + EXPECT_SUCCESS(s2n_encrypted_extensions_send(conn)); + + /* Reset server_name_used */ + conn->server_name_used = 0; + + EXPECT_SUCCESS(s2n_encrypted_extensions_recv(conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(stuffer), 0); + EXPECT_EQUAL(conn->server_name_used, 1); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Functional: Unencrypted EncryptedExtensions rejected */ + if (s2n_is_tls13_fully_supported()) { + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + struct s2n_cert_chain_and_key *chain_and_key; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + /* Create IO stuffers */ + DEFER_CLEANUP(struct s2n_stuffer client_to_server = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer server_to_client = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + + /* Do handshake up until EncryptedExtensions */ + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, ENCRYPTED_EXTENSIONS)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), ENCRYPTED_EXTENSIONS); + + /* Verify that the EncryptedExtension message would normally be encrypted */ + EXPECT_EQUAL(server_conn->server, server_conn->secure); + + /* Force the server to disable encryption for the EncryptedExtensions message */ + server_conn->server = server_conn->initial; + + /* Enable an extension to ensure the message is long enough to resemble an encrypted record. + * If the message is too short, we fail without even attempting decryption and this error + * is difficult to distinguish from other S2N_ERR_BAD_MESSAGE cases. + */ + uint8_t long_alpn[] = "httttttttttttttttttttps"; + EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(server_conn)); + EXPECT_MEMCPY_SUCCESS(server_conn->application_protocol, long_alpn, sizeof(long_alpn)); + + /* Reset the stuffer, potentially wiping any pending CCS messages. + * We don't need the complication of accidentally rereading old messages. + */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_to_client)); + + /* Write unencrypted EncryptedExtensions message */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), ENCRYPTED_EXTENSIONS); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_NOT_EQUAL(s2n_conn_get_current_message_type(server_conn), ENCRYPTED_EXTENSIONS); + + /* Verify message is unencrypted handshake message instead of + * encrypted APPLICATION_DATA message. + */ + uint8_t type = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&server_to_client, &type)); + EXPECT_EQUAL(type, TLS_HANDSHAKE); /* Record type not APPLICATION_DATA */ + EXPECT_SUCCESS(s2n_stuffer_reread(&server_to_client)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&server_to_client, S2N_TLS_RECORD_HEADER_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&server_to_client, &type)); + EXPECT_EQUAL(type, TLS_ENCRYPTED_EXTENSIONS); /* Actual handshake type not encrypted */ + EXPECT_SUCCESS(s2n_stuffer_reread(&server_to_client)); + + /* Client fails to parse the EncryptedExtensions */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), ENCRYPTED_EXTENSIONS); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(client_conn, &blocked), S2N_ERR_DECRYPT); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), ENCRYPTED_EXTENSIONS); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(config)); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_error_lookup_test.c b/tests/unit/s2n_error_lookup_test.c new file mode 100644 index 00000000000..e34e3ac223a --- /dev/null +++ b/tests/unit/s2n_error_lookup_test.c @@ -0,0 +1,116 @@ +/* + * 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 "api/s2n.h" +#include "error/s2n_errno.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_safety.h" + +/* constructs a debug string from a path */ +#define EXAMPLE_DEBUG_STR(path) (_S2N_EXTRACT_BASENAME(_S2N_DEBUG_LINE_PREFIX path)) + +S2N_RESULT test_function(bool is_valid) +{ + /* the line number of this check is important; tests below will fail if it changed */ + RESULT_ENSURE(is_valid, S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +int main(void) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* First, test that we can find error message for all defined errors */ + for (int err = S2N_ERR_T_OK_START; err < S2N_ERR_T_OK_END; err++) { + EXPECT_NOT_EQUAL(strcmp(s2n_strerror_name(err), "Internal s2n error"), 0); + EXPECT_NOT_EQUAL(strcmp(s2n_strerror(err, "EN"), "Internal s2n error"), 0); + } + for (int err = S2N_ERR_T_IO_START; err < S2N_ERR_T_IO_END; err++) { + EXPECT_NOT_EQUAL(strcmp(s2n_strerror_name(err), "Internal s2n error"), 0); + EXPECT_NOT_EQUAL(strcmp(s2n_strerror(err, "EN"), "Internal s2n error"), 0); + } + for (int err = S2N_ERR_T_CLOSED_START; err < S2N_ERR_T_CLOSED_END; err++) { + EXPECT_NOT_EQUAL(strcmp(s2n_strerror_name(err), "Internal s2n error"), 0); + EXPECT_NOT_EQUAL(strcmp(s2n_strerror(err, "EN"), "Internal s2n error"), 0); + } + for (int err = S2N_ERR_T_BLOCKED_START; err < S2N_ERR_T_BLOCKED_END; err++) { + EXPECT_NOT_EQUAL(strcmp(s2n_strerror_name(err), "Internal s2n error"), 0); + EXPECT_NOT_EQUAL(strcmp(s2n_strerror(err, "EN"), "Internal s2n error"), 0); + } + for (int err = S2N_ERR_T_ALERT_START; err < S2N_ERR_T_ALERT_END; err++) { + EXPECT_NOT_EQUAL(strcmp(s2n_strerror_name(err), "Internal s2n error"), 0); + EXPECT_NOT_EQUAL(strcmp(s2n_strerror(err, "EN"), "Internal s2n error"), 0); + } + for (int err = S2N_ERR_T_PROTO_START; err < S2N_ERR_T_PROTO_END; err++) { + EXPECT_NOT_EQUAL(strcmp(s2n_strerror_name(err), "Internal s2n error"), 0); + EXPECT_NOT_EQUAL(strcmp(s2n_strerror(err, "EN"), "Internal s2n error"), 0); + } + for (int err = S2N_ERR_T_INTERNAL_START; err < S2N_ERR_T_INTERNAL_END; err++) { + EXPECT_NOT_EQUAL(strcmp(s2n_strerror_name(err), "Internal s2n error"), 0); + EXPECT_NOT_EQUAL(strcmp(s2n_strerror(err, "EN"), "Internal s2n error"), 0); + } + for (int err = S2N_ERR_T_USAGE_START; err < S2N_ERR_T_USAGE_END; err++) { + EXPECT_NOT_EQUAL(strcmp(s2n_strerror_name(err), "Internal s2n error"), 0); + EXPECT_NOT_EQUAL(strcmp(s2n_strerror(err, "EN"), "Internal s2n error"), 0); + } + + /* Next, test that we get an error wen try to search for non-existing errors for each type */ + EXPECT_EQUAL(strcmp(s2n_strerror_name(S2N_ERR_T_OK_END), "Internal s2n error"), 0); + EXPECT_EQUAL(strcmp(s2n_strerror(S2N_ERR_T_OK_END, "EN"), "Internal s2n error"), 0); + + EXPECT_EQUAL(strcmp(s2n_strerror_name(S2N_ERR_T_IO_END), "Internal s2n error"), 0); + EXPECT_EQUAL(strcmp(s2n_strerror(S2N_ERR_T_IO_END, "EN"), "Internal s2n error"), 0); + + EXPECT_EQUAL(strcmp(s2n_strerror_name(S2N_ERR_T_CLOSED_END), "Internal s2n error"), 0); + EXPECT_EQUAL(strcmp(s2n_strerror(S2N_ERR_T_CLOSED_END, "EN"), "Internal s2n error"), 0); + + EXPECT_EQUAL(strcmp(s2n_strerror_name(S2N_ERR_T_BLOCKED_END), "Internal s2n error"), 0); + EXPECT_EQUAL(strcmp(s2n_strerror(S2N_ERR_T_BLOCKED_END, "EN"), "Internal s2n error"), 0); + + EXPECT_EQUAL(strcmp(s2n_strerror_name(S2N_ERR_T_ALERT_END), "Internal s2n error"), 0); + EXPECT_EQUAL(strcmp(s2n_strerror(S2N_ERR_T_ALERT_END, "EN"), "Internal s2n error"), 0); + + EXPECT_EQUAL(strcmp(s2n_strerror_name(S2N_ERR_T_PROTO_END), "Internal s2n error"), 0); + EXPECT_EQUAL(strcmp(s2n_strerror(S2N_ERR_T_PROTO_END, "EN"), "Internal s2n error"), 0); + + EXPECT_EQUAL(strcmp(s2n_strerror_name(S2N_ERR_T_INTERNAL_END), "Internal s2n error"), 0); + EXPECT_EQUAL(strcmp(s2n_strerror(S2N_ERR_T_INTERNAL_END, "EN"), "Internal s2n error"), 0); + + EXPECT_EQUAL(strcmp(s2n_strerror_name(S2N_ERR_T_USAGE_END), "Internal s2n error"), 0); + EXPECT_EQUAL(strcmp(s2n_strerror(S2N_ERR_T_USAGE_END, "EN"), "Internal s2n error"), 0); + + /* And ensure that we get an error in non-existing classes of errors */ + EXPECT_EQUAL(strcmp(s2n_strerror_name((S2N_ERR_T_USAGE + 1) << S2N_ERR_NUM_VALUE_BITS), "Internal s2n error"), 0); + EXPECT_EQUAL(strcmp(s2n_strerror((S2N_ERR_T_USAGE + 1) << S2N_ERR_NUM_VALUE_BITS, "EN"), "Internal s2n error"), 0); + + /* Ensure the file/line information is returned as expected */ + s2n_result_ignore(test_function(false)); + EXPECT_EQUAL(strcmp(s2n_strerror_source(S2N_ERR_SAFETY), "s2n_error_lookup_test.c:28"), 0); + + EXPECT_EQUAL(strcmp(EXAMPLE_DEBUG_STR("/absolute/path/to/file.c"), "file.c"), 0); + EXPECT_EQUAL(strcmp(EXAMPLE_DEBUG_STR("relative/path/to/file.c"), "file.c"), 0); + EXPECT_EQUAL(strcmp(EXAMPLE_DEBUG_STR("path / with / spaces /file.c"), "file.c"), 0); + EXPECT_EQUAL(strcmp(EXAMPLE_DEBUG_STR("file.c"), "file.c"), 0); + + /* Test that lookup works even after s2n_cleanup */ + EXPECT_SUCCESS(s2n_cleanup()); + + EXPECT_EQUAL(strcmp(s2n_strerror_name(S2N_ERR_OK), "S2N_ERR_OK"), 0); + EXPECT_EQUAL(strcmp(s2n_strerror(S2N_ERR_OK, "EN"), "no error"), 0); + + END_TEST(); +} diff --git a/tests/unit/s2n_error_type_test.c b/tests/unit/s2n_error_type_test.c new file mode 100644 index 00000000000..23544261549 --- /dev/null +++ b/tests/unit/s2n_error_type_test.c @@ -0,0 +1,55 @@ +/* + * 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 "api/s2n.h" +#include "error/s2n_errno.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +int main(void) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Ensure the address of `s2n_errno` is identical to the one returned in `s2n_errno_location()` */ + EXPECT_EQUAL(&s2n_errno, s2n_errno_location()); + + s2n_errno = S2N_ERR_OK; + EXPECT_EQUAL(S2N_ERR_T_OK, s2n_error_get_type(s2n_errno)); + EXPECT_EQUAL(S2N_ERR_T_OK, s2n_error_get_type(*s2n_errno_location())); + s2n_errno = S2N_ERR_IO; + EXPECT_EQUAL(S2N_ERR_T_IO, s2n_error_get_type(s2n_errno)); + EXPECT_EQUAL(S2N_ERR_T_IO, s2n_error_get_type(*s2n_errno_location())); + s2n_errno = S2N_ERR_CLOSED; + EXPECT_EQUAL(S2N_ERR_T_CLOSED, s2n_error_get_type(s2n_errno)); + EXPECT_EQUAL(S2N_ERR_T_CLOSED, s2n_error_get_type(*s2n_errno_location())); + s2n_errno = S2N_ERR_IO_BLOCKED; + EXPECT_EQUAL(S2N_ERR_T_BLOCKED, s2n_error_get_type(s2n_errno)); + EXPECT_EQUAL(S2N_ERR_T_BLOCKED, s2n_error_get_type(*s2n_errno_location())); + s2n_errno = S2N_ERR_ALERT; + EXPECT_EQUAL(S2N_ERR_T_ALERT, s2n_error_get_type(s2n_errno)); + EXPECT_EQUAL(S2N_ERR_T_ALERT, s2n_error_get_type(*s2n_errno_location())); + s2n_errno = S2N_ERR_BAD_MESSAGE; + EXPECT_EQUAL(S2N_ERR_T_PROTO, s2n_error_get_type(s2n_errno)); + EXPECT_EQUAL(S2N_ERR_T_PROTO, s2n_error_get_type(*s2n_errno_location())); + s2n_errno = S2N_ERR_FSTAT; + EXPECT_EQUAL(S2N_ERR_T_INTERNAL, s2n_error_get_type(s2n_errno)); + EXPECT_EQUAL(S2N_ERR_T_INTERNAL, s2n_error_get_type(*s2n_errno_location())); + s2n_errno = S2N_ERR_INVALID_BASE64; + EXPECT_EQUAL(S2N_ERR_T_USAGE, s2n_error_get_type(s2n_errno)); + EXPECT_EQUAL(S2N_ERR_T_USAGE, s2n_error_get_type(*s2n_errno_location())); + + END_TEST(); +} diff --git a/tests/unit/s2n_evp_signing_test.c b/tests/unit/s2n_evp_signing_test.c new file mode 100644 index 00000000000..d287ac47213 --- /dev/null +++ b/tests/unit/s2n_evp_signing_test.c @@ -0,0 +1,288 @@ +/* + * 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 "crypto/s2n_evp_signing.h" + +#include "crypto/s2n_ecdsa.h" +#include "crypto/s2n_fips.h" +#include "crypto/s2n_rsa_pss.h" +#include "crypto/s2n_rsa_signing.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +/* The ecdsa sign/verify methods are static */ +#include "crypto/s2n_ecdsa.c" +#include "crypto/s2n_rsa.c" + +#define INPUT_DATA_SIZE 100 +#define OUTPUT_DATA_SIZE 1000 + +#define EXPECT_PKEY_USES_EVP_SIGNING(pkey) \ + EXPECT_EQUAL(pkey->sign, &s2n_evp_sign); \ + EXPECT_EQUAL(pkey->verify, &s2n_evp_verify) + +const uint8_t input_data[INPUT_DATA_SIZE] = "hello hash"; + +static bool s2n_hash_alg_is_supported(s2n_signature_algorithm sig_alg, s2n_hash_algorithm hash_alg) +{ + return (hash_alg != S2N_HASH_NONE) && (hash_alg != S2N_HASH_MD5) + && (hash_alg != S2N_HASH_MD5_SHA1 || sig_alg == S2N_SIGNATURE_RSA); +} + +static S2N_RESULT s2n_test_hash_init(struct s2n_hash_state *hash_state, s2n_hash_algorithm hash_alg) +{ + RESULT_GUARD_POSIX(s2n_hash_init(hash_state, hash_alg)); + RESULT_GUARD_POSIX(s2n_hash_allow_md5_for_fips(hash_state)); + RESULT_GUARD_POSIX(s2n_hash_update(hash_state, input_data, s2n_array_len(input_data))); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_setup_public_key(struct s2n_pkey *public_key, struct s2n_cert_chain_and_key *chain) +{ + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_SUCCESS(s2n_asn1der_to_public_key_and_type(public_key, &pkey_type, + &chain->cert_chain->head->raw)); + EXPECT_EQUAL(pkey_type, chain->cert_chain->head->pkey_type); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_test_evp_sign(s2n_signature_algorithm sig_alg, s2n_hash_algorithm hash_alg, + struct s2n_pkey *private_key, struct s2n_blob *evp_signature_out) +{ + DEFER_CLEANUP(struct s2n_hash_state hash_state = { 0 }, s2n_hash_free); + RESULT_GUARD_POSIX(s2n_hash_new(&hash_state)); + RESULT_GUARD(s2n_test_hash_init(&hash_state, hash_alg)); + RESULT_GUARD_POSIX(s2n_evp_sign(private_key, sig_alg, &hash_state, evp_signature_out)); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_test_evp_verify(s2n_signature_algorithm sig_alg, s2n_hash_algorithm hash_alg, + struct s2n_pkey *public_key, + struct s2n_blob *evp_signature, struct s2n_blob *expected_signature) +{ + DEFER_CLEANUP(struct s2n_hash_state hash_state = { 0 }, s2n_hash_free); + RESULT_GUARD_POSIX(s2n_hash_new(&hash_state)); + + /* Verify that the EVP methods can verify their own signature */ + RESULT_GUARD(s2n_test_hash_init(&hash_state, hash_alg)); + RESULT_GUARD_POSIX(s2n_evp_verify(public_key, sig_alg, &hash_state, evp_signature)); + + /* Verify that using the pkey directly can verify own signature */ + RESULT_GUARD(s2n_test_hash_init(&hash_state, hash_alg)); + RESULT_GUARD_POSIX(s2n_pkey_verify(public_key, sig_alg, &hash_state, evp_signature)); + + /* Verify that the EVP methods can verify the known good signature */ + RESULT_GUARD(s2n_test_hash_init(&hash_state, hash_alg)); + RESULT_GUARD_POSIX(s2n_evp_verify(public_key, sig_alg, &hash_state, expected_signature)); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Sanity check that we're enabling evp signing properly. + * awslc-fips is known to require evp signing. + */ + if (s2n_is_in_fips_mode() && s2n_libcrypto_is_awslc()) { + EXPECT_TRUE(s2n_evp_signing_supported()); + } + + if (!s2n_evp_signing_supported()) { + END_TEST(); + } + + DEFER_CLEANUP(struct s2n_hash_state hash_state = { 0 }, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&hash_state)); + + struct s2n_cert_chain_and_key *rsa_cert_chain = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&rsa_cert_chain, + S2N_RSA_2048_PKCS1_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY)); + + /* Test that unsupported hash algs are treated as invalid. + * Later tests will ignore unsupported algs, so ensure they are actually invalid. */ + { + /* This pkey should never actually be needed -- any pkey will do */ + struct s2n_pkey *pkey = rsa_cert_chain->private_key; + + for (s2n_signature_algorithm sig_alg = 0; sig_alg <= UINT8_MAX; sig_alg++) { + for (s2n_hash_algorithm hash_alg = 0; hash_alg < S2N_HASH_SENTINEL; hash_alg++) { + if (s2n_hash_alg_is_supported(sig_alg, hash_alg)) { + continue; + } + + s2n_stack_blob(evp_signature, OUTPUT_DATA_SIZE, OUTPUT_DATA_SIZE); + EXPECT_ERROR_WITH_ERRNO(s2n_test_evp_sign(sig_alg, hash_alg, pkey, &evp_signature), + S2N_ERR_HASH_INVALID_ALGORITHM); + EXPECT_ERROR_WITH_ERRNO(s2n_test_evp_verify(sig_alg, hash_alg, pkey, &evp_signature, &evp_signature), + S2N_ERR_HASH_INVALID_ALGORITHM); + } + } + }; + + /* EVP signing must match RSA signing */ + { + s2n_signature_algorithm sig_alg = S2N_SIGNATURE_RSA; + + DEFER_CLEANUP(struct s2n_pkey public_key_parsed = { 0 }, s2n_pkey_free); + EXPECT_OK(s2n_setup_public_key(&public_key_parsed, rsa_cert_chain)); + + struct s2n_pkey *private_key = rsa_cert_chain->private_key; + struct s2n_pkey *public_key = &public_key_parsed; + EXPECT_PKEY_USES_EVP_SIGNING(private_key); + EXPECT_PKEY_USES_EVP_SIGNING(public_key); + + for (s2n_hash_algorithm hash_alg = 0; hash_alg < S2N_HASH_SENTINEL; hash_alg++) { + if (!s2n_hash_alg_is_supported(sig_alg, hash_alg)) { + continue; + } + + /* Calculate the signature using EVP methods */ + s2n_stack_blob(evp_signature, OUTPUT_DATA_SIZE, OUTPUT_DATA_SIZE); + EXPECT_OK(s2n_test_evp_sign(sig_alg, hash_alg, private_key, &evp_signature)); + + /* Calculate the signature using RSA methods */ + s2n_stack_blob(rsa_signature, OUTPUT_DATA_SIZE, OUTPUT_DATA_SIZE); + EXPECT_OK(s2n_test_hash_init(&hash_state, hash_alg)); + EXPECT_SUCCESS(s2n_rsa_pkcs1v15_sign(private_key, &hash_state, &rsa_signature)); + + /* Verify that the EVP methods can verify both signatures */ + EXPECT_OK(s2n_test_evp_verify(sig_alg, hash_alg, public_key, &evp_signature, &rsa_signature)); + + /* Verify that the RSA methods can verify the EVP signature */ + EXPECT_OK(s2n_test_hash_init(&hash_state, hash_alg)); + EXPECT_SUCCESS(s2n_rsa_pkcs1v15_verify(public_key, &hash_state, &evp_signature)); + } + }; + + /* EVP signing must match ECDSA signing */ + { + s2n_signature_algorithm sig_alg = S2N_SIGNATURE_ECDSA; + + struct s2n_cert_chain_and_key *ecdsa_cert_chain = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_cert_chain, + S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); + DEFER_CLEANUP(struct s2n_pkey public_key_parsed = { 0 }, s2n_pkey_free); + EXPECT_OK(s2n_setup_public_key(&public_key_parsed, ecdsa_cert_chain)); + + struct s2n_pkey *private_key = ecdsa_cert_chain->private_key; + struct s2n_pkey *public_key = &public_key_parsed; + EXPECT_PKEY_USES_EVP_SIGNING(private_key); + EXPECT_PKEY_USES_EVP_SIGNING(public_key); + + for (s2n_hash_algorithm hash_alg = 0; hash_alg < S2N_HASH_SENTINEL; hash_alg++) { + if (!s2n_hash_alg_is_supported(sig_alg, hash_alg)) { + continue; + } + + /* Calculate the signature using EVP methods */ + s2n_stack_blob(evp_signature, OUTPUT_DATA_SIZE, OUTPUT_DATA_SIZE); + EXPECT_OK(s2n_test_evp_sign(sig_alg, hash_alg, private_key, &evp_signature)); + + /* Calculate the signature using ECDSA methods */ + s2n_stack_blob(ecdsa_signature, OUTPUT_DATA_SIZE, OUTPUT_DATA_SIZE); + EXPECT_OK(s2n_test_hash_init(&hash_state, hash_alg)); + EXPECT_SUCCESS(s2n_ecdsa_sign(private_key, sig_alg, &hash_state, &ecdsa_signature)); + + /* Verify that the EVP methods can verify both signatures */ + EXPECT_OK(s2n_test_evp_verify(sig_alg, hash_alg, public_key, &evp_signature, &ecdsa_signature)); + + /* Verify that the ECDSA methods can verify the EVP signature */ + EXPECT_OK(s2n_test_hash_init(&hash_state, hash_alg)); + EXPECT_SUCCESS(s2n_ecdsa_verify(public_key, sig_alg, &hash_state, &evp_signature)); + } + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert_chain)); + }; + + /* EVP signing must match RSA-PSS-RSAE signing */ + if (s2n_is_rsa_pss_signing_supported()) { + s2n_signature_algorithm sig_alg = S2N_SIGNATURE_RSA_PSS_RSAE; + + DEFER_CLEANUP(struct s2n_pkey public_key_parsed = { 0 }, s2n_pkey_free); + EXPECT_OK(s2n_setup_public_key(&public_key_parsed, rsa_cert_chain)); + + struct s2n_pkey *private_key = rsa_cert_chain->private_key; + struct s2n_pkey *public_key = &public_key_parsed; + EXPECT_PKEY_USES_EVP_SIGNING(private_key); + EXPECT_PKEY_USES_EVP_SIGNING(public_key); + + for (s2n_hash_algorithm hash_alg = 0; hash_alg < S2N_HASH_SENTINEL; hash_alg++) { + if (!s2n_hash_alg_is_supported(sig_alg, hash_alg)) { + continue; + } + + /* Calculate the signature using EVP methods */ + s2n_stack_blob(evp_signature, OUTPUT_DATA_SIZE, OUTPUT_DATA_SIZE); + EXPECT_OK(s2n_test_evp_sign(sig_alg, hash_alg, private_key, &evp_signature)); + + /* Calculate the signature using RSA-PSS methods */ + s2n_stack_blob(rsa_pss_signature, OUTPUT_DATA_SIZE, OUTPUT_DATA_SIZE); + EXPECT_OK(s2n_test_hash_init(&hash_state, hash_alg)); + EXPECT_SUCCESS(s2n_rsa_pss_sign(private_key, &hash_state, &rsa_pss_signature)); + + /* Verify that the EVP methods can verify both signatures */ + EXPECT_OK(s2n_test_evp_verify(sig_alg, hash_alg, public_key, &evp_signature, &rsa_pss_signature)); + + /* Verify that the RSA-PSS methods can verify the EVP signature */ + EXPECT_OK(s2n_test_hash_init(&hash_state, hash_alg)); + EXPECT_SUCCESS(s2n_rsa_pss_verify(public_key, &hash_state, &evp_signature)); + } + } + + /* EVP signing must match RSA-PSS-PSS signing */ + if (s2n_is_rsa_pss_certs_supported()) { + s2n_signature_algorithm sig_alg = S2N_SIGNATURE_RSA_PSS_PSS; + + struct s2n_cert_chain_and_key *rsa_pss_cert_chain = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&rsa_pss_cert_chain, + S2N_RSA_PSS_2048_SHA256_LEAF_CERT, S2N_RSA_PSS_2048_SHA256_LEAF_KEY)); + DEFER_CLEANUP(struct s2n_pkey public_key_parsed = { 0 }, s2n_pkey_free); + EXPECT_OK(s2n_setup_public_key(&public_key_parsed, rsa_pss_cert_chain)); + + struct s2n_pkey *private_key = rsa_pss_cert_chain->private_key; + struct s2n_pkey *public_key = &public_key_parsed; + EXPECT_PKEY_USES_EVP_SIGNING(private_key); + EXPECT_PKEY_USES_EVP_SIGNING(public_key); + + for (s2n_hash_algorithm hash_alg = 0; hash_alg < S2N_HASH_SENTINEL; hash_alg++) { + if (!s2n_hash_alg_is_supported(sig_alg, hash_alg)) { + continue; + } + + /* Calculate the signature using EVP methods */ + s2n_stack_blob(evp_signature, OUTPUT_DATA_SIZE, OUTPUT_DATA_SIZE); + EXPECT_OK(s2n_test_evp_sign(sig_alg, hash_alg, private_key, &evp_signature)); + + /* Calculate the signature using RSA-PSS methods */ + s2n_stack_blob(rsa_pss_signature, OUTPUT_DATA_SIZE, OUTPUT_DATA_SIZE); + EXPECT_OK(s2n_test_hash_init(&hash_state, hash_alg)); + EXPECT_SUCCESS(s2n_rsa_pss_sign(private_key, &hash_state, &rsa_pss_signature)); + + /* Verify that the EVP methods can verify both signatures */ + EXPECT_OK(s2n_test_evp_verify(sig_alg, hash_alg, public_key, &evp_signature, &rsa_pss_signature)); + + /* Verify that the RSA-PSS methods can verify the EVP signature */ + EXPECT_OK(s2n_test_hash_init(&hash_state, hash_alg)); + EXPECT_SUCCESS(s2n_rsa_pss_verify(public_key, &hash_state, &evp_signature)); + } + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(rsa_pss_cert_chain)); + } + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(rsa_cert_chain)); + END_TEST(); +} diff --git a/tests/unit/s2n_examples_test.c b/tests/unit/s2n_examples_test.c new file mode 100644 index 00000000000..78ec10e276d --- /dev/null +++ b/tests/unit/s2n_examples_test.c @@ -0,0 +1,329 @@ +/* + * 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 "testlib/s2n_examples.h" + +#include +#include +#include + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_random.h" + +#define S2N_TEST_RECORD_COUNT 200 +#define S2N_TEST_BYTES_TO_SEND (S2N_DEFAULT_FRAGMENT_LENGTH * S2N_TEST_RECORD_COUNT) +#define S2N_TEST_LAST_SEQ_NUM_BYTE (S2N_TLS_SEQUENCE_NUM_LEN - 1) + +typedef int (*s2n_io_fn)(struct s2n_connection *conn, uint8_t *buffer, size_t buffer_size); +struct s2n_test_thread_input { + s2n_io_fn fn; + struct s2n_connection *conn; + struct s2n_blob *mem; +}; + +static void *s2n_run_io_fn(void *arg) +{ + struct s2n_test_thread_input *input = (struct s2n_test_thread_input *) arg; + if (input && input->fn && input->mem) { + if (input->fn(input->conn, input->mem->data, input->mem->size) == 0) { + return arg; + } + } + return NULL; +} + +static S2N_RESULT s2n_test_shutdown(struct s2n_connection *conn) +{ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + int r = 0; + while ((r = s2n_shutdown(conn, &blocked)) != S2N_SUCCESS) { + if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { + RESULT_GUARD_POSIX(r); + } + } + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_test_shutdown_send(struct s2n_connection *conn) +{ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + int r = 0; + while ((r = s2n_shutdown_send(conn, &blocked)) != S2N_SUCCESS) { + if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { + RESULT_GUARD_POSIX(r); + } + } + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_test_example_negotiate(struct s2n_connection *conn, + struct s2n_blob *input) +{ + RESULT_GUARD_POSIX(s2n_example_negotiate(conn)); + RESULT_GUARD(s2n_test_shutdown(conn)); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_test_example_send_and_recv(struct s2n_connection *conn, + struct s2n_blob *input) +{ + RESULT_GUARD_POSIX(s2n_example_negotiate(conn)); + + DEFER_CLEANUP(struct s2n_blob output = { 0 }, s2n_free); + RESULT_GUARD_POSIX(s2n_alloc(&output, input->size)); + + pthread_t reader = 0; + struct s2n_test_thread_input reader_input = { + .fn = s2n_example_recv, + .conn = conn, + .mem = &output, + }; + RESULT_ENSURE_EQ(pthread_create(&reader, NULL, s2n_run_io_fn, (void *) &reader_input), 0); + + pthread_t writer = 0; + struct s2n_test_thread_input writer_input = { + .fn = s2n_example_send, + .conn = conn, + .mem = input, + }; + RESULT_ENSURE_EQ(pthread_create(&writer, NULL, s2n_run_io_fn, (void *) &writer_input), 0); + + void *reader_return = NULL; + RESULT_ENSURE_EQ(pthread_join(reader, &reader_return), 0); + RESULT_ENSURE_REF(reader_return); + + void *writer_return = NULL; + RESULT_ENSURE_EQ(pthread_join(writer, &writer_return), 0); + RESULT_ENSURE_REF(writer_return); + + RESULT_ENSURE_EQ(memcmp(output.data, input->data, input->size), 0); + + RESULT_GUARD(s2n_test_shutdown(conn)); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_test_example_sendv(struct s2n_connection *conn, + struct s2n_blob *input) +{ + RESULT_GUARD_POSIX(s2n_example_negotiate(conn)); + + DEFER_CLEANUP(struct s2n_blob output = { 0 }, s2n_free); + RESULT_GUARD_POSIX(s2n_alloc(&output, input->size)); + + pthread_t reader = 0; + struct s2n_test_thread_input reader_input = { + .fn = s2n_example_recv, + .conn = conn, + .mem = &output, + }; + RESULT_ENSURE_EQ(pthread_create(&reader, NULL, s2n_run_io_fn, (void *) &reader_input), 0); + + pthread_t writer = 0; + struct s2n_test_thread_input writer_input = { + .fn = s2n_example_sendv, + .conn = conn, + .mem = input, + }; + RESULT_ENSURE_EQ(pthread_create(&writer, NULL, s2n_run_io_fn, (void *) &writer_input), 0); + + void *reader_return = NULL; + RESULT_ENSURE_EQ(pthread_join(reader, &reader_return), 0); + RESULT_ENSURE_REF(reader_return); + + void *writer_return = NULL; + RESULT_ENSURE_EQ(pthread_join(writer, &writer_return), 0); + RESULT_ENSURE_REF(writer_return); + + RESULT_ENSURE_EQ(memcmp(output.data, input->data, input->size), 0); + + RESULT_GUARD(s2n_test_shutdown(conn)); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_test_example_recv_echo(struct s2n_connection *conn, + struct s2n_blob *input) +{ + RESULT_GUARD_POSIX(s2n_example_negotiate(conn)); + + /* We need to send a close_notify to stop the reader thread, which will cause + * a full connection shutdown without TLS1.3's half-close behavior. + * That can lead to unexpected "connection closed" errors depending on the timings. + */ + if (conn->actual_protocol_version < S2N_TLS13) { + return S2N_RESULT_OK; + } + + DEFER_CLEANUP(struct s2n_blob output = { 0 }, s2n_free); + RESULT_GUARD_POSIX(s2n_alloc(&output, 100)); + + pthread_t reader = 0; + struct s2n_test_thread_input reader_input = { + .fn = s2n_example_recv_echo, + .conn = conn, + .mem = &output, + }; + RESULT_ENSURE_EQ(pthread_create(&reader, NULL, s2n_run_io_fn, (void *) &reader_input), 0); + + pthread_t writer = 0; + struct s2n_test_thread_input writer_input = { + .fn = s2n_example_send, + .conn = conn, + .mem = input, + }; + RESULT_ENSURE_EQ(pthread_create(&writer, NULL, s2n_run_io_fn, (void *) &writer_input), 0); + + void *writer_return = NULL; + RESULT_ENSURE_EQ(pthread_join(writer, &writer_return), 0); + RESULT_ENSURE_REF(writer_return); + + RESULT_GUARD(s2n_test_shutdown_send(conn)); + + void *reader_return = NULL; + RESULT_ENSURE_EQ(pthread_join(reader, &reader_return), 0); + RESULT_ENSURE_REF(reader_return); + + /* We can't verify the exact data read, since we read in chunks and the buffer + * never contains the full data sent, but we can sanity check the number of + * records and total bytes read. + */ + RESULT_ENSURE_GT(conn->wire_bytes_in, input->size); + RESULT_ENSURE_LTE(S2N_TEST_RECORD_COUNT, UINT8_MAX); + RESULT_ENSURE_GTE(conn->secure->client_sequence_number[S2N_TEST_LAST_SEQ_NUM_BYTE], + S2N_TEST_RECORD_COUNT); + RESULT_ENSURE_GTE(conn->secure->server_sequence_number[S2N_TEST_LAST_SEQ_NUM_BYTE], + S2N_TEST_RECORD_COUNT); + + RESULT_GUARD(s2n_test_shutdown(conn)); + return S2N_RESULT_OK; +} + +typedef S2N_RESULT (*s2n_test_scenario)(struct s2n_connection *conn, struct s2n_blob *input); +static S2N_RESULT s2n_run_self_talk_test(s2n_test_scenario scenario_fn) +{ + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + RESULT_GUARD_POSIX(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + RESULT_GUARD_POSIX(s2n_config_set_unsafe_for_testing(config)); + RESULT_GUARD_POSIX(s2n_config_set_cipher_preferences(config, "default_tls13")); + RESULT_GUARD_POSIX(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + RESULT_GUARD_POSIX(s2n_io_pair_init_non_blocking(&io_pair)); + + DEFER_CLEANUP(struct s2n_blob input = { 0 }, s2n_free); + RESULT_GUARD_POSIX(s2n_alloc(&input, S2N_TEST_BYTES_TO_SEND)); + RESULT_GUARD(s2n_get_public_random_data(&input)); + + pid_t client_pid = fork(); + if (client_pid == 0) { + /* Suppress stdout when running the examples. + * This only affects the new client process. + */ + fclose(stdout); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client, &io_pair)); + + EXPECT_OK(scenario_fn(client, &input)); + + exit(EXIT_SUCCESS); + } + + pid_t server_pid = fork(); + if (server_pid == 0) { + /* Suppress stdout when running the examples. + * This only affects the new server process. + */ + fclose(stdout); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server, &io_pair)); + + EXPECT_OK(scenario_fn(server, &input)); + + exit(EXIT_SUCCESS); + } + + int status = 0; + RESULT_ENSURE_EQ(waitpid(client_pid, &status, 0), client_pid); + RESULT_ENSURE_EQ(status, EXIT_SUCCESS); + RESULT_ENSURE_EQ(waitpid(server_pid, &status, 0), server_pid); + RESULT_ENSURE_EQ(status, EXIT_SUCCESS); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_run_failure_tests() +{ + uint8_t buffer[100] = { 0 }; + size_t buffer_size = sizeof(buffer); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); + + pid_t pid = fork(); + if (pid == 0) { + /* Suppress stdout AND stderr when running the examples. + * This only affects the new process. + */ + fclose(stdout); + fclose(stderr); + + EXPECT_EQUAL(s2n_example_negotiate(conn), S2N_FAILURE); + EXPECT_EQUAL(s2n_example_send(conn, buffer, buffer_size), S2N_FAILURE); + EXPECT_EQUAL(s2n_example_sendv(conn, buffer, buffer_size), S2N_FAILURE); + EXPECT_EQUAL(s2n_example_recv(conn, buffer, buffer_size), S2N_FAILURE); + EXPECT_EQUAL(s2n_example_recv_echo(conn, buffer, buffer_size), S2N_FAILURE); + + exit(EXIT_SUCCESS); + } + + int status = 0; + RESULT_ENSURE_EQ(waitpid(pid, &status, 0), pid); + RESULT_ENSURE_EQ(status, EXIT_SUCCESS); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* We're going to fork, so flush the initial test output first */ + EXPECT_EQUAL(fflush(stdout), 0); + + EXPECT_OK(s2n_run_failure_tests()); + EXPECT_OK(s2n_run_self_talk_test(s2n_test_example_negotiate)); + EXPECT_OK(s2n_run_self_talk_test(s2n_test_example_send_and_recv)); + EXPECT_OK(s2n_run_self_talk_test(s2n_test_example_sendv)); + EXPECT_OK(s2n_run_self_talk_test(s2n_test_example_recv_echo)); + END_TEST(); + + END_TEST(); +} diff --git a/tests/unit/s2n_extended_master_secret_test.c b/tests/unit/s2n_extended_master_secret_test.c new file mode 100644 index 00000000000..4e84e369db2 --- /dev/null +++ b/tests/unit/s2n_extended_master_secret_test.c @@ -0,0 +1,304 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_bitmap.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Test s2n_conn_set_handshake_type is processing EMS data correctly */ + { + struct s2n_config *config; + uint64_t current_time = 0; + EXPECT_NOT_NULL(config = s2n_config_new()); + + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(config->wall_clock(config->sys_clock_ctx, ¤t_time)); + uint8_t ticket_key_name[16] = "2016.07.26.15\0"; + /** + *= https://tools.ietf.org/rfc/rfc5869#appendix-A.1 + *# PRK = 0x077709362c2e32df0ddc3f0dc47bba63 + *# 90b6c73bb50f9c3122ec844ad7c2b3e5 (32 octets) + **/ + S2N_BLOB_FROM_HEX(ticket_key, + "077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5"); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), + ticket_key.data, ticket_key.size, current_time / ONE_SEC_IN_NANOS)); + + /** + *= https://tools.ietf.org/rfc/rfc7627#section-5.3 + *= type=test + *# 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. + **/ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->actual_protocol_version = S2N_TLS12; + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + /* Original connection negotiated an EMS */ + conn->ems_negotiated = true; + + struct s2n_stuffer ticket = { 0 }; + struct s2n_blob ticket_blob = { 0 }; + uint8_t ticket_data[S2N_TLS12_TICKET_SIZE_IN_BYTES] = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&ticket_blob, ticket_data, S2N_TLS12_TICKET_SIZE_IN_BYTES)); + EXPECT_SUCCESS(s2n_stuffer_init(&ticket, &ticket_blob)); + + /* Encrypt the ticket with EMS data */ + EXPECT_SUCCESS(s2n_encrypt_session_ticket(conn, &ticket)); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->actual_protocol_version = S2N_TLS12; + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + conn->session_ticket_status = S2N_DECRYPT_TICKET; + EXPECT_SUCCESS(s2n_stuffer_copy(&ticket, &conn->client_ticket_to_decrypt, S2N_TLS12_TICKET_SIZE_IN_BYTES)); + + /* Resumed session did not receive the EMS extension */ + EXPECT_FAILURE_WITH_ERRNO(s2n_conn_set_handshake_type(conn), S2N_ERR_MISSING_EXTENSION); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /** + *= https://tools.ietf.org/rfc/rfc7627#section-5.3 + *= type=test + *# If the original session did not use the "extended_master_secret" + *# extension but the new ClientHello contains the extension, then the + *# server MUST NOT perform the abbreviated handshake. Instead, it + *# SHOULD continue with a full handshake (as described in + *# Section 5.2) to negotiate a new session. + **/ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->actual_protocol_version = S2N_TLS12; + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + /* Original connection did not negotiate an EMS */ + conn->ems_negotiated = false; + + struct s2n_stuffer ticket = { 0 }; + struct s2n_blob ticket_blob = { 0 }; + uint8_t ticket_data[S2N_TLS12_TICKET_SIZE_IN_BYTES] = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&ticket_blob, ticket_data, S2N_TLS12_TICKET_SIZE_IN_BYTES)); + EXPECT_SUCCESS(s2n_stuffer_init(&ticket, &ticket_blob)); + + /* Encrypt the ticket without EMS data */ + EXPECT_SUCCESS(s2n_encrypt_session_ticket(conn, &ticket)); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->actual_protocol_version = S2N_TLS12; + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + conn->session_ticket_status = S2N_DECRYPT_TICKET; + EXPECT_SUCCESS(s2n_stuffer_copy(&ticket, &conn->client_ticket_to_decrypt, S2N_TLS12_TICKET_SIZE_IN_BYTES)); + + /* Resumed connection received the EMS extension */ + conn->ems_negotiated = true; + + EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); + + /* Fallback to full handshake */ + EXPECT_TRUE(s2n_handshake_type_check_flag(conn, FULL_HANDSHAKE)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Session ticket is processed correctly if the previous session and current session both negotiated EMS */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->actual_protocol_version = S2N_TLS12; + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + /* Original connection negotiated an EMS */ + conn->ems_negotiated = true; + + struct s2n_stuffer ticket = { 0 }; + struct s2n_blob ticket_blob = { 0 }; + uint8_t ticket_data[S2N_TLS12_TICKET_SIZE_IN_BYTES] = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&ticket_blob, ticket_data, S2N_TLS12_TICKET_SIZE_IN_BYTES)); + EXPECT_SUCCESS(s2n_stuffer_init(&ticket, &ticket_blob)); + + /* Encrypt the ticket with EMS data */ + EXPECT_SUCCESS(s2n_encrypt_session_ticket(conn, &ticket)); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->actual_protocol_version = S2N_TLS12; + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + conn->session_ticket_status = S2N_DECRYPT_TICKET; + EXPECT_SUCCESS(s2n_stuffer_copy(&ticket, &conn->client_ticket_to_decrypt, S2N_TLS12_TICKET_SIZE_IN_BYTES)); + + /* Resumed connection received the EMS extension */ + conn->ems_negotiated = true; + s2n_extension_type_id ems_ext_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_EMS, &ems_ext_id)); + S2N_CBIT_SET(conn->extension_requests_received, ems_ext_id); + + EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); + + EXPECT_FALSE(s2n_handshake_type_check_flag(conn, FULL_HANDSHAKE)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Connection where the client supports EMS but the server does not support EMS */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + + /* TLS1.2 cipher preferences */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + struct s2n_cert_chain_and_key *chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate until server has read the Client Hello message */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, SERVER_HELLO)); + + /* s2n servers by default support EMS. We turn it off by manually setting ems_negotiated to false + * and removing the EMS extension from our received extensions. */ + server_conn->ems_negotiated = false; + s2n_extension_type_id ems_ext_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_EMS, &ems_ext_id)); + S2N_CBIT_CLR(server_conn->extension_requests_received, ems_ext_id); + + /* Connection is successful and EMS is not negotiated */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_FALSE(server_conn->ems_negotiated); + EXPECT_FALSE(client_conn->ems_negotiated); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Connection where the server supports EMS but the client does not support EMS */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + struct s2n_cert_chain_and_key *chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* s2n clients support EMS by default. To manually prevent them from sending the EMS extension, add a + * resumption ticket to the connection, which indicates the previous session did not negotiate + * EMS and therefore this session shouldn't either. The resumption ticket does not have to be valid + * as this test is only interested in EMS. */ + client_conn->set_session = true; + + /* Connection is successful and EMS is not negotiated */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_FALSE(server_conn->ems_negotiated); + EXPECT_FALSE(client_conn->ems_negotiated); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Connection where both client and server support EMS */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + struct s2n_cert_chain_and_key *chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Connection is successful and EMS is negotiated */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_TRUE(server_conn->ems_negotiated); + EXPECT_TRUE(client_conn->ems_negotiated); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_extension_list_parse_test.c b/tests/unit/s2n_extension_list_parse_test.c new file mode 100644 index 00000000000..86acea3ed4a --- /dev/null +++ b/tests/unit/s2n_extension_list_parse_test.c @@ -0,0 +1,423 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_extension_list.h" +#include "tls/extensions/s2n_extension_type.h" + +#define S2N_UNKNOWN_EXTENSION_IANA 65280 /* Grease value: https://tools.ietf.org/html/rfc8701 */ + +const uint8_t test_data[] = "Test Data"; +const uint8_t other_test_data[] = "Different Test Data"; + +static int s2n_extension_send_test_data(struct s2n_connection *conn, struct s2n_stuffer *stuffer) +{ + return s2n_stuffer_write_bytes(stuffer, test_data, sizeof(test_data)); +} + +static int s2n_extension_send_other_test_data(struct s2n_connection *conn, struct s2n_stuffer *stuffer) +{ + return s2n_stuffer_write_bytes(stuffer, other_test_data, sizeof(other_test_data)); +} + +static int s2n_extension_send_no_data(struct s2n_connection *conn, struct s2n_stuffer *stuffer) +{ + return S2N_SUCCESS; +} + +const s2n_extension_type test_extension = { + .iana_value = TLS_EXTENSION_SUPPORTED_VERSIONS, + .is_response = false, + .send = s2n_extension_send_test_data, + .recv = s2n_extension_recv_unimplemented, + .should_send = s2n_extension_always_send, + .if_missing = s2n_extension_noop_if_missing, +}; + +#define EXPECT_PARSED_EXTENSION_EQUAL(list, type, d, n) \ + do { \ + s2n_extension_type_id id; \ + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(type, &id)); \ + EXPECT_NOT_NULL(list.parsed_extensions[id].extension.data); \ + EXPECT_EQUAL(list.parsed_extensions[id].extension.size, n); \ + EXPECT_BYTEARRAY_EQUAL(list.parsed_extensions[id].extension.data, d, n); \ + } while (0) + +#define EXPECT_RAW_EQUAL(list, stuffer) \ + EXPECT_EQUAL(list.raw.data, stuffer.blob.data + sizeof(uint16_t)); \ + EXPECT_EQUAL(list.raw.size, stuffer.high_water_mark - sizeof(uint16_t)) + +#define CLEAR_PARSED_EXTENSION(list, type) \ + do { \ + s2n_extension_type_id id; \ + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(type, &id)); \ + list.parsed_extensions[id] = EMPTY_PARSED_EXTENSIONS[0]; \ + } while (0) + +int main() +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + s2n_extension_type empty_test_extension = test_extension; + empty_test_extension.send = s2n_extension_send_no_data; + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + /* Safety checks */ + { + s2n_parsed_extensions_list parsed_extension_list = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + + EXPECT_FAILURE(s2n_extension_list_parse(NULL, &parsed_extension_list)); + EXPECT_FAILURE(s2n_extension_list_parse(&stuffer, NULL)); + }; + + /* Test that parse clears existing parsed_extensions */ + { + s2n_parsed_extensions_list parsed_extension_list = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1)); + + parsed_extension_list.parsed_extensions[0].extension_type = 0xFF; + parsed_extension_list.parsed_extensions[S2N_PARSED_EXTENSIONS_COUNT - 1].extension_type = 0xFF; + EXPECT_PARSED_EXTENSION_LIST_NOT_EMPTY(parsed_extension_list); + + EXPECT_SUCCESS(s2n_extension_list_parse(&stuffer, &parsed_extension_list)); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + EXPECT_PARSED_EXTENSION_LIST_EMPTY(parsed_extension_list); + + EXPECT_EQUAL(parsed_extension_list.raw.data, stuffer.blob.data); + EXPECT_EQUAL(parsed_extension_list.raw.size, 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + /* Test parse empty extension list - no extension list size */ + { + s2n_parsed_extensions_list parsed_extension_list = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1)); + + EXPECT_SUCCESS(s2n_extension_list_parse(&stuffer, &parsed_extension_list)); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + EXPECT_PARSED_EXTENSION_LIST_EMPTY(parsed_extension_list); + + EXPECT_EQUAL(parsed_extension_list.raw.data, stuffer.blob.data); + EXPECT_EQUAL(parsed_extension_list.raw.size, 0); + EXPECT_EQUAL(parsed_extension_list.count, 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + /* Test parse empty extension list - with extension list size */ + { + s2n_parsed_extensions_list parsed_extension_list = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + /* Write zero size */ + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_extension_list_parse(&stuffer, &parsed_extension_list)); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + EXPECT_PARSED_EXTENSION_LIST_EMPTY(parsed_extension_list); + EXPECT_RAW_EQUAL(parsed_extension_list, stuffer); + EXPECT_EQUAL(parsed_extension_list.raw.size, 0); + EXPECT_EQUAL(parsed_extension_list.count, 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + /* Test parse with insufficient data to match extension list size */ + { + s2n_parsed_extensions_list parsed_extension_list = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, 100)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_extension_list_parse(&stuffer, &parsed_extension_list), + S2N_ERR_BAD_MESSAGE); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + EXPECT_PARSED_EXTENSION_LIST_EMPTY(parsed_extension_list); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + /* Test parse with insufficient data for even one extension */ + { + s2n_parsed_extensions_list parsed_extension_list = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + /* Extension list size */ + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, sizeof(uint16_t) - 1)); + /* One less byte than the extension type takes */ + EXPECT_SUCCESS(s2n_stuffer_skip_write(&stuffer, sizeof(uint16_t) - 1)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_extension_list_parse(&stuffer, &parsed_extension_list), + S2N_ERR_BAD_MESSAGE); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + EXPECT_PARSED_EXTENSION_LIST_EMPTY(parsed_extension_list); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + /* Test parse single extension in list */ + { + s2n_parsed_extensions_list parsed_extension_list = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + /* Reserve size */ + struct s2n_stuffer_reservation extension_list_size = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&stuffer, &extension_list_size)); + /* Write extensions */ + EXPECT_SUCCESS(s2n_extension_send(&test_extension, conn, &stuffer)); + /* Check / write size */ + EXPECT_TRUE(s2n_stuffer_data_available(&stuffer) > extension_list_size.length); + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&extension_list_size)); + + EXPECT_SUCCESS(s2n_extension_list_parse(&stuffer, &parsed_extension_list)); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + EXPECT_PARSED_EXTENSION_LIST_NOT_EMPTY(parsed_extension_list); + EXPECT_RAW_EQUAL(parsed_extension_list, stuffer); + + EXPECT_PARSED_EXTENSION_EQUAL(parsed_extension_list, test_extension.iana_value, test_data, sizeof(test_data)); + EXPECT_EQUAL(parsed_extension_list.count, 1); + CLEAR_PARSED_EXTENSION(parsed_extension_list, test_extension.iana_value); + EXPECT_PARSED_EXTENSION_LIST_EMPTY(parsed_extension_list); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + /* Test parse single extension in list - malformed extension size */ + { + s2n_parsed_extensions_list parsed_extension_list = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + /* Reserve size */ + struct s2n_stuffer_reservation extension_list_size = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&stuffer, &extension_list_size)); + /* Write extensions */ + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, 100)); + /* Check / write size */ + EXPECT_TRUE(s2n_stuffer_data_available(&stuffer) > extension_list_size.length); + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&extension_list_size)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_extension_list_parse(&stuffer, &parsed_extension_list), + S2N_ERR_BAD_MESSAGE); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + EXPECT_PARSED_EXTENSION_LIST_EMPTY(parsed_extension_list); + EXPECT_RAW_EQUAL(parsed_extension_list, stuffer); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + /* Test parse single extension in list - extension is empty */ + { + s2n_parsed_extensions_list parsed_extension_list = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + /* Reserve size */ + struct s2n_stuffer_reservation extension_list_size = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&stuffer, &extension_list_size)); + /* Write extensions */ + EXPECT_SUCCESS(s2n_extension_send(&empty_test_extension, conn, &stuffer)); + /* Check / write size */ + EXPECT_TRUE(s2n_stuffer_data_available(&stuffer) > extension_list_size.length); + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&extension_list_size)); + + EXPECT_SUCCESS(s2n_extension_list_parse(&stuffer, &parsed_extension_list)); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + EXPECT_EQUAL(parsed_extension_list.count, 1); + EXPECT_PARSED_EXTENSION_LIST_NOT_EMPTY(parsed_extension_list); + EXPECT_RAW_EQUAL(parsed_extension_list, stuffer); + + EXPECT_PARSED_EXTENSION_EQUAL(parsed_extension_list, test_extension.iana_value, test_data, 0); + CLEAR_PARSED_EXTENSION(parsed_extension_list, test_extension.iana_value); + EXPECT_PARSED_EXTENSION_LIST_EMPTY(parsed_extension_list); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + /* Test parse single extension in list - ignore unknown extensions */ + { + s2n_parsed_extensions_list parsed_extension_list = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + /* Reserve size */ + struct s2n_stuffer_reservation extension_list_size = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&stuffer, &extension_list_size)); + /* Write extension - use grease value as type */ + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, S2N_UNKNOWN_EXTENSION_IANA)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, 0)); + /* Check / write size */ + EXPECT_TRUE(s2n_stuffer_data_available(&stuffer) > extension_list_size.length); + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&extension_list_size)); + + EXPECT_SUCCESS(s2n_extension_list_parse(&stuffer, &parsed_extension_list)); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + EXPECT_EQUAL(parsed_extension_list.count, 0); + EXPECT_PARSED_EXTENSION_LIST_EMPTY(parsed_extension_list); + EXPECT_RAW_EQUAL(parsed_extension_list, stuffer); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + /* Test error on duplicate extensions */ + { + s2n_parsed_extensions_list parsed_extension_list = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + /* Reserve size */ + struct s2n_stuffer_reservation extension_list_size = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&stuffer, &extension_list_size)); + /* Write extensions */ + EXPECT_SUCCESS(s2n_extension_send(&test_extension, conn, &stuffer)); + EXPECT_SUCCESS(s2n_extension_send(&test_extension, conn, &stuffer)); + /* Check / write size */ + EXPECT_TRUE(s2n_stuffer_data_available(&stuffer) > extension_list_size.length); + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&extension_list_size)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_extension_list_parse(&stuffer, &parsed_extension_list), + S2N_ERR_DUPLICATE_EXTENSION); + EXPECT_RAW_EQUAL(parsed_extension_list, stuffer); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + /* Test error on duplicate extensions - extensions are empty */ + { + s2n_parsed_extensions_list parsed_extension_list = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + /* Reserve size */ + struct s2n_stuffer_reservation extension_list_size = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&stuffer, &extension_list_size)); + /* Write extensions */ + EXPECT_SUCCESS(s2n_extension_send(&empty_test_extension, conn, &stuffer)); + EXPECT_SUCCESS(s2n_extension_send(&empty_test_extension, conn, &stuffer)); + /* Check / write size */ + EXPECT_TRUE(s2n_stuffer_data_available(&stuffer) > extension_list_size.length); + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&extension_list_size)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_extension_list_parse(&stuffer, &parsed_extension_list), + S2N_ERR_DUPLICATE_EXTENSION); + EXPECT_RAW_EQUAL(parsed_extension_list, stuffer); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + /* Test parse multiple extensions */ + { + s2n_parsed_extensions_list parsed_extension_list = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + s2n_extension_type test_extension_2 = empty_test_extension; + test_extension_2.iana_value = TLS_EXTENSION_SIGNATURE_ALGORITHMS; + s2n_extension_type test_extension_3 = test_extension; + test_extension_3.iana_value = TLS_EXTENSION_ALPN; + test_extension_3.send = s2n_extension_send_other_test_data; + + /* Reserve size */ + struct s2n_stuffer_reservation extension_list_size = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&stuffer, &extension_list_size)); + /* Write extensions */ + EXPECT_SUCCESS(s2n_extension_send(&test_extension, conn, &stuffer)); + EXPECT_SUCCESS(s2n_extension_send(&test_extension_2, conn, &stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, S2N_UNKNOWN_EXTENSION_IANA)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, 0)); + EXPECT_SUCCESS(s2n_extension_send(&test_extension_3, conn, &stuffer)); + /* Check / write size */ + EXPECT_TRUE(s2n_stuffer_data_available(&stuffer) > extension_list_size.length); + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&extension_list_size)); + + EXPECT_SUCCESS(s2n_extension_list_parse(&stuffer, &parsed_extension_list)); + + EXPECT_PARSED_EXTENSION_EQUAL(parsed_extension_list, test_extension.iana_value, test_data, sizeof(test_data)); + EXPECT_PARSED_EXTENSION_EQUAL(parsed_extension_list, test_extension_2.iana_value, test_data, 0); + EXPECT_PARSED_EXTENSION_EQUAL(parsed_extension_list, test_extension_3.iana_value, other_test_data, sizeof(other_test_data)); + EXPECT_RAW_EQUAL(parsed_extension_list, stuffer); + EXPECT_EQUAL(parsed_extension_list.count, 3); + + CLEAR_PARSED_EXTENSION(parsed_extension_list, test_extension.iana_value); + CLEAR_PARSED_EXTENSION(parsed_extension_list, test_extension_2.iana_value); + CLEAR_PARSED_EXTENSION(parsed_extension_list, test_extension_3.iana_value); + EXPECT_PARSED_EXTENSION_LIST_EMPTY(parsed_extension_list); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + /* Test parsed extensions assigned correct indexes */ + { + s2n_parsed_extensions_list parsed_extension_list = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + s2n_extension_type test_extension_2 = test_extension; + test_extension_2.iana_value = TLS_EXTENSION_SIGNATURE_ALGORITHMS; + s2n_extension_type test_extension_3 = test_extension; + test_extension_3.iana_value = TLS_EXTENSION_ALPN; + + /* Reserve size */ + struct s2n_stuffer_reservation extension_list_size = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&stuffer, &extension_list_size)); + /* Write extensions */ + EXPECT_SUCCESS(s2n_extension_send(&test_extension, conn, &stuffer)); + EXPECT_SUCCESS(s2n_extension_send(&test_extension_2, conn, &stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, S2N_UNKNOWN_EXTENSION_IANA)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, 0)); + EXPECT_SUCCESS(s2n_extension_send(&test_extension_3, conn, &stuffer)); + /* Check / write size */ + EXPECT_TRUE(s2n_stuffer_data_available(&stuffer) > extension_list_size.length); + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&extension_list_size)); + + EXPECT_SUCCESS(s2n_extension_list_parse(&stuffer, &parsed_extension_list)); + + uint16_t expected_order[] = { test_extension.iana_value, test_extension_2.iana_value, test_extension_3.iana_value }; + for (size_t i = 0; i < s2n_array_len(expected_order); i++) { + s2n_extension_type_id id; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(expected_order[i], &id)); + EXPECT_EQUAL(parsed_extension_list.parsed_extensions[id].wire_index, i); + } + EXPECT_EQUAL(parsed_extension_list.count, 3); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + + END_TEST(); +} diff --git a/tests/unit/s2n_extension_list_process_test.c b/tests/unit/s2n_extension_list_process_test.c new file mode 100644 index 00000000000..13d55a0309f --- /dev/null +++ b/tests/unit/s2n_extension_list_process_test.c @@ -0,0 +1,339 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_extension_list.h" +#include "tls/extensions/s2n_extension_type.h" +#include "tls/extensions/s2n_server_max_fragment_length.h" +#include "tls/extensions/s2n_server_server_name.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" + +#define SET_PARSED_EXTENSION(list, entry) \ + do { \ + s2n_extension_type_id id = 0; \ + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(entry.extension_type, &id)); \ + list.parsed_extensions[id] = entry; \ + } while (0) + +#define IS_EXTENSION_PROCESSED(list, id) ((list).parsed_extensions[id].processed) +#define EXPECT_EXTENSION_PROCESSED(list, id) EXPECT_TRUE(IS_EXTENSION_PROCESSED(list, id)) +#define EXPECT_NO_EXTENSIONS_PROCESSED(list) \ + do { \ + for (size_t i = 0; i < S2N_PARSED_EXTENSIONS_COUNT; i++) { \ + EXPECT_FALSE(IS_EXTENSION_PROCESSED(list, i)); \ + } \ + } while (0) + +static int s2n_setup_test_parsed_extension(const s2n_extension_type *extension_type, + s2n_parsed_extension *parsed_extension, struct s2n_connection *conn, struct s2n_stuffer *stuffer) +{ + parsed_extension->extension_type = extension_type->iana_value; + + POSIX_GUARD(extension_type->send(conn, stuffer)); + uint16_t extension_size = s2n_stuffer_data_available(stuffer); + POSIX_GUARD(s2n_blob_init(&parsed_extension->extension, s2n_stuffer_raw_read(stuffer, extension_size), extension_size)); + + return S2N_SUCCESS; +} + +static bool received_flag = false; +static int s2n_extension_test_recv(struct s2n_connection *conn, struct s2n_stuffer *stuffer) +{ + received_flag = true; + return S2N_SUCCESS; +} + +int main() +{ + BEGIN_TEST(); + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* Test s2n_extension_process */ + { + uint8_t extension_data[] = "data"; + struct s2n_blob extension_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&extension_blob, extension_data, sizeof(extension_data))); + + const s2n_extension_type test_extension_type = { + .iana_value = TLS_EXTENSION_SUPPORTED_VERSIONS, + .is_response = false, + .should_send = s2n_extension_never_send, + .send = s2n_extension_send_unimplemented, + .recv = s2n_extension_test_recv, + .if_missing = s2n_extension_noop_if_missing, + }; + + s2n_extension_type_id test_extension_type_internal_id; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(test_extension_type.iana_value, + &test_extension_type_internal_id)); + + /* Safety checks */ + { + struct s2n_connection conn = { 0 }; + s2n_parsed_extensions_list parsed_extension_list = { 0 }; + const s2n_extension_type extension_type = { 0 }; + + EXPECT_FAILURE(s2n_extension_process(NULL, &conn, &parsed_extension_list)); + EXPECT_FAILURE(s2n_extension_process(&extension_type, NULL, &parsed_extension_list)); + EXPECT_FAILURE(s2n_extension_process(&extension_type, &conn, NULL)); + }; + + /* Successfully process a basic parsed_extension */ + { + s2n_parsed_extensions_list parsed_extension_list = { 0 }; + const s2n_parsed_extension test_parsed_extension = { + .extension_type = test_extension_type.iana_value, + .extension = extension_blob, + }; + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + SET_PARSED_EXTENSION(parsed_extension_list, test_parsed_extension); + + received_flag = false; + EXPECT_SUCCESS(s2n_extension_process(&test_extension_type, conn, &parsed_extension_list)); + + EXPECT_EXTENSION_PROCESSED(parsed_extension_list, test_extension_type_internal_id); + EXPECT_TRUE(received_flag); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Processing an extension again should be a no-op */ + { + s2n_parsed_extensions_list parsed_extension_list = { 0 }; + const s2n_parsed_extension test_parsed_extension = { + .extension_type = test_extension_type.iana_value, + .extension = extension_blob, + }; + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + SET_PARSED_EXTENSION(parsed_extension_list, test_parsed_extension); + + /* First time processing */ + received_flag = false; + EXPECT_SUCCESS(s2n_extension_process(&test_extension_type, conn, &parsed_extension_list)); + EXPECT_EXTENSION_PROCESSED(parsed_extension_list, test_extension_type_internal_id); + EXPECT_TRUE(received_flag); + + /* Second time processing */ + received_flag = false; + EXPECT_SUCCESS(s2n_extension_process(&test_extension_type, conn, &parsed_extension_list)); + EXPECT_EXTENSION_PROCESSED(parsed_extension_list, test_extension_type_internal_id); + /* The extension has already been processed, so recv is not called again */ + EXPECT_FALSE(received_flag); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Successfully process an empty parsed_extension */ + { + s2n_parsed_extensions_list parsed_extension_list = { 0 }; + struct s2n_blob empty_blob = extension_blob; + empty_blob.size = 0; + const s2n_parsed_extension test_parsed_extension = { + .extension_type = test_extension_type.iana_value, + .extension = empty_blob, + }; + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + SET_PARSED_EXTENSION(parsed_extension_list, test_parsed_extension); + + received_flag = false; + EXPECT_SUCCESS(s2n_extension_process(&test_extension_type, conn, &parsed_extension_list)); + + EXPECT_EXTENSION_PROCESSED(parsed_extension_list, test_extension_type_internal_id); + EXPECT_TRUE(received_flag); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Fail if parsed_extension indexed incorrectly */ + { + s2n_parsed_extensions_list parsed_extension_list = { 0 }; + const s2n_parsed_extension test_parsed_extension = { + .extension_type = test_extension_type.iana_value - 1, + .extension = extension_blob, + }; + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + parsed_extension_list.parsed_extensions[test_extension_type_internal_id] = test_parsed_extension; + + received_flag = false; + EXPECT_FAILURE_WITH_ERRNO(s2n_extension_process(&test_extension_type, conn, &parsed_extension_list), + S2N_ERR_INVALID_PARSED_EXTENSIONS); + + EXPECT_NO_EXTENSIONS_PROCESSED(parsed_extension_list); + EXPECT_FALSE(received_flag); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* If no parsed_extension found for extension type */ + { + /* Fail if extension type is required */ + { + s2n_parsed_extensions_list parsed_extension_list = { 0 }; + + s2n_extension_type test_required_extension_type = test_extension_type; + test_required_extension_type.if_missing = s2n_extension_error_if_missing; + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + received_flag = false; + EXPECT_FAILURE_WITH_ERRNO(s2n_extension_process(&test_required_extension_type, conn, &parsed_extension_list), + S2N_ERR_MISSING_EXTENSION); + + EXPECT_NO_EXTENSIONS_PROCESSED(parsed_extension_list); + EXPECT_FALSE(received_flag); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Succeed (but don't call recv) if extension type is optional */ + { + s2n_parsed_extensions_list parsed_extension_list = { 0 }; + + s2n_extension_type test_optional_extension_type = test_extension_type; + test_optional_extension_type.if_missing = s2n_extension_noop_if_missing; + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + received_flag = false; + EXPECT_SUCCESS(s2n_extension_process(&test_optional_extension_type, conn, &parsed_extension_list)); + + EXPECT_EXTENSION_PROCESSED(parsed_extension_list, test_extension_type_internal_id); + EXPECT_FALSE(received_flag); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + }; + + /* Test s2n_extension_list_process */ + { + s2n_parsed_extension test_empty_parsed_extension = { 0 }; + s2n_parsed_extension test_parsed_extension = { 0 }; + + DEFER_CLEANUP(struct s2n_stuffer extension_data_stuffer, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_alloc(&extension_data_stuffer, 100)); + + /* Set up parsed_extensions for simple real extensions */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_SUCCESS(s2n_setup_test_parsed_extension(&s2n_server_server_name_extension, + &test_empty_parsed_extension, conn, &extension_data_stuffer)); + EXPECT_EQUAL(test_empty_parsed_extension.extension.size, 0); + + EXPECT_SUCCESS(s2n_setup_test_parsed_extension(&s2n_server_max_fragment_length_extension, + &test_parsed_extension, conn, &extension_data_stuffer)); + EXPECT_NOT_EQUAL(test_parsed_extension.extension.size, 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + s2n_extension_type_id test_internal_id = 0, test_empty_internal_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(test_parsed_extension.extension_type, &test_internal_id)); + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(test_empty_parsed_extension.extension_type, &test_empty_internal_id)); + EXPECT_NOT_EQUAL(test_internal_id, test_empty_internal_id); + + /* Safety checks */ + { + struct s2n_connection conn = { 0 }; + s2n_parsed_extensions_list parsed_extension_list = { 0 }; + + EXPECT_FAILURE(s2n_extension_list_process(0, NULL, &parsed_extension_list)); + EXPECT_FAILURE(s2n_extension_list_process(0, &conn, NULL)); + EXPECT_FAILURE(s2n_extension_list_process(-1, &conn, &parsed_extension_list)); + }; + + /* Process a single parsed_extension */ + { + s2n_parsed_extensions_list parsed_extension_list = { 0 }; + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(conn)); + + SET_PARSED_EXTENSION(parsed_extension_list, test_empty_parsed_extension); + + conn->server_name_used = false; + EXPECT_SUCCESS(s2n_extension_list_process(S2N_EXTENSION_LIST_ENCRYPTED_EXTENSIONS, + conn, &parsed_extension_list)); + + EXPECT_EXTENSION_PROCESSED(parsed_extension_list, test_empty_internal_id); + EXPECT_TRUE(conn->server_name_used); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Process several parsed_extensions */ + { + s2n_parsed_extensions_list parsed_extension_list = { 0 }; + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(conn)); + + SET_PARSED_EXTENSION(parsed_extension_list, test_empty_parsed_extension); + SET_PARSED_EXTENSION(parsed_extension_list, test_parsed_extension); + + conn->server_name_used = false; + EXPECT_SUCCESS(s2n_extension_list_process(S2N_EXTENSION_LIST_ENCRYPTED_EXTENSIONS, + conn, &parsed_extension_list)); + + EXPECT_EXTENSION_PROCESSED(parsed_extension_list, test_internal_id); + EXPECT_EXTENSION_PROCESSED(parsed_extension_list, test_empty_internal_id); + EXPECT_TRUE(conn->server_name_used); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Skips an unexpected parsed_extension */ + { + s2n_parsed_extensions_list parsed_extension_list = { 0 }; + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(conn)); + + SET_PARSED_EXTENSION(parsed_extension_list, test_empty_parsed_extension); + + conn->server_name_used = false; + EXPECT_SUCCESS(s2n_extension_list_process(S2N_EXTENSION_LIST_EMPTY, conn, &parsed_extension_list)); + + EXPECT_NO_EXTENSIONS_PROCESSED(parsed_extension_list); + EXPECT_FALSE(conn->server_name_used); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_extension_list_send_test.c b/tests/unit/s2n_extension_list_send_test.c new file mode 100644 index 00000000000..3302012f51a --- /dev/null +++ b/tests/unit/s2n_extension_list_send_test.c @@ -0,0 +1,129 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_client_supported_versions.h" +#include "tls/extensions/s2n_extension_list.h" +#include "tls/extensions/s2n_extension_type_lists.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* Safety checks */ + { + struct s2n_connection conn = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + + EXPECT_FAILURE(s2n_extension_list_send(0, NULL, &stuffer)); + EXPECT_FAILURE(s2n_extension_list_send(0, &conn, NULL)); + EXPECT_FAILURE(s2n_extension_list_send(-1, &conn, &stuffer)); + }; + + /* Writes just size if extension type list empty */ + { + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_extension_list_send(S2N_EXTENSION_LIST_EMPTY, conn, &stuffer)); + + uint16_t extension_list_size; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &extension_list_size)); + EXPECT_EQUAL(extension_list_size, s2n_stuffer_data_available(&stuffer)); + EXPECT_EQUAL(extension_list_size, 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Send performs basic, non-zero write */ + { + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_extension_list_send(S2N_EXTENSION_LIST_CLIENT_HELLO, conn, &stuffer)); + + uint16_t extension_list_size; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &extension_list_size)); + EXPECT_EQUAL(extension_list_size, s2n_stuffer_data_available(&stuffer)); + EXPECT_NOT_EQUAL(extension_list_size, 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Write empty list */ + { + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + /* S2N_EXTENSION_LIST_CERTIFICATE only sends responses, and we haven't received any requests. + * Therefore, it should write an empty extensions list. */ + EXPECT_SUCCESS(s2n_extension_list_send(S2N_EXTENSION_LIST_CERTIFICATE, conn, &stuffer)); + + uint16_t extension_list_size; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &extension_list_size)); + EXPECT_EQUAL(extension_list_size, s2n_stuffer_data_available(&stuffer)); + EXPECT_EQUAL(extension_list_size, 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Send writes valid supported_versions extension */ + { + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_extension_list_send(S2N_EXTENSION_LIST_CLIENT_HELLO, client_conn, &stuffer)); + + /* Skip list size - already tested */ + EXPECT_SUCCESS(s2n_stuffer_skip_read(&stuffer, sizeof(uint16_t))); + + uint16_t first_extension_type; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &first_extension_type)); + EXPECT_EQUAL(first_extension_type, TLS_EXTENSION_SUPPORTED_VERSIONS); + + uint16_t first_extension_size; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &first_extension_size)); + EXPECT_NOT_EQUAL(first_extension_size, 0); + + struct s2n_stuffer extensions_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extensions_stuffer, 0)); + EXPECT_SUCCESS(s2n_stuffer_copy(&stuffer, &extensions_stuffer, first_extension_size)); + + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_extension_recv(&s2n_client_supported_versions_extension, server_conn, &extensions_stuffer)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_stuffer_free(&extensions_stuffer)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_extension_type_lists_test.c b/tests/unit/s2n_extension_type_lists_test.c new file mode 100644 index 00000000000..a9cee88957b --- /dev/null +++ b/tests/unit/s2n_extension_type_lists_test.c @@ -0,0 +1,51 @@ +/* + * 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 "tls/extensions/s2n_extension_type_lists.h" + +#include "s2n_test.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* Test s2n_extension_type_list_get */ + { + s2n_extension_type_list *list = NULL; + + /* Safety checks */ + { + EXPECT_FAILURE(s2n_extension_type_list_get(0, NULL)); + + /* Should fail for a bad list type */ + EXPECT_FAILURE(s2n_extension_type_list_get(-1, &list)); + EXPECT_FAILURE(s2n_extension_type_list_get(S2N_EXTENSION_LIST_IDS_COUNT, &list)); + }; + + /* Can retrieve a list for every id */ + { + for (size_t i = 0; i < S2N_EXTENSION_LIST_IDS_COUNT; i++) { + list = NULL; + EXPECT_SUCCESS(s2n_extension_type_list_get(i, &list)); + EXPECT_NOT_NULL(list); + } + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_extension_type_test.c b/tests/unit/s2n_extension_type_test.c new file mode 100644 index 00000000000..b23c68ddaa2 --- /dev/null +++ b/tests/unit/s2n_extension_type_test.c @@ -0,0 +1,571 @@ +/* + * 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 "tls/extensions/s2n_extension_type.h" + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_extension_type_lists.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_bitmap.h" + +#define S2N_TEST_DATA_LEN 20 + +#define EXPECT_BITFIELD_CLEAR(field) EXPECT_BYTEARRAY_EQUAL((field), &empty_bitfield, S2N_SUPPORTED_EXTENSIONS_BITFIELD_LEN) + +s2n_extension_type_id s2n_extension_iana_value_to_id(uint16_t iana_value); + +const s2n_extension_bitfield empty_bitfield = { 0 }; + +static int test_send(struct s2n_connection *conn, struct s2n_stuffer *out) +{ + return s2n_stuffer_skip_write(out, S2N_TEST_DATA_LEN); +} + +static int test_send_too_much_data(struct s2n_connection *conn, struct s2n_stuffer *out) +{ + return s2n_stuffer_skip_write(out, UINT16_MAX + 1); +} + +static int test_recv(struct s2n_connection *conn, struct s2n_stuffer *in) +{ + return S2N_SUCCESS; +} + +const s2n_extension_type test_extension_type = { + .iana_value = TLS_EXTENSION_SUPPORTED_VERSIONS, + .is_response = false, + .send = test_send, + .recv = test_recv, + .should_send = s2n_extension_always_send, + .if_missing = s2n_extension_noop_if_missing, +}; + +int main() +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Test common implementations of methods */ + { + /* Test common implementations for send */ + EXPECT_FAILURE_WITH_ERRNO(s2n_extension_send_unimplemented(NULL, NULL), S2N_ERR_UNIMPLEMENTED); + EXPECT_SUCCESS(s2n_extension_send_noop(NULL, NULL)); + + /* Test common implementations for recv */ + EXPECT_FAILURE_WITH_ERRNO(s2n_extension_recv_unimplemented(NULL, NULL), S2N_ERR_UNIMPLEMENTED); + EXPECT_SUCCESS(s2n_extension_recv_noop(NULL, NULL)); + + /* Test common implementations for should_send */ + { + EXPECT_TRUE(s2n_extension_always_send(NULL)); + EXPECT_FALSE(s2n_extension_never_send(NULL)); + + struct s2n_connection conn = { 0 }; + conn.actual_protocol_version = S2N_TLS12; + EXPECT_FALSE(s2n_extension_send_if_tls13_connection(&conn)); + conn.actual_protocol_version = S2N_TLS13; + EXPECT_TRUE(s2n_extension_send_if_tls13_connection(&conn)); + }; + + /* Test common implementations for if_missing */ + EXPECT_FAILURE_WITH_ERRNO(s2n_extension_error_if_missing(NULL), S2N_ERR_MISSING_EXTENSION); + EXPECT_SUCCESS(s2n_extension_noop_if_missing(NULL)); + }; + + /* Test s2n_extension_iana_value_to_id */ + { + /* Extension appearing in the lookup table can be handled */ + EXPECT_EQUAL(s2n_extension_iana_value_to_id(s2n_supported_extensions[5]), 5); + + /* Unknown extension in the lookup table can be handled + * 15 == heartbeat, which s2n will probably never support :) */ + EXPECT_EQUAL(s2n_extension_iana_value_to_id(15), s2n_unsupported_extension); + + /* Extension with iana too large for the lookup table can be handled */ + EXPECT_EQUAL(s2n_extension_iana_value_to_id(TLS_EXTENSION_RENEGOTIATION_INFO), 0); + + /* Unknown extension with iana too large for the lookup table can be handled + * 65280 == grease value (see https://tools.ietf.org/html/rfc8701) */ + EXPECT_EQUAL(s2n_extension_iana_value_to_id(65280), s2n_unsupported_extension); + + /* Every supported extension can be handled */ + for (size_t i = 0; i < S2N_SUPPORTED_EXTENSIONS_COUNT; i++) { + EXPECT_EQUAL(s2n_extension_iana_value_to_id(s2n_supported_extensions[i]), i); + } + }; + + /* Test s2n_extension_supported_iana_value_to_id */ + { + s2n_extension_type_id id = s2n_unsupported_extension; + + /* Supported extension id returned */ + const uint16_t supported_extension_id = 5; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(s2n_supported_extensions[supported_extension_id], &id)); + EXPECT_EQUAL(id, supported_extension_id); + + /* Fail on unsupported iana value + * 15 == heartbeat, which s2n will probably never support :) */ + EXPECT_FAILURE_WITH_ERRNO(s2n_extension_supported_iana_value_to_id(15, &id), + S2N_ERR_UNRECOGNIZED_EXTENSION); + }; + + /* Test bitfield behavior */ + { + s2n_extension_bitfield test_bitfield = { 0 }; + for (size_t i = 0; i < S2N_SUPPORTED_EXTENSIONS_COUNT; i++) { + uint16_t iana = s2n_supported_extensions[i]; + s2n_extension_type_id id = s2n_extension_iana_value_to_id(iana); + + EXPECT_FALSE(S2N_CBIT_TEST(test_bitfield, id)); + S2N_CBIT_SET(test_bitfield, id); + EXPECT_TRUE(S2N_CBIT_TEST(test_bitfield, id)); + S2N_CBIT_CLR(test_bitfield, id); + EXPECT_FALSE(S2N_CBIT_TEST(test_bitfield, id)); + } + }; + + s2n_extension_type_id test_extension_id = s2n_extension_iana_value_to_id(test_extension_type.iana_value); + EXPECT_NOT_EQUAL(test_extension_id, s2n_unsupported_extension); + + /* Test s2n_extension_recv */ + { + struct s2n_stuffer stuffer = { 0 }; + + /* null check tests */ + { + struct s2n_connection conn = { 0 }; + + EXPECT_FAILURE(s2n_extension_recv(NULL, &conn, &stuffer)); + EXPECT_FAILURE(s2n_extension_recv(&test_extension_type, NULL, &stuffer)); + + s2n_extension_type extension_type_with_null_recv = test_extension_type; + extension_type_with_null_recv.recv = NULL; + EXPECT_FAILURE(s2n_extension_recv(&extension_type_with_null_recv, &conn, &stuffer)); + }; + + /* request extension */ + { + struct s2n_connection conn = { 0 }; + s2n_extension_type request_extension_type = test_extension_type; + request_extension_type.is_response = false; + + /* Succeeds and sets request flag */ + EXPECT_SUCCESS(s2n_extension_recv(&request_extension_type, &conn, &stuffer)); + EXPECT_TRUE(S2N_CBIT_TEST(conn.extension_requests_received, test_extension_id)); + }; + + /** + * Ensure response extensions are only received if sent + * + *= https://tools.ietf.org/rfc/rfc8446#section-4.2 + *= type=test + *# Upon receiving such an extension, an endpoint MUST abort the handshake + *# with an "unsupported_extension" alert. + * + *= https://tools.ietf.org/rfc/rfc7627#section-5.3 + *= type=test + *# If the original session did not use the "extended_master_secret" + *# extension but the new ServerHello contains the extension, the + *# client MUST abort the handshake. + * + *= https://tools.ietf.org/rfc/rfc8446#4.1.4 + *= type=test + *# As with the ServerHello, a HelloRetryRequest MUST NOT contain any + *# extensions that were not first offered by the client in its + *# ClientHello, with the exception of optionally the "cookie" (see + *# Section 4.2.2) extension. + **/ + { + struct s2n_connection conn = { 0 }; + s2n_extension_type response_extension_type = test_extension_type; + response_extension_type.is_response = true; + + /* Fails if request was not sent */ + EXPECT_FAILURE_WITH_ERRNO(s2n_extension_recv(&response_extension_type, &conn, &stuffer), S2N_ERR_UNSUPPORTED_EXTENSION); + /* cppcheck-suppress sizeofDivisionMemfunc */ + EXPECT_BITFIELD_CLEAR(conn.extension_requests_received); + + /* Succeeds (but does not set request flag) if request was sent */ + S2N_CBIT_SET(conn.extension_requests_sent, test_extension_id); + EXPECT_SUCCESS(s2n_extension_recv(&response_extension_type, &conn, &stuffer)); + /* cppcheck-suppress sizeofDivisionMemfunc */ + EXPECT_BITFIELD_CLEAR(conn.extension_requests_received); + }; + + /* "recv" errors */ + { + struct s2n_connection conn = { 0 }; + s2n_extension_type extension_type_with_failure = test_extension_type; + extension_type_with_failure.recv = s2n_extension_recv_unimplemented; + + EXPECT_FAILURE_WITH_ERRNO(s2n_extension_recv(&extension_type_with_failure, &conn, &stuffer), S2N_ERR_UNIMPLEMENTED); + /* cppcheck-suppress sizeofDivisionMemfunc */ + EXPECT_BITFIELD_CLEAR(conn.extension_requests_received); + }; + }; + + /* Test s2n_extension_send */ + { + /* null check tests */ + { + struct s2n_connection conn = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + + EXPECT_FAILURE(s2n_extension_send(NULL, &conn, &stuffer)); + EXPECT_FAILURE(s2n_extension_send(&test_extension_type, NULL, &stuffer)); + + s2n_extension_type extension_type_with_null_send = test_extension_type; + extension_type_with_null_send.send = NULL; + EXPECT_FAILURE(s2n_extension_send(&extension_type_with_null_send, &conn, &stuffer)); + + s2n_extension_type extension_type_with_null_should_send = test_extension_type; + extension_type_with_null_should_send.should_send = NULL; + EXPECT_FAILURE(s2n_extension_send(&extension_type_with_null_should_send, &conn, &stuffer)); + }; + + /* request extension */ + { + struct s2n_connection conn = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + s2n_stuffer_alloc(&stuffer, S2N_TEST_DATA_LEN * 2); + + s2n_extension_type request_extension_type = test_extension_type; + request_extension_type.is_response = false; + + /* Succeeds and sets request flag */ + EXPECT_SUCCESS(s2n_extension_send(&request_extension_type, &conn, &stuffer)); + EXPECT_TRUE(S2N_CBIT_TEST(conn.extension_requests_sent, test_extension_id)); + + /* writes iana_value */ + uint16_t iana_value; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &iana_value)); + EXPECT_EQUAL(iana_value, request_extension_type.iana_value); + + /* writes length */ + uint16_t length; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &length)); + EXPECT_EQUAL(length, s2n_stuffer_data_available(&stuffer)); + EXPECT_EQUAL(length, S2N_TEST_DATA_LEN); + + s2n_stuffer_free(&stuffer); + }; + + /** + * Ensure correct response extension send behavior + * + *= https://tools.ietf.org/rfc/rfc8446#section-4.2 + *= type=test + *# Implementations MUST NOT send extension responses if the remote + *# endpoint did not send the corresponding extension requests, with the + *# exception of the "cookie" extension in the HelloRetryRequest. + * + *= https://tools.ietf.org/rfc/rfc8446#4.1.4 + *= type=test + *# As with the ServerHello, a HelloRetryRequest MUST NOT contain any + *# extensions that were not first offered by the client in its + *# ClientHello, with the exception of optionally the "cookie" (see + *# Section 4.2.2) extension. + **/ + { + struct s2n_connection conn = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + s2n_stuffer_alloc(&stuffer, S2N_TEST_DATA_LEN * 2); + + s2n_extension_type response_extension_type = test_extension_type; + response_extension_type.is_response = true; + + /* Succeeds but no-op if request was not received */ + EXPECT_SUCCESS(s2n_extension_send(&response_extension_type, &conn, &stuffer)); + EXPECT_EQUAL(0, s2n_stuffer_data_available(&stuffer)); + /* cppcheck-suppress sizeofDivisionMemfunc */ + EXPECT_BITFIELD_CLEAR(conn.extension_requests_sent); + + /* Succeeds (but does not set request flag) if request was received */ + S2N_CBIT_SET(conn.extension_requests_received, test_extension_id); + EXPECT_SUCCESS(s2n_extension_send(&response_extension_type, &conn, &stuffer)); + /* cppcheck-suppress sizeofDivisionMemfunc */ + EXPECT_BITFIELD_CLEAR(conn.extension_requests_sent); + + /* writes iana_value */ + uint16_t iana_value; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &iana_value)); + EXPECT_EQUAL(iana_value, response_extension_type.iana_value); + + /* writes length */ + uint16_t length; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &length)); + EXPECT_EQUAL(length, s2n_stuffer_data_available(&stuffer)); + EXPECT_EQUAL(length, S2N_TEST_DATA_LEN); + + s2n_stuffer_free(&stuffer); + }; + + /* "should_send" returns false */ + { + struct s2n_connection conn = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + + s2n_extension_type extension_type_with_never_send = test_extension_type; + extension_type_with_never_send.should_send = s2n_extension_never_send; + + EXPECT_SUCCESS(s2n_extension_send(&extension_type_with_never_send, &conn, &stuffer)); + EXPECT_EQUAL(0, s2n_stuffer_data_available(&stuffer)); + /* cppcheck-suppress sizeofDivisionMemfunc */ + EXPECT_BITFIELD_CLEAR(conn.extension_requests_sent); + }; + + /* "send" errors */ + { + struct s2n_connection conn = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + s2n_stuffer_alloc(&stuffer, S2N_TEST_DATA_LEN); + + s2n_extension_type extension_type_with_failure = test_extension_type; + extension_type_with_failure.send = s2n_extension_send_unimplemented; + + EXPECT_FAILURE_WITH_ERRNO(s2n_extension_send(&extension_type_with_failure, &conn, &stuffer), S2N_ERR_UNIMPLEMENTED); + /* cppcheck-suppress sizeofDivisionMemfunc */ + EXPECT_BITFIELD_CLEAR(conn.extension_requests_sent); + + s2n_stuffer_free(&stuffer); + }; + + /* "send" writes more data than will fit in the extension size */ + { + struct s2n_connection conn = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + s2n_stuffer_growable_alloc(&stuffer, 0); + + s2n_extension_type extension_type_with_too_much_data = test_extension_type; + extension_type_with_too_much_data.send = test_send_too_much_data; + + EXPECT_FAILURE_WITH_ERRNO(s2n_extension_send(&extension_type_with_too_much_data, &conn, &stuffer), + S2N_ERR_SIZE_MISMATCH); + + s2n_stuffer_free(&stuffer); + }; + }; + + /* Test s2n_extension_is_missing */ + { + /* null check tests */ + { + struct s2n_connection conn = { 0 }; + + EXPECT_FAILURE(s2n_extension_is_missing(NULL, &conn)); + EXPECT_FAILURE(s2n_extension_is_missing(&test_extension_type, NULL)); + + s2n_extension_type extension_type_with_null_if_missing = test_extension_type; + extension_type_with_null_if_missing.if_missing = NULL; + EXPECT_FAILURE(s2n_extension_is_missing(&extension_type_with_null_if_missing, &conn)); + }; + + /* Test no-op if_missing */ + { + struct s2n_connection conn = { 0 }; + + s2n_extension_type extension_type_with_noop_if_missing = test_extension_type; + extension_type_with_noop_if_missing.if_missing = s2n_extension_noop_if_missing; + + extension_type_with_noop_if_missing.is_response = false; + EXPECT_SUCCESS(s2n_extension_is_missing(&extension_type_with_noop_if_missing, &conn)); + + extension_type_with_noop_if_missing.is_response = true; + EXPECT_SUCCESS(s2n_extension_is_missing(&extension_type_with_noop_if_missing, &conn)); + + S2N_CBIT_SET(conn.extension_requests_sent, test_extension_id); + EXPECT_SUCCESS(s2n_extension_is_missing(&extension_type_with_noop_if_missing, &conn)); + }; + + /* Test error if_missing */ + { + struct s2n_connection conn = { 0 }; + + s2n_extension_type extension_type_with_error_if_missing = test_extension_type; + extension_type_with_error_if_missing.if_missing = s2n_extension_error_if_missing; + + /* Should fail for a request */ + extension_type_with_error_if_missing.is_response = false; + EXPECT_FAILURE_WITH_ERRNO(s2n_extension_is_missing(&extension_type_with_error_if_missing, &conn), + S2N_ERR_MISSING_EXTENSION); + + /* Should succeed for a response without a corresponding request. + * We don't expect to receive the response, so it isn't considered missing. */ + extension_type_with_error_if_missing.is_response = true; + EXPECT_SUCCESS(s2n_extension_is_missing(&extension_type_with_error_if_missing, &conn)); + + /* Should fail for a response with a corresponding request */ + S2N_CBIT_SET(conn.extension_requests_sent, test_extension_id); + EXPECT_FAILURE_WITH_ERRNO(s2n_extension_is_missing(&extension_type_with_error_if_missing, &conn), + S2N_ERR_MISSING_EXTENSION); + }; + }; + + /* Test minimum_version field */ + { + EXPECT_SUCCESS(s2n_reset_tls13_in_test()); + + s2n_extension_type test_extension_type_with_min = test_extension_type; + test_extension_type_with_min.minimum_version = S2N_TLS13; + + /* If any of these methods actually execute, they will fail */ + test_extension_type_with_min.if_missing = s2n_extension_error_if_missing; + test_extension_type_with_min.send = s2n_extension_send_unimplemented; + test_extension_type_with_min.recv = s2n_extension_recv_unimplemented; + + struct s2n_connection conn = { 0 }; + + /* Does not meet minimum. + * No methods execute, so no errors. */ + { + conn.actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_extension_recv(&test_extension_type_with_min, &conn, NULL)); + EXPECT_SUCCESS(s2n_extension_send(&test_extension_type_with_min, &conn, NULL)); + EXPECT_SUCCESS(s2n_extension_is_missing(&test_extension_type_with_min, &conn)); + }; + + /* Meets minimum. + * All methods execute, so all errors. */ + { + conn.actual_protocol_version = S2N_TLS13; + EXPECT_FAILURE(s2n_extension_recv(&test_extension_type_with_min, &conn, NULL)); + EXPECT_FAILURE(s2n_extension_send(&test_extension_type_with_min, &conn, NULL)); + EXPECT_FAILURE(s2n_extension_is_missing(&test_extension_type_with_min, &conn)); + }; + + /* Ensure that no extension type sets nonzero minimum_version < S2N_TLS13. + * Currently, nonzero minimum_version < S2N_TLS13 will not necessarily work because earlier versions + * do not set their protocol version until after processing all extensions. + */ + { + s2n_extension_type_list *list = NULL; + const s2n_extension_type *type = NULL; + for (s2n_extension_list_id list_i = 0; list_i < S2N_EXTENSION_LIST_IDS_COUNT; list_i++) { + EXPECT_SUCCESS(s2n_extension_type_list_get(list_i, &list)); + EXPECT_NOT_NULL(list); + for (size_t ext_i = 0; ext_i < list->count; ext_i++) { + type = list->extension_types[ext_i]; + EXPECT_TRUE(type->minimum_version == 0 || type->minimum_version >= S2N_TLS13); + } + } + } + + /* Functional test: minimum-TLS1.3 extensions only used for TLS1.3 */ + { + struct s2n_cert_chain_and_key *cert_chain = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&cert_chain, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + struct s2n_config *test_all_config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(test_all_config, cert_chain)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(test_all_config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(test_all_config, "test_all")); + + uint16_t key_shares_id = s2n_extension_iana_value_to_id(TLS_EXTENSION_KEY_SHARE); + + /* Both TLS1.3 */ + if (s2n_is_tls13_fully_supported()) { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, test_all_config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, test_all_config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* All expected CLIENT_HELLO extensions sent and received */ + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, SERVER_HELLO)); + EXPECT_TRUE(S2N_CBIT_TEST(client_conn->extension_requests_sent, key_shares_id)); + EXPECT_TRUE(S2N_CBIT_TEST(server_conn->extension_requests_received, key_shares_id)); + + /* All expected SERVER_HELLO extensions received */ + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, ENCRYPTED_EXTENSIONS)); + EXPECT_TRUE(S2N_CBIT_TEST(client_conn->extension_responses_received, key_shares_id)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + } + + /* Client TLS1.2 */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, test_all_config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "test_all_tls12")); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, test_all_config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* No expected CLIENT_HELLO extensions sent and received */ + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, SERVER_HELLO)); + EXPECT_FALSE(S2N_CBIT_TEST(client_conn->extension_requests_sent, key_shares_id)); + EXPECT_FALSE(S2N_CBIT_TEST(server_conn->extension_requests_received, key_shares_id)); + + /* No expected SERVER_HELLO extensions sent and received */ + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, ENCRYPTED_EXTENSIONS)); + EXPECT_FALSE(S2N_CBIT_TEST(client_conn->extension_requests_received, key_shares_id)); + EXPECT_FALSE(S2N_CBIT_TEST(server_conn->extension_requests_sent, key_shares_id)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Client TLS 1.3 with Server TLS1.2 */ + if (s2n_is_tls13_fully_supported()) { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, test_all_config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, test_all_config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all_tls12")); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Expected CLIENT_HELLO extensions sent, but not received */ + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, SERVER_HELLO)); + EXPECT_TRUE(S2N_CBIT_TEST(client_conn->extension_requests_sent, key_shares_id)); + EXPECT_FALSE(S2N_CBIT_TEST(server_conn->extension_requests_received, key_shares_id)); + + /* No expected SERVER_HELLO extensions sent and received */ + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, ENCRYPTED_EXTENSIONS)); + EXPECT_FALSE(S2N_CBIT_TEST(client_conn->extension_requests_received, key_shares_id)); + EXPECT_FALSE(S2N_CBIT_TEST(server_conn->extension_requests_sent, key_shares_id)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + } + + EXPECT_SUCCESS(s2n_config_free(test_all_config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(cert_chain)); + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_extensions_server_key_share_select_test.c b/tests/unit/s2n_extensions_server_key_share_select_test.c new file mode 100644 index 00000000000..0e722129ac6 --- /dev/null +++ b/tests/unit/s2n_extensions_server_key_share_select_test.c @@ -0,0 +1,448 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_server_key_share.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" + +/* These tests check the server's logic when selecting a keyshare and supporting group. */ +int main() +{ + BEGIN_TEST(); + +/* Need at least two KEM's to test fallback */ +#if (S2N_SUPPORTED_KEM_GROUPS_COUNT > 1) + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* If client and server have no mutually supported groups, abort the handshake without sending HRR. */ + { + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + + EXPECT_FAILURE_WITH_ERRNO(s2n_extensions_server_key_share_select(server_conn), + S2N_ERR_ECDHE_UNSUPPORTED_CURVE); + + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_FALSE(s2n_is_hello_retry_handshake(server_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* If client has sent no valid keyshares, but server and client have a mutually supported EC curve, + * send Hello Retry Request. */ + { + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + server_conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[1]; + EXPECT_NULL(server_conn->kex_params.mutually_supported_curves[0]); + server_conn->kex_params.mutually_supported_curves[1] = ecc_pref->ecc_curves[1]; + EXPECT_NULL(server_conn->kex_params.client_ecc_evp_params.evp_pkey); + EXPECT_NULL(server_conn->kex_params.client_ecc_evp_params.negotiated_curve); + + EXPECT_SUCCESS(s2n_extensions_server_key_share_select(server_conn)); + + EXPECT_EQUAL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve, ecc_pref->ecc_curves[1]); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_TRUE(s2n_is_hello_retry_handshake(server_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* When client has only sent a valid keyshare for + * curve 1, Hello Retry Request is not sent and server chooses curve 1. */ + { + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + /* Server would have initially chosen curve[0] when processing the supported_groups extension */ + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + server_conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + server_conn->kex_params.mutually_supported_curves[0] = ecc_pref->ecc_curves[0]; + server_conn->kex_params.mutually_supported_curves[1] = ecc_pref->ecc_curves[1]; + + EXPECT_NULL(server_conn->kex_params.client_ecc_evp_params.evp_pkey); + EXPECT_NULL(server_conn->kex_params.client_ecc_evp_params.negotiated_curve); + server_conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[1]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&server_conn->kex_params.client_ecc_evp_params)); + + EXPECT_SUCCESS(s2n_extensions_server_key_share_select(server_conn)); + + /* Server should have updated it's choice to curve[1] after taking received keyshares into account */ + EXPECT_EQUAL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve, ecc_pref->ecc_curves[1]); + EXPECT_NULL(server_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_FALSE(s2n_is_hello_retry_handshake(server_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + { + const struct s2n_kem_group *test_kem_groups[] = { + &s2n_secp256r1_kyber_512_r3, + #if EVP_APIS_SUPPORTED + &s2n_x25519_kyber_512_r3, + #endif + }; + + const struct s2n_kem_preferences test_kem_pref = { + .kem_count = 0, + .kems = NULL, + .tls13_kem_group_count = s2n_array_len(test_kem_groups), + .tls13_kem_groups = test_kem_groups, + }; + + const struct s2n_security_policy test_security_policy = { + .minimum_protocol_version = S2N_SSLv3, + .cipher_preferences = &cipher_preferences_test_all_tls13, + .kem_preferences = &test_kem_pref, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20200310, + }; + + /* If both server_curve and server_kem_group are set (erroneous behavior), we should + * error and abort the handshake. */ + { + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->security_policy_override = &test_security_policy; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(server_conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + + server_conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + server_conn->kex_params.server_kem_group_params.kem_group = kem_pref->tls13_kem_groups[0]; + + EXPECT_FAILURE_WITH_ERRNO(s2n_extensions_server_key_share_select(server_conn), + S2N_ERR_ECDHE_UNSUPPORTED_CURVE); + + EXPECT_FALSE(s2n_is_hello_retry_handshake(server_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* If client has sent no valid keyshares but server and client mutually support KEM group 1, + * select KEM group 1 and send Hello Retry Request. */ + { + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->security_policy_override = &test_security_policy; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(server_conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + + /* Server would have initially chosen kem_group[1] when processing the supported_groups extension */ + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + struct s2n_kem_group_params *server_params = &server_conn->kex_params.server_kem_group_params; + const struct s2n_kem_group *kem_group1 = kem_pref->tls13_kem_groups[1]; + server_params->kem_group = kem_group1; + server_params->kem_params.kem = kem_group1->kem; + server_params->ecc_params.negotiated_curve = kem_group1->curve; + + /* 0 is not supported, 1 is */ + EXPECT_NULL(server_conn->kex_params.mutually_supported_kem_groups[0]); + server_conn->kex_params.mutually_supported_kem_groups[1] = kem_group1; + + /* No keyshares received */ + EXPECT_NULL(server_conn->kex_params.client_kem_group_params.kem_group); + EXPECT_NULL(server_conn->kex_params.client_kem_group_params.kem_params.kem); + EXPECT_NULL(server_conn->kex_params.client_kem_group_params.kem_params.public_key.data); + EXPECT_NULL(server_conn->kex_params.client_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.client_kem_group_params.ecc_params.evp_pkey); + + EXPECT_SUCCESS(s2n_extensions_server_key_share_select(server_conn)); + + /* Server maintains its selection of KEM group 1, sends HRR */ + EXPECT_EQUAL(server_params->kem_group, kem_group1); + EXPECT_EQUAL(server_params->kem_params.kem, kem_group1->kem); + EXPECT_EQUAL(server_params->ecc_params.negotiated_curve, kem_group1->curve); + EXPECT_NULL(server_conn->kex_params.client_kem_group_params.kem_group); + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_TRUE(s2n_is_hello_retry_handshake(server_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* When client has only sent a valid keyshare for + * KEM group 1, Hello Retry Request is not sent and server chooses group 1. */ + { + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->security_policy_override = &test_security_policy; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + EXPECT_OK(s2n_set_all_mutually_supported_groups(server_conn)); + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(server_conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + + /* Server would have initially chosen kem_group[0] when processing the supported_groups extension */ + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + struct s2n_kem_group_params *server_params = &server_conn->kex_params.server_kem_group_params; + const struct s2n_kem_group *kem_group0 = kem_pref->tls13_kem_groups[0]; + const struct s2n_kem_group *kem_group1 = kem_pref->tls13_kem_groups[1]; + server_params->kem_group = kem_group0; + server_params->kem_params.kem = kem_group0->kem; + server_params->ecc_params.negotiated_curve = kem_group0->curve; + + /* Received a keyshare for 1 only */ + EXPECT_NULL(server_conn->kex_params.client_kem_group_params.kem_group); + struct s2n_kem_group_params *client_params = &server_conn->kex_params.client_kem_group_params; + client_params->kem_group = kem_group1; + client_params->kem_params.kem = kem_group1->kem; + client_params->ecc_params.negotiated_curve = kem_group1->curve; + EXPECT_SUCCESS(s2n_alloc(&client_params->kem_params.public_key, kem_group1->kem->public_key_length)); + /* The PQ public key is fake; that's good enough for this test */ + POSIX_CHECKED_MEMSET(client_params->kem_params.public_key.data, 1, kem_group1->kem->public_key_length); + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_params->ecc_params)); + + EXPECT_SUCCESS(s2n_extensions_server_key_share_select(server_conn)); + + /* Server should have updated it's choice to kem_group[1] after taking received keyshares into account */ + EXPECT_EQUAL(server_params->kem_group, kem_group1); + EXPECT_EQUAL(server_params->kem_params.kem, kem_group1->kem); + EXPECT_EQUAL(server_params->ecc_params.negotiated_curve, kem_group1->curve); + EXPECT_EQUAL(server_conn->kex_params.client_kem_group_params.kem_group, kem_group1); + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_FALSE(s2n_is_hello_retry_handshake(server_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* When client has sent a valid keyshares for group 0, + * Hello Retry Request is not sent and server chooses group 0. */ + { + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->security_policy_override = &test_security_policy; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + EXPECT_OK(s2n_set_all_mutually_supported_groups(server_conn)); + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(server_conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + + /* Server would have initially chosen kem_group[0] when processing the supported_groups extension */ + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + struct s2n_kem_group_params *server_params = &server_conn->kex_params.server_kem_group_params; + const struct s2n_kem_group *kem_group0 = kem_pref->tls13_kem_groups[0]; + server_params->kem_group = kem_group0; + server_params->kem_params.kem = kem_group0->kem; + server_params->ecc_params.negotiated_curve = kem_group0->curve; + + /* Received key shares for all KEM groups */ + struct s2n_kem_group_params *client_params = &server_conn->kex_params.client_kem_group_params; + const struct s2n_kem_group *kem_group = kem_pref->tls13_kem_groups[0]; + client_params->kem_group = kem_group; + client_params->kem_params.kem = kem_group->kem; + client_params->ecc_params.negotiated_curve = kem_group->curve; + EXPECT_SUCCESS(s2n_alloc(&client_params->kem_params.public_key, kem_group->kem->public_key_length)); + /* The PQ public key is fake; that's good enough for this test */ + POSIX_CHECKED_MEMSET(client_params->kem_params.public_key.data, 1, kem_group->kem->public_key_length); + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_params->ecc_params)); + + EXPECT_SUCCESS(s2n_extensions_server_key_share_select(server_conn)); + + /* Server should still prefer kem_group[0] after taking received keyshares into account */ + EXPECT_EQUAL(server_params->kem_group, kem_group0); + EXPECT_EQUAL(server_params->kem_params.kem, kem_group0->kem); + EXPECT_EQUAL(server_params->ecc_params.negotiated_curve, kem_group0->curve); + EXPECT_EQUAL(server_conn->kex_params.client_kem_group_params.kem_group, kem_group0); + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_FALSE(s2n_is_hello_retry_handshake(server_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* When client sent no valid keyshares, + * server should choose kem_group[0] and send HRR. */ + { + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->security_policy_override = &test_security_policy; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + EXPECT_OK(s2n_set_all_mutually_supported_groups(server_conn)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(server_conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + + /* Server would have initially chosen kem_group[0] when processing the supported_groups extension */ + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + struct s2n_kem_group_params *server_params = &server_conn->kex_params.server_kem_group_params; + const struct s2n_kem_group *kem_group0 = kem_pref->tls13_kem_groups[0]; + server_params->kem_group = kem_group0; + server_params->kem_params.kem = kem_group0->kem; + server_params->ecc_params.negotiated_curve = kem_group0->curve; + + /* No keyshares received */ + EXPECT_NULL(server_conn->kex_params.client_kem_group_params.kem_group); + EXPECT_NULL(server_conn->kex_params.client_kem_group_params.kem_params.kem); + EXPECT_NULL(server_conn->kex_params.client_kem_group_params.kem_params.public_key.data); + EXPECT_NULL(server_conn->kex_params.client_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.client_kem_group_params.ecc_params.evp_pkey); + + EXPECT_SUCCESS(s2n_extensions_server_key_share_select(server_conn)); + + /* Server should still prefer kem_group[0], and send HRR */ + EXPECT_EQUAL(server_params->kem_group, kem_group0); + EXPECT_EQUAL(server_params->kem_params.kem, kem_group0->kem); + EXPECT_EQUAL(server_params->ecc_params.negotiated_curve, kem_group0->curve); + EXPECT_NULL(server_conn->kex_params.client_kem_group_params.kem_group); + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_TRUE(s2n_is_hello_retry_handshake(server_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* When client sent valid keyshares + * for everything, server should choose kem_group[0] and not send HRR. */ + { + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->security_policy_override = &test_security_policy; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + EXPECT_OK(s2n_set_all_mutually_supported_groups(server_conn)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(server_conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + + /* Server would have initially chosen kem_group[0] when processing the supported_groups extension */ + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + struct s2n_kem_group_params *server_params = &server_conn->kex_params.server_kem_group_params; + const struct s2n_kem_group *kem_group0 = kem_pref->tls13_kem_groups[0]; + server_params->kem_group = kem_group0; + server_params->kem_params.kem = kem_group0->kem; + server_params->ecc_params.negotiated_curve = kem_group0->curve; + + /* Receive highest priority KEM share */ + struct s2n_kem_group_params *client_kem_params = &server_conn->kex_params.client_kem_group_params; + const struct s2n_kem_group *kem_group = kem_pref->tls13_kem_groups[0]; + client_kem_params->kem_group = kem_group; + client_kem_params->kem_params.kem = kem_group->kem; + client_kem_params->ecc_params.negotiated_curve = kem_group->curve; + EXPECT_SUCCESS(s2n_alloc(&client_kem_params->kem_params.public_key, kem_group->kem->public_key_length)); + /* The PQ public key is fake; that's good enough for this test */ + POSIX_CHECKED_MEMSET(client_kem_params->kem_params.public_key.data, 1, kem_group->kem->public_key_length); + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_kem_params->ecc_params)); + + /* Receive highest priority ECC share */ + struct s2n_ecc_evp_params *client_params = &server_conn->kex_params.client_ecc_evp_params; + client_params->negotiated_curve = ecc_pref->ecc_curves[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(client_params)); + + EXPECT_SUCCESS(s2n_extensions_server_key_share_select(server_conn)); + + /* Server should still prefer kem_group[0], no HRR */ + EXPECT_EQUAL(server_params->kem_group, kem_group0); + EXPECT_EQUAL(server_params->kem_params.kem, kem_group0->kem); + EXPECT_EQUAL(server_params->ecc_params.negotiated_curve, kem_group0->curve); + EXPECT_EQUAL(server_conn->kex_params.client_kem_group_params.kem_group, kem_group0); + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_FALSE(s2n_is_hello_retry_handshake(server_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* When client sent valid keyshares + * only for ECC, server should choose curves[0] and not send HRR. */ + { + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->security_policy_override = &test_security_policy; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + EXPECT_OK(s2n_set_all_mutually_supported_groups(server_conn)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(server_conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + + /* Server would have initially chosen kem_group[0] when processing the supported_groups extension */ + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + struct s2n_kem_group_params *server_params = &server_conn->kex_params.server_kem_group_params; + const struct s2n_kem_group *kem_group0 = kem_pref->tls13_kem_groups[0]; + server_params->kem_group = kem_group0; + server_params->kem_params.kem = kem_group0->kem; + server_params->ecc_params.negotiated_curve = kem_group0->curve; + + /* No keyshares received */ + EXPECT_NULL(server_conn->kex_params.client_kem_group_params.kem_group); + EXPECT_NULL(server_conn->kex_params.client_kem_group_params.kem_params.kem); + EXPECT_NULL(server_conn->kex_params.client_kem_group_params.kem_params.public_key.data); + EXPECT_NULL(server_conn->kex_params.client_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.client_kem_group_params.ecc_params.evp_pkey); + + /* Highest priority keyshare chosen */ + struct s2n_ecc_evp_params *client_params = &server_conn->kex_params.client_ecc_evp_params; + client_params->negotiated_curve = ecc_pref->ecc_curves[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(client_params)); + + EXPECT_SUCCESS(s2n_extensions_server_key_share_select(server_conn)); + + /* Server should update its choice to curve[0], no HRR */ + EXPECT_EQUAL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve, ecc_pref->ecc_curves[0]); + EXPECT_NULL(server_params->kem_group); + EXPECT_NULL(server_params->kem_params.kem); + EXPECT_NULL(server_params->ecc_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.client_kem_group_params.kem_group); + EXPECT_FALSE(s2n_is_hello_retry_handshake(server_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + }; +#endif + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_fingerprint_ja3_test.c b/tests/unit/s2n_fingerprint_ja3_test.c new file mode 100644 index 00000000000..0d5a54f88e2 --- /dev/null +++ b/tests/unit/s2n_fingerprint_ja3_test.c @@ -0,0 +1,1005 @@ +/* + * 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 "api/unstable/fingerprint.h" +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "testlib/s2n_sslv2_client_hello.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_tls.h" + +/* SSLv2 == 0x0200 == 512 */ +#define S2N_JA3_SSLV2_VAL 512 + +/* clang-format off */ +#define S2N_TEST_CLIENT_HELLO_AFTER_VERSION \ + /* random */ \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \ + /* session id */ \ + 0x00 +/* clang-format on */ + +#define S2N_TEST_CLIENT_HELLO_AFTER_CIPHERS \ + /* legacy compression methods */ \ + 0x01, 0x00 + +/* This macro currently assumes that the message size is only one byte (<=255). */ +#define S2N_INIT_CLIENT_HELLO(name, ...) \ + uint8_t _##name##_message[] = { __VA_ARGS__ }; \ + EXPECT_TRUE(sizeof(_##name##_message) <= UINT8_MAX); \ + uint8_t name[] = { \ + TLS_CLIENT_HELLO, \ + 0x00, 0x00, sizeof(_##name##_message), \ + __VA_ARGS__ \ + } + +typedef enum { + S2N_CH_FROM_IO = 0, + S2N_CH_FROM_RAW, +} s2n_ch_source; + +static S2N_RESULT s2n_validate_ja3_str_list(struct s2n_stuffer *input) +{ + uint32_t skipped = 0; + size_t count = 0; + DEFER_CLEANUP(struct s2n_stuffer list = { 0 }, s2n_stuffer_free); + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&list, 0)); + RESULT_GUARD_POSIX(s2n_stuffer_read_token(input, &list, ',')); + while (s2n_stuffer_data_available(&list)) { + RESULT_GUARD_POSIX(s2n_stuffer_skip_to_char(&list, '-')); + RESULT_GUARD_POSIX(s2n_stuffer_skip_expected_char(&list, '-', + 0, 1, &skipped)); + count++; + } + RESULT_ENSURE_GT(count, 1); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_validate_ja3_str(uint8_t *ja3, uint32_t ja3_size, + const char *expected_version) +{ + struct s2n_blob input_blob = { 0 }; + struct s2n_stuffer input = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&input_blob, ja3, ja3_size)); + RESULT_GUARD_POSIX(s2n_stuffer_init(&input, &input_blob)); + RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&input, ja3_size)); + + /* Expect the provided version */ + RESULT_GUARD_POSIX(s2n_stuffer_read_expected_str(&input, expected_version)); + RESULT_GUARD_POSIX(s2n_stuffer_read_expected_str(&input, ",")); + + /* Expect at least one entry in the cipher list */ + RESULT_GUARD(s2n_validate_ja3_str_list(&input)); + + /* Expect at least one entry in the extensions list */ + RESULT_GUARD(s2n_validate_ja3_str_list(&input)); + + /* Expect at least one entry in the curves list */ + RESULT_GUARD(s2n_validate_ja3_str_list(&input)); + + /* Expect only one point format: 0 / uncompressed */ + RESULT_GUARD_POSIX(s2n_stuffer_read_expected_str(&input, "0")); + + RESULT_ENSURE_EQ(s2n_stuffer_data_available(&input), 0); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_client_hello_from_source(struct s2n_client_hello **client_hello, + struct s2n_connection *server, uint8_t *input, uint32_t input_size, s2n_ch_source source) +{ + switch (source) { + case S2N_CH_FROM_IO: + RESULT_GUARD_POSIX(s2n_connection_wipe(server)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&server->handshake.io, + input + TLS_HANDSHAKE_HEADER_LENGTH, input_size - TLS_HANDSHAKE_HEADER_LENGTH)); + RESULT_GUARD_POSIX(s2n_client_hello_recv(server)); + *client_hello = s2n_connection_get_client_hello(server); + RESULT_ENSURE_REF(*client_hello); + break; + case S2N_CH_FROM_RAW: + *client_hello = s2n_client_hello_parse_message(input, input_size); + RESULT_GUARD_PTR(*client_hello); + break; + } + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + uint8_t output_mem[500] = { 0 }; + s2n_ch_source sources[] = { + S2N_CH_FROM_IO, + S2N_CH_FROM_RAW, + }; + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + + uint8_t empty_md5_hash[MD5_DIGEST_LENGTH] = { 0 }; + DEFER_CLEANUP(struct s2n_hash_state md5_hash = { 0 }, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&md5_hash)); + if (s2n_is_in_fips_mode()) { + EXPECT_SUCCESS(s2n_hash_allow_md5_for_fips(&md5_hash)); + } + EXPECT_SUCCESS(s2n_hash_init(&md5_hash, S2N_HASH_MD5)); + EXPECT_SUCCESS(s2n_hash_digest(&md5_hash, empty_md5_hash, MD5_DIGEST_LENGTH)); + + /* Test: safety / input validation */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + struct s2n_client_hello *client_hello = &server->client_hello; + uint32_t output_size = 0, str_size = 0; + + /* Valid client hello required */ + EXPECT_FAILURE_WITH_ERRNO( + s2n_client_hello_get_fingerprint_string(NULL, S2N_FINGERPRINT_JA3, + sizeof(output_mem), output_mem, &output_size), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO( + s2n_client_hello_get_fingerprint_hash(NULL, S2N_FINGERPRINT_JA3, + sizeof(output_mem), output_mem, &output_size, &str_size), + S2N_ERR_NULL); + + /* Valid output buffer required */ + EXPECT_FAILURE_WITH_ERRNO( + s2n_client_hello_get_fingerprint_string(client_hello, S2N_FINGERPRINT_JA3, + sizeof(output_mem), NULL, &output_size), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO( + s2n_client_hello_get_fingerprint_hash(client_hello, S2N_FINGERPRINT_JA3, + sizeof(output_mem), NULL, &output_size, &str_size), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO( + s2n_client_hello_get_fingerprint_string(client_hello, S2N_FINGERPRINT_JA3, + 0, NULL, &output_size), + S2N_ERR_INSUFFICIENT_MEM_SIZE); + EXPECT_FAILURE_WITH_ERRNO( + s2n_client_hello_get_fingerprint_hash(client_hello, S2N_FINGERPRINT_JA3, + 0, NULL, &output_size, &str_size), + S2N_ERR_INSUFFICIENT_MEM_SIZE); + + /* Valid size ptr required */ + EXPECT_FAILURE_WITH_ERRNO( + s2n_client_hello_get_fingerprint_string(client_hello, S2N_FINGERPRINT_JA3, + sizeof(output_mem), output_mem, NULL), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO( + s2n_client_hello_get_fingerprint_hash(client_hello, S2N_FINGERPRINT_JA3, + sizeof(output_mem), output_mem, NULL, &str_size), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO( + s2n_client_hello_get_fingerprint_hash(client_hello, S2N_FINGERPRINT_JA3, + sizeof(output_mem), output_mem, &output_size, NULL), + S2N_ERR_NULL); + + /* Only JA3 currently supported */ + EXPECT_FAILURE_WITH_ERRNO( + s2n_client_hello_get_fingerprint_string(client_hello, S2N_FINGERPRINT_JA3 + 1, + sizeof(output_mem), output_mem, &output_size), + S2N_ERR_INVALID_ARGUMENT); + EXPECT_FAILURE_WITH_ERRNO( + s2n_client_hello_get_fingerprint_hash(client_hello, S2N_FINGERPRINT_JA3 + 1, + sizeof(output_mem), output_mem, &output_size, &str_size), + S2N_ERR_INVALID_ARGUMENT); + }; + + /* Test: ja3 string */ + { + /* Test: basic case */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client->handshake.io, &server->handshake.io, + s2n_stuffer_data_available(&client->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server)); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server); + EXPECT_NOT_NULL(client_hello); + EXPECT_FALSE(client_hello->sslv2); + + uint32_t output_size = 0; + EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_string(client_hello, + S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size)); + + /* Expect valid ja3. + * TLS1.2 == 0x0303 == 771 */ + EXPECT_OK(s2n_validate_ja3_str(output_mem, output_size, "771")); + }; + + /* Test: SSLv2 not supported */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server, "test_all")); + + /* SSLv2 ClientHellos are used by clients as a backwards-compatible attempt + * to communicate with servers that don't support later versions. + * s2n-tls DOES support later versions, so requires that SSLv2 ClientHellos + * advertise a higher version. + * + * This version negotiation is done when processing the record, + * not when processing the actual ClientHello message. + * So we need to set the versions manually. + */ + server->client_hello_version = S2N_SSLv2; + server->client_protocol_version = S2N_TLS12; + + uint8_t sslv2_client_hello[] = { + SSLv2_CLIENT_HELLO_PREFIX, + SSLv2_CLIENT_HELLO_CIPHER_SUITES, + SSLv2_CLIENT_HELLO_CHALLENGE, + }; + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&server->handshake.io, + sslv2_client_hello, sizeof(sslv2_client_hello))); + EXPECT_SUCCESS(s2n_client_hello_recv(server)); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server); + EXPECT_NOT_NULL(client_hello); + EXPECT_TRUE(client_hello->sslv2); + + uint32_t output_size = 0; + EXPECT_FAILURE_WITH_ERRNO( + s2n_client_hello_get_fingerprint_string(client_hello, + S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size), + S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + EXPECT_EQUAL(output_size, 0); + }; + + /* Test: single entry lists */ + for (size_t i = 0; i < s2n_array_len(sources); i++) { + s2n_ch_source source = sources[i]; + + S2N_INIT_CLIENT_HELLO(client_hello_bytes, + /* protocol version */ + 0x03, 0x02, + S2N_TEST_CLIENT_HELLO_AFTER_VERSION, + /* cipher suites size */ + 0x00, 0x02, + /* cipher suites */ + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + S2N_TEST_CLIENT_HELLO_AFTER_CIPHERS, + /* extensions size */ + 0x00, 0x08, + /* extension: supported groups */ + 0x00, TLS_EXTENSION_SUPPORTED_GROUPS, 0x00, 0x04, + 0x00, 0x02, 0x00, TLS_EC_CURVE_SECP_256_R1); + const uint8_t expected_ja3[] = "770,49199,10,23,"; + size_t expected_ja3_size = strlen((const char *) expected_ja3); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server, "test_all")); + + DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL, + s2n_client_hello_free); + EXPECT_OK(s2n_client_hello_from_source(&client_hello, server, + client_hello_bytes, sizeof(client_hello_bytes), source)); + + uint32_t output_size = 0; + EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_string(client_hello, + S2N_FINGERPRINT_JA3, expected_ja3_size, output_mem, &output_size)); + EXPECT_EQUAL(output_size, expected_ja3_size); + EXPECT_BYTEARRAY_EQUAL(expected_ja3, output_mem, output_size); + }; + + /* Test: missing fields + * + * We have to provide a protocol version and one cipher suite + * in order for the ClientHello to be considered valid, but all + * other fields can be empty. + */ + for (size_t i = 0; i < s2n_array_len(sources); i++) { + s2n_ch_source source = sources[i]; + + S2N_INIT_CLIENT_HELLO(client_hello_bytes, + /* protocol version */ + 0x03, 0x01, + S2N_TEST_CLIENT_HELLO_AFTER_VERSION, + /* cipher suites size */ + 0x00, 0x02, + /* cipher suites */ + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + S2N_TEST_CLIENT_HELLO_AFTER_CIPHERS, + /* extensions size */ + 0x00, 0x00); + const uint8_t expected_ja3[] = "769,49199,,,"; + size_t expected_ja3_size = strlen((const char *) expected_ja3); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server, "test_all")); + + DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL, + s2n_client_hello_free); + EXPECT_OK(s2n_client_hello_from_source(&client_hello, server, + client_hello_bytes, sizeof(client_hello_bytes), source)); + + uint32_t output_size = 0; + EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_string(client_hello, + S2N_FINGERPRINT_JA3, expected_ja3_size, output_mem, &output_size)); + EXPECT_EQUAL(output_size, expected_ja3_size); + EXPECT_BYTEARRAY_EQUAL(expected_ja3, output_mem, output_size); + }; + + /* Test: fails if insufficient memory */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client->handshake.io, &server->handshake.io, + s2n_stuffer_data_available(&client->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server)); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server); + EXPECT_NOT_NULL(client_hello); + + uint32_t output_size = 0; + for (size_t i = 0; i < 10; i++) { + EXPECT_FAILURE_WITH_ERRNO( + s2n_client_hello_get_fingerprint_string(client_hello, + S2N_FINGERPRINT_JA3, i, output_mem, &output_size), + S2N_ERR_INSUFFICIENT_MEM_SIZE); + EXPECT_EQUAL(output_size, 0); + } + }; + + /* Test: grease values ignored */ + for (size_t i = 0; i < s2n_array_len(sources); i++) { + s2n_ch_source source = sources[i]; + + const uint8_t grease_value = 0x0A; + S2N_INIT_CLIENT_HELLO(client_hello_bytes, + /* protocol version */ + 0x03, 0x02, + S2N_TEST_CLIENT_HELLO_AFTER_VERSION, + /* cipher suites size */ + 0x00, 0x04, + /* cipher suites */ + TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256, + grease_value, grease_value, + S2N_TEST_CLIENT_HELLO_AFTER_CIPHERS, + /* extensions size */ + 0x00, 0x0E, + /* extension: grease */ + grease_value, grease_value, 0x00, 0x00, + /* extension: supported groups */ + 0x00, TLS_EXTENSION_SUPPORTED_GROUPS, 0x00, 0x06, + 0x00, 0x04, 0x00, TLS_EC_CURVE_SECP_256_R1, grease_value, grease_value); + const uint8_t expected_ja3[] = "770,49199,10,23,"; + size_t expected_ja3_size = strlen((const char *) expected_ja3); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server, "test_all")); + + DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL, + s2n_client_hello_free); + EXPECT_OK(s2n_client_hello_from_source(&client_hello, server, + client_hello_bytes, sizeof(client_hello_bytes), source)); + + uint32_t output_size = 0; + EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_string(client_hello, + S2N_FINGERPRINT_JA3, expected_ja3_size, output_mem, &output_size)); + EXPECT_EQUAL(output_size, expected_ja3_size); + EXPECT_BYTEARRAY_EQUAL(expected_ja3, output_mem, output_size); + }; + }; + + /* Test: ja3 hash */ + { + /* Test: basic case */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client->handshake.io, &server->handshake.io, + s2n_stuffer_data_available(&client->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server)); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server); + EXPECT_NOT_NULL(client_hello); + + /* Assert reasonable result */ + uint32_t output_size = 0, str_size = 0; + EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_hash(client_hello, + S2N_FINGERPRINT_JA3, MD5_DIGEST_LENGTH, output_mem, &output_size, &str_size)); + EXPECT_TRUE(str_size > MD5_DIGEST_LENGTH); + EXPECT_EQUAL(output_size, MD5_DIGEST_LENGTH); + EXPECT_BYTEARRAY_NOT_EQUAL(empty_md5_hash, output_mem, output_size); + + /* Assert same result when given same inputs again */ + uint8_t output_mem_2[MD5_DIGEST_LENGTH] = { 0 }; + uint32_t output_size_2 = 0, str_size_2 = 0; + EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_hash(client_hello, + S2N_FINGERPRINT_JA3, MD5_DIGEST_LENGTH, output_mem_2, + &output_size_2, &str_size_2)); + EXPECT_EQUAL(str_size, str_size_2); + EXPECT_EQUAL(output_size, output_size_2); + EXPECT_BYTEARRAY_EQUAL(output_mem, output_mem_2, output_size_2); + + /* Assert length for full ja3 string is correct */ + EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_string(client_hello, + S2N_FINGERPRINT_JA3, str_size, output_mem, &output_size)); + EXPECT_FAILURE_WITH_ERRNO( + s2n_client_hello_get_fingerprint_string(client_hello, + S2N_FINGERPRINT_JA3, str_size - 1, output_mem, &output_size), + S2N_ERR_INSUFFICIENT_MEM_SIZE); + }; + + /* Test: fails if insufficient memory */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client->handshake.io, &server->handshake.io, + s2n_stuffer_data_available(&client->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server)); + + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(server); + EXPECT_NOT_NULL(client_hello); + + uint32_t output_size = 0, str_size = 0; + for (size_t i = 0; i < MD5_DIGEST_LENGTH; i++) { + EXPECT_FAILURE_WITH_ERRNO( + s2n_client_hello_get_fingerprint_hash(client_hello, + S2N_FINGERPRINT_JA3, i, output_mem, &output_size, &str_size), + S2N_ERR_INSUFFICIENT_MEM_SIZE); + } + EXPECT_EQUAL(output_size, 0); + }; + }; + + /* Test: Known values + * + * No definitive source exists for JA3 test vectors. + * We sample some test values used by other implementations. + */ + for (size_t i = 0; i < s2n_array_len(sources); i++) { + s2n_ch_source source = sources[i]; + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + /* Known value from Java implementation: + * https://github.com/lafaspot/ja3_4java/blob/d605ea2b51c1024eb9056568aac68c2d26011c4f/src/test/resources/openssl-ssl3.bin + */ + { + uint8_t raw_client_hello[135] = { + 0x01, 0x00, 0x00, 0x83, + 0x03, 0x00, 0x54, 0x3D, 0xD2, 0xA9, 0xB2, 0xD7, 0x59, 0xF7, 0xC4, + 0xCF, 0x64, 0x30, 0xEB, 0xCC, 0xF7, 0x36, 0x58, 0x9B, 0x78, 0xB8, + 0x9D, 0xB5, 0x0D, 0x59, 0xAF, 0x82, 0xA6, 0xC0, 0xAC, 0xFB, 0xA0, + 0xB1, 0x00, 0x00, 0x5C, 0xC0, 0x14, 0xC0, 0x0A, 0x00, 0x39, 0x00, + 0x38, 0x00, 0x88, 0x00, 0x87, 0xC0, 0x0F, 0xC0, 0x05, 0x00, 0x35, + 0x00, 0x84, 0xC0, 0x12, 0xC0, 0x08, 0x00, 0x16, 0x00, 0x13, 0xC0, + 0x0D, 0xC0, 0x03, 0x00, 0x0A, 0xC0, 0x13, 0xC0, 0x09, 0x00, 0x33, + 0x00, 0x32, 0x00, 0x9A, 0x00, 0x99, 0x00, 0x45, 0x00, 0x44, 0xC0, + 0x0E, 0xC0, 0x04, 0x00, 0x2F, 0x00, 0x96, 0x00, 0x41, 0x00, 0x07, + 0xC0, 0x11, 0xC0, 0x07, 0xC0, 0x0C, 0xC0, 0x02, 0x00, 0x05, 0x00, + 0x04, 0x00, 0x15, 0x00, 0x12, 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, + 0x00, 0x08, 0x00, 0x06, 0x00, 0x03, 0x00, 0xFF, 0x01, 0x00 + }; + const char expected_ja3[] = "768,49172-49162-57-56-136-135-49167-" + "49157-53-132-49170-49160-22-19-49165-49155-" + "10-49171-49161-51-50-154-153-69-68-49166-" + "49156-47-150-65-7-49169-49159-49164-49154-" + "5-4-21-18-9-20-17-8-6-3-255,,,"; + + DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL, + s2n_client_hello_free); + EXPECT_OK(s2n_client_hello_from_source(&client_hello, server, + raw_client_hello, sizeof(raw_client_hello), source)); + + uint32_t output_size = 0; + EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_string(client_hello, + S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size)); + EXPECT_EQUAL(strlen(expected_ja3), output_size); + EXPECT_BYTEARRAY_EQUAL(expected_ja3, output_mem, output_size); + }; + + /* Known value from Java implementation: + * https://github.com/lafaspot/ja3_4java/blob/d605ea2b51c1024eb9056568aac68c2d26011c4f/src/test/resources/openssl-ssl3.bin + */ + { + uint8_t raw_client_hello[164] = { + 0x01, 0x00, 0x00, 0xA0, + 0x03, 0x01, 0x54, 0x3D, 0xD2, 0xDD, 0x48, 0xF5, 0x17, 0xCA, 0x9A, + 0x93, 0xB1, 0xE5, 0x99, 0xF0, 0x19, 0xFD, 0xEC, 0xE7, 0x04, 0xA2, + 0x3E, 0x86, 0xC1, 0xDC, 0xAC, 0x58, 0x84, 0x27, 0xAB, 0xBA, 0xDD, + 0xF2, 0x00, 0x00, 0x5C, 0xC0, 0x14, 0xC0, 0x0A, 0x00, 0x39, 0x00, + 0x38, 0x00, 0x88, 0x00, 0x87, 0xC0, 0x0F, 0xC0, 0x05, 0x00, 0x35, + 0x00, 0x84, 0xC0, 0x12, 0xC0, 0x08, 0x00, 0x16, 0x00, 0x13, 0xC0, + 0x0D, 0xC0, 0x03, 0x00, 0x0A, 0xC0, 0x13, 0xC0, 0x09, 0x00, 0x33, + 0x00, 0x32, 0x00, 0x9A, 0x00, 0x99, 0x00, 0x45, 0x00, 0x44, 0xC0, + 0x0E, 0xC0, 0x04, 0x00, 0x2F, 0x00, 0x96, 0x00, 0x41, 0x00, 0x07, + 0xC0, 0x11, 0xC0, 0x07, 0xC0, 0x0C, 0xC0, 0x02, 0x00, 0x05, 0x00, + 0x04, 0x00, 0x15, 0x00, 0x12, 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, + 0x00, 0x08, 0x00, 0x06, 0x00, 0x03, 0x00, 0xFF, 0x01, 0x00, 0x00, + 0x1B, 0x00, 0x0B, 0x00, 0x04, 0x03, 0x00, 0x01, 0x02, 0x00, 0x0A, + 0x00, 0x06, 0x00, 0x04, 0x00, 0x18, 0x00, 0x17, 0x00, 0x23, 0x00, + 0x00, 0x00, 0x0F, 0x00, 0x01, 0x01 + }; + const char expected_ja3[] = "769,49172-49162-57-56-136-135-49167-" + "49157-53-132-49170-49160-22-19-49165-49155-" + "10-49171-49161-51-50-154-153-69-68-49166-" + "49156-47-150-65-7-49169-49159-49164-49154-" + "5-4-21-18-9-20-17-8-6-3-255,11-10-35-15," + "24-23,0-1-2"; + + DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL, + s2n_client_hello_free); + EXPECT_OK(s2n_client_hello_from_source(&client_hello, server, + raw_client_hello, sizeof(raw_client_hello), source)); + + uint32_t output_size = 0; + EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_string(client_hello, + S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size)); + EXPECT_EQUAL(strlen(expected_ja3), output_size); + EXPECT_BYTEARRAY_EQUAL(expected_ja3, output_mem, output_size); + }; + + /* Known value from Java implementation: + * https://github.com/lafaspot/ja3_4java/blob/d605ea2b51c1024eb9056568aac68c2d26011c4f/src/test/resources/openssl-tls1_1.bin + */ + { + uint8_t raw_client_hello[164] = { + 0x01, 0x00, 0x00, 0xA0, + 0x03, 0x02, 0x54, 0x3D, 0xD2, 0xED, 0x90, 0x7E, 0x47, 0xD0, 0x08, + 0x6F, 0x34, 0xBE, 0xE2, 0xC5, 0x2D, 0xD6, 0xCC, 0xD8, 0xDE, 0x63, + 0xBA, 0x93, 0x87, 0xF5, 0xE8, 0x10, 0xB0, 0x9D, 0x9D, 0x49, 0xB3, + 0x80, 0x00, 0x00, 0x5C, 0xC0, 0x14, 0xC0, 0x0A, 0x00, 0x39, 0x00, + 0x38, 0x00, 0x88, 0x00, 0x87, 0xC0, 0x0F, 0xC0, 0x05, 0x00, 0x35, + 0x00, 0x84, 0xC0, 0x12, 0xC0, 0x08, 0x00, 0x16, 0x00, 0x13, 0xC0, + 0x0D, 0xC0, 0x03, 0x00, 0x0A, 0xC0, 0x13, 0xC0, 0x09, 0x00, 0x33, + 0x00, 0x32, 0x00, 0x9A, 0x00, 0x99, 0x00, 0x45, 0x00, 0x44, 0xC0, + 0x0E, 0xC0, 0x04, 0x00, 0x2F, 0x00, 0x96, 0x00, 0x41, 0x00, 0x07, + 0xC0, 0x11, 0xC0, 0x07, 0xC0, 0x0C, 0xC0, 0x02, 0x00, 0x05, 0x00, + 0x04, 0x00, 0x15, 0x00, 0x12, 0x00, 0x09, 0x00, 0x14, 0x00, 0x11, + 0x00, 0x08, 0x00, 0x06, 0x00, 0x03, 0x00, 0xFF, 0x01, 0x00, 0x00, + 0x1B, 0x00, 0x0B, 0x00, 0x04, 0x03, 0x00, 0x01, 0x02, 0x00, 0x0A, + 0x00, 0x06, 0x00, 0x04, 0x00, 0x18, 0x00, 0x17, 0x00, 0x23, 0x00, + 0x00, 0x00, 0x0F, 0x00, 0x01, 0x01 + }; + const char expected_ja3[] = "770,49172-49162-57-56-136-135-49167-" + "49157-53-132-49170-49160-22-19-49165-49155-" + "10-49171-49161-51-50-154-153-69-68-49166-" + "49156-47-150-65-7-49169-49159-49164-49154-" + "5-4-21-18-9-20-17-8-6-3-255,11-10-35-15," + "24-23,0-1-2"; + + DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL, + s2n_client_hello_free); + EXPECT_OK(s2n_client_hello_from_source(&client_hello, server, + raw_client_hello, sizeof(raw_client_hello), source)); + + uint32_t output_size = 0; + EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_string(client_hello, + S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size)); + EXPECT_EQUAL(strlen(expected_ja3), output_size); + EXPECT_BYTEARRAY_EQUAL(expected_ja3, output_mem, output_size); + }; + + /* Known value from Java implementation: + * https://github.com/lafaspot/ja3_4java/blob/d605ea2b51c1024eb9056568aac68c2d26011c4f/src/test/resources/openssl-tls1_2.bin + */ + { + uint8_t raw_client_hello[258] = { + 0x01, 0x00, 0x00, 0xFE, + 0x03, 0x03, 0x54, 0x3D, 0xD3, 0x28, 0x32, 0x83, 0x69, 0x2D, 0x85, + 0xF9, 0x41, 0x6B, 0x5C, 0xCC, 0x65, 0xD2, 0xAA, 0xFC, 0xA4, 0x5C, + 0x65, 0x30, 0xB3, 0xC6, 0xEA, 0xFB, 0xF6, 0xD3, 0x71, 0xB6, 0xA0, + 0x15, 0x00, 0x00, 0x94, 0xC0, 0x30, 0xC0, 0x2C, 0xC0, 0x28, 0xC0, + 0x24, 0xC0, 0x14, 0xC0, 0x0A, 0x00, 0xA3, 0x00, 0x9F, 0x00, 0x6B, + 0x00, 0x6A, 0x00, 0x39, 0x00, 0x38, 0x00, 0x88, 0x00, 0x87, 0xC0, + 0x32, 0xC0, 0x2E, 0xC0, 0x2A, 0xC0, 0x26, 0xC0, 0x0F, 0xC0, 0x05, + 0x00, 0x9D, 0x00, 0x3D, 0x00, 0x35, 0x00, 0x84, 0xC0, 0x12, 0xC0, + 0x08, 0x00, 0x16, 0x00, 0x13, 0xC0, 0x0D, 0xC0, 0x03, 0x00, 0x0A, + 0xC0, 0x2F, 0xC0, 0x2B, 0xC0, 0x27, 0xC0, 0x23, 0xC0, 0x13, 0xC0, + 0x09, 0x00, 0xA2, 0x00, 0x9E, 0x00, 0x67, 0x00, 0x40, 0x00, 0x33, + 0x00, 0x32, 0x00, 0x9A, 0x00, 0x99, 0x00, 0x45, 0x00, 0x44, 0xC0, + 0x31, 0xC0, 0x2D, 0xC0, 0x29, 0xC0, 0x25, 0xC0, 0x0E, 0xC0, 0x04, + 0x00, 0x9C, 0x00, 0x3C, 0x00, 0x2F, 0x00, 0x96, 0x00, 0x41, 0x00, + 0x07, 0xC0, 0x11, 0xC0, 0x07, 0xC0, 0x0C, 0xC0, 0x02, 0x00, 0x05, + 0x00, 0x04, 0x00, 0x15, 0x00, 0x12, 0x00, 0x09, 0x00, 0x14, 0x00, + 0x11, 0x00, 0x08, 0x00, 0x06, 0x00, 0x03, 0x00, 0xFF, 0x01, 0x00, + 0x00, 0x41, 0x00, 0x0B, 0x00, 0x04, 0x03, 0x00, 0x01, 0x02, 0x00, + 0x0A, 0x00, 0x06, 0x00, 0x04, 0x00, 0x18, 0x00, 0x17, 0x00, 0x23, + 0x00, 0x00, 0x00, 0x0D, 0x00, 0x22, 0x00, 0x20, 0x06, 0x01, 0x06, + 0x02, 0x06, 0x03, 0x05, 0x01, 0x05, 0x02, 0x05, 0x03, 0x04, 0x01, + 0x04, 0x02, 0x04, 0x03, 0x03, 0x01, 0x03, 0x02, 0x03, 0x03, 0x02, + 0x01, 0x02, 0x02, 0x02, 0x03, 0x01, 0x01, 0x00, 0x0F, 0x00, 0x01, + 0x01 + }; + const char expected_ja3[] = "771,49200-49196-49192-49188-49172-49162-" + "163-159-107-106-57-56-136-135-49202-49198-" + "49194-49190-49167-49157-157-61-53-132-" + "49170-49160-22-19-49165-49155-10-49199-" + "49195-49191-49187-49171-49161-162-158-103-" + "64-51-50-154-153-69-68-49201-49197-49193-" + "49189-49166-49156-156-60-47-150-65-7-" + "49169-49159-49164-49154-5-4-21-18-9-20-" + "17-8-6-3-255,11-10-35-13-15,24-23,0-1-2"; + + DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL, + s2n_client_hello_free); + EXPECT_OK(s2n_client_hello_from_source(&client_hello, server, + raw_client_hello, sizeof(raw_client_hello), source)); + + uint32_t output_size = 0; + EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_string(client_hello, + S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size)); + EXPECT_EQUAL(strlen(expected_ja3), output_size); + EXPECT_BYTEARRAY_EQUAL(expected_ja3, output_mem, output_size); + }; + + /* Known values from Rust implementation: + * https://github.com/jabedude/ja3-rs/blob/4f2629b86ce3496b4614296f754954806c9c849c/tests/chrome-grease-single.pcap + */ + { + uint8_t raw_client_hello[512] = { + 0x01, 0x00, 0x01, 0xFC, + 0x03, 0x03, 0x86, 0xad, 0xa4, 0xcc, 0x19, 0xe7, 0x14, 0x54, 0x54, + 0xfd, 0xe7, 0x37, 0x33, 0xdf, 0x66, 0xcb, 0xf6, 0xef, 0x3e, 0xc0, + 0xa1, 0x54, 0xc6, 0xdd, 0x14, 0x5e, 0xc0, 0x83, 0xac, 0xb9, 0xb4, + 0xe7, 0x20, 0x1c, 0x64, 0xae, 0xa7, 0xa2, 0xc3, 0xe1, 0x8c, 0xd1, + 0x25, 0x02, 0x4d, 0xf7, 0x86, 0x4a, 0xc7, 0x19, 0xd0, 0xc4, 0xbd, + 0xfb, 0x40, 0xc2, 0xef, 0x7f, 0x6d, 0xd3, 0x9a, 0xa7, 0x53, 0xdf, + 0xdd, 0x00, 0x22, 0x1a, 0x1a, 0x13, 0x01, 0x13, 0x02, 0x13, 0x03, + 0xc0, 0x2b, 0xc0, 0x2f, 0xc0, 0x2c, 0xc0, 0x30, 0xcc, 0xa9, 0xcc, + 0xa8, 0xc0, 0x13, 0xc0, 0x14, 0x00, 0x9c, 0x00, 0x9d, 0x00, 0x2f, + 0x00, 0x35, 0x00, 0x0a, 0x01, 0x00, 0x01, 0x91, 0x0a, 0x0a, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x20, 0x00, 0x1e, 0x00, 0x00, 0x1b, 0x67, + 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x61, 0x64, 0x73, 0x2e, 0x67, 0x2e, + 0x64, 0x6f, 0x75, 0x62, 0x6c, 0x65, 0x63, 0x6c, 0x69, 0x63, 0x6b, + 0x2e, 0x6e, 0x65, 0x74, 0x00, 0x17, 0x00, 0x00, 0xff, 0x01, 0x00, + 0x01, 0x00, 0x00, 0x0a, 0x00, 0x0a, 0x00, 0x08, 0x9a, 0x9a, 0x00, + 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, + 0x00, 0x23, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x0c, 0x02, + 0x68, 0x32, 0x08, 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31, + 0x00, 0x05, 0x00, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, + 0x00, 0x14, 0x00, 0x12, 0x04, 0x03, 0x08, 0x04, 0x04, 0x01, 0x05, + 0x03, 0x08, 0x05, 0x05, 0x01, 0x08, 0x06, 0x06, 0x01, 0x02, 0x01, + 0x00, 0x12, 0x00, 0x00, 0x00, 0x33, 0x00, 0x2b, 0x00, 0x29, 0x9a, + 0x9a, 0x00, 0x01, 0x00, 0x00, 0x1d, 0x00, 0x20, 0x59, 0x08, 0x6f, + 0x41, 0x9a, 0xa5, 0xaa, 0x1d, 0x81, 0xe3, 0x47, 0xf0, 0x25, 0x5f, + 0x92, 0x07, 0xfc, 0x4b, 0x13, 0x74, 0x51, 0x46, 0x98, 0x08, 0x74, + 0x3b, 0xde, 0x57, 0x86, 0xe8, 0x2c, 0x74, 0x00, 0x2d, 0x00, 0x02, + 0x01, 0x01, 0x00, 0x2b, 0x00, 0x0b, 0x0a, 0xfa, 0xfa, 0x03, 0x04, + 0x03, 0x03, 0x03, 0x02, 0x03, 0x01, 0x00, 0x1b, 0x00, 0x03, 0x02, + 0x00, 0x02, 0xba, 0xba, 0x00, 0x01, 0x00, 0x00, 0x15, 0x00, 0xbd, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 + }; + const char expected_ja3[] = "771,4865-4866-4867-49195-49199-49196-" + "49200-52393-52392-49171-49172-156-157-47-" + "53-10,0-23-65281-10-11-35-16-5-13-18-51-" + "45-43-27-21,29-23-24,0"; + S2N_BLOB_FROM_HEX(expected_hash, "66918128f1b9b03303d77c6f2eefd128"); + + DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL, + s2n_client_hello_free); + EXPECT_OK(s2n_client_hello_from_source(&client_hello, server, + raw_client_hello, sizeof(raw_client_hello), source)); + + uint32_t output_size = 0; + EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_string(client_hello, + S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size)); + EXPECT_EQUAL(strlen(expected_ja3), output_size); + EXPECT_BYTEARRAY_EQUAL(expected_ja3, output_mem, output_size); + + uint32_t str_size = 0; + EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_hash(client_hello, + S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size, + &str_size)); + EXPECT_EQUAL(strlen(expected_ja3), str_size); + EXPECT_EQUAL(expected_hash.size, output_size); + EXPECT_BYTEARRAY_EQUAL(expected_hash.data, output_mem, output_size); + }; + + /* Known values from Rust implementation: + * https://github.com/jabedude/ja3-rs/blob/4f2629b86ce3496b4614296f754954806c9c849c/tests/curl-ipv6.pcap + */ + { + uint8_t raw_client_hello[512] = { + 0x01, 0x00, 0x01, 0xFC, + 0x03, 0x03, 0x40, 0xc7, 0x8a, 0xef, 0x5c, 0x7f, 0xed, 0x98, 0x4a, + 0x19, 0x8a, 0x03, 0x0b, 0xc0, 0x2d, 0xc0, 0xd6, 0x8f, 0x0b, 0x14, + 0x7d, 0x23, 0x3d, 0x90, 0xb4, 0x2b, 0x4b, 0x28, 0x2c, 0x44, 0x0c, + 0x4d, 0x20, 0xf4, 0x73, 0x04, 0xed, 0x17, 0x42, 0xd6, 0xb5, 0x08, + 0x2e, 0x73, 0x78, 0x71, 0x25, 0x52, 0x5c, 0xea, 0xe7, 0xd7, 0xe5, + 0x7c, 0xfa, 0x27, 0xfe, 0xa3, 0x52, 0x63, 0x7a, 0x27, 0xc3, 0x5d, + 0x58, 0x00, 0x3e, 0x13, 0x02, 0x13, 0x03, 0x13, 0x01, 0xc0, 0x2c, + 0xc0, 0x30, 0x00, 0x9f, 0xcc, 0xa9, 0xcc, 0xa8, 0xcc, 0xaa, 0xc0, + 0x2b, 0xc0, 0x2f, 0x00, 0x9e, 0xc0, 0x24, 0xc0, 0x28, 0x00, 0x6b, + 0xc0, 0x23, 0xc0, 0x27, 0x00, 0x67, 0xc0, 0x0a, 0xc0, 0x14, 0x00, + 0x39, 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x33, 0x00, 0x9d, 0x00, 0x9c, + 0x00, 0x3d, 0x00, 0x3c, 0x00, 0x35, 0x00, 0x2f, 0x00, 0xff, 0x01, + 0x00, 0x01, 0x75, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, 0x00, 0x00, + 0x0b, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x2e, 0x63, 0x6f, + 0x6d, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, 0x01, 0x02, 0x00, 0x0a, + 0x00, 0x0c, 0x00, 0x0a, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x1e, 0x00, + 0x19, 0x00, 0x18, 0x33, 0x74, 0x00, 0x00, 0x00, 0x10, 0x00, 0x0e, + 0x00, 0x0c, 0x02, 0x68, 0x32, 0x08, 0x68, 0x74, 0x74, 0x70, 0x2f, + 0x31, 0x2e, 0x31, 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, + 0x00, 0x0d, 0x00, 0x30, 0x00, 0x2e, 0x04, 0x03, 0x05, 0x03, 0x06, + 0x03, 0x08, 0x07, 0x08, 0x08, 0x08, 0x09, 0x08, 0x0a, 0x08, 0x0b, + 0x08, 0x04, 0x08, 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, + 0x01, 0x03, 0x03, 0x02, 0x03, 0x03, 0x01, 0x02, 0x01, 0x03, 0x02, + 0x02, 0x02, 0x04, 0x02, 0x05, 0x02, 0x06, 0x02, 0x00, 0x2b, 0x00, + 0x09, 0x08, 0x03, 0x04, 0x03, 0x03, 0x03, 0x02, 0x03, 0x01, 0x00, + 0x2d, 0x00, 0x02, 0x01, 0x01, 0x00, 0x33, 0x00, 0x26, 0x00, 0x24, + 0x00, 0x1d, 0x00, 0x20, 0x29, 0x90, 0xc2, 0xec, 0x21, 0x68, 0x2c, + 0x5a, 0x7a, 0x5a, 0x46, 0x49, 0x59, 0x42, 0x54, 0x66, 0x02, 0x92, + 0x0c, 0x08, 0x16, 0x59, 0xf6, 0xcc, 0x75, 0xb8, 0x16, 0x53, 0x20, + 0x46, 0x79, 0x23, 0x00, 0x15, 0x00, 0xb6, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 + }; + const char expected_ja3[] = "771,4866-4867-4865-49196-49200-159-52393-" + "52392-52394-49195-49199-158-49188-49192-" + "107-49187-49191-103-49162-49172-57-49161-" + "49171-51-157-156-61-60-53-47-255,0-11-" + "10-13172-16-22-23-13-43-45-51-21,29-23-" + "30-25-24,0-1-2"; + S2N_BLOB_FROM_HEX(expected_hash, "456523fc94726331a4d5a2e1d40b2cd7"); + + DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL, + s2n_client_hello_free); + EXPECT_OK(s2n_client_hello_from_source(&client_hello, server, + raw_client_hello, sizeof(raw_client_hello), source)); + + uint32_t output_size = 0; + EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_string(client_hello, + S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size)); + EXPECT_EQUAL(strlen(expected_ja3), output_size); + EXPECT_BYTEARRAY_EQUAL(expected_ja3, output_mem, output_size); + + uint32_t str_size = 0; + EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_hash(client_hello, + S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size, + &str_size)); + EXPECT_EQUAL(strlen(expected_ja3), str_size); + EXPECT_EQUAL(expected_hash.size, output_size); + EXPECT_BYTEARRAY_EQUAL(expected_hash.data, output_mem, output_size); + }; + + /* Known values from Rust implementation: + * https://github.com/jabedude/ja3-rs/blob/4f2629b86ce3496b4614296f754954806c9c849c/tests/test.pcap + */ + { + uint8_t raw_client_hello[240] = { + 0x01, 0x00, 0x00, 0xEC, + 0x03, 0x03, 0x90, 0xe8, 0xcc, 0xee, 0xe5, 0x70, 0xa2, 0xa1, 0x2f, + 0x6b, 0x69, 0xd2, 0x66, 0x96, 0x0f, 0xcf, 0x20, 0xd5, 0x32, 0x6e, + 0xc4, 0xb2, 0x8c, 0xc7, 0xbd, 0x0a, 0x06, 0xc2, 0xa5, 0x14, 0xfc, + 0x34, 0x20, 0xaf, 0x72, 0xbf, 0x39, 0x99, 0xfb, 0x20, 0x70, 0xc3, + 0x10, 0x83, 0x0c, 0xee, 0xfb, 0xfa, 0x72, 0xcc, 0x5d, 0xa8, 0x99, + 0xb4, 0xc5, 0x53, 0xd6, 0x3d, 0xa0, 0x53, 0x7a, 0x5c, 0xbc, 0xf5, + 0x0b, 0x00, 0x1e, 0xc0, 0x2b, 0xc0, 0x2f, 0xcc, 0xa9, 0xcc, 0xa8, + 0xc0, 0x2c, 0xc0, 0x30, 0xc0, 0x0a, 0xc0, 0x09, 0xc0, 0x13, 0xc0, + 0x14, 0x00, 0x33, 0x00, 0x39, 0x00, 0x2f, 0x00, 0x35, 0x00, 0x0a, + 0x01, 0x00, 0x00, 0x85, 0x00, 0x00, 0x00, 0x23, 0x00, 0x21, 0x00, + 0x00, 0x1e, 0x69, 0x6e, 0x63, 0x6f, 0x6d, 0x69, 0x6e, 0x67, 0x2e, + 0x74, 0x65, 0x6c, 0x65, 0x6d, 0x65, 0x74, 0x72, 0x79, 0x2e, 0x6d, + 0x6f, 0x7a, 0x69, 0x6c, 0x6c, 0x61, 0x2e, 0x6f, 0x72, 0x67, 0x00, + 0x17, 0x00, 0x00, 0xff, 0x01, 0x00, 0x01, 0x00, 0x00, 0x0a, 0x00, + 0x0a, 0x00, 0x08, 0x00, 0x1d, 0x00, 0x17, 0x00, 0x18, 0x00, 0x19, + 0x00, 0x0b, 0x00, 0x02, 0x01, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00, + 0x10, 0x00, 0x0e, 0x00, 0x0c, 0x02, 0x68, 0x32, 0x08, 0x68, 0x74, + 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31, 0x00, 0x05, 0x00, 0x05, 0x01, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x00, 0x18, 0x00, 0x16, 0x04, + 0x03, 0x05, 0x03, 0x06, 0x03, 0x08, 0x04, 0x08, 0x05, 0x08, 0x06, + 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x02, 0x03, 0x02, 0x01, 0x00, + 0x1c, 0x00, 0x02, 0x40, 0x00 + }; + const char expected_ja3[] = "771,49195-49199-52393-52392-49196-49200-" + "49162-49161-49171-49172-51-57-47-53-10,0-" + "23-65281-10-11-35-16-5-13-28,29-23-24-25,0"; + S2N_BLOB_FROM_HEX(expected_hash, "839bbe3ed07fed922ded5aaf714d6842"); + + DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL, + s2n_client_hello_free); + EXPECT_OK(s2n_client_hello_from_source(&client_hello, server, + raw_client_hello, sizeof(raw_client_hello), source)); + + uint32_t output_size = 0; + EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_string(client_hello, + S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size)); + EXPECT_EQUAL(strlen(expected_ja3), output_size); + EXPECT_BYTEARRAY_EQUAL(expected_ja3, output_mem, output_size); + + uint32_t str_size = 0; + EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_hash(client_hello, + S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size, + &str_size)); + EXPECT_EQUAL(strlen(expected_ja3), str_size); + EXPECT_EQUAL(expected_hash.size, output_size); + EXPECT_BYTEARRAY_EQUAL(expected_hash.data, output_mem, output_size); + }; + + /* Known values from Rust implementation: + * https://github.com/jabedude/ja3-rs/blob/4f2629b86ce3496b4614296f754954806c9c849c/tests/ncat-port-4450.pcap + */ + { + uint8_t raw_client_hello[512] = { + 0x01, 0x00, 0x01, 0xFC, + 0x03, 0x03, 0xf4, 0x0f, 0xfd, 0xee, 0xc7, 0x27, 0xc2, 0x1e, 0x32, + 0x70, 0x5f, 0x85, 0x25, 0xa6, 0xbb, 0x6c, 0xca, 0x4b, 0x6c, 0xbe, + 0x01, 0x66, 0x32, 0x66, 0x76, 0x4b, 0x67, 0x74, 0x3b, 0x91, 0xbd, + 0xb2, 0x20, 0x83, 0xd4, 0x9e, 0x77, 0xaf, 0xc1, 0x5a, 0x63, 0x35, + 0xba, 0x2f, 0xe9, 0x76, 0xbe, 0x9a, 0x42, 0x6b, 0x2e, 0xb5, 0x58, + 0x23, 0x84, 0x2a, 0x99, 0x2b, 0x37, 0x88, 0xd1, 0xf7, 0x9d, 0xd6, + 0x20, 0x00, 0x9c, 0x13, 0x02, 0x13, 0x03, 0x13, 0x01, 0xc0, 0x2c, + 0xc0, 0x30, 0x00, 0xa3, 0x00, 0x9f, 0xcc, 0xa9, 0xcc, 0xa8, 0xcc, + 0xaa, 0xc0, 0xaf, 0xc0, 0xad, 0xc0, 0xa3, 0xc0, 0x9f, 0xc0, 0x5d, + 0xc0, 0x61, 0xc0, 0x57, 0xc0, 0x53, 0xc0, 0x24, 0xc0, 0x28, 0x00, + 0x6b, 0x00, 0x6a, 0xc0, 0x73, 0xc0, 0x77, 0x00, 0xc4, 0x00, 0xc3, + 0xc0, 0x0a, 0xc0, 0x14, 0x00, 0x39, 0x00, 0x38, 0x00, 0x88, 0x00, + 0x87, 0x00, 0x9d, 0xc0, 0xa1, 0xc0, 0x9d, 0xc0, 0x51, 0x00, 0x3d, + 0x00, 0xc0, 0x00, 0x35, 0x00, 0x84, 0xc0, 0x2b, 0xc0, 0x2f, 0x00, + 0xa2, 0x00, 0x9e, 0xc0, 0xae, 0xc0, 0xac, 0xc0, 0xa2, 0xc0, 0x9e, + 0xc0, 0x5c, 0xc0, 0x60, 0xc0, 0x56, 0xc0, 0x52, 0xc0, 0x23, 0xc0, + 0x27, 0x00, 0x67, 0x00, 0x40, 0xc0, 0x72, 0xc0, 0x76, 0x00, 0xbe, + 0x00, 0xbd, 0xc0, 0x09, 0xc0, 0x13, 0x00, 0x33, 0x00, 0x32, 0x00, + 0x9a, 0x00, 0x99, 0x00, 0x45, 0x00, 0x44, 0x00, 0x9c, 0xc0, 0xa0, + 0xc0, 0x9c, 0xc0, 0x50, 0x00, 0x3c, 0x00, 0xba, 0x00, 0x2f, 0x00, + 0x96, 0x00, 0x41, 0x00, 0xff, 0x01, 0x00, 0x01, 0x17, 0x00, 0x00, + 0x00, 0x0e, 0x00, 0x0c, 0x00, 0x00, 0x09, 0x31, 0x32, 0x37, 0x2e, + 0x30, 0x2e, 0x30, 0x2e, 0x31, 0x00, 0x0b, 0x00, 0x04, 0x03, 0x00, + 0x01, 0x02, 0x00, 0x0a, 0x00, 0x0c, 0x00, 0x0a, 0x00, 0x1d, 0x00, + 0x17, 0x00, 0x1e, 0x00, 0x19, 0x00, 0x18, 0x00, 0x23, 0x00, 0x00, + 0x00, 0x16, 0x00, 0x00, 0x00, 0x17, 0x00, 0x00, 0x00, 0x0d, 0x00, + 0x30, 0x00, 0x2e, 0x04, 0x03, 0x05, 0x03, 0x06, 0x03, 0x08, 0x07, + 0x08, 0x08, 0x08, 0x09, 0x08, 0x0a, 0x08, 0x0b, 0x08, 0x04, 0x08, + 0x05, 0x08, 0x06, 0x04, 0x01, 0x05, 0x01, 0x06, 0x01, 0x03, 0x03, + 0x02, 0x03, 0x03, 0x01, 0x02, 0x01, 0x03, 0x02, 0x02, 0x02, 0x04, + 0x02, 0x05, 0x02, 0x06, 0x02, 0x00, 0x2b, 0x00, 0x09, 0x08, 0x03, + 0x04, 0x03, 0x03, 0x03, 0x02, 0x03, 0x01, 0x00, 0x2d, 0x00, 0x02, + 0x01, 0x01, 0x00, 0x33, 0x00, 0x26, 0x00, 0x24, 0x00, 0x1d, 0x00, + 0x20, 0x29, 0x61, 0x96, 0xc4, 0x0c, 0x16, 0x7c, 0xde, 0x20, 0x01, + 0x86, 0x32, 0xdf, 0x84, 0x2f, 0x67, 0x2f, 0x3f, 0x64, 0x17, 0xc0, + 0x2e, 0xa2, 0xb2, 0x9e, 0xfc, 0xa8, 0xb0, 0xc5, 0x71, 0x6e, 0x7d, + 0x00, 0x15, 0x00, 0x6c, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00 + }; + const char expected_ja3[] = "771,4866-4867-4865-49196-49200-163-159-" + "52393-52392-52394-49327-49325-49315-" + "49311-49245-49249-49239-49235-49188-49192-" + "107-106-49267-49271-196-195-49162-49172-" + "57-56-136-135-157-49313-49309-49233-61-" + "192-53-132-49195-49199-162-158-49326-" + "49324-49314-49310-49244-49248-49238-49234-" + "49187-49191-103-64-49266-49270-190-189-" + "49161-49171-51-50-154-153-69-68-156-49312-" + "49308-49232-60-186-47-150-65-255,0-11-" + "10-35-22-23-13-43-45-51-21,29-23-30-25-" + "24,0-1-2"; + S2N_BLOB_FROM_HEX(expected_hash, "10a6b69a81bac09072a536ce9d35dd43"); + + DEFER_CLEANUP(struct s2n_client_hello *client_hello = NULL, + s2n_client_hello_free); + EXPECT_OK(s2n_client_hello_from_source(&client_hello, server, + raw_client_hello, sizeof(raw_client_hello), source)); + + uint32_t output_size = 0; + EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_string(client_hello, + S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size)); + EXPECT_EQUAL(strlen(expected_ja3), output_size); + EXPECT_BYTEARRAY_EQUAL(expected_ja3, output_mem, output_size); + + uint32_t str_size = 0; + EXPECT_SUCCESS(s2n_client_hello_get_fingerprint_hash(client_hello, + S2N_FINGERPRINT_JA3, sizeof(output_mem), output_mem, &output_size, + &str_size)); + EXPECT_EQUAL(strlen(expected_ja3), str_size); + EXPECT_EQUAL(expected_hash.size, output_size); + EXPECT_BYTEARRAY_EQUAL(expected_hash.data, output_mem, output_size); + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_fork_generation_number_test.c b/tests/unit/s2n_fork_generation_number_test.c new file mode 100644 index 00000000000..6ccf9e9274f --- /dev/null +++ b/tests/unit/s2n_fork_generation_number_test.c @@ -0,0 +1,374 @@ +/* + * 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. + */ + +#ifdef __FreeBSD__ + /* FreeBSD requires POSIX compatibility off for its syscalls (enables __BSD_VISIBLE) + * Without the below line, cannot be imported (it requires __BSD_VISIBLE) */ + #undef _POSIX_C_SOURCE +#else + /* For clone() */ + #define _GNU_SOURCE +#endif + +#include +#include +#include +#include +#include + +#include "s2n_test.h" +#include "utils/s2n_fork_detection.h" + +#define NUMBER_OF_FGN_TEST_CASES 4 +#define MAX_NUMBER_OF_TEST_THREADS 2 +#define FORK_LEVEL_FOR_TESTS 2 +/* Before calling s2n_get_fork_generation_number() set the argument to this + * value to avoid any unlucky collisions + */ +#define UNEXPECTED_RETURNED_FGN 0xFF + +#define CLONE_TEST_NO 0 +#define CLONE_TEST_YES 1 +#define CLONE_TEST_DETERMINE_AT_RUNTIME 2 + +struct fgn_test_case { + const char *test_case_label; + int (*test_case_cb)(struct fgn_test_case *test_case); + int test_case_must_pass_clone_test; +}; + +static void s2n_verify_child_exit_status(pid_t proc_pid) +{ + int status = 0; +#if defined(S2N_CLONE_SUPPORTED) + EXPECT_EQUAL(waitpid(proc_pid, &status, __WALL), proc_pid); +#else + /* __WALL is not relevant when clone() is not supported + * https://man7.org/linux/man-pages/man2/wait.2.html#NOTES + */ + EXPECT_EQUAL(waitpid(proc_pid, &status, 0), proc_pid); +#endif + /* Check that child exited with EXIT_SUCCESS. If not, this indicates + * that an error was encountered in the unit tests executed in that + * child process. + */ + EXPECT_NOT_EQUAL(WIFEXITED(status), 0); + EXPECT_EQUAL(WEXITSTATUS(status), EXIT_SUCCESS); +} + +static void *s2n_unit_test_thread_get_fgn(void *expected_fork_generation_number) +{ + uint64_t return_fork_generation_number = UNEXPECTED_RETURNED_FGN; + EXPECT_OK(s2n_get_fork_generation_number(&return_fork_generation_number)); + EXPECT_EQUAL(return_fork_generation_number, *(uint64_t *) expected_fork_generation_number); + + return NULL; +} + +static int s2n_unit_test_thread(uint64_t expected_fork_generation_number) +{ + pthread_t threads[MAX_NUMBER_OF_TEST_THREADS]; + + for (size_t thread_index = 0; thread_index < MAX_NUMBER_OF_TEST_THREADS; thread_index++) { + EXPECT_EQUAL(pthread_create(&threads[thread_index], NULL, &s2n_unit_test_thread_get_fgn, (void *) &expected_fork_generation_number), 0); + } + + /* Wait for all threads to finish */ + for (size_t thread_index = 0; thread_index < MAX_NUMBER_OF_TEST_THREADS; thread_index++) { + pthread_join(threads[thread_index], NULL); + } + + return S2N_SUCCESS; +} + +static int s2n_unit_test_fork(uint64_t parent_process_fgn, int fork_level) +{ + pid_t proc_pid = fork(); + EXPECT_TRUE(proc_pid >= 0); + + fork_level = fork_level - 1; + + if (proc_pid == 0) { + /* In child */ + uint64_t return_fork_generation_number = UNEXPECTED_RETURNED_FGN; + EXPECT_OK(s2n_get_fork_generation_number(&return_fork_generation_number)); + EXPECT_EQUAL(return_fork_generation_number, parent_process_fgn + 1); + + /* Verify stability */ + return_fork_generation_number = UNEXPECTED_RETURNED_FGN; + EXPECT_OK(s2n_get_fork_generation_number(&return_fork_generation_number)); + EXPECT_EQUAL(return_fork_generation_number, parent_process_fgn + 1); + + /* Verify in threads */ + EXPECT_EQUAL(s2n_unit_test_thread(return_fork_generation_number), S2N_SUCCESS); + + if (fork_level > 0) { + /* Fork again and verify fork generation number */ + EXPECT_EQUAL(s2n_unit_test_fork(parent_process_fgn + 1, fork_level), S2N_SUCCESS); + } + + /* Exit code EXIT_SUCCESS means that tests in this process finished + * successfully. Any errors would have exited the process with an + * exit code != EXIT_SUCCESS. We verify this in the parent process. + */ + exit(EXIT_SUCCESS); + } else { + s2n_verify_child_exit_status(proc_pid); + + /* Verify stability */ + uint64_t return_fork_generation_number = UNEXPECTED_RETURNED_FGN; + EXPECT_OK(s2n_get_fork_generation_number(&return_fork_generation_number)); + EXPECT_EQUAL(return_fork_generation_number, parent_process_fgn); + } + + return S2N_SUCCESS; +} + +/* Similar test to unit_test_fork() but verify in threads first */ +static int s2n_unit_test_fork_check_threads_first(uint64_t parent_process_fgn) +{ + pid_t proc_pid = fork(); + EXPECT_TRUE(proc_pid >= 0); + + if (proc_pid == 0) { + /* In child. Verify threads first. */ + EXPECT_EQUAL(s2n_unit_test_thread(parent_process_fgn + 1), S2N_SUCCESS); + + /* Then in the thread spawned when forking */ + uint64_t return_fork_generation_number = UNEXPECTED_RETURNED_FGN; + EXPECT_OK(s2n_get_fork_generation_number(&return_fork_generation_number)); + EXPECT_EQUAL(return_fork_generation_number, parent_process_fgn + 1); + + /* Verify stability */ + return_fork_generation_number = UNEXPECTED_RETURNED_FGN; + EXPECT_OK(s2n_get_fork_generation_number(&return_fork_generation_number)); + EXPECT_EQUAL(return_fork_generation_number, parent_process_fgn + 1); + + /* Exit code EXIT_SUCCESS means that tests in this process finished + * successfully. Any errors would have exited the process with an + * exit code != EXIT_SUCCESS. We verify this in the parent process. + */ + exit(EXIT_SUCCESS); + } else { + s2n_verify_child_exit_status(proc_pid); + + /* Verify stability */ + uint64_t return_fork_generation_number = UNEXPECTED_RETURNED_FGN; + EXPECT_OK(s2n_get_fork_generation_number(&return_fork_generation_number)); + EXPECT_EQUAL(return_fork_generation_number, parent_process_fgn); + } + + return S2N_SUCCESS; +} + +static int s2n_unit_test_clone_child_process(void *parent_process_fgn) +{ + /* In child */ + uint64_t local_parent_process_fgn = *(uint64_t *) parent_process_fgn; + uint64_t return_fork_generation_number = UNEXPECTED_RETURNED_FGN; + EXPECT_OK(s2n_get_fork_generation_number(&return_fork_generation_number)); + EXPECT_EQUAL(return_fork_generation_number, local_parent_process_fgn + 1); + + /* Verify stability */ + return_fork_generation_number = UNEXPECTED_RETURNED_FGN; + EXPECT_OK(s2n_get_fork_generation_number(&return_fork_generation_number)); + EXPECT_EQUAL(return_fork_generation_number, local_parent_process_fgn + 1); + + /* Verify in threads */ + EXPECT_EQUAL(s2n_unit_test_thread(return_fork_generation_number), S2N_SUCCESS); + + /* This translates to the exit code for this child process */ + return EXIT_SUCCESS; +} + +#define PROCESS_CHILD_STACK_SIZE (1024 * 1024) /* Suggested by clone() man page... */ +static int s2n_unit_test_clone(uint64_t parent_process_fgn) +{ +#if defined(S2N_CLONE_SUPPORTED) + /* Verify stability */ + uint64_t return_fork_generation_number = UNEXPECTED_RETURNED_FGN; + EXPECT_OK(s2n_get_fork_generation_number(&return_fork_generation_number)); + EXPECT_EQUAL(return_fork_generation_number, parent_process_fgn); + + /* Use stack memory for this... We don't exit unit_test_clone() before this + * memory has served its purpose. + * Why? Using dynamically allocated memory causes Valgrind to squat on the + * allocated memory when the child process exists. + */ + char process_child_stack[PROCESS_CHILD_STACK_SIZE]; + EXPECT_NOT_NULL(process_child_stack); + + int proc_pid = clone(s2n_unit_test_clone_child_process, (void *) (process_child_stack + PROCESS_CHILD_STACK_SIZE), 0, (void *) &return_fork_generation_number); + EXPECT_NOT_EQUAL(proc_pid, -1); + + s2n_verify_child_exit_status(proc_pid); + + /* Verify stability */ + return_fork_generation_number = UNEXPECTED_RETURNED_FGN; + EXPECT_OK(s2n_get_fork_generation_number(&return_fork_generation_number)); + EXPECT_EQUAL(return_fork_generation_number, parent_process_fgn); +#endif + + return S2N_SUCCESS; +} + +static int s2n_unit_tests_common(struct fgn_test_case *test_case) +{ + uint64_t return_fork_generation_number = 0; + + EXPECT_OK(s2n_get_fork_generation_number(&return_fork_generation_number)); + EXPECT_EQUAL(return_fork_generation_number, 0); + + /* Should be idempotent if no fork event occurred */ + return_fork_generation_number = UNEXPECTED_RETURNED_FGN; + EXPECT_OK(s2n_get_fork_generation_number(&return_fork_generation_number)); + EXPECT_EQUAL(return_fork_generation_number, 0); + + /* Should be idempotent in threaded environment as well */ + EXPECT_EQUAL(s2n_unit_test_thread(return_fork_generation_number), S2N_SUCCESS); + + /* Cached FGN should increment if a fork event occurs */ + EXPECT_EQUAL(s2n_unit_test_fork(return_fork_generation_number, FORK_LEVEL_FOR_TESTS), S2N_SUCCESS); + EXPECT_EQUAL(s2n_unit_test_fork_check_threads_first(return_fork_generation_number), S2N_SUCCESS); + + /* Some fork detection mechanisms can also detect forks through clone() */ + if (test_case->test_case_must_pass_clone_test == CLONE_TEST_YES) { + EXPECT_EQUAL((s2n_is_madv_wipeonfork_supported() == true) || (s2n_is_map_inherit_zero_supported() == true), true); + EXPECT_EQUAL(s2n_unit_test_clone(return_fork_generation_number), S2N_SUCCESS); + } else if (test_case->test_case_must_pass_clone_test == CLONE_TEST_DETERMINE_AT_RUNTIME) { + if ((s2n_is_madv_wipeonfork_supported() == true) || (s2n_is_map_inherit_zero_supported() == true)) { + EXPECT_EQUAL(s2n_unit_test_clone(return_fork_generation_number), S2N_SUCCESS); + } + } + + return S2N_SUCCESS; +} + +static int s2n_test_case_default_cb(struct fgn_test_case *test_case) +{ + EXPECT_SUCCESS(s2n_init()); + + EXPECT_EQUAL(s2n_unit_tests_common(test_case), S2N_SUCCESS); + + EXPECT_SUCCESS(s2n_cleanup()); + + return S2N_SUCCESS; +} + +static int s2n_test_case_pthread_atfork_cb(struct fgn_test_case *test_case) +{ + if (s2n_is_pthread_atfork_supported() == false) { + TEST_DEBUG_PRINT("s2n_fork_generation_number_test.c test case not supported. Skipping.\nTest case: %s\n", test_case->test_case_label); + return S2N_SUCCESS; + } + POSIX_GUARD_RESULT(s2n_ignore_wipeonfork_and_inherit_zero_for_testing()); + + EXPECT_SUCCESS(s2n_init()); + + EXPECT_EQUAL(s2n_unit_tests_common(test_case), S2N_SUCCESS); + + EXPECT_SUCCESS(s2n_cleanup()); + + return S2N_SUCCESS; +} + +static int s2n_test_case_madv_wipeonfork_cb(struct fgn_test_case *test_case) +{ + if (s2n_is_madv_wipeonfork_supported() == false) { + TEST_DEBUG_PRINT("s2n_fork_generation_number_test.c test case not supported. Skipping.\nTest case: %s\n", test_case->test_case_label); + return S2N_SUCCESS; + } + POSIX_GUARD_RESULT(s2n_ignore_pthread_atfork_for_testing()); + + EXPECT_SUCCESS(s2n_init()); + + EXPECT_EQUAL(s2n_unit_tests_common(test_case), S2N_SUCCESS); + + EXPECT_SUCCESS(s2n_cleanup()); + + return S2N_SUCCESS; +} + +static int s2n_test_case_map_inherit_zero_cb(struct fgn_test_case *test_case) +{ + if (s2n_is_map_inherit_zero_supported() == false) { + TEST_DEBUG_PRINT("s2n_fork_generation_number_test.c test case not supported. Skipping.\nTest case: %s\n", test_case->test_case_label); + return S2N_SUCCESS; + } + POSIX_GUARD_RESULT(s2n_ignore_pthread_atfork_for_testing()); + + EXPECT_SUCCESS(s2n_init()); + + EXPECT_EQUAL(s2n_unit_tests_common(test_case), S2N_SUCCESS); + + EXPECT_SUCCESS(s2n_cleanup()); + + return S2N_SUCCESS; +} + +struct fgn_test_case fgn_test_cases[NUMBER_OF_FGN_TEST_CASES] = { + { "Default fork detect mechanisms.", s2n_test_case_default_cb, CLONE_TEST_DETERMINE_AT_RUNTIME }, + { "Only pthread_atfork fork detection mechanism.", s2n_test_case_pthread_atfork_cb, CLONE_TEST_NO }, + { "Only madv_wipeonfork fork detection mechanism.", s2n_test_case_madv_wipeonfork_cb, CLONE_TEST_YES }, + { "Only map_inherit_zero fork detection mechanism.", s2n_test_case_map_inherit_zero_cb, CLONE_TEST_YES } +}; + +int main(int argc, char **argv) +{ + BEGIN_TEST_NO_INIT(); + + EXPECT_TRUE(s2n_array_len(fgn_test_cases) == NUMBER_OF_FGN_TEST_CASES); + +/* Test: FreeBSD >= 12.0 should use map_inherit_zero */ +#ifdef __FreeBSD__ + #ifndef __FreeBSD_version + #error "Unknown FreeBSD version" + #endif + + #if __FreeBSD_version >= 1200000 + EXPECT_TRUE(s2n_is_map_inherit_zero_supported()); + #endif +#endif + + /* Create NUMBER_OF_FGN_TEST_CASES number of child processes that run each + * test case. + * + * Fork detection is lazily initialised on first invocation of + * s2n_get_fork_generation_number(). Hence, it is important that childs are + * created before calling into the fork detection code. + */ + pid_t proc_pids[NUMBER_OF_FGN_TEST_CASES] = { 0 }; + + for (size_t i = 0; i < NUMBER_OF_FGN_TEST_CASES; i++) { + proc_pids[i] = fork(); + EXPECT_TRUE(proc_pids[i] >= 0); + + if (proc_pids[i] == 0) { + /* In child */ + EXPECT_EQUAL(fgn_test_cases[i].test_case_cb(&fgn_test_cases[i]), S2N_SUCCESS); + + /* Exit code EXIT_SUCCESS means that tests in this process finished + * successfully. Any errors would have exited the process with an + * exit code != EXIT_SUCCESS. We verify this in the parent process. + * Also prevents child from creating more childs. + */ + exit(EXIT_SUCCESS); + } else { + s2n_verify_child_exit_status(proc_pids[i]); + } + } + + END_TEST_NO_INIT(); +} diff --git a/tests/unit/s2n_fragmentation_coalescing_test.c b/tests/unit/s2n_fragmentation_coalescing_test.c new file mode 100644 index 00000000000..0a0b6ff7bbc --- /dev/null +++ b/tests/unit/s2n_fragmentation_coalescing_test.c @@ -0,0 +1,646 @@ +/* + * 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 +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" + +/* + * The TLS protocol allows messages to be fragmented, interleaved and coalesced into 'records'. These + * tests check that fragmented messages are recombined, that several messages in the same record work + * and that messages interleaved with alerts (including a fragmented alert message) all work. + * + * To do this we fork() subprocesses that write records to a pipe, s2n is configured to read fragments + * from the pipe. + */ + +#define TLS_ALERT 21 +#define TLS_HANDSHAKE 22 +#define TLS_HEARTBEAT 24 + +#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F + +uint8_t zero_to_thirty_one[] = { ZERO_TO_THIRTY_ONE }; + +uint8_t server_hello_message[] = { /* SERVER HELLO */ + 0x02, + + /* Length */ + 0x00, 0x00, 0x46, + + /* Protocol version */ + 0x03, 0x03, + + /* Server random */ + ZERO_TO_THIRTY_ONE, + + /* SessionID len - 32 bytes */ + 0x20, + + /* Session ID */ + ZERO_TO_THIRTY_ONE, + + /* Cipher suite - TLS_RSA_WITH_AES_256_CBC_SHA256 */ + 0x00, 0x3D, + + /* Compression method: none */ + 0x00 +}; + +uint8_t server_cert[] = { /* SERVER CERT */ + 0x0B, + + /* Length of the handshake message */ + 0x00, 0x03, 0x38, + + /* Length of all certificates */ + 0x00, 0x03, 0x35, + + /* Length of the first cert */ + 0x00, 0x03, 0x32, + + /* Certificate data - via openssl x509 -in cert.pem -outform DER | xxd -i */ + 0x30, 0x82, 0x03, 0x2e, 0x30, 0x82, 0x02, 0x16, 0x02, 0x09, 0x00, 0xcb, + 0xd6, 0x5a, 0xfa, 0x37, 0xcf, 0xe0, 0xbf, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x59, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, + 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, + 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, + 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x31, 0x12, 0x30, + 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x09, 0x6c, 0x6f, 0x63, 0x61, + 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30, + 0x35, 0x31, 0x30, 0x31, 0x37, 0x30, 0x38, 0x32, 0x33, 0x5a, 0x17, 0x0d, + 0x32, 0x34, 0x30, 0x35, 0x30, 0x37, 0x31, 0x37, 0x30, 0x38, 0x32, 0x33, + 0x5a, 0x30, 0x59, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, + 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, + 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, + 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x09, 0x6c, + 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xc8, 0x96, 0xd6, 0x94, 0x98, 0x78, 0x3e, + 0x1b, 0xb2, 0x1d, 0x6b, 0x65, 0xc2, 0xb4, 0x44, 0x31, 0xd5, 0x87, 0x96, + 0x0d, 0x7e, 0x35, 0x53, 0x6c, 0xc1, 0x29, 0xb6, 0x34, 0x95, 0x3f, 0x9a, + 0xb9, 0x77, 0xfc, 0xd5, 0xc4, 0xf7, 0x75, 0x84, 0xdd, 0x7c, 0x96, 0xe5, + 0x6f, 0xeb, 0xf8, 0x09, 0x35, 0x9a, 0x18, 0xda, 0x1f, 0xa2, 0x07, 0x33, + 0x79, 0xb9, 0xbc, 0x07, 0x6f, 0xce, 0x17, 0xd1, 0x7e, 0x59, 0x69, 0x0a, + 0x00, 0x98, 0x4a, 0xb1, 0x33, 0xc0, 0x13, 0xbf, 0xd2, 0x34, 0x07, 0x62, + 0xe0, 0x4a, 0xaf, 0xe0, 0x57, 0xcd, 0x6d, 0x62, 0xa4, 0x19, 0xbe, 0x31, + 0x69, 0xcc, 0x71, 0x6f, 0x83, 0xc7, 0xd9, 0x73, 0xfd, 0x57, 0x70, 0xa1, + 0x27, 0xa9, 0x4c, 0x48, 0x8d, 0xd5, 0xeb, 0xc1, 0x66, 0x11, 0xfe, 0x24, + 0x70, 0x43, 0x75, 0xe1, 0x5f, 0x2f, 0xb9, 0xf2, 0x02, 0xe4, 0x71, 0x3f, + 0x2d, 0x3e, 0x20, 0x08, 0xf0, 0xc9, 0xe1, 0x47, 0xd4, 0x51, 0xb0, 0x20, + 0x12, 0x14, 0x9e, 0x6d, 0x3e, 0xab, 0xfc, 0xa1, 0x58, 0x07, 0x94, 0xf7, + 0x01, 0xe0, 0xdc, 0xd5, 0x57, 0x67, 0x69, 0xa4, 0x5b, 0x96, 0xb3, 0xfa, + 0x2b, 0x03, 0x38, 0xe6, 0xf4, 0xec, 0xd0, 0x88, 0xb4, 0xf7, 0xf6, 0x2b, + 0x97, 0x30, 0x71, 0x69, 0x33, 0xcc, 0x8c, 0xb1, 0x82, 0x29, 0xaf, 0x09, + 0x32, 0xff, 0x0f, 0x5b, 0x64, 0x74, 0x53, 0xd5, 0x82, 0xa8, 0x79, 0xb3, + 0x04, 0x7f, 0x96, 0xdd, 0x0f, 0x71, 0x3e, 0xb7, 0xe1, 0x08, 0x89, 0xe6, + 0xe0, 0x95, 0xa8, 0x6f, 0xc5, 0xa0, 0x33, 0x53, 0x6e, 0x89, 0x8b, 0xb3, + 0x14, 0x1d, 0x02, 0x35, 0xa4, 0x1c, 0x74, 0xc4, 0xbb, 0x87, 0x46, 0x99, + 0x10, 0x05, 0x67, 0x6b, 0x28, 0x50, 0xf7, 0xaf, 0xcf, 0x69, 0xda, 0x63, + 0x28, 0xd1, 0x34, 0x2e, 0xea, 0xfd, 0x9d, 0x4c, 0x5b, 0x02, 0x03, 0x01, + 0x00, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x64, 0x55, + 0xef, 0x5b, 0x91, 0xb7, 0xfd, 0x5d, 0x00, 0x3b, 0x0c, 0x0f, 0xd7, 0xe0, + 0x26, 0xfc, 0xd6, 0xf3, 0xd8, 0xc5, 0x00, 0xdf, 0x3b, 0x85, 0x70, 0x91, + 0x85, 0x35, 0xb1, 0x7d, 0x78, 0x58, 0x33, 0x39, 0x27, 0xc4, 0x9e, 0x56, + 0x31, 0xbd, 0x89, 0x02, 0x56, 0x8c, 0x73, 0xf8, 0x13, 0xa6, 0x20, 0xe2, + 0x40, 0x19, 0x1b, 0xbc, 0x1f, 0xa2, 0x25, 0xee, 0x40, 0x7a, 0x98, 0x10, + 0x59, 0xbc, 0xb1, 0x3c, 0x93, 0x6d, 0x4a, 0x50, 0x3d, 0x19, 0xf2, 0x81, + 0xcf, 0x52, 0x0d, 0x47, 0x97, 0x05, 0xb0, 0xe2, 0xf6, 0xed, 0x5a, 0xc1, + 0xa0, 0xc6, 0x07, 0x31, 0xaa, 0x25, 0xbd, 0xe7, 0xac, 0x95, 0xcd, 0x40, + 0x5b, 0x61, 0xdf, 0x06, 0xd5, 0xd6, 0x5d, 0xe5, 0x92, 0x10, 0x5e, 0xc5, + 0x40, 0xd8, 0x32, 0x7b, 0xc6, 0x43, 0x3c, 0xdc, 0xde, 0x49, 0x64, 0x88, + 0xd1, 0x5c, 0x8a, 0xdb, 0xbe, 0xb6, 0xc2, 0xc4, 0xe0, 0x4e, 0xe5, 0x21, + 0x1c, 0x06, 0x89, 0xe3, 0x9e, 0xba, 0xd1, 0xe5, 0xf9, 0xef, 0xe7, 0xbc, + 0x22, 0xf6, 0x8c, 0xef, 0x13, 0x84, 0x7c, 0x13, 0xc3, 0x29, 0x8b, 0x54, + 0xd1, 0xad, 0xbc, 0x66, 0xe8, 0x6f, 0x4a, 0xbd, 0x9a, 0x90, 0x9b, 0x46, + 0x0b, 0x07, 0x2c, 0xd8, 0x9e, 0xab, 0xb3, 0xa2, 0x3e, 0xad, 0x5f, 0x38, + 0x52, 0x4b, 0x43, 0xc4, 0x50, 0xbd, 0x2d, 0x47, 0xb3, 0x06, 0x8f, 0x03, + 0xf4, 0x59, 0x0c, 0x3c, 0xba, 0x0f, 0x28, 0xa3, 0x47, 0xd5, 0xd5, 0xd1, + 0xe8, 0xb3, 0xbc, 0x18, 0xe9, 0x2a, 0x59, 0x4a, 0xe1, 0x3c, 0x81, 0x26, + 0x7f, 0x2f, 0x4a, 0x61, 0xeb, 0x37, 0xab, 0x66, 0x57, 0xea, 0xcb, 0xe4, + 0xe2, 0xbc, 0x01, 0xb6, 0x89, 0xa6, 0x1d, 0x1b, 0xf7, 0xd2, 0x43, 0xf1, + 0x9e, 0x75, 0x35, 0x61, 0x7b, 0x79, 0xd9, 0x18, 0xbe, 0x5d, 0xcc, 0xce, + 0xc0, 0x4b +}; + +uint8_t heartbeat_message[] = { + 0x01, 0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10 +}; + +uint8_t warning_alert[] = { /* warning: user cancelled */ + 0x01, 0x5a +}; + +uint8_t fatal_alert[] = { /* Fatal: unexpected message */ + 0x02, 0x0a +}; + +message_type_t s2n_conn_get_current_message_type(struct s2n_connection *conn); + +void fragmented_message(int write_fd) +{ + int written = 0; + /* Split the hello message into 5 fragments and write it */ + for (int i = 0; i < 5; i++) { + int length = sizeof(server_hello_message) / 5; + + if (i == 0) { + length += sizeof(server_hello_message) % 5; + } + + uint8_t record_header[5] = { TLS_HANDSHAKE, 0x03, 0x03, (length >> 8), length & 0xff }; + + if (write(write_fd, record_header, 5) != 5) { + _exit(100); + } + + if (write(write_fd, server_hello_message + written, length) != length) { + _exit(100); + } + + written += length; + } + + /* Close the pipe and exit */ + close(write_fd); +} + +void coalesced_message(int write_fd) +{ + int length = sizeof(server_hello_message) + sizeof(server_cert); + + uint8_t record_header[5] = { TLS_HANDSHAKE, 0x03, 0x03, (length >> 8), length & 0xff }; + + if (write(write_fd, record_header, 5) != 5) { + _exit(100); + } + + if (write(write_fd, server_hello_message, sizeof(server_hello_message)) != sizeof(server_hello_message)) { + _exit(100); + } + + if (write(write_fd, server_cert, sizeof(server_cert)) != sizeof(server_cert)) { + _exit(100); + } + + close(write_fd); +} + +void interleaved_message(int write_fd) +{ + int length = sizeof(server_hello_message) / 2; + uint8_t record_header[5] = { TLS_HANDSHAKE, 0x03, 0x03, (length >> 8), length & 0xff }; + int written = 0; + + /* Write half of the message */ + if (write(write_fd, record_header, 5) != 5) { + _exit(100); + } + if (write(write_fd, server_hello_message, length) != length) { + _exit(100); + } + written += length; + + /* Write the heartbeat record */ + record_header[0] = TLS_HEARTBEAT; + record_header[3] = sizeof(heartbeat_message) >> 8; + record_header[4] = sizeof(heartbeat_message) & 0xff; + + if (write(write_fd, record_header, 5) != 5) { + _exit(100); + } + if (write(write_fd, heartbeat_message, sizeof(heartbeat_message)) != sizeof(heartbeat_message)) { + _exit(100); + } + + /* Write the rest of the hello message */ + length = sizeof(server_hello_message) - written; + record_header[0] = TLS_HANDSHAKE; + record_header[3] = length >> 8; + record_header[4] = length & 0xff; + if (write(write_fd, record_header, 5) != 5) { + _exit(100); + } + if (write(write_fd, server_hello_message + written, length) != length) { + _exit(100); + } + + /* Close the pipe and exit */ + close(write_fd); +} + +void interleaved_fragmented_fatal_alert(int write_fd) +{ + int length = sizeof(server_hello_message) / 2; + uint8_t record_header[5] = { TLS_HANDSHAKE, 0x03, 0x03, (length >> 8), length & 0xff }; + int written = 0; + + /* Write half of the message */ + if (write(write_fd, record_header, 5) != 5) { + _exit(100); + } + if (write(write_fd, server_hello_message, length) != length) { + _exit(100); + } + written += length; + + /* Write half of the alert message */ + record_header[0] = TLS_ALERT; + record_header[3] = 0; + record_header[4] = 1; + + if (write(write_fd, record_header, 5) != 5) { + _exit(100); + } + if (write(write_fd, fatal_alert, 1) != 1) { + _exit(100); + } + + /* Write another quarter of the of the hello message */ + length = sizeof(server_hello_message) / 4; + record_header[0] = TLS_HANDSHAKE; + record_header[3] = length >> 8; + record_header[4] = length & 0xff; + if (write(write_fd, record_header, 5) != 5) { + _exit(100); + } + if (write(write_fd, server_hello_message + written, length) != length) { + _exit(100); + } + written += length; + + /* Write second half of the alert message */ + record_header[0] = TLS_ALERT; + record_header[3] = 0; + record_header[4] = 1; + + if (write(write_fd, record_header, 5) != 5) { + _exit(100); + } + if (write(write_fd, fatal_alert + 1, 1) != 1) { + _exit(100); + } + + /* Write the rest of the hello message */ + length = sizeof(server_hello_message) - written; + record_header[0] = TLS_HANDSHAKE; + record_header[3] = length >> 8; + record_header[4] = length & 0xff; + if (write(write_fd, record_header, 5) != 5) { + _exit(100); + } + if (write(write_fd, server_hello_message + written, length) != length) { + _exit(100); + } + + /* Close the pipe and exit */ + close(write_fd); +} + +void interleaved_fragmented_warning_alert(int write_fd) +{ + int length = sizeof(server_hello_message) / 2; + uint8_t record_header[5] = { TLS_HANDSHAKE, 0x03, 0x03, (length >> 8), length & 0xff }; + int written = 0; + + /* Write half of the message */ + if (write(write_fd, record_header, 5) != 5) { + _exit(100); + } + if (write(write_fd, server_hello_message, length) != length) { + _exit(100); + } + written += length; + + /* Write half of the alert message */ + record_header[0] = TLS_ALERT; + record_header[3] = 0; + record_header[4] = 1; + if (write(write_fd, warning_alert, 1) != 1) { + _exit(100); + } + + /* Write another quarter of the of the hello message */ + length = sizeof(server_hello_message) / 4; + record_header[0] = TLS_HANDSHAKE; + record_header[3] = length >> 8; + record_header[4] = length & 0xff; + if (write(write_fd, record_header, 5) != 5) { + _exit(100); + } + if (write(write_fd, server_hello_message + written, length) != length) { + _exit(100); + } + written += length; + + /* Write second half of the alert message */ + record_header[0] = TLS_ALERT; + record_header[3] = 0; + record_header[4] = 1; + + if (write(write_fd, record_header, 5) != 5) { + _exit(100); + } + if (write(write_fd, warning_alert + 1, 1) != 1) { + _exit(100); + } + + /* Write the rest of the hello message */ + length = sizeof(server_hello_message) - written; + record_header[0] = TLS_HANDSHAKE; + record_header[3] = length >> 8; + record_header[4] = length & 0xff; + if (write(write_fd, record_header, 5) != 5) { + _exit(100); + } + if (write(write_fd, server_hello_message + written, length) != length) { + _exit(100); + } + + /* Close the pipe and exit */ + close(write_fd); +} + +int main(int argc, char **argv) +{ + struct s2n_connection *conn; + struct s2n_config *config; + + s2n_blocked_status blocked; + int status; + pid_t pid; + int p[2]; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(config, 0)); + /* The server hello has TLS_RSA_WITH_AES_256_CBC_SHA256 hardcoded, + so we need to set a cipher preference that will accept that value */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20170328")); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + + /* Create a pipe */ + EXPECT_SUCCESS(pipe(p)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_read_fd(conn, p[0])); + + /* Pretend the client hello has already been set */ + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; + conn->handshake.message_number = SERVER_HELLO; + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the child process, close the read end of the pipe */ + EXPECT_SUCCESS(close(p[0])); + + /* Write the fragmented hello message */ + fragmented_message(p[1]); + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + exit(0); + } + + /* This is the parent process, close the write end of the pipe */ + EXPECT_SUCCESS(close(p[1])); + + /* Negotiate the handshake. This will fail due to EOF, but that's ok. */ + EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); + + /* Verify that the data is as we expect it */ + EXPECT_EQUAL(memcmp(conn->handshake_params.server_random, zero_to_thirty_one, 32), 0); + + /* Check that the server hello message was processed */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_CERT); + + /* Clean up */ + EXPECT_EQUAL(waitpid(pid, &status, 0), pid); + EXPECT_EQUAL(status, 0); + EXPECT_SUCCESS(close(p[0])); + + /* Create a pipe */ + EXPECT_SUCCESS(pipe(p)); + + /* Wipe the connection */ + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_read_fd(conn, p[0])); + + /* Pretend the client hello has already been set */ + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; + conn->handshake.message_number = SERVER_HELLO; + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the child process, close the read end of the pipe */ + EXPECT_SUCCESS(close(p[0])); + + /* Write the fragmented hello message */ + coalesced_message(p[1]); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + exit(0); + } + + /* This is the parent process, close the write end of the pipe */ + EXPECT_SUCCESS(close(p[1])); + + /* Negotiate the handshake. This will fail due to EOF, but that's ok. */ + EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); + + /* Verify that the data is as we expect it */ + EXPECT_EQUAL(memcmp(conn->handshake_params.server_random, zero_to_thirty_one, 32), 0); + + /* Check that the server done message was processed */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_HELLO_DONE); + + /* Clean up */ + EXPECT_EQUAL(waitpid(pid, &status, 0), pid); + EXPECT_EQUAL(status, 0); + EXPECT_SUCCESS(close(p[0])); + + /* Create a pipe */ + EXPECT_SUCCESS(pipe(p)); + + /* Wipe the connection */ + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_read_fd(conn, p[0])); + + /* Pretend the client hello has already been set */ + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; + conn->handshake.message_number = SERVER_HELLO; + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the child process, close the read end of the pipe */ + EXPECT_SUCCESS(close(p[0])); + + /* Write the fragmented hello message */ + interleaved_message(p[1]); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + exit(0); + } + + /* This is the parent process, close the write end of the pipe */ + EXPECT_SUCCESS(close(p[1])); + + /* Negotiate the handshake. This will fail due to EOF, but that's ok. */ + EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); + + /* Verify that the data is as we expect it */ + EXPECT_EQUAL(memcmp(conn->handshake_params.server_random, zero_to_thirty_one, 32), 0); + + /* Check that the server hello message was processed */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_CERT); + + /* Clean up */ + EXPECT_EQUAL(waitpid(pid, &status, 0), pid); + EXPECT_EQUAL(status, 0); + EXPECT_SUCCESS(close(p[0])); + + /* Create a pipe */ + EXPECT_SUCCESS(pipe(p)); + + /* Wipe the connection */ + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_read_fd(conn, p[0])); + + /* Pretend the client hello has already been set */ + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; + conn->handshake.message_number = SERVER_HELLO; + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the child process, close the read end of the pipe */ + EXPECT_SUCCESS(close(p[0])); + + /* Write the fragmented hello message */ + interleaved_fragmented_warning_alert(p[1]); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + exit(0); + } + + /* This is the parent process, close the write end of the pipe */ + EXPECT_SUCCESS(close(p[1])); + + /* Negotiate the handshake. This will fail due to EOF, but that's ok. */ + EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); + + /* Verify that the data is as we expect it */ + EXPECT_NOT_EQUAL(memcmp(conn->handshake_params.server_random, zero_to_thirty_one, 32), 0); + + /* Check that the server hello message was not processed */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_HELLO); + + /* Clean up */ + EXPECT_EQUAL(waitpid(pid, &status, 0), pid); + EXPECT_EQUAL(status, 0); + EXPECT_SUCCESS(close(p[0])); + + /* Create a pipe */ + EXPECT_SUCCESS(pipe(p)); + + /* Wipe the connection */ + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_read_fd(conn, p[0])); + + /* Pretend the client hello has already been set */ + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; + conn->handshake.message_number = SERVER_HELLO; + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the child process, close the read end of the pipe */ + EXPECT_SUCCESS(close(p[0])); + + /* Write the fragmented hello message */ + interleaved_fragmented_fatal_alert(p[1]); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + exit(0); + } + + /* This is the parent process, close the write end of the pipe */ + EXPECT_SUCCESS(close(p[1])); + + /* Negotiate the handshake. This will fail due to EOF, but that's ok. */ + EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); + + /* Verify that the data failed */ + EXPECT_NOT_EQUAL(memcmp(conn->handshake_params.server_random, zero_to_thirty_one, 32), 0); + + /* Check that the server hello message was not processed */ + EXPECT_NOT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_CERT); + + /* Clean up */ + EXPECT_EQUAL(waitpid(pid, &status, 0), pid); + EXPECT_EQUAL(status, 0); + EXPECT_SUCCESS(close(p[0])); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + + END_TEST(); +} diff --git a/tests/unit/s2n_handshake_errno_test.c b/tests/unit/s2n_handshake_errno_test.c new file mode 100644 index 00000000000..22539f6ff7d --- /dev/null +++ b/tests/unit/s2n_handshake_errno_test.c @@ -0,0 +1,87 @@ +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "tls/s2n_connection.h" + +#define RANDOM_ERRNO 150 + +int fake_recv(void *io_context, uint8_t *buf, uint32_t len) +{ + /* Pretend that we have no data available to read for alert lookup. */ + errno = EAGAIN; + return -1; +} + +int fake_send(void *io_context, const uint8_t *buf, uint32_t len) +{ + /* Fail the write with non-retriable error. */ + errno = ENOENT; + return -1; +} + +int main(int argc, char **argv) +{ + struct s2n_connection *conn; + s2n_blocked_status blocked; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Non-retriable errnos in io are not overwritten by retriable errnos */ + { + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + /* Set custom recv/send callbacks. */ + EXPECT_SUCCESS(s2n_connection_set_recv_cb(conn, &fake_recv)); + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, &fake_send)); + + /* Perform the handshake and expect an error from write, instead of error from alert read. */ + EXPECT_EQUAL(s2n_negotiate(conn, &blocked), -1); + EXPECT_EQUAL(errno, ENOENT); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO); + s2n_errno = 0; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* s2n_negotiate should not overwrite a non-retriable s2n_error with a retriable s2n_error. + * This previously happened if errno was set to a retriable error before s2n_negotiate + * was called, and a non-retriable error occurred in an io function before errno was + * reset for the system call. */ + { + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + /* Set errno to a random value */ + errno = RANDOM_ERRNO; + + /* Break io setup. Will fail a non-null check before io system call. */ + conn->recv = NULL; + + /* Perform the handshake and expect an s2n_error */ + /* Do not use EXPECT_FAILURE here -- it resets s2n_errno */ + EXPECT_EQUAL(s2n_negotiate(conn, &blocked), -1); + EXPECT_EQUAL(errno, 0); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO); + s2n_errno = 0; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_handshake_fragment_test.c b/tests/unit/s2n_handshake_fragment_test.c new file mode 100644 index 00000000000..f85af77fba9 --- /dev/null +++ b/tests/unit/s2n_handshake_fragment_test.c @@ -0,0 +1,373 @@ +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +#define S2N_FRAG_LEN_SMALLER_THAN_CH 150 + +#define TIMES_TO_BLOCK 3 + +/* Handling blocked IO is important to fragmentation. + * Even if we fail to read or write one fragment, we should be able to + * retry and eventually write all fragments. + * + * Therefore, wrap our normal test IO in special logic to block + * repeatedly on every read and write call. + */ +struct s2n_io_wrapper { + uint8_t times_recv_blocked; + uint8_t times_send_blocked; + s2n_recv_fn *inner_recv; + s2n_send_fn *inner_send; + void *inner_recv_ctx; + void *inner_send_ctx; +}; + +struct s2n_io_wrapper_pair { + struct s2n_io_wrapper client; + struct s2n_io_wrapper server; +}; + +static int s2n_blocking_read(void *io_context, uint8_t *buf, uint32_t len) +{ + struct s2n_io_wrapper *context = (struct s2n_io_wrapper *) io_context; + if (context->times_recv_blocked < TIMES_TO_BLOCK) { + context->times_recv_blocked++; + errno = EAGAIN; + return -1; + } + context->times_recv_blocked = 0; + return context->inner_recv(context->inner_recv_ctx, buf, len); +} + +static int s2n_blocking_write(void *io_context, const uint8_t *buf, uint32_t len) +{ + struct s2n_io_wrapper *context = (struct s2n_io_wrapper *) io_context; + if (context->times_send_blocked < TIMES_TO_BLOCK) { + context->times_send_blocked++; + errno = EAGAIN; + return -1; + } + context->times_send_blocked = 0; + return context->inner_send(context->inner_send_ctx, buf, len); +} + +static int s2n_client_hello_test_fn(struct s2n_connection *conn, void *ctx) +{ + return S2N_SUCCESS; +} + +struct s2n_async_pkey_op *pkey_op = NULL; +static int async_pkey_test_fn(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + pkey_op = op; + + /* Extract pkey */ + struct s2n_cert_chain_and_key *chain_and_key = s2n_connection_get_selected_cert(conn); + POSIX_ENSURE_REF(chain_and_key); + + s2n_cert_private_key *pkey = s2n_cert_chain_and_key_get_private_key(chain_and_key); + POSIX_ENSURE_REF(pkey); + + /* Perform, but don't apply yet. We want the handshake to block. */ + EXPECT_SUCCESS(s2n_async_pkey_op_perform(pkey_op, pkey)); + + return S2N_SUCCESS; +} + +static struct s2n_config *s2n_test_config_new(struct s2n_cert_chain_and_key *chain_and_key) +{ + struct s2n_config *config = s2n_config_new(); + PTR_GUARD_POSIX(s2n_config_set_cipher_preferences(config, "default_tls13")); + PTR_GUARD_POSIX(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + PTR_GUARD_POSIX(s2n_config_disable_x509_verification(config)); + return config; +} + +static S2N_RESULT s2n_connections_set_blocking_io_pair(struct s2n_io_wrapper_pair *io_context, + struct s2n_connection *client_conn, struct s2n_connection *server_conn, struct s2n_test_io_stuffer_pair *io_pair) +{ + RESULT_GUARD(s2n_io_stuffer_pair_init(io_pair)); + RESULT_GUARD(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, io_pair)); + + io_context->client = (struct s2n_io_wrapper){ + .inner_recv = client_conn->recv, + .inner_send = client_conn->send, + .inner_recv_ctx = client_conn->recv_io_context, + .inner_send_ctx = client_conn->send_io_context, + }; + + RESULT_GUARD_POSIX(s2n_connection_set_recv_cb(client_conn, s2n_blocking_read)); + RESULT_GUARD_POSIX(s2n_connection_set_recv_ctx(client_conn, &io_context->client)); + RESULT_GUARD_POSIX(s2n_connection_set_send_cb(client_conn, s2n_blocking_write)); + RESULT_GUARD_POSIX(s2n_connection_set_send_ctx(client_conn, &io_context->client)); + + io_context->server = (struct s2n_io_wrapper){ + .inner_recv = server_conn->recv, + .inner_send = server_conn->send, + .inner_recv_ctx = server_conn->recv_io_context, + .inner_send_ctx = server_conn->send_io_context, + }; + + RESULT_GUARD_POSIX(s2n_connection_set_recv_cb(server_conn, s2n_blocking_read)); + RESULT_GUARD_POSIX(s2n_connection_set_recv_ctx(server_conn, &io_context->server)); + RESULT_GUARD_POSIX(s2n_connection_set_send_cb(server_conn, s2n_blocking_write)); + RESULT_GUARD_POSIX(s2n_connection_set_send_ctx(server_conn, &io_context->server)); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + const uint32_t fragment_sizes[] = { + 1, + 2, + TLS_HANDSHAKE_HEADER_LENGTH, + TLS_HANDSHAKE_HEADER_LENGTH + 1, + S2N_FRAG_LEN_SMALLER_THAN_CH, + S2N_DEFAULT_FRAGMENT_LENGTH, + }; + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); + + /* Test sending and receiving fragmented handshake messages */ + for (size_t i = 0; i < s2n_array_len(fragment_sizes); i++) { + /* Use different fragment sizes for the client and server, + * to ensure that they handle outgoing and incoming fragment sizes separately. + */ + uint32_t server_fragment_size = fragment_sizes[i]; + uint32_t client_fragment_size = fragment_sizes[i] + 1; + + /* Test: basic TLS1.3 handshake with fragmented messages */ + if (s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_config *config = s2n_test_config_new(chain_and_key), + s2n_config_ptr_free); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + server_conn->max_outgoing_fragment_length = server_fragment_size; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + client_conn->max_outgoing_fragment_length = client_fragment_size; + + struct s2n_io_wrapper_pair io_wrapper = { 0 }; + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_connections_set_blocking_io_pair(&io_wrapper, client_conn, server_conn, &io_pair)); + + while (s2n_negotiate_test_server_and_client(server_conn, client_conn) < S2N_SUCCESS) { + POSIX_ENSURE(s2n_errno, S2N_ERR_IO_BLOCKED); + } + + /* Handshake completed */ + EXPECT_TRUE(IS_NEGOTIATED(server_conn)); + EXPECT_TRUE(IS_NEGOTIATED(client_conn)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); + + /* TLS1.3 negotiated */ + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + }; + + /* Test: basic TLS1.2 handshake with fragmented messages */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_test_config_new(chain_and_key), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all_tls12")); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + server_conn->max_outgoing_fragment_length = server_fragment_size; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + client_conn->max_outgoing_fragment_length = client_fragment_size; + + struct s2n_io_wrapper_pair io_wrapper = { 0 }; + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_connections_set_blocking_io_pair(&io_wrapper, client_conn, server_conn, &io_pair)); + + while (s2n_negotiate_test_server_and_client(server_conn, client_conn) < S2N_SUCCESS) { + POSIX_ENSURE(s2n_errno, S2N_ERR_IO_BLOCKED); + } + + /* Handshake completed */ + EXPECT_TRUE(IS_NEGOTIATED(server_conn)); + EXPECT_TRUE(IS_NEGOTIATED(client_conn)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); + + /* TLS1.2 negotiated */ + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + }; + + /* Test: handshake with reader async callback and fragmented messages + * + * Resuming the handshake after an async callback follows a different code path. + * + * We use the client hello callback because it triggers when reading the client hello message. + */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_test_config_new(chain_and_key), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_client_hello_test_fn, NULL)); + EXPECT_SUCCESS(s2n_config_set_client_hello_cb_mode(config, S2N_CLIENT_HELLO_CB_NONBLOCKING)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + server_conn->max_outgoing_fragment_length = server_fragment_size; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + client_conn->max_outgoing_fragment_length = client_fragment_size; + + struct s2n_io_wrapper_pair io_wrapper = { 0 }; + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_connections_set_blocking_io_pair(&io_wrapper, client_conn, server_conn, &io_pair)); + + bool async_block_triggered = false; + while (s2n_negotiate_test_server_and_client(server_conn, client_conn) < S2N_SUCCESS) { + if (s2n_errno == S2N_ERR_ASYNC_BLOCKED) { + EXPECT_SUCCESS(s2n_client_hello_cb_done(server_conn)); + async_block_triggered = true; + } else { + POSIX_ENSURE(s2n_errno, S2N_ERR_IO_BLOCKED); + } + } + EXPECT_TRUE(async_block_triggered); + + /* Handshake completed */ + EXPECT_TRUE(IS_NEGOTIATED(server_conn)); + EXPECT_TRUE(IS_NEGOTIATED(client_conn)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); + }; + + /* Test: handshake with writer async callback and fragmented messages + * + * Resuming the handshake after an async callback follows a different code path. + * + * We use the async pkey callback because it triggers when writing the server cert verify message. + * It would also trigger for the client when reading the server cert verify message, + * except that this test disables x509 validation. + */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_test_config_new(chain_and_key), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(config, async_pkey_test_fn)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + server_conn->max_outgoing_fragment_length = server_fragment_size; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + client_conn->max_outgoing_fragment_length = client_fragment_size; + + struct s2n_io_wrapper_pair io_wrapper = { 0 }; + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_connections_set_blocking_io_pair(&io_wrapper, client_conn, server_conn, &io_pair)); + + bool async_block_triggered = false; + while (s2n_negotiate_test_server_and_client(server_conn, client_conn) < S2N_SUCCESS) { + if (s2n_errno == S2N_ERR_ASYNC_BLOCKED) { + EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, server_conn)); + EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); + async_block_triggered = true; + } else { + POSIX_ENSURE(s2n_errno, S2N_ERR_IO_BLOCKED); + } + } + EXPECT_TRUE(async_block_triggered); + + /* Handshake completed */ + EXPECT_TRUE(IS_NEGOTIATED(server_conn)); + EXPECT_TRUE(IS_NEGOTIATED(client_conn)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); + }; + + /* Test: handshake with early data and fragmented messages */ + if (s2n_is_tls13_fully_supported()) { + uint8_t early_data_bytes[] = "hello world"; + struct s2n_blob early_data = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&early_data, early_data_bytes, sizeof(early_data_bytes))); + + DEFER_CLEANUP(struct s2n_config *config = s2n_test_config_new(chain_and_key), + s2n_config_ptr_free); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_OK(s2n_append_test_psk_with_early_data(server_conn, early_data.size, &s2n_tls13_aes_256_gcm_sha384)); + server_conn->max_outgoing_fragment_length = server_fragment_size; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_OK(s2n_append_test_psk_with_early_data(client_conn, early_data.size, &s2n_tls13_aes_256_gcm_sha384)); + client_conn->max_outgoing_fragment_length = client_fragment_size; + + struct s2n_io_wrapper_pair io_wrapper = { 0 }; + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_connections_set_blocking_io_pair(&io_wrapper, client_conn, server_conn, &io_pair)); + + uint8_t recv_buffer[sizeof(early_data_bytes)] = { 0 }; + struct s2n_blob early_data_received = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&early_data_received, recv_buffer, sizeof(recv_buffer))); + + EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_test_server_and_client_with_early_data(server_conn, client_conn, + &early_data, &early_data_received), + S2N_ERR_IO_BLOCKED); + + /* All early data received */ + EXPECT_TRUE(WITH_EARLY_DATA(server_conn)); + EXPECT_TRUE(WITH_EARLY_DATA(client_conn)); + S2N_BLOB_EXPECT_EQUAL(early_data, early_data_received); + + while (s2n_negotiate_test_server_and_client(server_conn, client_conn) < S2N_SUCCESS) { + POSIX_ENSURE(s2n_errno, S2N_ERR_IO_BLOCKED); + } + + /* Handshake completed */ + EXPECT_TRUE(IS_NEGOTIATED(server_conn)); + EXPECT_TRUE(IS_NEGOTIATED(client_conn)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); + }; + } + + END_TEST(); +} diff --git a/tests/unit/s2n_handshake_hashes_test.c b/tests/unit/s2n_handshake_hashes_test.c new file mode 100644 index 00000000000..caa4cbd9a96 --- /dev/null +++ b/tests/unit/s2n_handshake_hashes_test.c @@ -0,0 +1,154 @@ +/* + * 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 "tls/s2n_handshake_hashes.h" + +#include "s2n_test.h" +#include "tls/s2n_connection.h" + +/* Needed for s2n_handshake_get_hash_state_ptr */ +#include "tls/s2n_handshake.c" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Test s2n_handshake_hashes_new */ + { + /* Safety */ + EXPECT_ERROR_WITH_ERRNO(s2n_handshake_hashes_new(NULL), S2N_ERR_NULL); + + /* Allocates a new s2n_handshake_hashes struct */ + { + /* Allocates the struct */ + struct s2n_connection conn = { 0 }; + EXPECT_NULL(conn.handshake.hashes); + EXPECT_OK(s2n_handshake_hashes_new(&conn.handshake.hashes)); + EXPECT_NOT_NULL(conn.handshake.hashes); + + uint8_t data[100] = { 0 }; + + /* Allocates all hashes */ + for (s2n_hash_algorithm alg = 0; alg < S2N_HASH_SENTINEL; alg++) { + if (alg == S2N_HASH_NONE) { + continue; + } + + uint8_t size = 0; + EXPECT_SUCCESS(s2n_hash_digest_size(alg, &size)); + EXPECT_TRUE(size < sizeof(data)); + + struct s2n_hash_state *hash_state = NULL; + EXPECT_SUCCESS(s2n_handshake_get_hash_state_ptr(&conn, alg, &hash_state)); + + /* Hash is setup / useable */ + EXPECT_SUCCESS(s2n_hash_digest(hash_state, data, size)); + } + + s2n_handshake_hashes_free(&conn.handshake.hashes); + }; + }; + + /* Test s2n_handshake_hashes_wipe */ + { + /* Safety */ + EXPECT_ERROR_WITH_ERRNO(s2n_handshake_hashes_wipe(NULL), S2N_ERR_NULL); + + /* Resets all hashes */ + { + struct s2n_connection conn = { 0 }; + EXPECT_OK(s2n_handshake_hashes_new(&conn.handshake.hashes)); + EXPECT_NOT_NULL(conn.handshake.hashes); + + uint8_t data[100] = { 0 }; + + for (s2n_hash_algorithm alg = 0; alg < S2N_HASH_SENTINEL; alg++) { + if (alg == S2N_HASH_NONE) { + continue; + } + + uint8_t size = 0; + EXPECT_SUCCESS(s2n_hash_digest_size(alg, &size)); + EXPECT_TRUE(size < sizeof(data)); + + struct s2n_hash_state *hash_state = NULL; + EXPECT_SUCCESS(s2n_handshake_get_hash_state_ptr(&conn, alg, &hash_state)); + EXPECT_SUCCESS(s2n_hash_digest(hash_state, data, size)); + + /* Can't calculate the digest again: only one digest allowed per hash */ + EXPECT_FAILURE_WITH_ERRNO(s2n_hash_digest(hash_state, data, size), S2N_ERR_HASH_NOT_READY); + + /* Wiping the hashes allows them to be successfully reused */ + EXPECT_OK(s2n_handshake_hashes_wipe(conn.handshake.hashes)); + EXPECT_SUCCESS(s2n_hash_digest(hash_state, data, size)); + } + + s2n_handshake_hashes_free(&conn.handshake.hashes); + }; + }; + + /* Test s2n_handshake_hashes_free */ + { + /* Safety */ + EXPECT_ERROR_WITH_ERRNO(s2n_handshake_hashes_free(NULL), S2N_ERR_NULL); + + /* Frees the hashes with no memory leaks */ + { + struct s2n_handshake_hashes *hashes = NULL; + EXPECT_OK(s2n_handshake_hashes_new(&hashes)); + EXPECT_NOT_NULL(hashes); + + EXPECT_OK(s2n_handshake_hashes_free(&hashes)); + EXPECT_NULL(hashes); + }; + }; + + /* Test s2n_handshake_hashes connection lifecycle */ + { + uint8_t digest[SHA256_DIGEST_LENGTH] = { 0 }; + + /* A new connection's hashes are properly set up and can be used. */ + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + struct s2n_hash_state *hash_state = NULL; + EXPECT_SUCCESS(s2n_handshake_get_hash_state_ptr(conn, S2N_HASH_SHA256, &hash_state)); + EXPECT_NOT_NULL(hash_state); + EXPECT_SUCCESS(s2n_hash_digest(hash_state, digest, SHA256_DIGEST_LENGTH)); + EXPECT_FAILURE_WITH_ERRNO(s2n_hash_digest(hash_state, digest, SHA256_DIGEST_LENGTH), S2N_ERR_HASH_NOT_READY); + + /* A wiped connection's hashes can be reused. */ + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_SUCCESS(s2n_handshake_get_hash_state_ptr(conn, S2N_HASH_SHA256, &hash_state)); + EXPECT_NOT_NULL(hash_state); + EXPECT_SUCCESS(s2n_hash_digest(hash_state, digest, SHA256_DIGEST_LENGTH)); + + /* Freeing the handshake frees the hashes */ + EXPECT_SUCCESS(s2n_connection_free_handshake(conn)); + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_get_hash_state_ptr(conn, S2N_HASH_SHA256, &hash_state), S2N_ERR_NULL); + EXPECT_NULL(conn->handshake.hashes); + + /* Wiping the connection should restore the freed hashes */ + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_SUCCESS(s2n_handshake_get_hash_state_ptr(conn, S2N_HASH_SHA256, &hash_state)); + EXPECT_NOT_NULL(hash_state); + EXPECT_SUCCESS(s2n_hash_digest(hash_state, digest, SHA256_DIGEST_LENGTH)); + + /* Freeing the connection should free the hashes */ + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_handshake_invariant_test.c b/tests/unit/s2n_handshake_invariant_test.c new file mode 100644 index 00000000000..a99837f6340 --- /dev/null +++ b/tests/unit/s2n_handshake_invariant_test.c @@ -0,0 +1,89 @@ +/* + * 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 +#include +#include + +#include "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "crypto/s2n_rsa_pss.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_preferences.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_handshake_io.c" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +/* A full record that has an "APPLICATION_DATA" inside according to s2n's + * handshake message_types. + */ +uint8_t record[] = { + /* Record type HANDSHAKE */ + 0x16, + /* Protocol version TLS 1.2 */ + 0x03, 0x03, + /* record len */ + 0x00, 0x05, + /* Type(s2n has this as expected message type for APPLICATION_DATA handler. + * This is not a standardized value, just something s2n has hardcoded as a placeholder + * For the APPLICATON_DATA state in the state machine. + */ + 0x00, + /* Len */ + 0x00, 0x00, 0x01, + /* Data */ + 0x00 +}; +static int amt_written = 0; + +int s2n_app_data_in_handshake_record_recv_fn(void *io_context, uint8_t *buf, uint32_t len) +{ + int amt_left = sizeof(record) - amt_written; + int to_write = MIN(len, amt_left); + POSIX_CHECKED_MEMCPY(buf, record + amt_written, to_write); + amt_written += to_write; + return to_write; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + /* Initialize *some* handshake type. Not terribly relevant for this test. */ + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY; + /* Fast forward the handshake state machine to the end of this "handshake_type". + * APPLICATION_DATA is the 11th state for "NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY". + */ + conn->handshake.message_number = 10; + conn->actual_protocol_version = S2N_TLS12; + /* Provide the crafted record to s2n's I/O */ + s2n_connection_set_recv_cb(conn, s2n_app_data_in_handshake_record_recv_fn); + + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + + s2n_connection_free(conn); + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_handshake_io_async_test.c b/tests/unit/s2n_handshake_io_async_test.c new file mode 100644 index 00000000000..15c6a16ac91 --- /dev/null +++ b/tests/unit/s2n_handshake_io_async_test.c @@ -0,0 +1,117 @@ +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_result.h" + +/* Get access to s2n_handshake_read_io */ +#include "tls/s2n_handshake_io.c" + +bool async_blocked = false; +size_t blocking_handler_count = 0; +static int s2n_blocking_handler(struct s2n_connection *conn) +{ + blocking_handler_count++; + if (async_blocked) { + POSIX_BAIL(S2N_ERR_ASYNC_BLOCKED); + } + return S2N_SUCCESS; +} + +static int s2n_error_handler(struct s2n_connection *conn) +{ + POSIX_BAIL(S2N_ERR_UNIMPLEMENTED); +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Async blocking errors block handshake negotiation */ + { + const size_t repeat_count = 10; + + DEFER_CLEANUP(struct s2n_stuffer io_buffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&io_buffer, 0)); + + /* Write handles async blocking */ + { + tls13_state_machine[CLIENT_HELLO].handler[S2N_CLIENT] = s2n_blocking_handler; + tls13_state_machine[SERVER_HELLO].handler[S2N_CLIENT] = s2n_error_handler; + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(NULL, &io_buffer, conn)); + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + /* Consistently blocks */ + async_blocked = true; + blocking_handler_count = 0; + for (size_t i = 0; i < repeat_count; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked), S2N_ERR_ASYNC_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_INPUT); + EXPECT_EQUAL(s2n_stuffer_data_available(&io_buffer), 0); + } + EXPECT_EQUAL(blocking_handler_count, repeat_count); + + /* If unblocked, continues. Fails to read next message because there is no next message. */ + async_blocked = false; + blocking_handler_count = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked), S2N_ERR_IO); + EXPECT_EQUAL(blocking_handler_count, 1); + + /* Only wrote one record/message */ + EXPECT_EQUAL(s2n_stuffer_data_available(&io_buffer), S2N_TLS_RECORD_HEADER_LENGTH + TLS_HANDSHAKE_HEADER_LENGTH); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Use the output of writing to test reading */ + EXPECT_SUCCESS(s2n_stuffer_reread(&io_buffer)); + + /* Read handles async blocking */ + { + state_machine[CLIENT_HELLO].handler[S2N_SERVER] = s2n_blocking_handler; + state_machine[SERVER_HELLO].handler[S2N_SERVER] = s2n_error_handler; + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&io_buffer, NULL, conn)); + + /* Consistently blocks */ + async_blocked = true; + blocking_handler_count = 0; + for (size_t i = 0; i < repeat_count; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked), S2N_ERR_ASYNC_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_INPUT); + EXPECT_EQUAL(s2n_stuffer_data_available(&io_buffer), 0); + } + EXPECT_EQUAL(blocking_handler_count, repeat_count); + + /* If unblocked, continues */ + async_blocked = false; + blocking_handler_count = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked), S2N_ERR_UNIMPLEMENTED); + EXPECT_EQUAL(blocking_handler_count, 1); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_handshake_io_early_data_test.c b/tests/unit/s2n_handshake_io_early_data_test.c new file mode 100644 index 00000000000..5d99ec1e492 --- /dev/null +++ b/tests/unit/s2n_handshake_io_early_data_test.c @@ -0,0 +1,129 @@ +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_result.h" + +/* Get access to s2n_handshake_read_io */ +#include "tls/s2n_handshake_io.c" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + uint8_t bad_record[] = { + 0x17, /* ContentType opaque_type = application_data */ + 0x03, 0x03, /* ProtocolVersion legacy_record_version = 0x0303 */ + 0x00, 0x10, /* uint16 length */ + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, /* opaque encrypted_record[TLSCiphertext.length] */ + }; + + struct s2n_cipher_suite *test_cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + uint8_t test_key_bytes[S2N_TLS13_SECRET_MAX_LEN] = "gibberish key"; + struct s2n_blob test_key = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&test_key, test_key_bytes, + test_cipher_suite->record_alg->cipher->key_material_size)); + + /** + *= https://tools.ietf.org/rfc/rfc8446#section-4.2.10 + *= type=test + *# 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. + */ + { + /* Server */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + server_conn->secure->cipher_suite = test_cipher_suite; + POSIX_GUARD(server_conn->secure->cipher_suite->record_alg->cipher->init(&server_conn->secure->client_key)); + POSIX_GUARD(server_conn->secure->cipher_suite->record_alg->cipher->set_decryption_key(&server_conn->secure->client_key, &test_key)); + server_conn->client = server_conn->secure; + + DEFER_CLEANUP(struct s2n_stuffer io_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&io_stuffer, S2N_DEFAULT_RECORD_LENGTH)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&io_stuffer, &io_stuffer, server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&io_stuffer, bad_record, sizeof(bad_record))); + + /* Fail for bad record if early data was not requested */ + { + EXPECT_SUCCESS(s2n_stuffer_reread(&io_stuffer)); + server_conn->early_data_state = S2N_EARLY_DATA_NOT_REQUESTED; + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(server_conn), S2N_ERR_DECRYPT); + }; + + /* Fail for bad record if early data was accepted */ + { + EXPECT_SUCCESS(s2n_stuffer_reread(&io_stuffer)); + server_conn->early_data_state = S2N_EARLY_DATA_ACCEPTED; + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(server_conn), S2N_ERR_DECRYPT); + }; + + /* Succeed for bad record if early data was rejected */ + { + EXPECT_SUCCESS(s2n_stuffer_reread(&io_stuffer)); + server_conn->early_data_state = S2N_EARLY_DATA_REJECTED; + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + }; + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Client */ + { + /* Fail for bad record if early data was rejected. + * Clients send early data but do not receive it, so a bad record is still an error. */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + client_conn->secure->cipher_suite = test_cipher_suite; + POSIX_GUARD(client_conn->secure->cipher_suite->record_alg->cipher->init(&client_conn->secure->server_key)); + POSIX_GUARD(client_conn->secure->cipher_suite->record_alg->cipher->set_decryption_key(&client_conn->secure->server_key, &test_key)); + client_conn->server = client_conn->secure; + + DEFER_CLEANUP(struct s2n_stuffer io_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&io_stuffer, S2N_DEFAULT_RECORD_LENGTH)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&io_stuffer, &io_stuffer, client_conn)); + + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&io_stuffer, bad_record, sizeof(bad_record))); + + client_conn->early_data_state = S2N_EARLY_DATA_REJECTED; + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(client_conn), S2N_ERR_DECRYPT); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_handshake_io_errors_test.c b/tests/unit/s2n_handshake_io_errors_test.c new file mode 100644 index 00000000000..54ef3c49aa2 --- /dev/null +++ b/tests/unit/s2n_handshake_io_errors_test.c @@ -0,0 +1,129 @@ +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_result.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* IO blocking on read does not close connection or invoke blinding */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + + DEFER_CLEANUP(struct s2n_stuffer io_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&io_stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&io_stuffer, &io_stuffer, server_conn)); + + /* Try to read the ClientHello, which hasn't been written yet */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &blocked), S2N_ERR_IO_BLOCKED); + + /* Error did not close connection */ + EXPECT_TRUE(s2n_connection_check_io_status(server_conn, S2N_IO_FULL_DUPLEX)); + + /* Error did not trigger blinding */ + EXPECT_EQUAL(s2n_connection_get_delay(server_conn), 0); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Failure in read handler closes connection and invokes blinding */ + { + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + + DEFER_CLEANUP(struct s2n_stuffer io_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&io_stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&io_stuffer, &io_stuffer, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&io_stuffer, &io_stuffer, server_conn)); + + /* Write the ClientHello */ + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Overwrite everything except the headers */ + uint32_t content_size = s2n_stuffer_data_available(&io_stuffer) + - S2N_TLS_RECORD_HEADER_LENGTH - TLS_HANDSHAKE_HEADER_LENGTH; + EXPECT_SUCCESS(s2n_stuffer_wipe_n(&io_stuffer, content_size)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&io_stuffer, content_size)); + + /* Read the ClientHello */ + EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_until_message(server_conn, &blocked, SERVER_HELLO), + S2N_ERR_BAD_MESSAGE); + + /* Error closes connection */ + EXPECT_TRUE(s2n_connection_check_io_status(server_conn, S2N_IO_CLOSED)); + + /* Error triggers blinding */ + EXPECT_NOT_EQUAL(s2n_connection_get_delay(server_conn), 0); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Decrypt failure closes connection and invokes blinding */ + { + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + + DEFER_CLEANUP(struct s2n_stuffer io_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&io_stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&io_stuffer, &io_stuffer, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&io_stuffer, &io_stuffer, server_conn)); + + /* Write the ClientHello */ + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Set up encryption on the server */ + EXPECT_OK(s2n_connection_set_secrets(server_conn)); + + /* Read the ClientHello */ + EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_until_message(server_conn, &blocked, SERVER_HELLO), + S2N_ERR_DECRYPT); + + /* Error closes connection */ + EXPECT_TRUE(s2n_connection_check_io_status(server_conn, S2N_IO_CLOSED)); + + /* Error triggers blinding */ + EXPECT_NOT_EQUAL(s2n_connection_get_delay(server_conn), 0); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_handshake_io_test.c b/tests/unit/s2n_handshake_io_test.c new file mode 100644 index 00000000000..4885abeae2a --- /dev/null +++ b/tests/unit/s2n_handshake_io_test.c @@ -0,0 +1,61 @@ +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_result.h" + +bool s2n_custom_send_fn_called = false; +static int s2n_expect_concurrent_error_send_fn(void *io_context, const uint8_t *buf, uint32_t len) +{ + struct s2n_connection *conn = (struct s2n_connection *) io_context; + s2n_custom_send_fn_called = true; + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t result = s2n_negotiate(conn, &blocked); + EXPECT_FAILURE_WITH_ERRNO(result, S2N_ERR_REENTRANCY); + return result; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* s2n_negotiate can't be called recursively */ + { + /* Setup connections */ + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + /* Setup bad callback */ + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_expect_concurrent_error_send_fn)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) conn)); + + /* Negotiate. When we attempt to send the ClientHello, the send callback + * should fail due to a reentrancy error. + */ + s2n_custom_send_fn_called = false; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked), S2N_ERR_IO); + EXPECT_TRUE(s2n_custom_send_fn_called); + + /* Cleanup */ + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_handshake_misc_test.c b/tests/unit/s2n_handshake_misc_test.c new file mode 100644 index 00000000000..4d2a40a3c1e --- /dev/null +++ b/tests/unit/s2n_handshake_misc_test.c @@ -0,0 +1,52 @@ +/* + * 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 "s2n_test.h" +#include "tls/s2n_handshake.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Test s2n_handshake_set_finished_len */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + const uint8_t max_len = sizeof(conn->handshake.client_finished); + + /* Safety */ + EXPECT_ERROR_WITH_ERRNO(s2n_handshake_set_finished_len(NULL, 0), S2N_ERR_NULL); + + /* Length must be less than available memory */ + EXPECT_ERROR_WITH_ERRNO(s2n_handshake_set_finished_len(conn, UINT8_MAX), S2N_ERR_SAFETY); + EXPECT_ERROR_WITH_ERRNO(s2n_handshake_set_finished_len(conn, max_len + 1), S2N_ERR_SAFETY); + + /* Length must be greater than zero */ + EXPECT_ERROR_WITH_ERRNO(s2n_handshake_set_finished_len(conn, 0), S2N_ERR_SAFETY); + + /* Length can change from zero to a valid length */ + EXPECT_EQUAL(conn->handshake.finished_len, 0); + EXPECT_OK(s2n_handshake_set_finished_len(conn, max_len)); + EXPECT_EQUAL(conn->handshake.finished_len, max_len); + + /* Length can't change if already set. + * This method will be called when calculating both the client and server finished / verify_data. + * Both client and server should have the same length, or something has gone wrong in our implementation. + */ + EXPECT_ERROR_WITH_ERRNO(s2n_handshake_set_finished_len(conn, max_len - 1), S2N_ERR_SAFETY); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_handshake_partial_test.c b/tests/unit/s2n_handshake_partial_test.c new file mode 100644 index 00000000000..0ec829635d8 --- /dev/null +++ b/tests/unit/s2n_handshake_partial_test.c @@ -0,0 +1,192 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_record.h" +#include "tls/s2n_tls.h" + +static S2N_RESULT s2n_get_test_client_and_server(struct s2n_connection **client_conn, struct s2n_connection **server_conn, + struct s2n_config *config) +{ + *client_conn = s2n_connection_new(S2N_CLIENT); + RESULT_ENSURE_REF(*client_conn); + + *server_conn = s2n_connection_new(S2N_SERVER); + RESULT_GUARD_POSIX(s2n_connection_set_blinding(*server_conn, S2N_SELF_SERVICE_BLINDING)); + RESULT_ENSURE_REF(*server_conn); + + RESULT_GUARD_POSIX(s2n_connection_set_config(*client_conn, config)); + RESULT_GUARD_POSIX(s2n_connection_set_config(*server_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + RESULT_GUARD_POSIX(s2n_io_pair_init_non_blocking(&io_pair)); + RESULT_GUARD_POSIX(s2n_connections_set_io_pair(*client_conn, *server_conn, &io_pair)); + + return S2N_RESULT_OK; +} + +int main() +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + const uint8_t test_psk_data[] = "very secret"; + DEFER_CLEANUP(struct s2n_psk *test_psk = s2n_external_psk_new(), s2n_psk_free); + EXPECT_SUCCESS(s2n_psk_set_identity(test_psk, test_psk_data, sizeof(test_psk_data))); + EXPECT_SUCCESS(s2n_psk_set_secret(test_psk, test_psk_data, sizeof(test_psk_data))); + EXPECT_SUCCESS(s2n_psk_configure_early_data(test_psk, 100, 0x13, 0x01)); + + struct s2n_cert_chain_and_key *cert_chain = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&cert_chain, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + struct s2n_config *config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, cert_chain)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + + /* Test s2n_negotiate_until_message */ + { + /* Safety */ + { + struct s2n_connection conn = { 0 }; + s2n_blocked_status blocked = 0; + EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_until_message(NULL, &blocked, CLIENT_HELLO), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_negotiate_until_message(&conn, NULL, CLIENT_HELLO), S2N_ERR_NULL); + }; + + /* If message is never encountered, complete the handshake */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_get_test_client_and_server(&client_conn, &server_conn, config)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + SERVER_KEY)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Can stop on a given message */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_get_test_client_and_server(&client_conn, &server_conn, config)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + SERVER_CERT_VERIFY)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT_VERIFY); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CERT_VERIFY); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Can be called repeatedly */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_get_test_client_and_server(&client_conn, &server_conn, config)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + CLIENT_HELLO)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + CLIENT_HELLO)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + SERVER_HELLO)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_HELLO); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + SERVER_HELLO)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_HELLO); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + APPLICATION_DATA)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + APPLICATION_DATA)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Can continue as normal after stopping */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_get_test_client_and_server(&client_conn, &server_conn, config)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + CLIENT_FINISHED)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_FINISHED); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_FINISHED); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Can stop on END_OF_EARLY_DATA when using early data, then continue. + * (This is the non-test use case for this feature) */ + { + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + EXPECT_OK(s2n_get_test_client_and_server(&client_conn, &server_conn, config)); + + EXPECT_SUCCESS(s2n_connection_append_psk(client_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, test_psk)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, + END_OF_EARLY_DATA)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), END_OF_EARLY_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), END_OF_EARLY_DATA); + + EXPECT_SUCCESS(s2n_connection_set_end_of_early_data(client_conn)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); + + EXPECT_TRUE(WITH_EARLY_DATA(client_conn)); + EXPECT_TRUE(WITH_EARLY_DATA(server_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + }; + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(cert_chain)); + END_TEST(); +} diff --git a/tests/unit/s2n_handshake_test.c b/tests/unit/s2n_handshake_test.c new file mode 100644 index 00000000000..c5e0ca314eb --- /dev/null +++ b/tests/unit/s2n_handshake_test.c @@ -0,0 +1,439 @@ +/* + * 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 "tls/s2n_handshake.h" + +#include +#include +#include +#include +#include + +#include "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "crypto/s2n_rsa_pss.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +enum test_type { + TEST_TYPE_START, + TEST_TYPE_SYNC = TEST_TYPE_START, + TEST_TYPE_ASYNC, + TEST_TYPE_END +} test_type; + +struct s2n_async_pkey_op *pkey_op = NULL; +int async_pkey_op_called = 0; +int async_pkey_op_performed = 0; + +static int handle_async(struct s2n_connection *server_conn) +{ + s2n_blocked_status server_blocked; + + /* Test that handshake can't proceed until async pkey op is complete */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &server_blocked), S2N_ERR_ASYNC_BLOCKED); + + /* Check that we have pkey_op */ + EXPECT_NOT_NULL(pkey_op); + + /* Test that not performed pkey can't be applied */ + EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_apply(pkey_op, server_conn), + S2N_ERR_ASYNC_NOT_PERFORMED); + + /* Extract pkey */ + struct s2n_cert_chain_and_key *chain_and_key = s2n_connection_get_selected_cert(server_conn); + EXPECT_NOT_NULL(chain_and_key); + + s2n_cert_private_key *pkey = s2n_cert_chain_and_key_get_private_key(chain_and_key); + EXPECT_NOT_NULL(pkey); + + /* Test that we can perform pkey operation only once */ + EXPECT_SUCCESS(s2n_async_pkey_op_perform(pkey_op, pkey)); + EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_perform(pkey_op, pkey), S2N_ERR_ASYNC_ALREADY_PERFORMED); + + /* Test that pkey op can't be applied to connection other than original one */ + struct s2n_connection *server_conn2 = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn2); + EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_apply(pkey_op, server_conn2), + S2N_ERR_ASYNC_WRONG_CONNECTION); + EXPECT_SUCCESS(s2n_connection_free(server_conn2)); + + /* Test that pkey op can be applied to original connection */ + EXPECT_SUCCESS(s2n_async_pkey_op_apply(pkey_op, server_conn)); + + /* Test that pkey op can't be applied to original connection more than once */ + EXPECT_FAILURE_WITH_ERRNO(s2n_async_pkey_op_apply(pkey_op, server_conn), + S2N_ERR_ASYNC_ALREADY_APPLIED); + + /* Free the pkey op */ + EXPECT_SUCCESS(s2n_async_pkey_op_free(pkey_op)); + pkey_op = NULL; + + async_pkey_op_performed++; + + return 0; +} + +static int try_handshake(struct s2n_connection *server_conn, struct s2n_connection *client_conn) +{ + s2n_blocked_status server_blocked; + s2n_blocked_status client_blocked; + + int tries = 0; + do { + int client_rc = s2n_negotiate(client_conn, &client_blocked); + if (!(client_rc == 0 || (client_blocked && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED))) { + return -1; + } + + int server_rc = s2n_negotiate(server_conn, &server_blocked); + if (!(server_rc == 0 || (server_blocked && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED))) { + return -1; + } + + if (server_blocked == S2N_BLOCKED_ON_APPLICATION_INPUT) { + /* Only can happen in async tests */ + EXPECT_EQUAL(test_type, TEST_TYPE_ASYNC); + EXPECT_SUCCESS(handle_async(server_conn)); + } + + EXPECT_NOT_EQUAL(++tries, 5); + } while (client_blocked || server_blocked); + + POSIX_GUARD(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + return S2N_SUCCESS; +} + +int test_cipher_preferences(struct s2n_config *server_config, struct s2n_config *client_config, + struct s2n_cert_chain_and_key *expected_cert_chain, s2n_signature_algorithm expected_sig_alg) +{ + const struct s2n_security_policy *security_policy = server_config->security_policy; + EXPECT_NOT_NULL(security_policy); + + if (s2n_is_in_fips_mode()) { + /* Override default client config ciphers when in FIPS mode to ensure all FIPS + * default ciphers are tested. + */ + client_config->security_policy = security_policy; + } + + const struct s2n_cipher_preferences *cipher_preferences = security_policy->cipher_preferences; + EXPECT_NOT_NULL(cipher_preferences); + + /* Verify that a handshake succeeds for every available cipher in the default list. */ + for (int cipher_idx = 0; cipher_idx < cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_suite *expected_cipher = cipher_preferences->suites[cipher_idx]; + uint8_t expect_failure = 0; + + /* Expect failure if the libcrypto we're building with can't support the cipher */ + if (!expected_cipher->available) { + expect_failure = 1; + } + + TEST_DEBUG_PRINT("Testing %s in %s mode, expect_failure=%d\n", expected_cipher->name, + test_type == TEST_TYPE_SYNC ? "synchronous" : "asynchronous", expect_failure); + + struct s2n_security_policy server_security_policy; + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(client_conn); + EXPECT_NOT_NULL(server_conn); + + /* Craft a cipher preference with a cipher_idx cipher and assign it to server */ + EXPECT_MEMCPY_SUCCESS(&server_cipher_preferences, cipher_preferences, sizeof(server_cipher_preferences)); + server_cipher_preferences.count = 1; + server_cipher_preferences.suites = &expected_cipher; + + EXPECT_MEMCPY_SUCCESS(&server_security_policy, security_policy, sizeof(server_security_policy)); + server_security_policy.cipher_preferences = &server_cipher_preferences; + + server_conn->security_policy_override = &server_security_policy; + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Reset counters */ + async_pkey_op_called = 0; + async_pkey_op_performed = 0; + + if (!expect_failure) { + POSIX_GUARD(try_handshake(server_conn, client_conn)); + + EXPECT_STRING_EQUAL(s2n_connection_get_cipher(server_conn), expected_cipher->name); + + EXPECT_EQUAL(server_conn->handshake_params.our_chain_and_key, expected_cert_chain); + EXPECT_NOT_NULL(server_conn->handshake_params.server_cert_sig_scheme); + EXPECT_EQUAL(server_conn->handshake_params.server_cert_sig_scheme->sig_alg, expected_sig_alg); + + EXPECT_TRUE(IS_NEGOTIATED(server_conn)); + EXPECT_TRUE(IS_NEGOTIATED(client_conn)); + + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn)); + + EXPECT_STRING_EQUAL(s2n_connection_get_last_message_name(server_conn), "APPLICATION_DATA"); + EXPECT_STRING_EQUAL(s2n_connection_get_last_message_name(client_conn), "APPLICATION_DATA"); + + if (test_type == TEST_TYPE_ASYNC) { + EXPECT_EQUAL(async_pkey_op_called, 1); + EXPECT_EQUAL(async_pkey_op_performed, 1); + } else { + EXPECT_EQUAL(async_pkey_op_called, 0); + EXPECT_EQUAL(async_pkey_op_performed, 0); + } + } else { + POSIX_ENSURE_EQ(try_handshake(server_conn, client_conn), -1); + EXPECT_STRING_NOT_EQUAL(s2n_connection_get_last_message_name(server_conn), "APPLICATION_DATA"); + EXPECT_STRING_NOT_EQUAL(s2n_connection_get_last_message_name(client_conn), "APPLICATION_DATA"); + EXPECT_EQUAL(async_pkey_op_called, 0); + EXPECT_EQUAL(async_pkey_op_performed, 0); + } + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + return 0; +} + +int async_pkey_fn(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + /* Just store the op, we will process it later */ + pkey_op = op; + async_pkey_op_called++; + + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + char dhparams_pem[S2N_MAX_TEST_PEM_SIZE]; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + + for (test_type = TEST_TYPE_START; test_type < TEST_TYPE_END; test_type++) { + /* Test: RSA cert */ + { + struct s2n_config *server_config, *client_config; + + struct s2n_cert_chain_and_key *chain_and_key; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + /* We need a security policy that only supports RSA certificates for auth */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20170210")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + /* Enable signature validation */ + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, S2N_VERIFY_AFTER_SIGN_ENABLED)); + if (test_type == TEST_TYPE_ASYNC) { + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_fn)); + } + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, + chain_and_key, S2N_SIGNATURE_RSA)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Test: RSA (TLS 1.2) key exchanges with TLS 1.3 client */ + { + if (!s2n_is_in_fips_mode()) { + /* Enable TLS 1.3 for the client */ + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + struct s2n_config *server_config, *client_config; + + struct s2n_cert_chain_and_key *chain_and_key; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + /* Configures server with maximum version 1.2 with only RSA key exchange ciphersuites */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all_rsa_kex")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + /* Enable signature validation */ + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, S2N_VERIFY_AFTER_SIGN_ENABLED)); + if (test_type == TEST_TYPE_ASYNC) { + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_fn)); + } + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* RSA encrypted premaster secret key exchange requires client versions + * to be set and read correctly, this test covers the behavior with a 1.3 client */ + EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, + chain_and_key, S2N_SIGNATURE_RSA)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + }; + + /* Test: ECDSA cert */ + { + struct s2n_config *server_config, *client_config; + + struct s2n_cert_chain_and_key *chain_and_key; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "test_all_ecdsa")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + /* Enable signature validation */ + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, S2N_VERIFY_AFTER_SIGN_ENABLED)); + if (test_type == TEST_TYPE_ASYNC) { + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_fn)); + } + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all_ecdsa")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_ECDSA_P384_PKCS1_CERT_CHAIN, NULL)); + + EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, + chain_and_key, S2N_SIGNATURE_ECDSA)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Test: RSA cert with RSA PSS signatures */ + if (s2n_is_rsa_pss_signing_supported()) { + const struct s2n_signature_scheme *const rsa_pss_rsae_sig_schemes[] = { + /* RSA PSS */ + &s2n_rsa_pss_rsae_sha256, + &s2n_rsa_pss_rsae_sha384, + &s2n_rsa_pss_rsae_sha512, + }; + + struct s2n_signature_preferences sig_prefs = { + .count = 3, + .signature_schemes = rsa_pss_rsae_sig_schemes, + }; + + struct s2n_config *server_config, *client_config; + + struct s2n_cert_chain_and_key *chain_and_key; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + /* We need a security policy that only supports RSA certificates for auth */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20170210")); + + struct s2n_security_policy security_policy = { + .minimum_protocol_version = server_config->security_policy->minimum_protocol_version, + .cipher_preferences = server_config->security_policy->cipher_preferences, + .kem_preferences = server_config->security_policy->kem_preferences, + .signature_preferences = &sig_prefs, + .ecc_preferences = server_config->security_policy->ecc_preferences, + }; + + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + if (test_type == TEST_TYPE_ASYNC) { + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_fn)); + } + server_config->security_policy = &security_policy; + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + client_config->client_cert_auth_type = S2N_CERT_AUTH_NONE; + client_config->check_ocsp = 0; + client_config->disable_x509_validation = 1; + client_config->security_policy = &security_policy; + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, + chain_and_key, S2N_SIGNATURE_RSA_PSS_RSAE)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Test: RSA_PSS cert with RSA_PSS signatures */ + if (s2n_is_rsa_pss_certs_supported()) { + s2n_enable_tls13_in_test(); + + struct s2n_config *server_config, *client_config; + + struct s2n_cert_chain_and_key *chain_and_key; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_RSA_PSS_2048_SHA256_LEAF_CERT, S2N_RSA_PSS_2048_SHA256_LEAF_KEY)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "20200207")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + /* Enable signature validation */ + EXPECT_SUCCESS(s2n_config_set_verify_after_sign(server_config, S2N_VERIFY_AFTER_SIGN_ENABLED)); + if (test_type == TEST_TYPE_ASYNC) { + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(server_config, async_pkey_fn)); + } + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "20200207")); + client_config->client_cert_auth_type = S2N_CERT_AUTH_NONE; + client_config->check_ocsp = 0; + client_config->disable_x509_validation = 1; + + EXPECT_SUCCESS(test_cipher_preferences(server_config, client_config, + chain_and_key, S2N_SIGNATURE_RSA_PSS_PSS)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + + s2n_disable_tls13_in_test(); + } + } + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_handshake_type_test.c b/tests/unit/s2n_handshake_type_test.c new file mode 100644 index 00000000000..bbb16b8fab5 --- /dev/null +++ b/tests/unit/s2n_handshake_type_test.c @@ -0,0 +1,275 @@ +/* + * 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 "s2n_test.h" +#include "tls/s2n_connection.h" + +#define S2N_FIRST_COMMON_HANDSHAKE_FLAG NEGOTIATED +#define S2N_LAST_COMMON_HANDSHAKE_FLAG NO_CLIENT_CERT +#define S2N_FIRST_TLS12_HANDSHAKE_FLAG TLS12_PERFECT_FORWARD_SECRECY +#define S2N_LAST_TLS12_HANDSHAKE_FLAG WITH_SESSION_TICKET +#define S2N_FIRST_TLS13_HANDSHAKE_FLAG HELLO_RETRY_REQUEST +#define S2N_LAST_TLS13_HANDSHAKE_FLAG EARLY_CLIENT_CCS + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Sanity check test setup */ + EXPECT_EQUAL(S2N_FIRST_COMMON_HANDSHAKE_FLAG, 1); + EXPECT_TRUE(S2N_FIRST_COMMON_HANDSHAKE_FLAG < S2N_LAST_COMMON_HANDSHAKE_FLAG); + EXPECT_EQUAL(S2N_FIRST_TLS12_HANDSHAKE_FLAG, S2N_LAST_COMMON_HANDSHAKE_FLAG * 2); + EXPECT_TRUE(S2N_FIRST_TLS12_HANDSHAKE_FLAG < S2N_LAST_TLS12_HANDSHAKE_FLAG); + EXPECT_EQUAL(S2N_FIRST_TLS13_HANDSHAKE_FLAG, S2N_LAST_COMMON_HANDSHAKE_FLAG * 2); + EXPECT_TRUE(S2N_FIRST_TLS13_HANDSHAKE_FLAG < S2N_LAST_TLS13_HANDSHAKE_FLAG); + EXPECT_EQUAL(MAX(S2N_LAST_TLS12_HANDSHAKE_FLAG, S2N_LAST_TLS13_HANDSHAKE_FLAG), S2N_HANDSHAKES_COUNT / 2); + + /* Test s2n_handshake_type_reset */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + conn->handshake.handshake_type = 0x12AB; + EXPECT_OK(s2n_handshake_type_reset(conn)); + EXPECT_EQUAL(conn->handshake.handshake_type, 0); + + EXPECT_OK(s2n_handshake_type_set_flag(conn, FULL_HANDSHAKE)); + EXPECT_OK(s2n_handshake_type_reset(conn)); + EXPECT_EQUAL(conn->handshake.handshake_type, 0); + + EXPECT_OK(s2n_handshake_type_set_flag(conn, 0xFFFF)); + EXPECT_OK(s2n_handshake_type_reset(conn)); + EXPECT_EQUAL(conn->handshake.handshake_type, 0); + + EXPECT_OK(s2n_handshake_type_set_flag(conn, 0)); + EXPECT_OK(s2n_handshake_type_reset(conn)); + EXPECT_EQUAL(conn->handshake.handshake_type, 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_handshake_type_set_flag */ + { + /* Safety */ + EXPECT_ERROR_WITH_ERRNO(s2n_handshake_type_set_flag(NULL, 0), S2N_ERR_NULL); + + /* Sets all common flags */ + for (s2n_handshake_type_flag flag = S2N_FIRST_COMMON_HANDSHAKE_FLAG; flag <= S2N_LAST_COMMON_HANDSHAKE_FLAG; flag++) { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_OK(s2n_handshake_type_set_flag(conn, flag)); + EXPECT_EQUAL(conn->handshake.handshake_type, flag); + + EXPECT_OK(s2n_handshake_type_reset(conn)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_handshake_type_set_flag(conn, flag)); + EXPECT_EQUAL(conn->handshake.handshake_type, flag); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + }; + + /* Test s2n_handshake_type_check_flag */ + { + /* Safety */ + EXPECT_FALSE(s2n_handshake_type_check_flag(NULL, 0)); + + /* Check when common flags set */ + for (s2n_handshake_type_flag flag = S2N_FIRST_COMMON_HANDSHAKE_FLAG; flag <= S2N_LAST_COMMON_HANDSHAKE_FLAG; flag++) { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + /* All flags set */ + { + conn->handshake.handshake_type = 0xFFFF; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_TRUE(s2n_handshake_type_check_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_TRUE(s2n_handshake_type_check_flag(conn, flag)); + }; + + /* No flags set */ + { + conn->handshake.handshake_type = 0; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_FALSE(s2n_handshake_type_check_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FALSE(s2n_handshake_type_check_flag(conn, flag)); + }; + + /* One flag set */ + { + conn->handshake.handshake_type = flag; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_TRUE(s2n_handshake_type_check_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_TRUE(s2n_handshake_type_check_flag(conn, flag)); + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + }; + + /* Test s2n_handshake_type_set_tls12_flag */ + { + /* Safety */ + EXPECT_ERROR_WITH_ERRNO(s2n_handshake_type_set_tls12_flag(NULL, 0), S2N_ERR_NULL); + + /* Sets all TLS1.2 flags */ + for (s2n_tls12_handshake_type_flag flag = S2N_FIRST_TLS12_HANDSHAKE_FLAG; flag <= S2N_LAST_TLS12_HANDSHAKE_FLAG; flag++) { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_OK(s2n_handshake_type_set_tls12_flag(conn, flag)); + EXPECT_EQUAL(conn->handshake.handshake_type, flag); + + EXPECT_OK(s2n_handshake_type_reset(conn)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_ERROR_WITH_ERRNO(s2n_handshake_type_set_tls12_flag(conn, flag), S2N_ERR_HANDSHAKE_STATE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + }; + + /* Test s2n_handshake_type_check_tls12_flag */ + { + /* Safety */ + EXPECT_FALSE(s2n_handshake_type_check_tls12_flag(NULL, 0)); + + /* Check when common flags set */ + for (s2n_tls12_handshake_type_flag flag = S2N_FIRST_TLS12_HANDSHAKE_FLAG; flag <= S2N_LAST_TLS12_HANDSHAKE_FLAG; flag++) { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + /* All flags set */ + { + conn->handshake.handshake_type = 0xFFFF; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_TRUE(s2n_handshake_type_check_tls12_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FALSE(s2n_handshake_type_check_tls12_flag(conn, flag)); + }; + + /* No flags set */ + { + conn->handshake.handshake_type = 0; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_FALSE(s2n_handshake_type_check_tls12_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FALSE(s2n_handshake_type_check_tls12_flag(conn, flag)); + }; + + /* One flag set */ + { + conn->handshake.handshake_type = flag; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_TRUE(s2n_handshake_type_check_tls12_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FALSE(s2n_handshake_type_check_tls12_flag(conn, flag)); + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + }; + + /* Test s2n_handshake_type_set_tls13_flag */ + { + /* Safety */ + EXPECT_ERROR_WITH_ERRNO(s2n_handshake_type_set_tls13_flag(NULL, 0), S2N_ERR_NULL); + + /* Sets all TLS1.3 flags */ + for (s2n_tls13_handshake_type_flag flag = S2N_FIRST_TLS13_HANDSHAKE_FLAG; flag <= S2N_LAST_TLS13_HANDSHAKE_FLAG; flag++) { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_handshake_type_set_tls13_flag(conn, flag)); + EXPECT_EQUAL(conn->handshake.handshake_type, flag); + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_ERROR_WITH_ERRNO(s2n_handshake_type_set_tls13_flag(conn, flag), S2N_ERR_HANDSHAKE_STATE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + }; + + /* Test s2n_handshake_type_check_tls13_flag */ + { + /* Safety */ + EXPECT_FALSE(s2n_handshake_type_check_tls13_flag(NULL, 0)); + + /* Check when common flags set */ + for (s2n_tls13_handshake_type_flag flag = S2N_FIRST_TLS13_HANDSHAKE_FLAG; flag <= S2N_LAST_TLS13_HANDSHAKE_FLAG; flag++) { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + /* All flags set */ + { + conn->handshake.handshake_type = 0xFFFF; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_FALSE(s2n_handshake_type_check_tls13_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_TRUE(s2n_handshake_type_check_tls13_flag(conn, flag)); + }; + + /* No flags set */ + { + conn->handshake.handshake_type = 0; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_FALSE(s2n_handshake_type_check_tls13_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FALSE(s2n_handshake_type_check_tls13_flag(conn, flag)); + }; + + /* One flag set */ + { + conn->handshake.handshake_type = flag; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_FALSE(s2n_handshake_type_check_tls13_flag(conn, flag)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_TRUE(s2n_handshake_type_check_tls13_flag(conn, flag)); + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_hash_all_algs_test.c b/tests/unit/s2n_hash_all_algs_test.c new file mode 100644 index 00000000000..af4dc4963ff --- /dev/null +++ b/tests/unit/s2n_hash_all_algs_test.c @@ -0,0 +1,159 @@ +/* + * 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 "crypto/s2n_evp.h" +#include "crypto/s2n_fips.h" +#include "crypto/s2n_hash.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +#define INPUT_DATA_SIZE 100 +#define OUTPUT_DATA_SIZE 100 + +const uint8_t input_data[INPUT_DATA_SIZE] = "hello hash"; + +/* These values were generated using the low level s2n_hash implementation. + * They are useful to validate that the results of the low level implementation + * never change and match the results of the EVP implementation. + */ +const char *expected_result_hex[S2N_HASH_SENTINEL] = { + [S2N_HASH_NONE] = "", + [S2N_HASH_MD5] = "f5d589043253ca6ae54124c31be43701", + [S2N_HASH_SHA1] = "ccf8abd6b03ef5054a4f257e7c712e17f965272d", + [S2N_HASH_SHA224] = "dae80554ab74bf098b1a39e48c85c58e4af4628d2a357ee5cf6b1b85", + [S2N_HASH_SHA256] = "a8a7fb9d2d3ff62eee5bed1bfcc7b2e17ffebf00c3c77fdf43259d690022041f", + [S2N_HASH_SHA384] = "d7131b24ea0985fc9f6462139969decff21f24967f6df17e31ce2410fda6534a5c" + "f85cb4be737961eddce0c201c0dac0", + [S2N_HASH_SHA512] = "b11305336d6071d8cbab6709fc1019f874961e13a04611f8e7d4c1f9164a2c923f" + "7b3da0a37001cef5fdb71584a0f92020a45f23a6fc06cc3ab42ceaa0467a34", + [S2N_HASH_MD5_SHA1] = "f5d589043253ca6ae54124c31be43701ccf8abd6b03ef5054a4f257e7c712e17f965272d", +}; + +S2N_RESULT s2n_hash_test_state(struct s2n_hash_state *hash_state, s2n_hash_algorithm hash_alg, struct s2n_blob *digest) +{ + /* Test s2n_hash_update */ + { + /* Break the test data into some arbitrarily sized chunks. */ + size_t chunk_sizes[] = { 1, 0, 10, 17, 31 }; + + size_t offset = 0; + for (size_t i = 0; i < s2n_array_len(chunk_sizes); i++) { + RESULT_GUARD_POSIX(s2n_hash_update(hash_state, input_data + offset, chunk_sizes[i])); + offset += chunk_sizes[i]; + RESULT_ENSURE_EQ(hash_state->currently_in_hash, offset); + } + + /* Add the rest */ + RESULT_GUARD_POSIX(s2n_hash_update(hash_state, input_data + offset, (INPUT_DATA_SIZE - offset))); + RESULT_ENSURE_EQ(hash_state->currently_in_hash, INPUT_DATA_SIZE); + }; + + /* Test s2n_hash_copy */ + struct s2n_hash_state hash_copy = { 0 }; + { + struct s2n_blob result = { 0 }; + uint8_t result_data[OUTPUT_DATA_SIZE] = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&result, result_data, OUTPUT_DATA_SIZE)); + + RESULT_GUARD_POSIX(s2n_hash_new(&hash_copy)); + RESULT_GUARD_POSIX(s2n_hash_copy(&hash_copy, hash_state)); + RESULT_ENSURE_EQ(hash_copy.currently_in_hash, hash_state->currently_in_hash); + RESULT_ENSURE_EQ(hash_copy.is_ready_for_input, hash_state->is_ready_for_input); + }; + + /* Test s2n_hash_digest */ + { + uint8_t digest_size = 0; + RESULT_GUARD_POSIX(s2n_hash_digest_size(hash_alg, &digest_size)); + digest->size = digest_size; + + RESULT_GUARD_POSIX(s2n_hash_digest(hash_state, digest->data, digest_size)); + RESULT_ENSURE_EQ(hash_state->currently_in_hash, 0); + RESULT_ENSURE_EQ(hash_state->is_ready_for_input, false); + + uint8_t copy_result[OUTPUT_DATA_SIZE] = { 0 }; + RESULT_GUARD_POSIX(s2n_hash_digest(&hash_copy, copy_result, digest_size)); + RESULT_ENSURE_EQ(hash_state->currently_in_hash, 0); + RESULT_ENSURE_EQ(hash_state->is_ready_for_input, false); + RESULT_ENSURE_EQ(memcmp(digest->data, copy_result, digest_size), 0); + }; + + RESULT_GUARD_POSIX(s2n_hash_free(&hash_copy)); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_hash_test(s2n_hash_algorithm hash_alg, struct s2n_blob *digest) +{ + struct s2n_hash_state hash_state = { 0 }; + + /* Test s2n_hash_new + s2n_hash_init */ + { + RESULT_GUARD_POSIX(s2n_hash_new(&hash_state)); + RESULT_ENSURE_EQ(hash_state.currently_in_hash, 0); + RESULT_ENSURE_EQ(hash_state.is_ready_for_input, false); + + /* Allow MD5 when necessary */ + if (s2n_is_in_fips_mode() && (hash_alg == S2N_HASH_MD5 || hash_alg == S2N_HASH_MD5_SHA1)) { + RESULT_GUARD_POSIX(s2n_hash_allow_md5_for_fips(&hash_state)); + } + + RESULT_GUARD_POSIX(s2n_hash_init(&hash_state, hash_alg)); + RESULT_ENSURE_EQ(hash_state.currently_in_hash, 0); + RESULT_ENSURE_EQ(hash_state.is_ready_for_input, true); + + RESULT_GUARD(s2n_hash_test_state(&hash_state, hash_alg, digest)); + }; + + /* Test s2n_hash_reset */ + { + struct s2n_blob result = { 0 }; + uint8_t result_data[OUTPUT_DATA_SIZE] = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&result, result_data, OUTPUT_DATA_SIZE)); + + RESULT_GUARD_POSIX(s2n_hash_reset(&hash_state)); + RESULT_ENSURE_EQ(hash_state.currently_in_hash, 0); + RESULT_ENSURE_EQ(hash_state.is_ready_for_input, true); + + RESULT_GUARD(s2n_hash_test_state(&hash_state, hash_alg, &result)); + RESULT_ENSURE_EQ(digest->size, result.size); + RESULT_ENSURE_EQ(memcmp(digest->data, result.data, result.size), 0); + }; + + RESULT_GUARD_POSIX(s2n_hash_free(&hash_state)); + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Calculate digests when not in FIPS mode. They must match. */ + for (s2n_hash_algorithm hash_alg = 0; hash_alg < S2N_HASH_SENTINEL; hash_alg++) { + struct s2n_blob actual_result = { 0 }; + uint8_t actual_result_data[OUTPUT_DATA_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&actual_result, actual_result_data, OUTPUT_DATA_SIZE)); + + struct s2n_blob expected_result = { 0 }; + uint8_t expected_result_data[OUTPUT_DATA_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&expected_result, expected_result_data, OUTPUT_DATA_SIZE)); + EXPECT_SUCCESS(s2n_hex_string_to_bytes((const uint8_t *) expected_result_hex[hash_alg], &expected_result)); + + EXPECT_OK(s2n_hash_test(hash_alg, &actual_result)); + EXPECT_EQUAL(expected_result.size, actual_result.size); + EXPECT_BYTEARRAY_EQUAL(expected_result.data, actual_result.data, actual_result.size); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_hash_test.c b/tests/unit/s2n_hash_test.c new file mode 100644 index 00000000000..9c9d6aceb03 --- /dev/null +++ b/tests/unit/s2n_hash_test.c @@ -0,0 +1,335 @@ +/* + * 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 "crypto/s2n_hash.h" + +#include + +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_safety.h" + +int main(int argc, char **argv) +{ + uint8_t digest_pad[64]; + uint8_t output_pad[128]; + uint8_t hello[] = "Hello world!\n"; + uint8_t string1[] = "String 1\n"; + uint8_t string2[] = "and String 2\n"; + struct s2n_stuffer output = { 0 }; + struct s2n_hash_state hash, copy; + struct s2n_blob out = { 0 }; + POSIX_GUARD(s2n_blob_init(&out, output_pad, sizeof(output_pad))); + uint64_t bytes_in_hash; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Sanity check that we're setting S2N_LIBCRYPTO_SUPPORTS_EVP_MD5_SHA1_HASH properly. + * AWS-LC is known to support EVP_md5_sha1(). If this fails, something is wrong with our + * S2N_LIBCRYPTO_SUPPORTS_EVP_MD5_SHA1_HASH feature testing. + */ + if (s2n_libcrypto_is_awslc()) { + EXPECT_NOT_NULL(s2n_hash_alg_to_evp_md(S2N_HASH_MD5_SHA1)); + } + + POSIX_GUARD(s2n_hash_new(&hash)); + EXPECT_FALSE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_FAILURE(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + POSIX_GUARD(s2n_hash_new(©)); + EXPECT_FALSE(s2n_hash_is_ready_for_input(©)); + EXPECT_FAILURE(s2n_hash_get_currently_in_hash_total(©, &bytes_in_hash)); + + if (s2n_hash_is_available(S2N_HASH_MD5)) { + /* Try MD5 */ + uint8_t md5_digest_size; + POSIX_GUARD(s2n_hash_digest_size(S2N_HASH_MD5, &md5_digest_size)); + EXPECT_EQUAL(md5_digest_size, 16); + EXPECT_SUCCESS(s2n_hash_init(&hash, S2N_HASH_MD5)); + EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 0); + + EXPECT_SUCCESS(s2n_hash_update(&hash, hello, strlen((char *) hello))); + EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 13); + + EXPECT_SUCCESS(s2n_hash_digest(&hash, digest_pad, MD5_DIGEST_LENGTH)); + EXPECT_FALSE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_FAILURE(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + + EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); + for (int i = 0; i < 16; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); + } + + /* Reference value from command line md5sum */ + EXPECT_EQUAL(memcmp(output_pad, "59ca0efa9f5633cb0371bbc0355478d8", 16 * 2), 0); + + POSIX_GUARD(s2n_hash_reset(&hash)); + EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 0); + } + + /* Try SHA1 */ + uint8_t sha1_digest_size; + POSIX_GUARD(s2n_hash_digest_size(S2N_HASH_SHA1, &sha1_digest_size)); + EXPECT_EQUAL(sha1_digest_size, 20); + EXPECT_SUCCESS(s2n_hash_init(&hash, S2N_HASH_SHA1)); + EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 0); + + EXPECT_SUCCESS(s2n_hash_update(&hash, hello, strlen((char *) hello))); + EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 13); + + EXPECT_SUCCESS(s2n_hash_copy(©, &hash)); + EXPECT_TRUE(s2n_hash_is_ready_for_input(©)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(©, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 13); + + EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 13); + + EXPECT_SUCCESS(s2n_hash_digest(&hash, digest_pad, SHA_DIGEST_LENGTH)); + EXPECT_FALSE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_FAILURE(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + + EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); + for (int i = 0; i < 20; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); + } + + /* Reference value from command line sha1sum */ + EXPECT_EQUAL(memcmp(output_pad, "47a013e660d408619d894b20806b1d5086aab03b", 20 * 2), 0); + + /* Check the copy */ + EXPECT_SUCCESS(s2n_hash_digest(©, digest_pad, SHA_DIGEST_LENGTH)); + EXPECT_FALSE(s2n_hash_is_ready_for_input(©)); + EXPECT_FAILURE(s2n_hash_get_currently_in_hash_total(©, &bytes_in_hash)); + + EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); + for (int i = 0; i < 20; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); + } + + /* Reference value from command line sha1sum */ + EXPECT_EQUAL(memcmp(output_pad, "47a013e660d408619d894b20806b1d5086aab03b", 20 * 2), 0); + + EXPECT_SUCCESS(s2n_hash_reset(&hash)); + EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 0); + + EXPECT_SUCCESS(s2n_hash_reset(©)); + EXPECT_TRUE(s2n_hash_is_ready_for_input(©)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(©, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 0); + + /* Test that a multi-update works */ + EXPECT_SUCCESS(s2n_hash_update(&hash, string1, strlen((char *) string1))); + EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 9); + + EXPECT_SUCCESS(s2n_hash_copy(©, &hash)); + EXPECT_TRUE(s2n_hash_is_ready_for_input(©)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(©, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 9); + + EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 9); + + EXPECT_SUCCESS(s2n_hash_update(&hash, string2, strlen((char *) string2))); + EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 22); + + EXPECT_SUCCESS(s2n_hash_digest(&hash, digest_pad, SHA_DIGEST_LENGTH)); + EXPECT_FALSE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_FAILURE(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + + EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); + for (int i = 0; i < 20; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); + } + + /* Reference value from command line sha1sum */ + EXPECT_EQUAL(memcmp(output_pad, "4afd618f797f0c6bd85b2035338bb26c62ab0dbc", 20 * 2), 0); + + /* Test that a copy-update works */ + EXPECT_SUCCESS(s2n_hash_update(©, string2, strlen((char *) string2))); + EXPECT_TRUE(s2n_hash_is_ready_for_input(©)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(©, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 22); + + EXPECT_SUCCESS(s2n_hash_digest(©, digest_pad, SHA_DIGEST_LENGTH)); + EXPECT_FALSE(s2n_hash_is_ready_for_input(©)); + EXPECT_FAILURE(s2n_hash_get_currently_in_hash_total(©, &bytes_in_hash)); + + EXPECT_SUCCESS(s2n_hash_free(©)); + EXPECT_FALSE(s2n_hash_is_ready_for_input(©)); + EXPECT_FAILURE(s2n_hash_get_currently_in_hash_total(©, &bytes_in_hash)); + + EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); + for (int i = 0; i < 20; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); + } + + /* Reference value from command line sha1sum */ + EXPECT_EQUAL(memcmp(output_pad, "4afd618f797f0c6bd85b2035338bb26c62ab0dbc", 20 * 2), 0); + + POSIX_GUARD(s2n_hash_reset(&hash)); + EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 0); + + /* Try SHA224 and test s2n_hash_free */ + uint8_t sha224_digest_size; + POSIX_GUARD(s2n_hash_digest_size(S2N_HASH_SHA224, &sha224_digest_size)); + EXPECT_EQUAL(sha224_digest_size, 28); + EXPECT_SUCCESS(s2n_hash_init(&hash, S2N_HASH_SHA224)); + EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 0); + + EXPECT_SUCCESS(s2n_hash_update(&hash, hello, strlen((char *) hello))); + EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 13); + + EXPECT_SUCCESS(s2n_hash_digest(&hash, digest_pad, SHA224_DIGEST_LENGTH)); + EXPECT_FALSE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_FAILURE(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + + EXPECT_SUCCESS(s2n_hash_free(&hash)); + EXPECT_FALSE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_FAILURE(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + + EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); + for (int i = 0; i < 28; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); + } + + /* Reference value from command line sha224sum */ + EXPECT_EQUAL(memcmp(output_pad, "f771a839cff678857feee21492184ca7a456ac3cf57e78057b7beaf5", 28 * 2), 0); + + /* Try SHA256 using a freed hash state */ + POSIX_GUARD(s2n_hash_new(&hash)); + EXPECT_FALSE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_FAILURE(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + + uint8_t sha256_digest_size; + POSIX_GUARD(s2n_hash_digest_size(S2N_HASH_SHA256, &sha256_digest_size)); + EXPECT_EQUAL(sha256_digest_size, 32); + EXPECT_SUCCESS(s2n_hash_init(&hash, S2N_HASH_SHA256)); + EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 0); + + EXPECT_SUCCESS(s2n_hash_update(&hash, hello, strlen((char *) hello))); + EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 13); + + EXPECT_SUCCESS(s2n_hash_digest(&hash, digest_pad, SHA256_DIGEST_LENGTH)); + EXPECT_FALSE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_FAILURE(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + + EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); + for (int i = 0; i < 32; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); + } + + /* Reference value from command line sha256sum */ + EXPECT_EQUAL(memcmp(output_pad, "0ba904eae8773b70c75333db4de2f3ac45a8ad4ddba1b242f0b3cfc199391dd8", 32 * 2), 0); + + POSIX_GUARD(s2n_hash_reset(&hash)); + EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 0); + + /* Try SHA384 */ + uint8_t sha384_digest_size; + POSIX_GUARD(s2n_hash_digest_size(S2N_HASH_SHA384, &sha384_digest_size)); + EXPECT_EQUAL(sha384_digest_size, 48); + EXPECT_SUCCESS(s2n_hash_init(&hash, S2N_HASH_SHA384)); + EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 0); + + EXPECT_SUCCESS(s2n_hash_update(&hash, hello, strlen((char *) hello))); + EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 13); + + EXPECT_SUCCESS(s2n_hash_digest(&hash, digest_pad, SHA384_DIGEST_LENGTH)); + EXPECT_FALSE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_FAILURE(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + + EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); + for (int i = 0; i < 48; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); + } + + /* Reference value from command line sha384sum */ + EXPECT_EQUAL(memcmp(output_pad, "f7f8f1b9d5a9a61742eeda26c20990282ac08dabda14e70376fcb4c8b46198a9959ea9d7d194b38520eed5397ffe6d8e", 48 * 2), 0); + + POSIX_GUARD(s2n_hash_reset(&hash)); + EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 0); + + /* Try SHA512 */ + uint8_t sha512_digest_size; + POSIX_GUARD(s2n_hash_digest_size(S2N_HASH_SHA512, &sha512_digest_size)); + EXPECT_EQUAL(sha512_digest_size, 64); + EXPECT_SUCCESS(s2n_hash_init(&hash, S2N_HASH_SHA512)); + EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 0); + + EXPECT_SUCCESS(s2n_hash_update(&hash, hello, strlen((char *) hello))); + EXPECT_TRUE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 13); + + EXPECT_SUCCESS(s2n_hash_digest(&hash, digest_pad, SHA512_DIGEST_LENGTH)); + EXPECT_FALSE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_FAILURE(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + + EXPECT_SUCCESS(s2n_hash_free(&hash)); + EXPECT_FALSE(s2n_hash_is_ready_for_input(&hash)); + EXPECT_FAILURE(s2n_hash_get_currently_in_hash_total(&hash, &bytes_in_hash)); + + EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); + for (int i = 0; i < 64; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); + } + + /* Reference value from command line sha512sum */ + EXPECT_EQUAL(memcmp(output_pad, "32c07a0b3a3fd0dd8f28021b4eea1c19d871f4586316b394124f3c99fb68e59579e05039c3bd9aab9841214f1c132f7666eb8800f14be8b9b091a7dba32bfe6f", 64 * 2), 0); + + END_TEST(); +} diff --git a/tests/unit/s2n_hkdf_test.c b/tests/unit/s2n_hkdf_test.c new file mode 100644 index 00000000000..0a095c5fe36 --- /dev/null +++ b/tests/unit/s2n_hkdf_test.c @@ -0,0 +1,478 @@ +/* + * 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 "crypto/s2n_hkdf.h" + +#include + +#include "crypto/s2n_fips.h" +#include "crypto/s2n_hmac.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_blob.h" + +#define NUM_TESTS 12 +#define MAX_OUTPUT_SIZE 82 +#define MAX_PSEUDO_RAND_KEY_SIZE 64 + +int s2n_hkdf_extract(struct s2n_hmac_state *hmac, s2n_hmac_algorithm alg, const struct s2n_blob *salt, const struct s2n_blob *key, struct s2n_blob *pseudo_rand_key); + +struct hkdf_test_vector { + s2n_hmac_algorithm alg; + uint8_t in_key[80]; + uint32_t in_key_len; + uint8_t salt[80]; + uint32_t salt_len; + uint8_t info[80]; + uint32_t info_len; + uint8_t pseudo_rand_key[MAX_PSEUDO_RAND_KEY_SIZE]; + uint32_t prk_len; + uint8_t output[MAX_OUTPUT_SIZE]; + uint32_t output_len; +}; + +/* Test vectors #0-6 obtained from RFC 5869: + * Includes SHA-256 and SHA-1 vectors + * + * Test vectors #7-11 obtained from Kullo Blog: + * Includes SHA-512 vectors + * https://www.kullo.net/blog/hkdf-sha-512-test-vectors/ + */ +static struct hkdf_test_vector tests[] = { + { + S2N_HMAC_SHA256, + { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b }, + 22, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c }, + 13, + { 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9 }, + 10, + { 0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, 0x0d, 0xdc, + 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63, 0x90, 0xb6, 0xc7, 0x3b, + 0xb5, 0x0f, 0x9c, 0x31, 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, + 0xb3, 0xe5 }, + 32, + { 0x3c, 0xb2, 0x5f, 0x25, 0xfa, 0xac, 0xd5, 0x7a, 0x90, 0x43, + 0x4f, 0x64, 0xd0, 0x36, 0x2f, 0x2a, 0x2d, 0x2d, 0x0a, 0x90, + 0xcf, 0x1a, 0x5a, 0x4c, 0x5d, 0xb0, 0x2d, 0x56, 0xec, 0xc4, + 0xc5, 0xbf, 0x34, 0x00, 0x72, 0x08, 0xd5, 0xb8, 0x87, 0x18, + 0x58, 0x65 }, + 42, + }, + { + S2N_HMAC_SHA256, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, + 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, + 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f }, + 80, + { 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, + 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, + 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, + 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf }, + 80, + { 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, + 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, + 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }, + 80, + { 0x06, 0xa6, 0xb8, 0x8c, 0x58, 0x53, 0x36, 0x1a, 0x06, 0x10, + 0x4c, 0x9c, 0xeb, 0x35, 0xb4, 0x5c, 0xef, 0x76, 0x00, 0x14, + 0x90, 0x46, 0x71, 0x01, 0x4a, 0x19, 0x3f, 0x40, 0xc1, 0x5f, + 0xc2, 0x44 }, + 32, + { 0xb1, 0x1e, 0x39, 0x8d, 0xc8, 0x03, 0x27, 0xa1, 0xc8, 0xe7, + 0xf7, 0x8c, 0x59, 0x6a, 0x49, 0x34, 0x4f, 0x01, 0x2e, 0xda, + 0x2d, 0x4e, 0xfa, 0xd8, 0xa0, 0x50, 0xcc, 0x4c, 0x19, 0xaf, + 0xa9, 0x7c, 0x59, 0x04, 0x5a, 0x99, 0xca, 0xc7, 0x82, 0x72, + 0x71, 0xcb, 0x41, 0xc6, 0x5e, 0x59, 0x0e, 0x09, 0xda, 0x32, + 0x75, 0x60, 0x0c, 0x2f, 0x09, 0xb8, 0x36, 0x77, 0x93, 0xa9, + 0xac, 0xa3, 0xdb, 0x71, 0xcc, 0x30, 0xc5, 0x81, 0x79, 0xec, + 0x3e, 0x87, 0xc1, 0x4c, 0x01, 0xd5, 0xc1, 0xf3, 0x43, 0x4f, + 0x1d, 0x87 }, + 82, + }, + { + S2N_HMAC_SHA256, + { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b }, + 22, + { 0x00 }, + 0, + { 0x00 }, + 0, + { 0x19, 0xef, 0x24, 0xa3, 0x2c, 0x71, 0x7b, 0x16, 0x7f, 0x33, + 0xa9, 0x1d, 0x6f, 0x64, 0x8b, 0xdf, 0x96, 0x59, 0x67, 0x76, + 0xaf, 0xdb, 0x63, 0x77, 0xac, 0x43, 0x4c, 0x1c, 0x29, 0x3c, + 0xcb, 0x04 }, + 22, + { 0x8d, 0xa4, 0xe7, 0x75, 0xa5, 0x63, 0xc1, 0x8f, 0x71, 0x5f, + 0x80, 0x2a, 0x06, 0x3c, 0x5a, 0x31, 0xb8, 0xa1, 0x1f, 0x5c, + 0x5e, 0xe1, 0x87, 0x9e, 0xc3, 0x45, 0x4e, 0x5f, 0x3c, 0x73, + 0x8d, 0x2d, 0x9d, 0x20, 0x13, 0x95, 0xfa, 0xa4, 0xb6, 0x1a, + 0x96, 0xc8 }, + 42, + }, + { + S2N_HMAC_SHA1, + { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b }, + 11, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c }, + 13, + { 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9 }, + 10, + { 0x9b, 0x6c, 0x18, 0xc4, 0x32, 0xa7, 0xbf, 0x8f, 0x0e, 0x71, + 0xc8, 0xeb, 0x88, 0xf4, 0xb3, 0x0b, 0xaa, 0x2b, 0xa2, 0x43 }, + 20, + { 0x08, 0x5a, 0x01, 0xea, 0x1b, 0x10, 0xf3, 0x69, 0x33, 0x06, + 0x8b, 0x56, 0xef, 0xa5, 0xad, 0x81, 0xa4, 0xf1, 0x4b, 0x82, + 0x2f, 0x5b, 0x09, 0x15, 0x68, 0xa9, 0xcd, 0xd4, 0xf1, 0x55, + 0xfd, 0xa2, 0xc2, 0x2e, 0x42, 0x24, 0x78, 0xd3, 0x05, 0xf3, + 0xf8, 0x96 }, + 42, + }, + { + S2N_HMAC_SHA1, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, + 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, + 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f }, + 80, + { 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, + 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, + 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, + 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf }, + 80, + { 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, + 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, + 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }, + 80, + { 0x8a, 0xda, 0xe0, 0x9a, 0x2a, 0x30, 0x70, 0x59, 0x47, 0x8d, + 0x30, 0x9b, 0x26, 0xc4, 0x11, 0x5a, 0x22, 0x4c, 0xfa, 0xf6 }, + 20, + { 0x0b, 0xd7, 0x70, 0xa7, 0x4d, 0x11, 0x60, 0xf7, 0xc9, 0xf1, + 0x2c, 0xd5, 0x91, 0x2a, 0x06, 0xeb, 0xff, 0x6a, 0xdc, 0xae, + 0x89, 0x9d, 0x92, 0x19, 0x1f, 0xe4, 0x30, 0x56, 0x73, 0xba, + 0x2f, 0xfe, 0x8f, 0xa3, 0xf1, 0xa4, 0xe5, 0xad, 0x79, 0xf3, + 0xf3, 0x34, 0xb3, 0xb2, 0x02, 0xb2, 0x17, 0x3c, 0x48, 0x6e, + 0xa3, 0x7c, 0xe3, 0xd3, 0x97, 0xed, 0x03, 0x4c, 0x7f, 0x9d, + 0xfe, 0xb1, 0x5c, 0x5e, 0x92, 0x73, 0x36, 0xd0, 0x44, 0x1f, + 0x4c, 0x43, 0x00, 0xe2, 0xcf, 0xf0, 0xd0, 0x90, 0x0b, 0x52, + 0xd3, 0xb4 }, + 82, + }, + { + S2N_HMAC_SHA1, + { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b }, + 22, + { + 0x00, + }, + 0, + { + 0x00, + }, + 0, + { 0xda, 0x8c, 0x8a, 0x73, 0xc7, 0xfa, 0x77, 0x28, 0x8e, 0xc6, + 0xf5, 0xe7, 0xc2, 0x97, 0x78, 0x6a, 0xa0, 0xd3, 0x2d, 0x01 }, + 20, + { 0x0a, 0xc1, 0xaf, 0x70, 0x02, 0xb3, 0xd7, 0x61, 0xd1, 0xe5, + 0x52, 0x98, 0xda, 0x9d, 0x05, 0x06, 0xb9, 0xae, 0x52, 0x05, + 0x72, 0x20, 0xa3, 0x06, 0xe0, 0x7b, 0x6b, 0x87, 0xe8, 0xdf, + 0x21, 0xd0, 0xea, 0x00, 0x03, 0x3d, 0xe0, 0x39, 0x84, 0xd3, + 0x49, 0x18 }, + 42, + }, + { + S2N_HMAC_SHA1, + { 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c }, + 22, + { 0x00 }, + 0, + { 0x00 }, + 0, + { 0x2a, 0xdc, 0xca, 0xda, 0x18, 0x77, 0x9e, 0x7c, 0x20, 0x77, + 0xad, 0x2e, 0xb1, 0x9d, 0x3f, 0x3e, 0x73, 0x13, 0x85, 0xdd }, + 20, + { 0x2c, 0x91, 0x11, 0x72, 0x04, 0xd7, 0x45, 0xf3, 0x50, 0x0d, + 0x63, 0x6a, 0x62, 0xf6, 0x4f, 0x0a, 0xb3, 0xba, 0xe5, 0x48, + 0xaa, 0x53, 0xd4, 0x23, 0xb0, 0xd1, 0xf2, 0x7e, 0xbb, 0xa6, + 0xf5, 0xe5, 0x67, 0x3a, 0x08, 0x1d, 0x70, 0xcc, 0xe7, 0xac, + 0xfc, 0x48 }, + 42, + }, + { + S2N_HMAC_SHA512, + { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b }, + 22, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c }, + 13, + { 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9 }, + 10, + { 0x66, 0x57, 0x99, 0x82, 0x37, 0x37, 0xde, 0xd0, 0x4a, 0x88, + 0xe4, 0x7e, 0x54, 0xa5, 0x89, 0x0b, 0xb2, 0xc3, 0xd2, 0x47, + 0xc7, 0xa4, 0x25, 0x4a, 0x8e, 0x61, 0x35, 0x07, 0x23, 0x59, + 0x0a, 0x26, 0xc3, 0x62, 0x38, 0x12, 0x7d, 0x86, 0x61, 0xb8, + 0x8c, 0xf8, 0x0e, 0xf8, 0x02, 0xd5, 0x7e, 0x2f, 0x7c, 0xeb, + 0xcf, 0x1e, 0x00, 0xe0, 0x83, 0x84, 0x8b, 0xe1, 0x99, 0x29, + 0xc6, 0x1b, 0x42, 0x37 }, + 64, + { 0x83, 0x23, 0x90, 0x08, 0x6c, 0xda, 0x71, 0xfb, 0x47, 0x62, + 0x5b, 0xb5, 0xce, 0xb1, 0x68, 0xe4, 0xc8, 0xe2, 0x6a, 0x1a, + 0x16, 0xed, 0x34, 0xd9, 0xfc, 0x7f, 0xe9, 0x2c, 0x14, 0x81, + 0x57, 0x93, 0x38, 0xda, 0x36, 0x2c, 0xb8, 0xd9, 0xf9, 0x25, + 0xd7, 0xcb }, + 42, + }, + { + S2N_HMAC_SHA512, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, + 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, + 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, + 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, + 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3a, 0x3b, + 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, + 0x46, 0x47, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f }, + 80, + { 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, + 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x70, 0x71, 0x72, 0x73, + 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, + 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, + 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x90, 0x91, + 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9a, 0x9b, + 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, + 0xa6, 0xa7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf }, + 80, + { 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, + 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, + 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, + 0xce, 0xcf, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, + 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, + 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xea, 0xeb, + 0xec, 0xed, 0xee, 0xef, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, + 0xf6, 0xf7, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }, + 80, + { 0x35, 0x67, 0x25, 0x42, 0x90, 0x7d, 0x4e, 0x14, 0x2c, 0x00, + 0xe8, 0x44, 0x99, 0xe7, 0x4e, 0x1d, 0xe0, 0x8b, 0xe8, 0x65, + 0x35, 0xf9, 0x24, 0xe0, 0x22, 0x80, 0x4a, 0xd7, 0x75, 0xdd, + 0xe2, 0x7e, 0xc8, 0x6c, 0xd1, 0xe5, 0xb7, 0xd1, 0x78, 0xc7, + 0x44, 0x89, 0xbd, 0xbe, 0xb3, 0x07, 0x12, 0xbe, 0xb8, 0x2d, + 0x4f, 0x97, 0x41, 0x6c, 0x5a, 0x94, 0xea, 0x81, 0xeb, 0xdf, + 0x3e, 0x62, 0x9e, 0x4a }, + 64, + { 0xce, 0x6c, 0x97, 0x19, 0x28, 0x05, 0xb3, 0x46, 0xe6, 0x16, + 0x1e, 0x82, 0x1e, 0xd1, 0x65, 0x67, 0x3b, 0x84, 0xf4, 0x00, + 0xa2, 0xb5, 0x14, 0xb2, 0xfe, 0x23, 0xd8, 0x4c, 0xd1, 0x89, + 0xdd, 0xf1, 0xb6, 0x95, 0xb4, 0x8c, 0xbd, 0x1c, 0x83, 0x88, + 0x44, 0x11, 0x37, 0xb3, 0xce, 0x28, 0xf1, 0x6a, 0xa6, 0x4b, + 0xa3, 0x3b, 0xa4, 0x66, 0xb2, 0x4d, 0xf6, 0xcf, 0xcb, 0x02, + 0x1e, 0xcf, 0xf2, 0x35, 0xf6, 0xa2, 0x05, 0x6c, 0xe3, 0xaf, + 0x1d, 0xe4, 0x4d, 0x57, 0x20, 0x97, 0xa8, 0x50, 0x5d, 0x9e, + 0x7a, 0x93 }, + 82, + }, + { + S2N_HMAC_SHA512, + { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b, 0x0b }, + 22, + { 0x00 }, + 0, + { 0x00 }, + 0, + { 0xfd, 0x20, 0x0c, 0x49, 0x87, 0xac, 0x49, 0x13, 0x13, 0xbd, + 0x4a, 0x2a, 0x13, 0x28, 0x71, 0x21, 0x24, 0x72, 0x39, 0xe1, + 0x1c, 0x9e, 0xf8, 0x28, 0x02, 0x04, 0x4b, 0x66, 0xef, 0x35, + 0x7e, 0x5b, 0x19, 0x44, 0x98, 0xd0, 0x68, 0x26, 0x11, 0x38, + 0x23, 0x48, 0x57, 0x2a, 0x7b, 0x16, 0x11, 0xde, 0x54, 0x76, + 0x40, 0x94, 0x28, 0x63, 0x20, 0x57, 0x8a, 0x86, 0x3f, 0x36, + 0x56, 0x2b, 0x0d, 0xf6 }, + 64, + { 0xf5, 0xfa, 0x02, 0xb1, 0x82, 0x98, 0xa7, 0x2a, 0x8c, 0x23, + 0x89, 0x8a, 0x87, 0x03, 0x47, 0x2c, 0x6e, 0xb1, 0x79, 0xdc, + 0x20, 0x4c, 0x03, 0x42, 0x5c, 0x97, 0x0e, 0x3b, 0x16, 0x4b, + 0xf9, 0x0f, 0xff, 0x22, 0xd0, 0x48, 0x36, 0xd0, 0xe2, 0x34, + 0x3b, 0xac }, + 42, + }, + { + S2N_HMAC_SHA512, + { 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, 0x0b, + 0x0b }, + 11, + { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, + 0x0a, 0x0b, 0x0c }, + 13, + { 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9 }, + 10, + { 0x67, 0x40, 0x9c, 0x9c, 0xac, 0x28, 0xb5, 0x2e, 0xe9, 0xfa, + 0xd9, 0x1c, 0x2f, 0xda, 0x99, 0x9f, 0x7c, 0xa2, 0x2e, 0x34, + 0x34, 0xf0, 0xae, 0x77, 0x28, 0x63, 0x83, 0x65, 0x68, 0xad, + 0x6a, 0x7f, 0x10, 0xcf, 0x11, 0x3b, 0xfd, 0xdd, 0x56, 0x01, + 0x29, 0xa5, 0x94, 0xa8, 0xf5, 0x23, 0x85, 0xc2, 0xd6, 0x61, + 0xd7, 0x85, 0xd2, 0x9c, 0xe9, 0x3a, 0x11, 0x40, 0x0c, 0x92, + 0x06, 0x83, 0x18, 0x1d }, + 64, + { 0x74, 0x13, 0xe8, 0x99, 0x7e, 0x02, 0x06, 0x10, 0xfb, 0xf6, + 0x82, 0x3f, 0x2c, 0xe1, 0x4b, 0xff, 0x01, 0x87, 0x5d, 0xb1, + 0xca, 0x55, 0xf6, 0x8c, 0xfc, 0xf3, 0x95, 0x4d, 0xc8, 0xaf, + 0xf5, 0x35, 0x59, 0xbd, 0x5e, 0x30, 0x28, 0xb0, 0x80, 0xf7, + 0xc0, 0x68 }, + 42, + }, + { + S2N_HMAC_SHA512, + { 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, 0x0c, + 0x0c, 0x0c }, + 22, + { 0x00 }, + 0, + { 0x00 }, + 0, + { 0x53, 0x46, 0xb3, 0x76, 0xbf, 0x3a, 0xa9, 0xf8, 0x4f, 0x8f, + 0x6e, 0xd5, 0xb1, 0xc4, 0xf4, 0x89, 0x17, 0x2e, 0x24, 0x4d, + 0xac, 0x30, 0x3d, 0x12, 0xf6, 0x8e, 0xcc, 0x76, 0x6e, 0xa6, + 0x00, 0xaa, 0x88, 0x49, 0x5e, 0x7f, 0xb6, 0x05, 0x80, 0x31, + 0x22, 0xfa, 0x13, 0x69, 0x24, 0xa8, 0x40, 0xb1, 0xf0, 0x71, + 0x9d, 0x2d, 0x5f, 0x68, 0xe2, 0x9b, 0x24, 0x22, 0x99, 0xd7, + 0x58, 0xed, 0x68, 0x0c }, + 64, + { 0x14, 0x07, 0xd4, 0x60, 0x13, 0xd9, 0x8b, 0xc6, 0xde, 0xce, + 0xfc, 0xfe, 0xe5, 0x5f, 0x0f, 0x90, 0xb0, 0xc7, 0xf6, 0x3d, + 0x68, 0xeb, 0x1a, 0x80, 0xea, 0xf0, 0x7e, 0x95, 0x3c, 0xfc, + 0x0a, 0x3a, 0x52, 0x40, 0xa1, 0x55, 0xd6, 0xe4, 0xda, 0xa9, + 0x65, 0xbb }, + 42, + }, +}; + +int main(int argc, char **argv) +{ + struct s2n_hmac_state hmac = { 0 }; + + uint8_t prk_pad[MAX_PSEUDO_RAND_KEY_SIZE] = { 0 }; + uint8_t output_pad[MAX_OUTPUT_SIZE] = { 0 }; + + struct s2n_blob in_key_blob = { 0 }; + struct s2n_blob salt_blob = { 0 }; + struct s2n_blob info_blob = { 0 }; + struct s2n_blob actual_prk_blob = { 0 }; + struct s2n_blob actual_output_blob = { 0 }; + struct s2n_blob prk_result = { 0 }; + struct s2n_blob out_result = { 0 }; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_SUCCESS(s2n_hmac_new(&hmac)); + + for (uint8_t i = 0; i < NUM_TESTS; i++) { + struct hkdf_test_vector *test = &tests[i]; + + EXPECT_SUCCESS(s2n_blob_init(&in_key_blob, test->in_key, test->in_key_len)); + EXPECT_SUCCESS(s2n_blob_init(&salt_blob, test->salt, test->salt_len)); + EXPECT_SUCCESS(s2n_blob_init(&info_blob, test->info, test->info_len)); + EXPECT_SUCCESS(s2n_blob_init(&actual_prk_blob, test->pseudo_rand_key, test->prk_len)); + EXPECT_SUCCESS(s2n_blob_init(&actual_output_blob, test->output, test->output_len)); + EXPECT_SUCCESS(s2n_blob_init(&prk_result, prk_pad, sizeof(prk_pad))); + EXPECT_SUCCESS(s2n_blob_init(&out_result, output_pad, sizeof(output_pad))); + + EXPECT_SUCCESS(s2n_hkdf_extract(&hmac, test->alg, &salt_blob, &in_key_blob, &prk_result)); + EXPECT_EQUAL(memcmp(prk_pad, actual_prk_blob.data, actual_prk_blob.size), 0); + + /* The size of the PRK output should match the digest size */ + uint8_t digest_size = 0; + EXPECT_SUCCESS(s2n_hmac_digest_size(test->alg, &digest_size)); + EXPECT_EQUAL(prk_result.size, digest_size); + + EXPECT_SUCCESS(s2n_hkdf(&hmac, test->alg, &salt_blob, &in_key_blob, &info_blob, &out_result)); + EXPECT_EQUAL(memcmp(output_pad, actual_output_blob.data, actual_output_blob.size), 0); + } + + /* Ensure that the PRK output size can't be set beyond its max size */ + { + s2n_stack_blob(small_prk_output, 10, 10); + EXPECT_FAILURE_WITH_ERRNO(s2n_hkdf_extract(&hmac, S2N_HMAC_SHA512, &salt_blob, &in_key_blob, &small_prk_output), + S2N_ERR_HKDF_OUTPUT_SIZE); + + /* s2n_hkdf_extract succeeds with the correct output size */ + s2n_stack_blob(large_prk_output, SHA512_DIGEST_LENGTH, SHA512_DIGEST_LENGTH); + EXPECT_SUCCESS(s2n_hkdf_extract(&hmac, S2N_HMAC_SHA512, &salt_blob, &in_key_blob, &large_prk_output)); + } + + /* Ensure that the libcrypto HKDF implementation is supported when s2n-tls is linked with AWSLC-FIPS */ + if (s2n_libcrypto_is_awslc() && s2n_is_in_fips_mode()) { + EXPECT_TRUE(s2n_libcrypto_supports_hkdf()); + } + + /* This size (5101) is obtained by multiplying the digest size for + * SHA1 (20) by the maximum number of rounds allowed for HKDF (255), + * then adding 1 + */ + uint8_t error_out_pad[5101]; + struct s2n_blob error_out = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&error_out, error_out_pad, sizeof(error_out_pad))); + struct s2n_blob zero_out = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&zero_out, output_pad, 0)); + + s2n_hmac_algorithm alg = S2N_HMAC_SHA1; + + EXPECT_FAILURE(s2n_hkdf(&hmac, alg, &salt_blob, &in_key_blob, &info_blob, &error_out)); + EXPECT_FAILURE(s2n_hkdf(&hmac, alg, &salt_blob, &in_key_blob, &info_blob, &zero_out)); + + EXPECT_SUCCESS(s2n_hmac_free(&hmac)); + + END_TEST(); +} diff --git a/tests/unit/s2n_hmac_test.c b/tests/unit/s2n_hmac_test.c new file mode 100644 index 00000000000..123b2da7eb8 --- /dev/null +++ b/tests/unit/s2n_hmac_test.c @@ -0,0 +1,293 @@ +/* + * 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 "crypto/s2n_hmac.h" + +#include + +#include "crypto/s2n_fips.h" +#include "crypto/s2n_hash.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_safety.h" + +int main(int argc, char **argv) +{ + uint8_t digest_pad[256]; + uint8_t check_pad[256]; + uint8_t output_pad[256]; + struct s2n_stuffer output = { 0 }; + uint8_t sekrit[] = "sekrit"; + uint8_t longsekrit[] = "This is a really really really long key on purpose to make sure that it's longer than the block size"; + uint8_t hello[] = "Hello world!"; + uint8_t string1[] = "String 1"; + uint8_t string2[] = "and String 2"; + struct s2n_hmac_state hmac, copy, cmac; + + struct s2n_blob out = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&out, output_pad, sizeof(output_pad))); + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_SUCCESS(s2n_hmac_new(&hmac)); + EXPECT_SUCCESS(s2n_hmac_new(©)); + EXPECT_SUCCESS(s2n_hmac_new(&cmac)); + + if (s2n_hmac_is_available(S2N_HMAC_SSLv3_MD5)) { + /* Try SSLv3 MD5 */ + uint8_t hmac_sslv3_md5_size; + POSIX_GUARD(s2n_hmac_digest_size(S2N_HMAC_SSLv3_MD5, &hmac_sslv3_md5_size)); + EXPECT_EQUAL(hmac_sslv3_md5_size, 16); + EXPECT_SUCCESS(s2n_hmac_init(&hmac, S2N_HMAC_SSLv3_MD5, sekrit, strlen((char *) sekrit))); + EXPECT_SUCCESS(s2n_hmac_update(&hmac, hello, strlen((char *) hello))); + EXPECT_SUCCESS(s2n_hmac_digest(&hmac, digest_pad, 16)); + + EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); + for (int i = 0; i < 16; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); + } + + /* Reference value from Go */ + EXPECT_EQUAL(memcmp(output_pad, "d4f0d06b9765de23e6c3e33a24c5ded0", 16 * 2), 0); + + /* Test that a reset works */ + EXPECT_SUCCESS(s2n_hmac_reset(&hmac)); + EXPECT_SUCCESS(s2n_hmac_update(&hmac, hello, strlen((char *) hello))); + EXPECT_SUCCESS(s2n_hmac_digest(&hmac, digest_pad, 16)); + + EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); + for (int i = 0; i < 16; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); + } + + /* Reference value from Go */ + EXPECT_EQUAL(memcmp(output_pad, "d4f0d06b9765de23e6c3e33a24c5ded0", 16 * 2), 0); + + EXPECT_SUCCESS(s2n_hmac_reset(&hmac)); + } + + if (s2n_hmac_is_available(S2N_HMAC_SSLv3_SHA1)) { + /* Try SSLv3 SHA1 */ + uint8_t hmac_sslv3_sha1_size; + POSIX_GUARD(s2n_hmac_digest_size(S2N_HMAC_SSLv3_SHA1, &hmac_sslv3_sha1_size)); + EXPECT_EQUAL(hmac_sslv3_sha1_size, 20); + EXPECT_SUCCESS(s2n_hmac_init(&hmac, S2N_HMAC_SSLv3_SHA1, sekrit, strlen((char *) sekrit))); + EXPECT_SUCCESS(s2n_hmac_update(&hmac, hello, strlen((char *) hello))); + EXPECT_SUCCESS(s2n_hmac_digest(&hmac, digest_pad, 20)); + + EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); + for (int i = 0; i < 20; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); + } + + /* Reference value from Go */ + EXPECT_EQUAL(memcmp(output_pad, "b0c66179f6eb5a46b4b7c4fca84b3ea5161b7326", 20 * 2), 0); + + /* Test that a reset works */ + EXPECT_SUCCESS(s2n_hmac_reset(&hmac)); + EXPECT_SUCCESS(s2n_hmac_update(&hmac, hello, strlen((char *) hello))); + EXPECT_SUCCESS(s2n_hmac_digest(&hmac, digest_pad, 20)); + + EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); + for (int i = 0; i < 20; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); + } + + /* Reference value from Go */ + EXPECT_EQUAL(memcmp(output_pad, "b0c66179f6eb5a46b4b7c4fca84b3ea5161b7326", 20 * 2), 0); + + EXPECT_SUCCESS(s2n_hmac_reset(&hmac)); + } + + if (s2n_hmac_is_available(S2N_HMAC_MD5)) { + /* Try MD5 */ + uint8_t hmac_md5_size; + POSIX_GUARD(s2n_hmac_digest_size(S2N_HMAC_MD5, &hmac_md5_size)); + EXPECT_EQUAL(hmac_md5_size, 16); + EXPECT_SUCCESS(s2n_hmac_init(&hmac, S2N_HMAC_MD5, sekrit, strlen((char *) sekrit))); + EXPECT_SUCCESS(s2n_hmac_update(&hmac, hello, strlen((char *) hello))); + EXPECT_SUCCESS(s2n_hmac_digest(&hmac, digest_pad, 16)); + + EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); + for (int i = 0; i < 16; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); + } + + /* Reference value from python */ + EXPECT_EQUAL(memcmp(output_pad, "3ad68c53dc1a3cf35f6469877fae4585", 16 * 2), 0); + + EXPECT_SUCCESS(s2n_hmac_reset(&hmac)); + } + + /* Try SHA1 */ + uint8_t hmac_sha1_size; + POSIX_GUARD(s2n_hmac_digest_size(S2N_HMAC_SHA1, &hmac_sha1_size)); + EXPECT_EQUAL(hmac_sha1_size, 20); + EXPECT_SUCCESS(s2n_hmac_init(&hmac, S2N_HMAC_SHA1, sekrit, strlen((char *) sekrit))); + EXPECT_SUCCESS(s2n_hmac_update(&hmac, hello, strlen((char *) hello))); + EXPECT_SUCCESS(s2n_hmac_copy(©, &hmac)); + EXPECT_SUCCESS(s2n_hmac_digest(&hmac, digest_pad, 20)); + + EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); + for (int i = 0; i < 20; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); + } + + /* Reference value from python */ + EXPECT_EQUAL(memcmp(output_pad, "6d301861b599938eca94f6de917362886d97882f", 20 * 2), 0); + + /* Check the copy */ + EXPECT_SUCCESS(s2n_hmac_digest(©, digest_pad, 20)); + + EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); + for (int i = 0; i < 20; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); + } + + /* Reference value from python */ + EXPECT_EQUAL(memcmp(output_pad, "6d301861b599938eca94f6de917362886d97882f", 20 * 2), 0); + + /* Test that a reset works, and test that a multi-update works */ + EXPECT_SUCCESS(s2n_hmac_reset(&hmac)); + EXPECT_SUCCESS(s2n_hmac_reset(©)); + EXPECT_SUCCESS(s2n_hmac_update(&hmac, string1, strlen((char *) string1))); + EXPECT_SUCCESS(s2n_hmac_copy(©, &hmac)); + EXPECT_SUCCESS(s2n_hmac_update(&hmac, string2, strlen((char *) string2))); + EXPECT_SUCCESS(s2n_hmac_digest(&hmac, digest_pad, 20)); + EXPECT_SUCCESS(s2n_hmac_free(&hmac)); + + EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); + for (int i = 0; i < 20; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); + } + + /* Reference value from python */ + EXPECT_EQUAL(memcmp(output_pad, "6144020409573b75a7093c1ec16c0c43030769aa", 20 * 2), 0); + + /* Test that a copy-update works */ + EXPECT_SUCCESS(s2n_hmac_update(©, string2, strlen((char *) string2))); + EXPECT_SUCCESS(s2n_hmac_digest(©, digest_pad, 20)); + EXPECT_SUCCESS(s2n_hmac_free(©)); + + EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); + for (int i = 0; i < 20; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); + } + + /* Reference value from python */ + EXPECT_EQUAL(memcmp(output_pad, "6144020409573b75a7093c1ec16c0c43030769aa", 20 * 2), 0); + + /* Test that a long secret works */ + EXPECT_SUCCESS(s2n_hmac_new(&hmac)); + EXPECT_SUCCESS(s2n_hmac_init(&hmac, S2N_HMAC_SHA1, longsekrit, strlen((char *) longsekrit))); + EXPECT_SUCCESS(s2n_hmac_update(&hmac, hello, strlen((char *) hello))); + EXPECT_SUCCESS(s2n_hmac_digest(&hmac, digest_pad, 20)); + EXPECT_SUCCESS(s2n_hmac_free(&hmac)); + + EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); + for (int i = 0; i < 20; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); + } + + /* Reference value from python */ + EXPECT_EQUAL(memcmp(output_pad, "9c0e7b5a2f0efe770db4640a6f30c76d2b9c0fee", 20 * 2), 0); + + /* Verify that _verify works */ + EXPECT_SUCCESS(s2n_hmac_init(&cmac, S2N_HMAC_SHA1, longsekrit, strlen((char *) longsekrit))); + EXPECT_SUCCESS(s2n_hmac_update(&cmac, hello, strlen((char *) hello))); + EXPECT_SUCCESS(s2n_hmac_digest(&cmac, check_pad, 20)); + EXPECT_SUCCESS(s2n_hmac_digest_verify(digest_pad, check_pad, 20)); + EXPECT_SUCCESS(s2n_hmac_free(&cmac)); + + /* Try SHA224 */ + EXPECT_SUCCESS(s2n_hmac_new(&hmac)); + + uint8_t hmac_sha224_size; + POSIX_GUARD(s2n_hmac_digest_size(S2N_HMAC_SHA224, &hmac_sha224_size)); + EXPECT_EQUAL(hmac_sha224_size, 28); + EXPECT_SUCCESS(s2n_hmac_init(&hmac, S2N_HMAC_SHA224, sekrit, strlen((char *) sekrit))); + EXPECT_SUCCESS(s2n_hmac_update(&hmac, hello, strlen((char *) hello))); + EXPECT_SUCCESS(s2n_hmac_digest(&hmac, digest_pad, 28)); + EXPECT_SUCCESS(s2n_hmac_free(&hmac)); + + EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); + for (int i = 0; i < 28; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); + } + + /* Reference value from python */ + EXPECT_EQUAL(memcmp(output_pad, "095f9b54295575c9ac5352b1c366dd6adb2bb1133b6587e4990f33b4", 28 * 2), 0); + + /* Try SHA256 */ + EXPECT_SUCCESS(s2n_hmac_new(&hmac)); + + uint8_t hmac_sha256_size; + POSIX_GUARD(s2n_hmac_digest_size(S2N_HMAC_SHA256, &hmac_sha256_size)); + EXPECT_EQUAL(hmac_sha256_size, 32); + EXPECT_SUCCESS(s2n_hmac_init(&hmac, S2N_HMAC_SHA256, sekrit, strlen((char *) sekrit))); + EXPECT_SUCCESS(s2n_hmac_update(&hmac, hello, strlen((char *) hello))); + EXPECT_SUCCESS(s2n_hmac_digest(&hmac, digest_pad, 32)); + EXPECT_SUCCESS(s2n_hmac_free(&hmac)); + + EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); + for (int i = 0; i < 32; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); + } + + /* Reference value from python */ + EXPECT_EQUAL(memcmp(output_pad, "adc20b12d236e6d1824d690622e33ead4f67ba5a2be9606fe762b2dd859a78a9", 32 * 2), 0); + + /* Try SHA384 */ + EXPECT_SUCCESS(s2n_hmac_new(&hmac)); + + uint8_t hmac_sha384_size; + POSIX_GUARD(s2n_hmac_digest_size(S2N_HMAC_SHA384, &hmac_sha384_size)); + EXPECT_EQUAL(hmac_sha384_size, 48); + EXPECT_SUCCESS(s2n_hmac_init(&hmac, S2N_HMAC_SHA384, sekrit, strlen((char *) sekrit))); + EXPECT_SUCCESS(s2n_hmac_update(&hmac, hello, strlen((char *) hello))); + EXPECT_SUCCESS(s2n_hmac_digest(&hmac, digest_pad, 48)); + EXPECT_SUCCESS(s2n_hmac_free(&hmac)); + + EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); + for (int i = 0; i < 48; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); + } + + /* Reference value from python */ + EXPECT_EQUAL(memcmp(output_pad, "8552563cadd583b79dcc7225bb79bc6483c63f259187162e1c9d4283eb6299ef1bc3ca81c0c40fc7b22f7a1f3b93adb4", 48 * 2), 0); + + /* Try SHA512 */ + EXPECT_SUCCESS(s2n_hmac_new(&hmac)); + + uint8_t hmac_sha512_size; + POSIX_GUARD(s2n_hmac_digest_size(S2N_HMAC_SHA512, &hmac_sha512_size)); + EXPECT_EQUAL(hmac_sha512_size, 64); + EXPECT_SUCCESS(s2n_hmac_init(&hmac, S2N_HMAC_SHA512, sekrit, strlen((char *) sekrit))); + EXPECT_SUCCESS(s2n_hmac_update(&hmac, hello, strlen((char *) hello))); + EXPECT_SUCCESS(s2n_hmac_digest(&hmac, digest_pad, 64)); + EXPECT_SUCCESS(s2n_hmac_free(&hmac)); + + EXPECT_SUCCESS(s2n_stuffer_init(&output, &out)); + for (int i = 0; i < 64; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&output, digest_pad[i])); + } + + /* Reference value from python */ + EXPECT_EQUAL(memcmp(output_pad, "0a834a1ed265042e2897405edb4fdd9818950cd5bea10b828f2fed45a1cb6dbd2107e4b04eb20f211998cd4e8c7e11ebdcb0103ac63882481e1bb8083d07f4be", 64 * 2), 0); + + END_TEST(); +} diff --git a/tests/unit/s2n_init_test.c b/tests/unit/s2n_init_test.c new file mode 100644 index 00000000000..80e2e63fbdd --- /dev/null +++ b/tests/unit/s2n_init_test.c @@ -0,0 +1,92 @@ +/* + * 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 "s2n_test.h" + +static void *s2n_init_fail_cb(void *_unused_arg) +{ + (void) _unused_arg; + + EXPECT_FAILURE_WITH_ERRNO(s2n_init(), S2N_ERR_INITIALIZED); + return NULL; +} + +static void *s2n_init_success_cb(void *_unused_arg) +{ + (void) _unused_arg; + + EXPECT_SUCCESS(s2n_init()); + return NULL; +} + +int s2n_enable_atexit(void); + +int main(int argc, char **argv) +{ + BEGIN_TEST_NO_INIT(); + + /* Disabling the atexit handler makes it easier for us to test s2n_init and s2n_cleanup + * behavior. Otherwise we'd have to create and exit a bunch of processes to test this + * interaction. */ + EXPECT_SUCCESS(s2n_disable_atexit()); + + /* Calling s2n_init twice in a row will cause an error */ + EXPECT_SUCCESS(s2n_init()); + EXPECT_FAILURE_WITH_ERRNO(s2n_init(), S2N_ERR_INITIALIZED); + EXPECT_SUCCESS(s2n_cleanup()); + + /* Second call to s2n_cleanup will fail, since the full cleanup is not idempotent. + * This behavior only exists when atexit is disabled. */ + EXPECT_FAILURE_WITH_ERRNO(s2n_cleanup(), S2N_ERR_NOT_INITIALIZED); + + /* Clean up and init multiple times */ + for (size_t i = 0; i < 10; i++) { + EXPECT_SUCCESS(s2n_init()); + EXPECT_SUCCESS(s2n_cleanup()); + } + + /* Calling s2n_init again after creating a process will cause an error */ + EXPECT_SUCCESS(s2n_init()); + int pid = fork(); + if (pid == 0) { + /* Child process */ + EXPECT_FAILURE_WITH_ERRNO(s2n_init(), S2N_ERR_INITIALIZED); + EXPECT_SUCCESS(s2n_cleanup()); + return 0; + } + EXPECT_SUCCESS(s2n_cleanup()); + + /* Calling s2n_init again after creating a thread will cause an error */ + EXPECT_SUCCESS(s2n_init()); + pthread_t init_thread = { 0 }; + EXPECT_EQUAL(pthread_create(&init_thread, NULL, s2n_init_fail_cb, NULL), 0); + EXPECT_EQUAL(pthread_join(init_thread, NULL), 0); + EXPECT_SUCCESS(s2n_cleanup()); + + /* The following test requires atexit to be enabled. */ + EXPECT_SUCCESS(s2n_enable_atexit()); + + /* Initializing s2n on a child thread without calling s2n_cleanup on that + * thread will not result in a memory leak. This is because we register + * thread-local memory to be cleaned up at thread-exit + * and then our atexit handler cleans up the rest at proccess-exit. */ + pthread_t init_success_thread = { 0 }; + EXPECT_EQUAL(pthread_create(&init_success_thread, NULL, s2n_init_success_cb, NULL), 0); + EXPECT_EQUAL(pthread_join(init_success_thread, NULL), 0); + + END_TEST_NO_INIT(); +} diff --git a/tests/unit/s2n_io_test.c b/tests/unit/s2n_io_test.c new file mode 100644 index 00000000000..c06782f9308 --- /dev/null +++ b/tests/unit/s2n_io_test.c @@ -0,0 +1,131 @@ +/* + * 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 "utils/s2n_io.h" + +#include + +#include "s2n_test.h" +#include "tests/testlib/s2n_testlib.h" + +#define S2N_TEST_SIGNAL SIGUSR1 +#define S2N_TEST_SUCCESS 123 + +static int s2n_test_mocked_interrupt(uint8_t n_times, uint8_t *counter) +{ + if (*counter < n_times) { + (*counter)++; + + errno = EINTR; + return -1; + } + return S2N_TEST_SUCCESS; +} + +static int s2n_test_real_interrupt(int fd, uint8_t n_times, uint8_t *counter) +{ + if (*counter < n_times) { + (*counter)++; + + /* There is no data being written to fd, + * so this will block indefinitely unless interrupted. + */ + uint8_t buffer = 0; + int r = read(fd, &buffer, 1); + EXPECT_TRUE(r < 0); + EXPECT_EQUAL(errno, EINTR); + return r; + } + return S2N_TEST_SUCCESS; +} + +static void s2n_test_sig_handler(int signum) +{ + EXPECT_EQUAL(signum, S2N_TEST_SIGNAL); +} + +static int s2n_fail_without_errno(uint8_t *counter) +{ + (*counter)++; + if (*counter > 10) { + /* To avoid an infinite loop on test case failure, eventually return success */ + return 0; + } + return -1; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Test S2N_IO_RETRY_EINTR */ + { + const uint8_t n_times = 5; + + /* Retries on errno == EINTR */ + { + uint8_t counter = 0; + int result = 0; + S2N_IO_RETRY_EINTR(result, s2n_test_mocked_interrupt(n_times, &counter)); + EXPECT_EQUAL(result, S2N_TEST_SUCCESS); + EXPECT_EQUAL(counter, n_times); + }; + + /* Retries on real interrupt */ + { + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + struct sigaction action = { + .sa_handler = s2n_test_sig_handler, + }; + sigaction(SIGUSR1, &action, NULL); + + fflush(stdout); + pid_t pid = fork(); + if (pid == 0) { + uint8_t counter = 0; + int result = 0; + S2N_IO_RETRY_EINTR(result, + s2n_test_real_interrupt(io_pair.client, n_times, &counter)); + EXPECT_EQUAL(result, S2N_TEST_SUCCESS); + EXPECT_EQUAL(counter, n_times); + exit(0); + } + + /* Keep sending the signal until the process exits */ + int status = 0; + while (waitpid(pid, &status, WNOHANG) == 0) { + EXPECT_EQUAL(kill(pid, S2N_TEST_SIGNAL), 0); + } + EXPECT_EQUAL(status, EXIT_SUCCESS); + }; + + /* Handles IO methods that don't properly set errno */ + { + /* Set errno to EINTR to try to trigger retry */ + errno = EINTR; + + uint8_t counter = 0; + int result = 0; + S2N_IO_RETRY_EINTR(result, s2n_fail_without_errno(&counter)); + EXPECT_EQUAL(result, -1); + EXPECT_EQUAL(counter, 1); + EXPECT_EQUAL(errno, 0); + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_kem_preferences_test.c b/tests/unit/s2n_kem_preferences_test.c new file mode 100644 index 00000000000..5d78737b150 --- /dev/null +++ b/tests/unit/s2n_kem_preferences_test.c @@ -0,0 +1,69 @@ +/* + * 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 "tls/s2n_kem_preferences.h" + +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "tls/s2n_tls_parameters.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_FALSE(s2n_kem_preferences_includes_tls13_kem_group(&kem_preferences_null, TLS_PQ_KEM_GROUP_ID_X25519_KYBER_512_R3)); + EXPECT_FALSE(s2n_kem_preferences_includes_tls13_kem_group(&kem_preferences_null, TLS_PQ_KEM_GROUP_ID_SECP256R1_KYBER_512_R3)); + EXPECT_FALSE(s2n_kem_preferences_includes_tls13_kem_group(&kem_preferences_null, TLS_PQ_KEM_GROUP_ID_SECP384R1_KYBER_768_R3)); + EXPECT_FALSE(s2n_kem_preferences_includes_tls13_kem_group(&kem_preferences_null, TLS_PQ_KEM_GROUP_ID_SECP521R1_KYBER_1024_R3)); + + { + const struct s2n_kem_group *test_kem_groups[] = { + &s2n_secp256r1_kyber_512_r3, +#if EVP_APIS_SUPPORTED + &s2n_x25519_kyber_512_r3, +#endif +#if defined(S2N_LIBCRYPTO_SUPPORTS_KYBER) + &s2n_secp384r1_kyber_768_r3, + &s2n_secp521r1_kyber_1024_r3, +#endif + }; + + const struct s2n_kem_preferences test_prefs = { + .kem_count = 0, + .kems = NULL, + .tls13_kem_group_count = s2n_array_len(test_kem_groups), + .tls13_kem_groups = test_kem_groups, + }; + + EXPECT_TRUE(s2n_kem_preferences_includes_tls13_kem_group(&test_prefs, TLS_PQ_KEM_GROUP_ID_SECP256R1_KYBER_512_R3)); + +#if EVP_APIS_SUPPORTED + EXPECT_TRUE(s2n_kem_preferences_includes_tls13_kem_group(&test_prefs, TLS_PQ_KEM_GROUP_ID_X25519_KYBER_512_R3)); +#else + EXPECT_FALSE(s2n_kem_preferences_includes_tls13_kem_group(&test_prefs, TLS_PQ_KEM_GROUP_ID_X25519_KYBER_512_R3)); +#endif + +#if defined(S2N_LIBCRYPTO_SUPPORTS_KYBER) + EXPECT_TRUE(s2n_kem_preferences_includes_tls13_kem_group(&test_prefs, TLS_PQ_KEM_GROUP_ID_SECP384R1_KYBER_768_R3)); + EXPECT_TRUE(s2n_kem_preferences_includes_tls13_kem_group(&test_prefs, TLS_PQ_KEM_GROUP_ID_SECP521R1_KYBER_1024_R3)); +#else + EXPECT_FALSE(s2n_kem_preferences_includes_tls13_kem_group(&test_prefs, TLS_PQ_KEM_GROUP_ID_SECP384R1_KYBER_768_R3)); + EXPECT_FALSE(s2n_kem_preferences_includes_tls13_kem_group(&test_prefs, TLS_PQ_KEM_GROUP_ID_SECP521R1_KYBER_1024_R3)); +#endif + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_kem_test.c b/tests/unit/s2n_kem_test.c new file mode 100644 index 00000000000..57f8325a7b8 --- /dev/null +++ b/tests/unit/s2n_kem_test.c @@ -0,0 +1,513 @@ +/* + * 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 "tls/s2n_kem.h" + +#include "crypto/s2n_ecc_evp.h" +#include "pq-crypto/s2n_pq.h" +#include "tests/s2n_test.h" +#include "tls/extensions/s2n_key_share.h" +#include "tls/s2n_cipher_preferences.h" +#include "tls/s2n_kem_preferences.h" +#include "tls/s2n_kex.h" +#include "tls/s2n_tls_parameters.h" +#include "utils/s2n_safety.h" + +#define TEST_PUBLIC_KEY_LENGTH 2 +const uint8_t TEST_PUBLIC_KEY[] = { 2, 2 }; +#define TEST_PRIVATE_KEY_LENGTH 3 +const uint8_t TEST_PRIVATE_KEY[] = { 3, 3, 3 }; +#define TEST_SHARED_SECRET_LENGTH 4 +const uint8_t TEST_SHARED_SECRET[] = { 4, 4, 4, 4 }; +#define TEST_CIPHERTEXT_LENGTH 5 +const uint8_t TEST_CIPHERTEXT[] = { 5, 5, 5, 5, 5 }; + +static const uint8_t kyber_iana[S2N_TLS_CIPHER_SUITE_LEN] = { TLS_ECDHE_KYBER_RSA_WITH_AES_256_GCM_SHA384 }; +static const uint8_t classic_ecdhe_iana[S2N_TLS_CIPHER_SUITE_LEN] = { TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA }; + +int alloc_test_kem_params(struct s2n_kem_params *kem_params) +{ + POSIX_GUARD(s2n_alloc(&(kem_params->private_key), TEST_PRIVATE_KEY_LENGTH)); + struct s2n_stuffer private_key_stuffer = { 0 }; + POSIX_GUARD(s2n_stuffer_init(&private_key_stuffer, &(kem_params->private_key))); + POSIX_GUARD(s2n_stuffer_write_bytes(&private_key_stuffer, TEST_PRIVATE_KEY, TEST_PRIVATE_KEY_LENGTH)); + + POSIX_GUARD(s2n_alloc(&(kem_params->public_key), TEST_PUBLIC_KEY_LENGTH)); + struct s2n_stuffer public_key_stuffer = { 0 }; + POSIX_GUARD(s2n_stuffer_init(&public_key_stuffer, &(kem_params->public_key))); + POSIX_GUARD(s2n_stuffer_write_bytes(&public_key_stuffer, TEST_PUBLIC_KEY, TEST_PUBLIC_KEY_LENGTH)); + + POSIX_GUARD(s2n_alloc(&(kem_params->shared_secret), TEST_SHARED_SECRET_LENGTH)); + struct s2n_stuffer shared_secret_stuffer = { 0 }; + POSIX_GUARD(s2n_stuffer_init(&shared_secret_stuffer, &(kem_params->shared_secret))); + POSIX_GUARD(s2n_stuffer_write_bytes(&shared_secret_stuffer, TEST_SHARED_SECRET, TEST_SHARED_SECRET_LENGTH)); + + POSIX_ENSURE_NE(0, kem_params->private_key.allocated); + POSIX_ENSURE_NE(0, kem_params->public_key.allocated); + POSIX_ENSURE_NE(0, kem_params->shared_secret.allocated); + + return S2N_SUCCESS; +} + +int assert_kem_params_free(struct s2n_kem_params *kem_params) +{ + POSIX_ENSURE_EQ(NULL, kem_params->private_key.data); + POSIX_ENSURE_EQ(0, kem_params->private_key.size); + POSIX_ENSURE_EQ(0, kem_params->private_key.allocated); + + POSIX_ENSURE_EQ(NULL, kem_params->public_key.data); + POSIX_ENSURE_EQ(0, kem_params->public_key.size); + POSIX_ENSURE_EQ(0, kem_params->public_key.allocated); + + POSIX_ENSURE_EQ(NULL, kem_params->shared_secret.data); + POSIX_ENSURE_EQ(0, kem_params->shared_secret.size); + POSIX_ENSURE_EQ(0, kem_params->shared_secret.allocated); + + return S2N_SUCCESS; +} + +int s2n_test_generate_keypair(const struct s2n_kem *kem, unsigned char *public_key, unsigned char *private_key) +{ + memset(public_key, kem->public_key_length, kem->public_key_length); + memset(private_key, kem->private_key_length, kem->private_key_length); + return 0; +} + +int s2n_test_encrypt(const struct s2n_kem *kem, unsigned char *ciphertext, unsigned char *shared_secret, const unsigned char *public_key) +{ + POSIX_GUARD(memcmp(public_key, TEST_PUBLIC_KEY, TEST_PUBLIC_KEY_LENGTH)); + memset(ciphertext, kem->ciphertext_length, kem->ciphertext_length); + memset(shared_secret, kem->shared_secret_key_length, kem->shared_secret_key_length); + return 0; +} + +int s2n_test_decrypt(const struct s2n_kem *kem, unsigned char *shared_secret, const unsigned char *ciphertext, const unsigned char *private_key) +{ + POSIX_GUARD(memcmp(ciphertext, TEST_CIPHERTEXT, TEST_CIPHERTEXT_LENGTH)); + POSIX_GUARD(memcmp(private_key, TEST_PRIVATE_KEY, TEST_PRIVATE_KEY_LENGTH)); + memset(shared_secret, kem->shared_secret_key_length, kem->shared_secret_key_length); + return 0; +} + +const struct s2n_kem s2n_test_kem = { + .public_key_length = TEST_PUBLIC_KEY_LENGTH, + .private_key_length = TEST_PRIVATE_KEY_LENGTH, + .shared_secret_key_length = TEST_SHARED_SECRET_LENGTH, + .ciphertext_length = TEST_CIPHERTEXT_LENGTH, + .generate_keypair = &s2n_test_generate_keypair, + .encapsulate = &s2n_test_encrypt, + .decapsulate = &s2n_test_decrypt, +}; + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Run KEM tests that don't depend on the value of len_prefix */ + { + /* Regression test for network parsing data of expected sizes */ + EXPECT_EQUAL(sizeof(kem_extension_size), 2); + EXPECT_EQUAL(sizeof(kem_public_key_size), 2); + EXPECT_EQUAL(sizeof(kem_private_key_size), 2); + EXPECT_EQUAL(sizeof(kem_shared_secret_size), 2); + EXPECT_EQUAL(sizeof(kem_ciphertext_key_size), 2); + }; + { + const struct s2n_iana_to_kem *compatible_params = NULL; + EXPECT_FAILURE_WITH_ERRNO(s2n_cipher_suite_to_kem(classic_ecdhe_iana, &compatible_params), S2N_ERR_KEM_UNSUPPORTED_PARAMS); + EXPECT_NULL(compatible_params); + + EXPECT_SUCCESS(s2n_cipher_suite_to_kem(kyber_iana, &compatible_params)); + EXPECT_NOT_NULL(compatible_params); + EXPECT_EQUAL(compatible_params->kem_count, 1); + EXPECT_EQUAL(compatible_params->kems[0]->kem_extension_id, s2n_kyber_512_r3.kem_extension_id); + }; + { + /* Tests for s2n_kem_free() */ + EXPECT_SUCCESS(s2n_kem_free(NULL)); + + struct s2n_kem_params kem_params = { 0 }; + EXPECT_SUCCESS(s2n_kem_free(&kem_params)); + + /* Fill kem_params with secrets and ensure that they have been freed */ + EXPECT_SUCCESS(alloc_test_kem_params(&kem_params)); + EXPECT_SUCCESS(s2n_kem_free(&kem_params)); + EXPECT_SUCCESS(assert_kem_params_free(&kem_params)); + }; + { + /* Tests for s2n_kem_group_free() */ + EXPECT_SUCCESS(s2n_kem_group_free(NULL)); + + struct s2n_kem_group_params kem_group_params = { 0 }; + EXPECT_SUCCESS(s2n_kem_group_free(&kem_group_params)); + + /* Fill the kem_group_params with secrets */ + EXPECT_SUCCESS(alloc_test_kem_params(&kem_group_params.kem_params)); + struct s2n_stuffer wire = { 0 }; + POSIX_GUARD(s2n_stuffer_growable_alloc(&wire, 1024)); + kem_group_params.ecc_params.negotiated_curve = &s2n_ecc_curve_secp256r1; + POSIX_GUARD(s2n_ecdhe_parameters_send(&kem_group_params.ecc_params, &wire)); + POSIX_GUARD(s2n_stuffer_free(&wire)); + EXPECT_NOT_NULL(kem_group_params.ecc_params.evp_pkey); + + /* Ensure that secrets have been freed */ + EXPECT_SUCCESS(s2n_kem_group_free(&kem_group_params)); + EXPECT_SUCCESS(assert_kem_params_free(&kem_group_params.kem_params)); + EXPECT_NULL(kem_group_params.ecc_params.evp_pkey); + }; + { + /* Happy case(s) for s2n_get_kem_from_extension_id() */ + + /* The kem_extensions and kems arrays should be kept in sync with each other */ + kem_extension_size kem_extensions[] = { + TLS_PQ_KEM_EXTENSION_ID_KYBER_512_R3, + }; + + const struct s2n_kem *kems[] = { + &s2n_kyber_512_r3, + }; + + for (size_t i = 0; i < s2n_array_len(kems); i++) { + kem_extension_size kem_id = kem_extensions[i]; + const struct s2n_kem *returned_kem = NULL; + + EXPECT_SUCCESS(s2n_get_kem_from_extension_id(kem_id, &returned_kem)); + EXPECT_NOT_NULL(returned_kem); + EXPECT_EQUAL(kems[i], returned_kem); + } + }; + { + /* Failure cases for s2n_get_kem_from_extension_id() */ + const struct s2n_kem *returned_kem = NULL; + kem_extension_size non_existent_kem_id = 65535; + EXPECT_FAILURE_WITH_ERRNO(s2n_get_kem_from_extension_id(non_existent_kem_id, &returned_kem), S2N_ERR_KEM_UNSUPPORTED_PARAMS); + }; + + /* If KEM tests depend on len_prefix, test with both possible values */ + for (int len_prefixed = 0; len_prefixed < 2; len_prefixed++) { + { + struct s2n_kem_params server_kem_params = { 0 }; + server_kem_params.kem = &s2n_test_kem; + server_kem_params.len_prefixed = len_prefixed; + EXPECT_SUCCESS(s2n_alloc(&server_kem_params.public_key, TEST_PUBLIC_KEY_LENGTH)); + EXPECT_OK(s2n_kem_generate_keypair(&server_kem_params)); + EXPECT_EQUAL(TEST_PUBLIC_KEY_LENGTH, server_kem_params.public_key.size); + EXPECT_EQUAL(TEST_PRIVATE_KEY_LENGTH, server_kem_params.private_key.size); + EXPECT_BYTEARRAY_EQUAL(TEST_PUBLIC_KEY, server_kem_params.public_key.data, TEST_PUBLIC_KEY_LENGTH); + EXPECT_BYTEARRAY_EQUAL(TEST_PRIVATE_KEY, server_kem_params.private_key.data, TEST_PRIVATE_KEY_LENGTH); + /* KeyGen shouldn't modify the shared secret */ + EXPECT_EQUAL(0, server_kem_params.shared_secret.size); + EXPECT_EQUAL(0, server_kem_params.shared_secret.allocated); + EXPECT_NULL(server_kem_params.shared_secret.data); + + struct s2n_kem_params client_kem_params = { 0 }; + client_kem_params.kem = &s2n_test_kem; + client_kem_params.len_prefixed = len_prefixed; + /* This would be handled by client/server key exchange methods which isn't being tested */ + POSIX_GUARD(s2n_alloc(&client_kem_params.public_key, TEST_PUBLIC_KEY_LENGTH)); + memset(client_kem_params.public_key.data, TEST_PUBLIC_KEY_LENGTH, TEST_PUBLIC_KEY_LENGTH); + + DEFER_CLEANUP(struct s2n_blob ciphertext = { 0 }, s2n_free); + POSIX_GUARD(s2n_alloc(&ciphertext, TEST_CIPHERTEXT_LENGTH)); + + EXPECT_OK(s2n_kem_encapsulate(&client_kem_params, &ciphertext)); + EXPECT_EQUAL(TEST_SHARED_SECRET_LENGTH, client_kem_params.shared_secret.size); + EXPECT_EQUAL(TEST_CIPHERTEXT_LENGTH, ciphertext.size); + EXPECT_BYTEARRAY_EQUAL(TEST_SHARED_SECRET, client_kem_params.shared_secret.data, TEST_SHARED_SECRET_LENGTH); + EXPECT_BYTEARRAY_EQUAL(TEST_CIPHERTEXT, ciphertext.data, TEST_CIPHERTEXT_LENGTH); + /* Encaps shouldn't modify the public or private keys */ + EXPECT_EQUAL(TEST_PUBLIC_KEY_LENGTH, client_kem_params.public_key.size); + EXPECT_BYTEARRAY_EQUAL(TEST_PUBLIC_KEY, client_kem_params.public_key.data, TEST_PUBLIC_KEY_LENGTH); + EXPECT_EQUAL(0, client_kem_params.private_key.size); + EXPECT_EQUAL(0, client_kem_params.private_key.allocated); + EXPECT_NULL(client_kem_params.private_key.data); + + EXPECT_OK(s2n_kem_decapsulate(&server_kem_params, &ciphertext)); + EXPECT_EQUAL(TEST_SHARED_SECRET_LENGTH, server_kem_params.shared_secret.size); + EXPECT_BYTEARRAY_EQUAL(TEST_SHARED_SECRET, server_kem_params.shared_secret.data, TEST_SHARED_SECRET_LENGTH); + /* Decaps shouldn't modify the public or private keys */ + EXPECT_EQUAL(TEST_PUBLIC_KEY_LENGTH, server_kem_params.public_key.size); + EXPECT_BYTEARRAY_EQUAL(TEST_PUBLIC_KEY, server_kem_params.public_key.data, TEST_PUBLIC_KEY_LENGTH); + EXPECT_EQUAL(TEST_PRIVATE_KEY_LENGTH, server_kem_params.private_key.size); + EXPECT_BYTEARRAY_EQUAL(TEST_PRIVATE_KEY, server_kem_params.private_key.data, TEST_PRIVATE_KEY_LENGTH); + + EXPECT_SUCCESS(s2n_kem_free(&server_kem_params)); + EXPECT_SUCCESS(s2n_kem_free(&client_kem_params)); + }; + { + /* Happy case for s2n_kem_send_public_key() */ + struct s2n_kem_params kem_params = { .kem = &s2n_test_kem, .len_prefixed = len_prefixed }; + + DEFER_CLEANUP(struct s2n_blob io_blob = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&io_blob, TEST_PUBLIC_KEY_LENGTH + 2)); + struct s2n_stuffer io_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&io_stuffer, &io_blob)); + + EXPECT_SUCCESS(s2n_kem_send_public_key(&io_stuffer, &kem_params)); + + /* {0, 2} = length of public key to follow + * {2, 2} = test public key */ + uint8_t prefixed_output[] = { 0, 2, 2, 2 }; + uint8_t unprefixed_output[] = { 2, 2 }; + + uint8_t *output = unprefixed_output; + uint16_t output_len = TEST_PUBLIC_KEY_LENGTH; + + if (len_prefixed) { + output = prefixed_output; + output_len = TEST_PUBLIC_KEY_LENGTH + 2; + } + + EXPECT_BYTEARRAY_EQUAL(io_stuffer.blob.data, output, output_len); + + EXPECT_EQUAL(kem_params.private_key.size, TEST_PRIVATE_KEY_LENGTH); + EXPECT_BYTEARRAY_EQUAL(kem_params.private_key.data, TEST_PRIVATE_KEY, TEST_PRIVATE_KEY_LENGTH); + EXPECT_EQUAL(kem_params.public_key.size, 0); + EXPECT_NULL(kem_params.public_key.data); + EXPECT_EQUAL(kem_params.shared_secret.size, 0); + EXPECT_NULL(kem_params.shared_secret.data); + + /* The private key gets alloc'ed in s2n_kem_generate_keypair(). + * Nothing else should have been alloc'ed. */ + EXPECT_EQUAL(0, kem_params.public_key.allocated); + EXPECT_EQUAL(0, kem_params.shared_secret.allocated); + EXPECT_NOT_EQUAL(0, kem_params.private_key.allocated); + EXPECT_SUCCESS(s2n_kem_free(&kem_params)); + }; + { + /* Failure cases for s2n_kem_send_public_key() */ + EXPECT_FAILURE_WITH_ERRNO(s2n_kem_send_public_key(NULL, NULL), S2N_ERR_NULL); + + DEFER_CLEANUP(struct s2n_blob io_blob = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&io_blob, 1)); + struct s2n_stuffer io_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&io_stuffer, &io_blob)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_kem_send_public_key(&io_stuffer, NULL), S2N_ERR_NULL); + + struct s2n_kem_params kem_params = { 0 }; + kem_params.len_prefixed = len_prefixed; + EXPECT_FAILURE_WITH_ERRNO(s2n_kem_send_public_key(&io_stuffer, &kem_params), S2N_ERR_NULL); + }; + { + /* Happy case for s2n_kem_send_ciphertext() */ + struct s2n_kem_params kem_params = { .kem = &s2n_test_kem, .len_prefixed = len_prefixed }; + + DEFER_CLEANUP(struct s2n_blob io_blob = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&io_blob, TEST_CIPHERTEXT_LENGTH + 2)); + struct s2n_stuffer io_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&io_stuffer, &io_blob)); + + EXPECT_SUCCESS(s2n_alloc(&(kem_params.public_key), TEST_PUBLIC_KEY_LENGTH)); + POSIX_CHECKED_MEMCPY(kem_params.public_key.data, TEST_PUBLIC_KEY, TEST_PUBLIC_KEY_LENGTH); + + EXPECT_SUCCESS(s2n_kem_send_ciphertext(&io_stuffer, &kem_params)); + + /* {0, 5} = length of ciphertext to follow + * {5, 5, 5, 5, 5} = test ciphertext */ + uint8_t prefixed_output[] = { 0, 5, 5, 5, 5, 5, 5 }; + uint8_t unprefixed_output[] = { 5, 5, 5, 5, 5 }; + + uint8_t *output = unprefixed_output; + uint16_t output_len = TEST_CIPHERTEXT_LENGTH; + + if (len_prefixed) { + output = prefixed_output; + output_len = TEST_CIPHERTEXT_LENGTH + 2; + } + + EXPECT_BYTEARRAY_EQUAL(io_stuffer.blob.data, output, output_len); + + EXPECT_EQUAL(kem_params.shared_secret.size, TEST_SHARED_SECRET_LENGTH); + EXPECT_BYTEARRAY_EQUAL(kem_params.shared_secret.data, TEST_SHARED_SECRET, TEST_SHARED_SECRET_LENGTH); + EXPECT_EQUAL(kem_params.public_key.size, TEST_PUBLIC_KEY_LENGTH); + EXPECT_BYTEARRAY_EQUAL(kem_params.public_key.data, TEST_PUBLIC_KEY, TEST_PUBLIC_KEY_LENGTH); + EXPECT_EQUAL(kem_params.private_key.size, 0); + EXPECT_NULL(kem_params.private_key.data); + + /* We alloc'ed the public key previously in the test; the shared secret was + * alloc'ed in Encaps; the private key should not have been alloc'ed */ + EXPECT_EQUAL(0, kem_params.private_key.allocated); + EXPECT_NOT_EQUAL(0, kem_params.public_key.allocated); + EXPECT_NOT_EQUAL(0, kem_params.public_key.allocated); + EXPECT_SUCCESS(s2n_kem_free(&kem_params)); + }; + { + /* Failure cases for s2n_kem_send_ciphertext() */ + EXPECT_FAILURE_WITH_ERRNO(s2n_kem_send_ciphertext(NULL, NULL), S2N_ERR_NULL); + + DEFER_CLEANUP(struct s2n_blob io_blob = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&io_blob, 1)); + struct s2n_stuffer io_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&io_stuffer, &io_blob)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_kem_send_ciphertext(&io_stuffer, NULL), S2N_ERR_NULL); + + struct s2n_kem_params kem_params = { 0 }; + kem_params.len_prefixed = len_prefixed; + EXPECT_FAILURE_WITH_ERRNO(s2n_kem_send_ciphertext(&io_stuffer, &kem_params), S2N_ERR_NULL); + + kem_params.kem = &s2n_test_kem; + EXPECT_FAILURE_WITH_ERRNO(s2n_kem_send_ciphertext(&io_stuffer, &kem_params), S2N_ERR_NULL); + }; + { + /* Happy case for s2n_kem_recv_ciphertext() */ + struct s2n_kem_params kem_params = { .kem = &s2n_test_kem, .len_prefixed = len_prefixed }; + + DEFER_CLEANUP(struct s2n_blob io_blob = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&io_blob, TEST_CIPHERTEXT_LENGTH + 2)); + struct s2n_stuffer io_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&io_stuffer, &io_blob)); + + s2n_alloc(&(kem_params.private_key), TEST_PRIVATE_KEY_LENGTH); + POSIX_CHECKED_MEMCPY(kem_params.private_key.data, TEST_PRIVATE_KEY, TEST_PRIVATE_KEY_LENGTH); + + /* {0, 5} = length of ciphertext to follow + * {5, 5, 5, 5, 5} = test ciphertext */ + uint8_t prefixed_input[] = { 0, 5, 5, 5, 5, 5, 5 }; + uint8_t unprefixed_input[] = { 5, 5, 5, 5, 5 }; + + uint8_t *input = unprefixed_input; + uint16_t input_len = TEST_CIPHERTEXT_LENGTH; + if (len_prefixed) { + input = prefixed_input; + input_len = TEST_CIPHERTEXT_LENGTH + 2; + } + + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&io_stuffer, input, input_len)); + EXPECT_SUCCESS(s2n_stuffer_reread(&io_stuffer)); + + EXPECT_SUCCESS(s2n_kem_recv_ciphertext(&io_stuffer, &kem_params)); + + EXPECT_EQUAL(kem_params.shared_secret.size, TEST_SHARED_SECRET_LENGTH); + EXPECT_BYTEARRAY_EQUAL(kem_params.shared_secret.data, TEST_SHARED_SECRET, TEST_SHARED_SECRET_LENGTH); + EXPECT_EQUAL(0, kem_params.public_key.size); + EXPECT_NULL(kem_params.public_key.data); + + /* We alloc'ed the private key previously in the test; the shared secret was + * alloc'ed in Decaps; the public key should not have been alloc'ed */ + EXPECT_EQUAL(0, kem_params.public_key.allocated); + EXPECT_NOT_EQUAL(0, kem_params.private_key.allocated); + EXPECT_NOT_EQUAL(0, kem_params.shared_secret.allocated); + EXPECT_SUCCESS(s2n_kem_free(&kem_params)); + }; + { + /* Failure cases for s2n_kem_recv_ciphertext() */ + EXPECT_FAILURE_WITH_ERRNO(s2n_kem_recv_ciphertext(NULL, NULL), S2N_ERR_NULL); + + DEFER_CLEANUP(struct s2n_blob io_blob = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&io_blob, 1)); + struct s2n_stuffer io_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&io_stuffer, &io_blob)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_kem_recv_ciphertext(&io_stuffer, NULL), S2N_ERR_NULL); + + struct s2n_kem_params kem_params = { 0 }; + kem_params.len_prefixed = len_prefixed; + EXPECT_FAILURE_WITH_ERRNO(s2n_kem_recv_ciphertext(&io_stuffer, &kem_params), S2N_ERR_NULL); + + kem_params.kem = &s2n_test_kem; + EXPECT_FAILURE_WITH_ERRNO(s2n_kem_recv_ciphertext(&io_stuffer, &kem_params), S2N_ERR_NULL); + + /* The given ciphertext length doesn't match the KEM's actual ciphertext length */ + EXPECT_SUCCESS(s2n_alloc(&(kem_params.private_key), TEST_PRIVATE_KEY_LENGTH)); + POSIX_CHECKED_MEMCPY(kem_params.private_key.data, TEST_PRIVATE_KEY, TEST_PRIVATE_KEY_LENGTH); + DEFER_CLEANUP(struct s2n_blob io_blob_3 = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&io_blob_3, TEST_CIPHERTEXT_LENGTH + 2)); + struct s2n_stuffer io_stuffer_3 = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&io_stuffer_3, &io_blob_3)); + uint8_t bad_ct_input_3[] = { 0, 2, 2, 2 }; + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&io_stuffer_3, bad_ct_input_3, 4)); + EXPECT_SUCCESS(s2n_stuffer_reread(&io_stuffer_3)); + + if (len_prefixed) { + EXPECT_FAILURE_WITH_ERRNO(s2n_kem_recv_ciphertext(&io_stuffer_3, &kem_params), S2N_ERR_BAD_MESSAGE); + } + + /* We alloc'ed the private key previously in the test; our failure cases for + * s2n_kem_recv_ciphertext() never reached a point where we alloc'ed anything else */ + EXPECT_NOT_EQUAL(0, kem_params.private_key.allocated); + EXPECT_EQUAL(0, kem_params.public_key.allocated); + EXPECT_EQUAL(0, kem_params.shared_secret.allocated); + EXPECT_SUCCESS(s2n_kem_free(&kem_params)); + }; + { + /* Happy case for s2n_kem_recv_public_key() */ + struct s2n_kem_params kem_params = { .kem = &s2n_test_kem, .len_prefixed = len_prefixed }; + + DEFER_CLEANUP(struct s2n_blob io_blob = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&io_blob, TEST_PUBLIC_KEY_LENGTH + 2)); + struct s2n_stuffer io_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&io_stuffer, &io_blob)); + + /* {0, 2} = length of public key to follow + * {2, 2} = test public key */ + uint8_t prefixed_input[] = { 0, 2, 2, 2 }; + uint8_t unprefixed_input[] = { 2, 2 }; + + uint8_t *input = unprefixed_input; + uint16_t input_len = TEST_PUBLIC_KEY_LENGTH; + + if (len_prefixed) { + input = prefixed_input; + input_len = TEST_PUBLIC_KEY_LENGTH + 2; + } + + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&io_stuffer, input, input_len)); + EXPECT_SUCCESS(s2n_stuffer_reread(&io_stuffer)); + + EXPECT_SUCCESS(s2n_kem_recv_public_key(&io_stuffer, &kem_params)); + + /* s2n_kem_recv_public_key() should alloc kem_params->public_key and nothing else */ + EXPECT_EQUAL(kem_params.public_key.size, TEST_PUBLIC_KEY_LENGTH); + EXPECT_NOT_EQUAL(0, kem_params.public_key.allocated); + EXPECT_BYTEARRAY_EQUAL(kem_params.public_key.data, TEST_PUBLIC_KEY, TEST_PUBLIC_KEY_LENGTH); + EXPECT_EQUAL(0, kem_params.shared_secret.allocated); + EXPECT_EQUAL(0, kem_params.private_key.allocated); + EXPECT_SUCCESS(s2n_kem_free(&kem_params)); + }; + { + /* Failure cases for s2n_kem_recv_public_key() */ + EXPECT_FAILURE_WITH_ERRNO(s2n_kem_recv_public_key(NULL, NULL), S2N_ERR_NULL); + + DEFER_CLEANUP(struct s2n_blob io_blob = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&io_blob, 1)); + struct s2n_stuffer io_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&io_stuffer, &io_blob)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_kem_recv_public_key(&io_stuffer, NULL), S2N_ERR_NULL); + + struct s2n_kem_params kem_params = { 0 }; + kem_params.len_prefixed = len_prefixed; + EXPECT_FAILURE_WITH_ERRNO(s2n_kem_recv_public_key(&io_stuffer, &kem_params), S2N_ERR_NULL); + + kem_params.kem = &s2n_test_kem; + + /* The given public key length doesn't match the KEM's actual public key length */ + DEFER_CLEANUP(struct s2n_blob io_blob_3 = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&io_blob_3, 5)); + struct s2n_stuffer io_stuffer_3 = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&io_stuffer_3, &io_blob_3)); + uint8_t bad_pk_input_3[] = { 0, 3, 3, 3, 3 }; + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&io_stuffer_3, bad_pk_input_3, 5)); + EXPECT_SUCCESS(s2n_stuffer_reread(&io_stuffer_3)); + if (len_prefixed) { + EXPECT_FAILURE_WITH_ERRNO(s2n_kem_recv_public_key(&io_stuffer_3, &kem_params), S2N_ERR_BAD_MESSAGE); + } + }; + } + + END_TEST(); +} diff --git a/tests/unit/s2n_kex_test.c b/tests/unit/s2n_kex_test.c new file mode 100644 index 00000000000..9013f0323a0 --- /dev/null +++ b/tests/unit/s2n_kex_test.c @@ -0,0 +1,79 @@ +/* + * 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 "tls/s2n_kex.h" + +#include "tests/s2n_test.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Test safety checks */ + { + struct s2n_connection conn = { 0 }; + struct s2n_blob blob = { 0 }; + struct s2n_kex_raw_server_data test_raw_server_data = { 0 }; + struct s2n_cipher_suite test_cipher = s2n_ecdhe_rsa_with_aes_256_cbc_sha; + struct s2n_cipher_suite test_cipher_with_null_kex = test_cipher; + test_cipher_with_null_kex.key_exchange_alg = NULL; + + /* Null cipher suite kex - possible with tls1.3 cipher suites */ + EXPECT_ERROR(s2n_configure_kex(NULL, &conn)); + EXPECT_ERROR(s2n_configure_kex(&test_cipher_with_null_kex, NULL)); + + /* Null kex -- possible with tls1.3 cipher suites */ + bool is_ephemeral = false; + EXPECT_ERROR(s2n_kex_is_ephemeral(NULL, &is_ephemeral)); + EXPECT_ERROR(s2n_kex_is_ephemeral(&s2n_rsa, NULL)); + EXPECT_ERROR(s2n_kex_server_key_recv_parse_data(NULL, &conn, &test_raw_server_data)); + EXPECT_ERROR(s2n_kex_server_key_recv_read_data(NULL, &conn, &blob, &test_raw_server_data)); + EXPECT_ERROR(s2n_kex_server_key_send(NULL, &conn, &blob)); + EXPECT_ERROR(s2n_kex_client_key_recv(NULL, &conn, &blob)); + EXPECT_ERROR(s2n_kex_client_key_send(NULL, &conn, &blob)); + EXPECT_ERROR(s2n_kex_tls_prf(NULL, &conn, &blob)); + }; + + /* Test s2n_kex_includes */ + { + /* True if same kex */ + EXPECT_TRUE(s2n_kex_includes(NULL, NULL)); + EXPECT_TRUE(s2n_kex_includes(&s2n_rsa, &s2n_rsa)); + EXPECT_TRUE(s2n_kex_includes(&s2n_hybrid_ecdhe_kem, &s2n_hybrid_ecdhe_kem)); + + /* False if different kex */ + EXPECT_FALSE(s2n_kex_includes(&s2n_rsa, &s2n_dhe)); + EXPECT_FALSE(s2n_kex_includes(&s2n_kem, &s2n_ecdhe)); + + /* True if hybrid that contains */ + EXPECT_TRUE(s2n_kex_includes(&s2n_hybrid_ecdhe_kem, &s2n_ecdhe)); + EXPECT_TRUE(s2n_kex_includes(&s2n_hybrid_ecdhe_kem, &s2n_kem)); + + /* False if hybrid "contains" relationship reversed */ + EXPECT_FALSE(s2n_kex_includes(&s2n_ecdhe, &s2n_hybrid_ecdhe_kem)); + EXPECT_FALSE(s2n_kex_includes(&s2n_kem, &s2n_hybrid_ecdhe_kem)); + + /* False if hybrid that does not contain */ + EXPECT_FALSE(s2n_kex_includes(&s2n_hybrid_ecdhe_kem, &s2n_rsa)); + EXPECT_FALSE(s2n_kex_includes(&s2n_hybrid_ecdhe_kem, &s2n_dhe)); + + /* False if one kex null */ + EXPECT_FALSE(s2n_kex_includes(&s2n_rsa, NULL)); + EXPECT_FALSE(s2n_kex_includes(NULL, &s2n_rsa)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_kex_with_kem_test.c b/tests/unit/s2n_kex_with_kem_test.c new file mode 100644 index 00000000000..504135379bd --- /dev/null +++ b/tests/unit/s2n_kex_with_kem_test.c @@ -0,0 +1,183 @@ +/* + * 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 "pq-crypto/s2n_pq.h" +#include "tests/s2n_test.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_client_key_exchange.h" +#include "tls/s2n_kem.h" +#include "tls/s2n_kex.h" +#include "tls/s2n_kex_data.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_server_key_exchange.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_safety.h" + +static struct s2n_kex s2n_test_kem_kex = { + .server_key_recv_read_data = &s2n_kem_server_key_recv_read_data, + .server_key_recv_parse_data = &s2n_kem_server_key_recv_parse_data, + .server_key_send = &s2n_kem_server_key_send, + .client_key_recv = &s2n_kem_client_key_recv, + .client_key_send = &s2n_kem_client_key_send, +}; + +static struct s2n_cipher_suite kyber_test_suite = { + .iana_value = { TLS_ECDHE_KYBER_RSA_WITH_AES_256_GCM_SHA384 }, + .key_exchange_alg = &s2n_test_kem_kex, +}; + +static int do_kex_with_kem(struct s2n_cipher_suite *cipher_suite, const char *security_policy_version, const struct s2n_kem *negotiated_kem) +{ + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + + POSIX_GUARD_PTR(client_conn = s2n_connection_new(S2N_CLIENT)); + POSIX_GUARD_PTR(server_conn = s2n_connection_new(S2N_SERVER)); + + const struct s2n_security_policy *security_policy = NULL; + POSIX_GUARD(s2n_find_security_policy_from_version(security_policy_version, &security_policy)); + POSIX_GUARD_PTR(security_policy); + + client_conn->kex_params.kem_params.kem = negotiated_kem; + client_conn->secure->cipher_suite = cipher_suite; + client_conn->security_policy_override = security_policy; + + server_conn->kex_params.kem_params.kem = negotiated_kem; + server_conn->secure->cipher_suite = cipher_suite; + server_conn->security_policy_override = security_policy; + + /* Part 1: Server calls send_key */ + struct s2n_blob data_to_sign = { 0 }; + POSIX_GUARD(s2n_kem_server_key_send(server_conn, &data_to_sign)); + /* 2 extra bytes for the kem extension id and 2 additional bytes for the length of the public key sent over the wire. */ + const uint32_t KEM_PUBLIC_KEY_MESSAGE_SIZE = (*negotiated_kem).public_key_length + 4; + POSIX_ENSURE_EQ(data_to_sign.size, KEM_PUBLIC_KEY_MESSAGE_SIZE); + + POSIX_ENSURE_EQ((*negotiated_kem).private_key_length, server_conn->kex_params.kem_params.private_key.size); + struct s2n_blob server_key_message = { .size = KEM_PUBLIC_KEY_MESSAGE_SIZE, .data = s2n_stuffer_raw_read(&server_conn->handshake.io, KEM_PUBLIC_KEY_MESSAGE_SIZE) }; + POSIX_GUARD_PTR(server_key_message.data); + + /* The KEM public key should get written directly to the server's handshake IO; kem_params.public_key + * should point to NULL */ + POSIX_ENSURE_EQ(NULL, server_conn->kex_params.kem_params.public_key.data); + POSIX_ENSURE_EQ(0, server_conn->kex_params.kem_params.public_key.size); + + /* Part 1.1: feed that to the client */ + POSIX_GUARD(s2n_stuffer_write(&client_conn->handshake.io, &server_key_message)); + + /* Part 2: Client calls recv_read and recv_parse */ + struct s2n_kex_raw_server_data raw_params = { 0 }; + struct s2n_blob data_to_verify = { 0 }; + POSIX_GUARD(s2n_kem_server_key_recv_read_data(client_conn, &data_to_verify, &raw_params)); + POSIX_ENSURE_EQ(data_to_verify.size, KEM_PUBLIC_KEY_MESSAGE_SIZE); + + if (s2n_kem_server_key_recv_parse_data(client_conn, &raw_params) != 0) { + /* Tests with incompatible parameters are expected to fail here; + * we want to clean up the connections before failing. */ + POSIX_GUARD(s2n_connection_free(client_conn)); + POSIX_GUARD(s2n_connection_free(server_conn)); + S2N_ERROR_PRESERVE_ERRNO(); + } + + POSIX_ENSURE_EQ((*negotiated_kem).public_key_length, client_conn->kex_params.kem_params.public_key.size); + + /* Part 3: Client calls send_key. The additional 2 bytes are for the ciphertext length sent over the wire */ + const uint32_t KEM_CIPHERTEXT_MESSAGE_SIZE = (*negotiated_kem).ciphertext_length + 2; + struct s2n_blob *client_shared_key = &(client_conn->kex_params.kem_params.shared_secret); + POSIX_GUARD(s2n_kem_client_key_send(client_conn, client_shared_key)); + struct s2n_blob client_key_message = { .size = KEM_CIPHERTEXT_MESSAGE_SIZE, .data = s2n_stuffer_raw_read(&client_conn->handshake.io, KEM_CIPHERTEXT_MESSAGE_SIZE) }; + POSIX_GUARD_PTR(client_key_message.data); + + /* Part 3.1: Send that back to the server */ + POSIX_GUARD(s2n_stuffer_write(&server_conn->handshake.io, &client_key_message)); + + /* Part 4: Call client key recv */ + struct s2n_blob *server_shared_key = &(server_conn->kex_params.kem_params.shared_secret); + POSIX_GUARD(s2n_kem_client_key_recv(server_conn, server_shared_key)); + POSIX_ENSURE_EQ(memcmp(client_shared_key->data, server_shared_key->data, (*negotiated_kem).shared_secret_key_length), 0); + + POSIX_GUARD(s2n_connection_free(client_conn)); + POSIX_GUARD(s2n_connection_free(server_conn)); + + return 0; +} + +static int assert_pq_disabled_checks(struct s2n_cipher_suite *cipher_suite, const char *security_policy_version, const struct s2n_kem *negotiated_kem) +{ + struct s2n_connection *server_conn; + POSIX_GUARD_PTR(server_conn = s2n_connection_new(S2N_SERVER)); + const struct s2n_security_policy *security_policy = NULL; + POSIX_GUARD(s2n_find_security_policy_from_version(security_policy_version, &security_policy)); + POSIX_GUARD_PTR(security_policy); + server_conn->kex_params.kem_params.kem = negotiated_kem; + server_conn->secure->cipher_suite = cipher_suite; + server_conn->security_policy_override = security_policy; + + /* If PQ is disabled: + * s2n_check_kem() (s2n_hybrid_ecdhe_kem.connection_supported) should indicate that the connection is not supported + * s2n_configure_kem() (s2n_hybrid_ecdhe_kem.configure_connection) should return S2N_RESULT_ERROR + * set s2n_errno to S2N_ERR_PQ_DISABLED */ + bool connection_supported = true; + POSIX_GUARD_RESULT(s2n_hybrid_ecdhe_kem.connection_supported(cipher_suite, server_conn, &connection_supported)); + POSIX_ENSURE_EQ(connection_supported, false); + + POSIX_ENSURE_EQ(s2n_result_is_error(s2n_hybrid_ecdhe_kem.configure_connection(cipher_suite, server_conn)), true); + + POSIX_ENSURE_EQ(s2n_errno, S2N_ERR_PQ_DISABLED); + + POSIX_GUARD(s2n_connection_free(server_conn)); + s2n_errno = 0; + s2n_debug_info_reset(); + + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + if (!s2n_pq_is_enabled()) { + /* Verify s2n_check_kem() and s2n_configure_kem() are performing their pq-enabled checks appropriately. */ + EXPECT_SUCCESS(assert_pq_disabled_checks(&kyber_test_suite, "KMS-PQ-TLS-1-0-2019-06", &s2n_kyber_512_r3)); + EXPECT_SUCCESS(assert_pq_disabled_checks(&kyber_test_suite, "KMS-PQ-TLS-1-0-2020-02", &s2n_kyber_512_r3)); + EXPECT_SUCCESS(assert_pq_disabled_checks(&kyber_test_suite, "KMS-PQ-TLS-1-0-2020-07", &s2n_kyber_512_r3)); + + EXPECT_SUCCESS(assert_pq_disabled_checks(&kyber_test_suite, "PQ-TLS-1-1-2021-05-17", &s2n_kyber_512_r3)); + EXPECT_SUCCESS(assert_pq_disabled_checks(&kyber_test_suite, "PQ-TLS-1-0-2021-05-18", &s2n_kyber_512_r3)); + EXPECT_SUCCESS(assert_pq_disabled_checks(&kyber_test_suite, "PQ-TLS-1-0-2021-05-19", &s2n_kyber_512_r3)); + EXPECT_SUCCESS(assert_pq_disabled_checks(&kyber_test_suite, "PQ-TLS-1-0-2021-05-20", &s2n_kyber_512_r3)); + EXPECT_SUCCESS(assert_pq_disabled_checks(&kyber_test_suite, "PQ-TLS-1-1-2021-05-21", &s2n_kyber_512_r3)); + EXPECT_SUCCESS(assert_pq_disabled_checks(&kyber_test_suite, "PQ-TLS-1-0-2021-05-22", &s2n_kyber_512_r3)); + EXPECT_SUCCESS(assert_pq_disabled_checks(&kyber_test_suite, "PQ-TLS-1-0-2021-05-23", &s2n_kyber_512_r3)); + EXPECT_SUCCESS(assert_pq_disabled_checks(&kyber_test_suite, "PQ-TLS-1-0-2021-05-24", &s2n_kyber_512_r3)); + EXPECT_SUCCESS(assert_pq_disabled_checks(&kyber_test_suite, "PQ-TLS-1-0-2021-05-25", &s2n_kyber_512_r3)); + EXPECT_SUCCESS(assert_pq_disabled_checks(&kyber_test_suite, "PQ-TLS-1-0-2021-05-26", &s2n_kyber_512_r3)); + + } else { + EXPECT_SUCCESS(do_kex_with_kem(&kyber_test_suite, "PQ-TLS-1-1-2021-05-17", &s2n_kyber_512_r3)); + EXPECT_SUCCESS(do_kex_with_kem(&kyber_test_suite, "PQ-TLS-1-0-2021-05-18", &s2n_kyber_512_r3)); + EXPECT_SUCCESS(do_kex_with_kem(&kyber_test_suite, "PQ-TLS-1-0-2021-05-19", &s2n_kyber_512_r3)); + EXPECT_SUCCESS(do_kex_with_kem(&kyber_test_suite, "PQ-TLS-1-0-2021-05-20", &s2n_kyber_512_r3)); + EXPECT_SUCCESS(do_kex_with_kem(&kyber_test_suite, "PQ-TLS-1-1-2021-05-21", &s2n_kyber_512_r3)); + EXPECT_SUCCESS(do_kex_with_kem(&kyber_test_suite, "PQ-TLS-1-0-2021-05-22", &s2n_kyber_512_r3)); + EXPECT_SUCCESS(do_kex_with_kem(&kyber_test_suite, "PQ-TLS-1-0-2021-05-23", &s2n_kyber_512_r3)); + EXPECT_SUCCESS(do_kex_with_kem(&kyber_test_suite, "PQ-TLS-1-0-2021-05-24", &s2n_kyber_512_r3)); + EXPECT_SUCCESS(do_kex_with_kem(&kyber_test_suite, "PQ-TLS-1-0-2021-05-25", &s2n_kyber_512_r3)); + EXPECT_SUCCESS(do_kex_with_kem(&kyber_test_suite, "PQ-TLS-1-0-2021-05-26", &s2n_kyber_512_r3)); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_key_share_extension_test.c b/tests/unit/s2n_key_share_extension_test.c new file mode 100644 index 00000000000..9ae5eb4fe04 --- /dev/null +++ b/tests/unit/s2n_key_share_extension_test.c @@ -0,0 +1,74 @@ +/* + * 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 "crypto/s2n_ecc_evp.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_key_share.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_safety.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + const struct s2n_ecc_named_curve *test_curve = s2n_all_supported_curves_list[0]; + + /* Test s2n_ecdhe_parameters_send write with valid ecc params */ + { + struct s2n_stuffer out = { 0 }; + + struct s2n_ecc_evp_params ecc_evp_params; + ecc_evp_params.negotiated_curve = test_curve; + ecc_evp_params.evp_pkey = NULL; + + EXPECT_SUCCESS(s2n_stuffer_alloc(&out, test_curve->share_size + 4)); + EXPECT_SUCCESS(s2n_ecdhe_parameters_send(&ecc_evp_params, &out)); + S2N_STUFFER_READ_EXPECT_EQUAL(&out, test_curve->iana_id, uint16); + S2N_STUFFER_READ_EXPECT_EQUAL(&out, test_curve->share_size, uint16); + EXPECT_EQUAL(s2n_stuffer_data_available(&out), test_curve->share_size); + + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&ecc_evp_params)); + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + }; + + /* Test s2n_ecdhe_parameters_send failure with bad ecc params */ + { + struct s2n_stuffer out = { 0 }; + + struct s2n_ecc_evp_params ecc_evp_params; + const struct s2n_ecc_named_curve bad_curve = { + .iana_id = 12345, + .libcrypto_nid = 0, + .name = test_curve->name, + .share_size = test_curve->share_size + }; + + ecc_evp_params.negotiated_curve = &bad_curve; + ecc_evp_params.evp_pkey = NULL; + + EXPECT_SUCCESS(s2n_stuffer_alloc(&out, bad_curve.share_size + 4)); + /* generating an ECDHE key should fail */ + EXPECT_FAILURE(s2n_ecdhe_parameters_send(&ecc_evp_params, &out)); + + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&ecc_evp_params)); + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_key_update_test.c b/tests/unit/s2n_key_update_test.c new file mode 100644 index 00000000000..0cacfc9e50d --- /dev/null +++ b/tests/unit/s2n_key_update_test.c @@ -0,0 +1,593 @@ +/* + * 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 "tls/s2n_key_update.h" + +#include "crypto/s2n_sequence.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_post_handshake.h" +#include "tls/s2n_quic_support.h" +#include "tls/s2n_tls13_handshake.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_safety.h" + +#define LOWEST_BYTE (S2N_TLS_SEQUENCE_NUM_LEN - 1) + +int s2n_key_update_write(struct s2n_blob *out); +int s2n_check_record_limit(struct s2n_connection *conn, struct s2n_blob *sequence_number); + +static S2N_RESULT s2n_write_uint64(uint64_t input, uint8_t *output) +{ + struct s2n_blob blob = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&blob, output, S2N_TLS_SEQUENCE_NUM_LEN)); + EXPECT_SUCCESS(s2n_stuffer_init(&stuffer, &blob)); + EXPECT_SUCCESS(s2n_stuffer_write_uint64(&stuffer, input)); + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + S2N_BLOB_FROM_HEX(application_secret, + "4bc28934ddd802b00f479e14a72d7725dab45d32b3b145f29" + "e4c5b56677560eb5236b168c71c5c75aa52f3e20ee89bfb"); + + struct s2n_cipher_suite *cipher_suite_with_limit = &s2n_tls13_aes_256_gcm_sha384; + const uint64_t record_limit = cipher_suite_with_limit->record_alg->encryption_limit; + struct s2n_cipher_suite *cipher_suite_without_limit = &s2n_tls13_chacha20_poly1305_sha256; + + /* We can use a TLS1.2 cipher suite if chacha20 isn't available, + * since no TLS1.2 cipher suites have record limits. + */ + if (!cipher_suite_without_limit->available) { + cipher_suite_without_limit = &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384; + } + + EXPECT_TRUE(cipher_suite_with_limit->available); + EXPECT_TRUE(cipher_suite_without_limit->available); + + uint8_t zeroed_sequence_number[S2N_TLS_SEQUENCE_NUM_LEN] = { 0 }; + + /* s2n_key_update_write */ + { + /* Tests s2n_key_update_write writes as expected */ + { + uint8_t key_update_data[S2N_KEY_UPDATE_MESSAGE_SIZE]; + struct s2n_blob key_update_blob = { 0 }; + struct s2n_stuffer key_update_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&key_update_blob, key_update_data, sizeof(key_update_data))); + EXPECT_SUCCESS(s2n_stuffer_init(&key_update_stuffer, &key_update_blob)); + + /* Write key update message */ + EXPECT_SUCCESS(s2n_key_update_write(&key_update_blob)); + + /* Move stuffer write cursor to correct position */ + EXPECT_SUCCESS(s2n_stuffer_skip_write(&key_update_stuffer, S2N_KEY_UPDATE_MESSAGE_SIZE)); + + uint8_t post_handshake_id; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&key_update_stuffer, &post_handshake_id)); + EXPECT_EQUAL(post_handshake_id, TLS_KEY_UPDATE); + + uint32_t request_length; + EXPECT_SUCCESS(s2n_stuffer_read_uint24(&key_update_stuffer, &request_length)); + EXPECT_EQUAL(request_length, S2N_KEY_UPDATE_LENGTH); + + uint8_t key_update_request; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&key_update_stuffer, &key_update_request)); + EXPECT_EQUAL(key_update_request, S2N_KEY_UPDATE_NOT_REQUESTED); + }; + }; + + /* s2n_key_update_recv */ + { + /* Key update message not allowed when running with QUIC + * + *= https://tools.ietf.org/rfc/rfc9001.txt#6 + *= type=test + *# Endpoints MUST treat the receipt of a TLS KeyUpdate message as a connection error + *# of type 0x010a, equivalent to a fatal TLS alert of unexpected_message + **/ + { + const size_t test_data_len = 10; + DEFER_CLEANUP(struct s2n_stuffer input, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_alloc(&input, test_data_len)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&input, test_data_len)); + + struct s2n_config *quic_config; + EXPECT_NOT_NULL(quic_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_enable_quic(quic_config)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, quic_config)); + + EXPECT_FAILURE_WITH_ALERT(s2n_key_update_recv(conn, &input), + S2N_ERR_BAD_MESSAGE, S2N_TLS_ALERT_UNEXPECTED_MESSAGE); + + /* Verify method was a no-op and the message was not read */ + EXPECT_EQUAL(s2n_stuffer_data_available(&input), test_data_len); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(quic_config)); + }; + + /* Key update message not allowed in TLS1.2 */ + { + const size_t test_data_len = 10; + DEFER_CLEANUP(struct s2n_stuffer input, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_alloc(&input, test_data_len)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&input, test_data_len)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS12; + + EXPECT_FAILURE_WITH_ERRNO(s2n_key_update_recv(conn, &input), S2N_ERR_BAD_MESSAGE); + + /* Verify method was a no-op and the message was not read */ + EXPECT_EQUAL(s2n_stuffer_data_available(&input), test_data_len); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Key update message received contains invalid key update request */ + { + DEFER_CLEANUP(struct s2n_stuffer input, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + /* Write invalid value for key update request type */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&input, -1)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_key_update_recv(conn, &input), S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Server receives valid key update request */ + { + DEFER_CLEANUP(struct s2n_stuffer input, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->secure->cipher_suite = cipher_suite_with_limit; + POSIX_CHECKED_MEMCPY(server_conn->secrets.version.tls13.client_app_secret, application_secret.data, application_secret.size); + + server_conn->secure->client_sequence_number[0] = 1; + /* Write the key update request to the correct stuffer */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&input, S2N_KEY_UPDATE_NOT_REQUESTED)); + + EXPECT_SUCCESS(s2n_key_update_recv(server_conn, &input)); + EXPECT_EQUAL(server_conn->secure->client_sequence_number[0], 0); + EXPECT_FALSE(s2n_atomic_flag_test(&server_conn->key_update_pending)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Client receives valid key update request */ + { + DEFER_CLEANUP(struct s2n_stuffer input, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->actual_protocol_version = S2N_TLS13; + client_conn->secure->cipher_suite = cipher_suite_with_limit; + POSIX_CHECKED_MEMCPY(client_conn->secrets.version.tls13.server_app_secret, application_secret.data, application_secret.size); + + client_conn->secure->server_sequence_number[0] = 1; + /* Write the key update request to the correct stuffer */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&input, S2N_KEY_UPDATE_NOT_REQUESTED)); + + EXPECT_SUCCESS(s2n_key_update_recv(client_conn, &input)); + EXPECT_EQUAL(client_conn->secure->server_sequence_number[0], 0); + EXPECT_FALSE(s2n_atomic_flag_test(&client_conn->key_update_pending)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* Receiving a KeyUpdate request sets key_update_pending */ + { + DEFER_CLEANUP(struct s2n_stuffer input, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = cipher_suite_with_limit; + + /* KeyUpdate not pending */ + s2n_atomic_flag_clear(&conn->key_update_pending); + + /* KeyUpdate received */ + conn->secure->client_sequence_number[0] = 1; + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&input, S2N_KEY_UPDATE_REQUESTED)); + EXPECT_SUCCESS(s2n_key_update_recv(conn, &input)); + EXPECT_EQUAL(conn->secure->client_sequence_number[0], 0); + + /* KeyUpdate pending */ + EXPECT_TRUE(s2n_atomic_flag_test(&conn->key_update_pending)); + }; + + /* Receiving a KeyUpdate cannot reset key_update_pending */ + { + DEFER_CLEANUP(struct s2n_stuffer input, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = cipher_suite_with_limit; + + /* KeyUpdate already pending */ + s2n_atomic_flag_set(&conn->key_update_pending); + + /* KeyUpdate received */ + conn->secure->client_sequence_number[0] = 1; + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&input, S2N_KEY_UPDATE_NOT_REQUESTED)); + EXPECT_SUCCESS(s2n_key_update_recv(conn, &input)); + EXPECT_EQUAL(conn->secure->client_sequence_number[0], 0); + + /* KeyUpdate still pending */ + EXPECT_TRUE(s2n_atomic_flag_test(&conn->key_update_pending)); + }; + }; + + /* s2n_key_update_send */ + { + /* Key update has been requested */ + { + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->actual_protocol_version = S2N_TLS13; + client_conn->secure->cipher_suite = cipher_suite_with_limit; + POSIX_CHECKED_MEMCPY(client_conn->secrets.version.tls13.client_app_secret, application_secret.data, application_secret.size); + + /* Setup io */ + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, client_conn)); + + s2n_atomic_flag_set(&client_conn->key_update_pending); + + s2n_blocked_status blocked = 0; + EXPECT_SUCCESS(s2n_key_update_send(client_conn, &blocked)); + + EXPECT_EQUAL(s2n_atomic_flag_test(&client_conn->key_update_pending), false); + EXPECT_BYTEARRAY_EQUAL(client_conn->secure->client_sequence_number, zeroed_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN); + EXPECT_TRUE(s2n_stuffer_data_available(&stuffer) > 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* Key update is triggered by encryption limits */ + { + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->actual_protocol_version = S2N_TLS13; + client_conn->secure->cipher_suite = cipher_suite_with_limit; + POSIX_CHECKED_MEMCPY(client_conn->secrets.version.tls13.client_app_secret, application_secret.data, application_secret.size); + + /* Setup io */ + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, client_conn)); + + s2n_atomic_flag_clear(&client_conn->key_update_pending); + EXPECT_OK(s2n_write_uint64(record_limit, client_conn->secure->client_sequence_number)); + + s2n_blocked_status blocked = 0; + EXPECT_SUCCESS(s2n_key_update_send(client_conn, &blocked)); + + EXPECT_EQUAL(s2n_atomic_flag_test(&client_conn->key_update_pending), false); + EXPECT_BYTEARRAY_EQUAL(client_conn->secure->client_sequence_number, zeroed_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN); + EXPECT_TRUE(s2n_stuffer_data_available(&stuffer) > 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* Key update never sent for actual_protocol_version = S2N_TLS12; + conn->secure->cipher_suite = cipher_suite_with_limit; + POSIX_CHECKED_MEMCPY(conn->secrets.version.tls13.client_app_secret, + application_secret.data, application_secret.size); + + /* Setup io */ + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + /* Key update both pending and required by record limit */ + s2n_atomic_flag_set(&conn->key_update_pending); + EXPECT_OK(s2n_write_uint64(record_limit, conn->secure->client_sequence_number)); + + s2n_blocked_status blocked = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_key_update_send(conn, &blocked), S2N_ERR_SAFETY); + + /* Sequence number not reset and no KeyUpdate sent */ + EXPECT_BYTEARRAY_NOT_EQUAL(conn->secure->client_sequence_number, + zeroed_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN); + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + }; + + /* Key update is not triggered */ + { + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->actual_protocol_version = S2N_TLS13; + client_conn->secure->cipher_suite = cipher_suite_with_limit; + POSIX_CHECKED_MEMCPY(client_conn->secrets.version.tls13.client_app_secret, application_secret.data, application_secret.size); + uint8_t expected_sequence_number[S2N_TLS_SEQUENCE_NUM_LEN] = { 0 }; + + /* Setup io */ + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, client_conn)); + + client_conn->secure->client_sequence_number[LOWEST_BYTE] = 1; + expected_sequence_number[LOWEST_BYTE] = 1; + s2n_atomic_flag_clear(&client_conn->key_update_pending); + + s2n_blocked_status blocked = 0; + EXPECT_SUCCESS(s2n_key_update_send(client_conn, &blocked)); + + EXPECT_EQUAL(s2n_atomic_flag_test(&client_conn->key_update_pending), false); + EXPECT_BYTEARRAY_EQUAL(client_conn->secure->client_sequence_number, expected_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN); + EXPECT_TRUE(s2n_stuffer_data_available(&stuffer) == 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* Key update eventually occurs when record limit reached */ + { + const uint64_t expected = record_limit; + const uint64_t start = expected - 100; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + client_conn->actual_protocol_version = S2N_TLS13; + client_conn->secure->cipher_suite = cipher_suite_with_limit; + POSIX_CHECKED_MEMCPY(client_conn->secrets.version.tls13.client_app_secret, application_secret.data, application_secret.size); + + /* Setup io */ + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, client_conn)); + + struct s2n_blob sequence_number = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&sequence_number, + client_conn->secure->client_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + EXPECT_OK(s2n_write_uint64(start, client_conn->secure->client_sequence_number)); + + uint64_t key_update_seq_num = 0; + for (uint64_t i = start; i <= UINT64_MAX; i++) { + s2n_blocked_status blocked = 0; + EXPECT_SUCCESS(s2n_key_update_send(client_conn, &blocked)); + + if (s2n_stuffer_data_available(&stuffer) > 0) { + key_update_seq_num = i; + break; + } + + EXPECT_SUCCESS(s2n_increment_sequence_number(&sequence_number)); + } + + EXPECT_EQUAL(key_update_seq_num, expected); + }; + + /* Key update eventually occurs before we run out of sequence numbers */ + { + const uint64_t expected = UINT64_MAX; + const uint64_t start = expected - 100; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + client_conn->actual_protocol_version = S2N_TLS13; + client_conn->secure->cipher_suite = cipher_suite_without_limit; + POSIX_CHECKED_MEMCPY(client_conn->secrets.version.tls13.client_app_secret, application_secret.data, application_secret.size); + + /* Setup io */ + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, client_conn)); + + struct s2n_blob sequence_number = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&sequence_number, + client_conn->secure->client_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + EXPECT_OK(s2n_write_uint64(start, client_conn->secure->client_sequence_number)); + + uint64_t key_update_seq_num = 0; + for (uint64_t i = start; i <= UINT64_MAX; i++) { + s2n_blocked_status blocked = 0; + EXPECT_SUCCESS(s2n_key_update_send(client_conn, &blocked)); + + if (s2n_stuffer_data_available(&stuffer) > 0) { + key_update_seq_num = i; + break; + } + + EXPECT_SUCCESS(s2n_increment_sequence_number(&sequence_number)); + } + + EXPECT_EQUAL(key_update_seq_num, expected); + }; + }; + + /* s2n_check_record_limit */ + { + /* Record encryption limit exists (AES-GCM) */ + { + struct s2n_blob sequence_number = { 0 }; + uint8_t sequence_number_bytes[S2N_TLS_SEQUENCE_NUM_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&sequence_number, sequence_number_bytes, sizeof(sequence_number_bytes))); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->secure->cipher_suite = cipher_suite_with_limit; + + /* Not at limit yet: no records sent */ + s2n_atomic_flag_clear(&conn->key_update_pending); + EXPECT_OK(s2n_write_uint64(0, sequence_number_bytes)); + EXPECT_SUCCESS(s2n_check_record_limit(conn, &sequence_number)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); + + /* Not at limit yet: 2 records less than limit */ + s2n_atomic_flag_clear(&conn->key_update_pending); + EXPECT_OK(s2n_write_uint64(record_limit - 2, sequence_number_bytes)); + EXPECT_SUCCESS(s2n_check_record_limit(conn, &sequence_number)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); + + /* 1 record less than limit */ + s2n_atomic_flag_clear(&conn->key_update_pending); + EXPECT_OK(s2n_write_uint64(record_limit - 1, sequence_number_bytes)); + EXPECT_SUCCESS(s2n_check_record_limit(conn, &sequence_number)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); + + /* Limit */ + s2n_atomic_flag_clear(&conn->key_update_pending); + EXPECT_OK(s2n_write_uint64(record_limit, sequence_number_bytes)); + EXPECT_SUCCESS(s2n_check_record_limit(conn, &sequence_number)); + EXPECT_TRUE(s2n_atomic_flag_test(&conn->key_update_pending)); + + /* Don't reset the key_update_pending flag if already set */ + EXPECT_OK(s2n_write_uint64(0, sequence_number_bytes)); + EXPECT_SUCCESS(s2n_check_record_limit(conn, &sequence_number)); + EXPECT_TRUE(s2n_atomic_flag_test(&conn->key_update_pending)); + + /* Over limit */ + s2n_atomic_flag_clear(&conn->key_update_pending); + EXPECT_OK(s2n_write_uint64(record_limit + 1, sequence_number_bytes)); + EXPECT_SUCCESS(s2n_check_record_limit(conn, &sequence_number)); + EXPECT_TRUE(s2n_atomic_flag_test(&conn->key_update_pending)); + }; + + /* No record encryption limit (CHACHA20) */ + { + struct s2n_blob sequence_number = { 0 }; + uint8_t sequence_number_bytes[S2N_TLS_SEQUENCE_NUM_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&sequence_number, sequence_number_bytes, sizeof(sequence_number_bytes))); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->secure->cipher_suite = cipher_suite_without_limit; + + /* Not at limit yet: no records sent */ + s2n_atomic_flag_clear(&conn->key_update_pending); + EXPECT_OK(s2n_write_uint64(0, sequence_number_bytes)); + EXPECT_SUCCESS(s2n_check_record_limit(conn, &sequence_number)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); + + /* Not at limit yet: 2 records less than limit */ + s2n_atomic_flag_clear(&conn->key_update_pending); + EXPECT_OK(s2n_write_uint64(UINT64_MAX - 2, sequence_number_bytes)); + EXPECT_SUCCESS(s2n_check_record_limit(conn, &sequence_number)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); + + /* 1 record less than limit */ + s2n_atomic_flag_clear(&conn->key_update_pending); + EXPECT_OK(s2n_write_uint64(UINT64_MAX - 1, sequence_number_bytes)); + EXPECT_SUCCESS(s2n_check_record_limit(conn, &sequence_number)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); + + /* Limit */ + s2n_atomic_flag_clear(&conn->key_update_pending); + EXPECT_OK(s2n_write_uint64(UINT64_MAX, sequence_number_bytes)); + EXPECT_SUCCESS(s2n_check_record_limit(conn, &sequence_number)); + EXPECT_TRUE(s2n_atomic_flag_test(&conn->key_update_pending)); + + /* Don't reset the key_update_pending flag if already set */ + EXPECT_OK(s2n_write_uint64(0, sequence_number_bytes)); + EXPECT_SUCCESS(s2n_check_record_limit(conn, &sequence_number)); + EXPECT_TRUE(s2n_atomic_flag_test(&conn->key_update_pending)); + + /* Over limit not possible: limit is maximum value */ + }; + }; + + /* Test: KeyUpdate fails if fragmentation required */ + { + const size_t key_update_record_size = S2N_TLS_MAX_RECORD_LEN_FOR(S2N_KEY_UPDATE_MESSAGE_SIZE); + + /* Test: send buffer cannot be set smaller than a KeyUpdate record */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_send_buffer_size(config, key_update_record_size - 1), + S2N_ERR_INVALID_ARGUMENT); + }; + + /* Test: send fails if send buffer is too small for a KeyUpdate record */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_OK(s2n_connection_set_secrets(conn)); + conn->actual_protocol_version = S2N_TLS13; + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + /* Sanity check: send buffer just large enough for KeyUpdate record */ + config->send_buffer_size_override = key_update_record_size; + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + s2n_atomic_flag_set(&conn->key_update_pending); + EXPECT_SUCCESS(s2n_key_update_send(conn, &blocked)); + + EXPECT_SUCCESS(s2n_connection_release_buffers(conn)); + + /* Test: send buffer too small for KeyUpdate record */ + config->send_buffer_size_override = key_update_record_size - 1; + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + s2n_atomic_flag_set(&conn->key_update_pending); + EXPECT_FAILURE_WITH_ERRNO(s2n_key_update_send(conn, &blocked), S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); + }; + }; + + /* Test: all cipher suites must have record limits set. + * + * If this ever changes, then s2n_check_record_limit needs to consider + * the case where there is no record limit. + */ + for (size_t i = 0; i < cipher_preferences_test_all.count; i++) { + struct s2n_cipher_suite *cipher_suite = cipher_preferences_test_all.suites[i]; + EXPECT_NOT_NULL(cipher_suite); + if (cipher_suite->available) { + EXPECT_NOT_NULL(cipher_suite->record_alg); + EXPECT_TRUE(cipher_suite->record_alg->encryption_limit > 0); + } + } + + END_TEST(); +} diff --git a/tests/unit/s2n_key_update_threads_test.c b/tests/unit/s2n_key_update_threads_test.c new file mode 100644 index 00000000000..33a98d27497 --- /dev/null +++ b/tests/unit/s2n_key_update_threads_test.c @@ -0,0 +1,264 @@ +/* + * 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 + +#include "crypto/s2n_sequence.h" +#include "s2n_test.h" +#include "testlib/s2n_examples.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_random.h" + +#define S2N_TEST_BUFFER_SIZE 1000 +#define S2N_TEST_ENCRYPTION_LIMIT 3 +#define S2N_TEST_KEY_UPDATE_COUNT 25 +#define S2N_TEST_RECORD_COUNT (S2N_TEST_ENCRYPTION_LIMIT * S2N_TEST_KEY_UPDATE_COUNT) +#define S2N_TEST_BYTES_TO_SEND (S2N_DEFAULT_FRAGMENT_LENGTH * S2N_TEST_RECORD_COUNT) + +#define S2N_CIPHER_SUITE_WITH_LIMIT(name, source, limit) \ + struct s2n_cipher_suite name = *(source); \ + struct s2n_record_algorithm _##name##_record_alg = *name.record_alg; \ + _##name##_record_alg.encryption_limit = limit; \ + name.record_alg = &_##name##_record_alg; + +S2N_RESULT s2n_set_key_update_request_for_testing(keyupdate_request request); + +static void *s2n_send_random_data(void *arg) +{ + struct s2n_connection *conn = (struct s2n_connection *) arg; + + uint8_t buffer[S2N_TEST_BUFFER_SIZE] = "hello world"; + + size_t bytes_to_send = S2N_TEST_BYTES_TO_SEND; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + while (bytes_to_send) { + int r = s2n_send(conn, buffer, MIN(sizeof(buffer), bytes_to_send), &blocked); + if (r >= 0) { + bytes_to_send -= r; + } else if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { + fprintf(stderr, "Send error: %s. %s\n", s2n_strerror(s2n_errno, NULL), s2n_strerror_debug(s2n_errno, NULL)); + return NULL; + } + } + return conn; +} + +static void *s2n_recv_random_data(void *arg) +{ + struct s2n_connection *conn = (struct s2n_connection *) arg; + + uint8_t buffer[S2N_TEST_BUFFER_SIZE] = { 0 }; + + size_t bytes_to_read = S2N_TEST_BYTES_TO_SEND; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + while (bytes_to_read) { + int r = s2n_recv(conn, buffer, MIN(sizeof(buffer), bytes_to_read), &blocked); + if (r >= 0) { + bytes_to_read -= r; + } else if (s2n_error_get_type(s2n_errno) != S2N_ERR_T_BLOCKED) { + fprintf(stderr, "Recv error: %s. %s\n", s2n_strerror(s2n_errno, NULL), s2n_strerror_debug(s2n_errno, NULL)); + return NULL; + } + } + return conn; +} + +static S2N_RESULT s2n_send_and_recv_random_data(struct s2n_connection *conn) +{ + /* + * This test is intended to find concurrency issues when sending and receiving + * KeyUpdates, so we need to run the reader and writer in separate threads. + */ + + pthread_t reader = 0; + RESULT_ENSURE_EQ(pthread_create(&reader, NULL, s2n_recv_random_data, (void *) conn), 0); + + pthread_t writer = 0; + RESULT_ENSURE_EQ(pthread_create(&writer, NULL, s2n_send_random_data, (void *) conn), 0); + + void *reader_return = NULL; + RESULT_ENSURE_EQ(pthread_join(reader, &reader_return), 0); + RESULT_ENSURE_REF(reader_return); + + void *writer_return = NULL; + RESULT_ENSURE_EQ(pthread_join(writer, &writer_return), 0); + RESULT_ENSURE_REF(writer_return); + + RESULT_ENSURE_GT(conn->wire_bytes_out, S2N_TEST_BYTES_TO_SEND); + RESULT_ENSURE_GT(conn->wire_bytes_in, S2N_TEST_BYTES_TO_SEND); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_sanity_check_key_updates_sent(struct s2n_connection *conn) +{ + struct s2n_blob seq_num_blob = { 0 }; + if (conn->mode == S2N_CLIENT) { + RESULT_GUARD_POSIX(s2n_blob_init(&seq_num_blob, conn->secure->client_sequence_number, + sizeof(conn->secure->client_sequence_number))); + } else { + RESULT_GUARD_POSIX(s2n_blob_init(&seq_num_blob, conn->secure->server_sequence_number, + sizeof(conn->secure->server_sequence_number))); + } + + uint64_t seq_num = 0; + RESULT_GUARD_POSIX(s2n_sequence_number_to_uint64(&seq_num_blob, &seq_num)); + RESULT_ENSURE_LTE(seq_num, conn->secure->cipher_suite->record_alg->encryption_limit); + + /* s2n-tls doesn't keep a running count of KeyUpdates, so to sanity check that + * at least one KeyUpdate occurred we have to rely on some math. + * + * wire_bytes_out represents the total bytes sent, and should therefore be + * less than or equal to (number of records sent) * (maximum size of a record). + * + * (maximum size of a record) can be calculated based on max_outgoing_fragment_length. + * We will call it max_record_size. + * + * (number of records sent) is seq_num, if no KeyUpdates were sent. seq_num + * starts at 0, is incremented by one for every record, and is reset to 0 by + * a KeyUpdate. So if no KeyUpdate occurs, seq_num represents the total number + * of records sent. + * + * If seq_num represents the total number of records sent, then wire_bytes_out + * must be less than or equal to (seq_num) * (max_record_size). + * If wire_bytes_out is instead greater than (seq_num) * (max_record_size), + * then more records were sent than seq_num accounts for. That means that seq_num + * must have been reset, which means that at least one KeyUpdate was sent. + */ + size_t max_record_size = S2N_TLS13_MAX_RECORD_LEN_FOR(conn->max_outgoing_fragment_length); + RESULT_ENSURE_GT(conn->wire_bytes_out, max_record_size * seq_num); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_test_encryption_limits(struct s2n_connection *conn) +{ + RESULT_GUARD_POSIX(s2n_example_negotiate(conn)); + + struct s2n_cipher_suite *original_suite = conn->secure->cipher_suite; + S2N_CIPHER_SUITE_WITH_LIMIT(key_limit_suite, original_suite, S2N_TEST_ENCRYPTION_LIMIT); + + conn->secure->cipher_suite = &key_limit_suite; + + RESULT_GUARD(s2n_send_and_recv_random_data(conn)); + RESULT_GUARD(s2n_sanity_check_key_updates_sent(conn)); + + conn->secure->cipher_suite = original_suite; + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_test_peer_requests(struct s2n_connection *conn) +{ + RESULT_GUARD_POSIX(s2n_example_negotiate(conn)); + + struct s2n_cipher_suite *original_suite = conn->secure->cipher_suite; + S2N_CIPHER_SUITE_WITH_LIMIT(key_limit_suite, original_suite, S2N_TEST_ENCRYPTION_LIMIT); + + conn->secure->cipher_suite = &key_limit_suite; + if (conn->mode == S2N_CLIENT) { + RESULT_GUARD(s2n_set_key_update_request_for_testing(S2N_KEY_UPDATE_REQUESTED)); + } + + RESULT_GUARD(s2n_send_and_recv_random_data(conn)); + RESULT_GUARD(s2n_sanity_check_key_updates_sent(conn)); + + conn->secure->cipher_suite = original_suite; + return S2N_RESULT_OK; +} + +typedef S2N_RESULT (*s2n_test_scenario)(struct s2n_connection *conn); +static S2N_RESULT s2n_run_self_talk_test(s2n_test_scenario scenario_fn) +{ + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + RESULT_GUARD_POSIX(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + RESULT_GUARD_POSIX(s2n_config_set_unsafe_for_testing(config)); + RESULT_GUARD_POSIX(s2n_config_set_cipher_preferences(config, "default_tls13")); + RESULT_GUARD_POSIX(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + RESULT_GUARD_POSIX(s2n_io_pair_init_non_blocking(&io_pair)); + + pid_t client_pid = fork(); + if (client_pid == 0) { + /* Suppress stdout. + * This only affects the new client process. + */ + fclose(stdout); + + struct s2n_connection *client = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client, &io_pair)); + + EXPECT_OK(scenario_fn(client)); + + EXPECT_SUCCESS(s2n_connection_free(client)); + exit(EXIT_SUCCESS); + } + + pid_t server_pid = fork(); + if (server_pid == 0) { + /* Suppress stdouts. + * This only affects the new server process. + */ + fclose(stdout); + + struct s2n_connection *server = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server, &io_pair)); + + EXPECT_OK(scenario_fn(server)); + + EXPECT_SUCCESS(s2n_connection_free(server)); + exit(EXIT_SUCCESS); + } + + int status = 0; + RESULT_ENSURE_EQ(waitpid(client_pid, &status, 0), client_pid); + RESULT_ENSURE_EQ(status, EXIT_SUCCESS); + RESULT_ENSURE_EQ(waitpid(server_pid, &status, 0), server_pid); + RESULT_ENSURE_EQ(status, EXIT_SUCCESS); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* KeyUpdate requires TLS1.3 */ + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + /* We're going to fork, so flush the initial test output first */ + EXPECT_EQUAL(fflush(stdout), 0); + + EXPECT_OK(s2n_run_self_talk_test(s2n_test_encryption_limits)); + EXPECT_OK(s2n_run_self_talk_test(s2n_test_peer_requests)); + + END_TEST(); +} diff --git a/tests/unit/s2n_ktls_io_sendfile_test.c b/tests/unit/s2n_ktls_io_sendfile_test.c new file mode 100644 index 00000000000..7993100594c --- /dev/null +++ b/tests/unit/s2n_ktls_io_sendfile_test.c @@ -0,0 +1,218 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_ktls.h" +#include "utils/s2n_random.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + +#ifdef S2N_LINUX_SENDFILE + const bool sendfile_supported = true; +#else + const bool sendfile_supported = false; +#endif + + /* Test feature probe */ + { +#if defined(__linux__) + EXPECT_TRUE(sendfile_supported); +#endif +#if defined(__FreeBSD__) + EXPECT_FALSE(sendfile_supported); +#endif + }; + + /* Safety */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + size_t bytes_written = 0; + + EXPECT_FAILURE_WITH_ERRNO( + s2n_sendfile(NULL, 0, 0, 0, &bytes_written, &blocked), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO( + s2n_sendfile(conn, 0, 0, 0, NULL, &blocked), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO( + s2n_sendfile(conn, 0, 0, 0, &bytes_written, NULL), + S2N_ERR_NULL); + }; + + /* Test s2n_sendfile unsupported */ + if (!sendfile_supported) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->ktls_send_enabled = true; + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + size_t bytes_written = 0; + int result = s2n_sendfile(conn, 1, 0, 1, &bytes_written, &blocked); + EXPECT_FAILURE_WITH_ERRNO(result, S2N_ERR_UNIMPLEMENTED); + + /* We do not run any further tests */ + END_TEST(); + }; + + /* The one file we know definitely exists is our own executable */ + int ro_file = open(argv[0], O_RDONLY); + EXPECT_TRUE(ro_file > 0); + + /* use pread to read the beginning of the file without updating its offset. + * Careful: if any call to sendfile sets offset=NULL, the file's offset will + * be updated and different data will be read. + */ + uint8_t test_data[100] = { 0 }; + EXPECT_EQUAL(pread(ro_file, test_data, sizeof(test_data), 0), sizeof(test_data)); + + /* Test: successful send */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->ktls_send_enabled = true; + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + int write_fd = io_pair.server; + int read_fd = io_pair.client; + EXPECT_SUCCESS(s2n_connection_set_write_fd(conn, write_fd)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + size_t bytes_written = 0; + EXPECT_SUCCESS(s2n_sendfile(conn, ro_file, 0, sizeof(test_data), + &bytes_written, &blocked)); + EXPECT_EQUAL(bytes_written, sizeof(test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + uint8_t written[sizeof(test_data)] = { 0 }; + EXPECT_EQUAL(read(read_fd, written, sizeof(written)), sizeof(test_data)); + EXPECT_BYTEARRAY_EQUAL(written, test_data, sizeof(test_data)); + EXPECT_TRUE(read(read_fd, written, sizeof(written)) < 0); + }; + + /* Test: IO error */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->ktls_send_enabled = true; + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + int write_fd = io_pair.server; + int read_fd = io_pair.client; + EXPECT_SUCCESS(s2n_connection_set_write_fd(conn, write_fd)); + + /* Close one side of the stream to make the fds invalid */ + close(read_fd); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + size_t bytes_written = 0; + int ret = s2n_sendfile(conn, ro_file, 0, sizeof(test_data), + &bytes_written, &blocked); + EXPECT_FAILURE_WITH_ERRNO(ret, S2N_ERR_IO); + EXPECT_EQUAL(bytes_written, 0); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + }; + + /* Test: send blocks */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->ktls_send_enabled = true; + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + int write_fd = io_pair.server; + EXPECT_SUCCESS(s2n_connection_set_write_fd(conn, write_fd)); + + /* We can force the socket to block by filling up its send buffer. */ + int buffer_size = 0; + socklen_t optlen = sizeof(buffer_size); + EXPECT_EQUAL(getsockopt(write_fd, SOL_SOCKET, SO_SNDBUF, &buffer_size, &optlen), 0); + EXPECT_TRUE(buffer_size > 0); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + size_t bytes_written = 0; + size_t total_bytes_written = 0; + while (true) { + int result = s2n_sendfile(conn, ro_file, 0, sizeof(test_data), + &bytes_written, &blocked); + if (result < 0) { + EXPECT_FAILURE_WITH_ERRNO(result, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(bytes_written, 0); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + break; + } + + EXPECT_TRUE(bytes_written <= sizeof(test_data)); + EXPECT_TRUE(bytes_written > 0); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + total_bytes_written += bytes_written; + + /* The socket will block before buffer_size bytes are written. + * If we successfully send buffer_size bytes, something is wrong. + */ + EXPECT_TRUE(total_bytes_written < buffer_size); + } + }; + + /* Test: partial write */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->ktls_send_enabled = true; + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + int write_fd = io_pair.server; + EXPECT_SUCCESS(s2n_connection_set_write_fd(conn, write_fd)); + + int buffer_size = 0; + socklen_t optlen = sizeof(buffer_size); + EXPECT_EQUAL(getsockopt(write_fd, SOL_SOCKET, SO_SNDBUF, &buffer_size, &optlen), 0); + EXPECT_TRUE(buffer_size > 0); + + /* Try to write more data than the buffer can hold in a single sendfile call */ + size_t bytes_to_write = buffer_size * 2; + size_t bytes_written = 0; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_SUCCESS(s2n_sendfile(conn, ro_file, 0, bytes_to_write, + &bytes_written, &blocked)); + EXPECT_TRUE(bytes_written > 0); + EXPECT_TRUE(bytes_written < bytes_to_write); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + }; + + EXPECT_EQUAL(close(ro_file), 0); + END_TEST(); +} diff --git a/tests/unit/s2n_ktls_io_test.c b/tests/unit/s2n_ktls_io_test.c new file mode 100644 index 00000000000..8a651bc94a2 --- /dev/null +++ b/tests/unit/s2n_ktls_io_test.c @@ -0,0 +1,1189 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_ktls_test_utils.h" +#include "testlib/s2n_mem_testlib.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_ktls.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_random.h" + +#define S2N_TEST_TO_SEND 10 +#define S2N_TEST_MSG_IOVLEN 5 + +S2N_RESULT s2n_ktls_set_control_data(struct msghdr *msg, char *buf, size_t buf_size, + int cmsg_type, uint8_t record_type); +S2N_RESULT s2n_ktls_get_control_data(struct msghdr *msg, int cmsg_type, uint8_t *record_type); + +/* Mock implementation used for validating failure behavior */ +struct s2n_test_ktls_io_fail_ctx { + size_t errno_code; + size_t invoked_count; +}; + +static ssize_t s2n_test_ktls_sendmsg_fail(void *io_context, const struct msghdr *msg) +{ + struct s2n_test_ktls_io_fail_ctx *io_ctx = (struct s2n_test_ktls_io_fail_ctx *) io_context; + POSIX_ENSURE_REF(io_ctx); + io_ctx->invoked_count++; + errno = io_ctx->errno_code; + return -1; +} + +static ssize_t s2n_test_ktls_recvmsg_fail(void *io_context, struct msghdr *msg) +{ + POSIX_ENSURE_REF(msg); + + struct s2n_test_ktls_io_fail_ctx *io_ctx = (struct s2n_test_ktls_io_fail_ctx *) io_context; + POSIX_ENSURE_REF(io_ctx); + io_ctx->invoked_count++; + errno = io_ctx->errno_code; + return -1; +} + +static ssize_t s2n_test_ktls_recvmsg_eof(void *io_context, struct msghdr *msg) +{ + struct s2n_test_ktls_io_fail_ctx *io_ctx = (struct s2n_test_ktls_io_fail_ctx *) io_context; + POSIX_ENSURE_REF(io_ctx); + io_ctx->invoked_count++; + return 0; +} + +ssize_t s2n_test_ktls_recvmsg_io_stuffer_and_ctrunc(void *io_context, struct msghdr *msg) +{ + POSIX_ENSURE_REF(msg); + + /* The stuffer mock IO is used to ensure `cmsghdr` is otherwise properly constructed + * and that the failure occurs due to the MSG_CTRUNC flag. */ + ssize_t ret = s2n_test_ktls_recvmsg_io_stuffer(io_context, msg); + POSIX_GUARD(ret); + msg->msg_flags = MSG_CTRUNC; + return ret; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + const uint8_t test_record_type = 43; + /* test data */ + uint8_t test_data[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 }; + struct s2n_blob test_data_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&test_data_blob, test_data, sizeof(test_data))); + EXPECT_OK(s2n_get_public_random_data(&test_data_blob)); + + /* Test s2n_ktls_set_control_data and s2n_ktls_get_control_data */ + { + /* Test: Safety */ + { + struct msghdr msg = { 0 }; + char buf[100] = { 0 }; + EXPECT_ERROR_WITH_ERRNO(s2n_ktls_set_control_data(NULL, buf, sizeof(buf), 0, 0), + S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_ktls_set_control_data(&msg, NULL, sizeof(buf), 0, 0), + S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_ktls_set_control_data(&msg, buf, 0, 0, 0), + S2N_ERR_NULL); + + uint8_t record_type = 0; + EXPECT_ERROR_WITH_ERRNO(s2n_ktls_get_control_data(NULL, 0, &record_type), + S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_ktls_get_control_data(&msg, 0, NULL), + S2N_ERR_NULL); + }; + + /* Test: s2n_ktls_set_control_data msg is parseable by s2n_ktls_get_control_data */ + { + const uint8_t set_record_type = 5; + struct msghdr msg = { 0 }; + const int cmsg_type = 11; + char buf[100] = { 0 }; + EXPECT_OK(s2n_ktls_set_control_data(&msg, buf, sizeof(buf), cmsg_type, set_record_type)); + + uint8_t get_record_type = 0; + EXPECT_OK(s2n_ktls_get_control_data(&msg, cmsg_type, &get_record_type)); + + EXPECT_EQUAL(set_record_type, get_record_type); + }; + + /* Test: s2n_ktls_get_control_data fails with unexpected cmsg_type */ + { + const uint8_t set_record_type = 5; + struct msghdr msg = { 0 }; + const int cmsg_type = 11; + char buf[100] = { 0 }; + EXPECT_OK(s2n_ktls_set_control_data(&msg, buf, sizeof(buf), cmsg_type, set_record_type)); + + const int bad_cmsg_type = 99; + uint8_t get_record_type = 0; + EXPECT_ERROR_WITH_ERRNO(s2n_ktls_get_control_data(&msg, bad_cmsg_type, &get_record_type), + S2N_ERR_KTLS_BAD_CMSG); + }; + }; + + /* Test s2n_ktls_sendmsg */ + { + /* Safety */ + { + struct s2n_test_ktls_io_stuffer ctx = { 0 }; + struct iovec msg_iov_valid = { .iov_base = test_data, .iov_len = S2N_TEST_TO_SEND }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + size_t bytes_written = 0; + + EXPECT_ERROR_WITH_ERRNO( + s2n_ktls_sendmsg(NULL, test_record_type, &msg_iov_valid, 1, &blocked, &bytes_written), + S2N_ERR_IO); + EXPECT_ERROR_WITH_ERRNO( + s2n_ktls_sendmsg(&ctx, test_record_type, NULL, 1, &blocked, &bytes_written), + S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO( + s2n_ktls_sendmsg(&ctx, test_record_type, &msg_iov_valid, 1, NULL, &bytes_written), + S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO( + s2n_ktls_sendmsg(&ctx, test_record_type, &msg_iov_valid, 1, &blocked, NULL), + S2N_ERR_NULL); + }; + + /* Happy case: msg_iovlen = 1 */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer client_in = { 0 }, + s2n_ktls_io_stuffer_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer_send(server, &client_in)); + + struct iovec msg_iov = { .iov_base = test_data, .iov_len = S2N_TEST_TO_SEND }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + size_t bytes_written = 0; + EXPECT_OK(s2n_ktls_sendmsg(server->send_io_context, test_record_type, + &msg_iov, 1, &blocked, &bytes_written)); + EXPECT_EQUAL(bytes_written, S2N_TEST_TO_SEND); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + /* confirm sent data */ + EXPECT_OK(s2n_test_validate_ancillary(&client_in, test_record_type, S2N_TEST_TO_SEND)); + EXPECT_OK(s2n_test_validate_data(&client_in, test_data, S2N_TEST_TO_SEND)); + + EXPECT_EQUAL(client_in.sendmsg_invoked_count, 1); + }; + + /* Happy case: msg_iovlen > 1 */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer client_in = { 0 }, + s2n_ktls_io_stuffer_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer_send(server, &client_in)); + + struct iovec msg_iov[S2N_TEST_MSG_IOVLEN] = { 0 }; + size_t total_sent = 0; + for (size_t i = 0; i < S2N_TEST_MSG_IOVLEN; i++) { + msg_iov[i].iov_base = test_data + total_sent; + msg_iov[i].iov_len = S2N_TEST_TO_SEND; + total_sent += S2N_TEST_TO_SEND; + } + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + size_t bytes_written = 0; + EXPECT_OK(s2n_ktls_sendmsg(server->send_io_context, test_record_type, + msg_iov, S2N_TEST_MSG_IOVLEN, &blocked, &bytes_written)); + EXPECT_EQUAL(bytes_written, total_sent); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + /* confirm sent data */ + EXPECT_OK(s2n_test_validate_ancillary(&client_in, test_record_type, total_sent)); + EXPECT_OK(s2n_test_validate_data(&client_in, test_data, total_sent)); + /* validate only 1 record was sent */ + EXPECT_EQUAL(s2n_stuffer_data_available(&client_in.ancillary_buffer), + S2N_TEST_KTLS_MOCK_HEADER_SIZE); + + EXPECT_EQUAL(client_in.sendmsg_invoked_count, 1); + }; + + /* Simulate a blocked network and handle a S2N_ERR_IO_BLOCKED error */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer client_in = { 0 }, + s2n_ktls_io_stuffer_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer_send(server, &client_in)); + /* disable growable to simulate blocked/network buffer full */ + client_in.data_buffer.growable = false; + + struct iovec msg_iov = { .iov_base = test_data, .iov_len = S2N_TEST_TO_SEND }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + size_t blocked_invoked_count = 5; + size_t bytes_written = 0; + for (size_t i = 0; i < blocked_invoked_count; i++) { + EXPECT_ERROR_WITH_ERRNO( + s2n_ktls_sendmsg(server->send_io_context, test_record_type, + &msg_iov, 1, &blocked, &bytes_written), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + } + + /* enable growable to unblock write */ + /* cppcheck-suppress redundantAssignment */ + client_in.data_buffer.growable = true; + EXPECT_OK(s2n_ktls_sendmsg(server->send_io_context, test_record_type, + &msg_iov, 1, &blocked, &bytes_written)); + EXPECT_EQUAL(bytes_written, S2N_TEST_TO_SEND); + + /* confirm sent data */ + EXPECT_OK(s2n_test_validate_ancillary(&client_in, test_record_type, S2N_TEST_TO_SEND)); + EXPECT_OK(s2n_test_validate_data(&client_in, test_data, S2N_TEST_TO_SEND)); + + EXPECT_EQUAL(client_in.sendmsg_invoked_count, blocked_invoked_count + 1); + }; + + /* Both EWOULDBLOCK and EAGAIN should return a S2N_ERR_IO_BLOCKED error */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + struct s2n_test_ktls_io_fail_ctx io_ctx = { 0 }; + EXPECT_OK(s2n_ktls_set_sendmsg_cb(server, s2n_test_ktls_sendmsg_fail, &io_ctx)); + + struct iovec msg_iov = { .iov_base = test_data, .iov_len = S2N_TEST_TO_SEND }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + size_t bytes_written = 0; + + io_ctx.errno_code = EWOULDBLOCK; + EXPECT_ERROR_WITH_ERRNO( + s2n_ktls_sendmsg(server->send_io_context, test_record_type, + &msg_iov, 1, &blocked, &bytes_written), + S2N_ERR_IO_BLOCKED); + + /* cppcheck-suppress redundantAssignment */ + io_ctx.errno_code = EAGAIN; + EXPECT_ERROR_WITH_ERRNO( + s2n_ktls_sendmsg(server->send_io_context, test_record_type, + &msg_iov, 1, &blocked, &bytes_written), + S2N_ERR_IO_BLOCKED); + + EXPECT_EQUAL(io_ctx.invoked_count, 2); + }; + + /* Handle a S2N_ERR_IO error */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + struct s2n_test_ktls_io_fail_ctx io_ctx = { + .errno_code = EINVAL, + }; + EXPECT_OK(s2n_ktls_set_sendmsg_cb(server, s2n_test_ktls_sendmsg_fail, &io_ctx)); + + struct iovec msg_iov = { .iov_base = test_data, .iov_len = S2N_TEST_TO_SEND }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + size_t bytes_written = 0; + EXPECT_ERROR_WITH_ERRNO( + s2n_ktls_sendmsg(server->send_io_context, test_record_type, + &msg_iov, 1, &blocked, &bytes_written), + S2N_ERR_IO); + /* Blocked status intentionally not reset to preserve legacy s2n_send behavior */ + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + + EXPECT_EQUAL(io_ctx.invoked_count, 1); + }; + + /* Should be able to invoke s2n_ktls_sendmsg with '0' data */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer client_in = { 0 }, + s2n_ktls_io_stuffer_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer_send(server, &client_in)); + + struct iovec msg_iov = { .iov_base = test_data, .iov_len = S2N_TEST_TO_SEND }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + size_t bytes_written = 0; + + size_t iovlen_zero = 0; + EXPECT_OK(s2n_ktls_sendmsg(server->send_io_context, test_record_type, + &msg_iov, iovlen_zero, &blocked, &bytes_written)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(bytes_written, 0); + + struct iovec msg_iov_len_zero = { .iov_base = test_data, .iov_len = 0 }; + EXPECT_OK(s2n_ktls_sendmsg(server->send_io_context, test_record_type, + &msg_iov_len_zero, 1, &blocked, &bytes_written)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(bytes_written, 0); + + EXPECT_EQUAL(client_in.sendmsg_invoked_count, 2); + }; + }; + + /* Test s2n_ktls_recvmsg */ + { + /* Safety */ + { + struct s2n_test_ktls_io_stuffer ctx = { 0 }; + uint8_t recv_buf[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + uint8_t recv_record_type = 0; + size_t bytes_read = 0; + + EXPECT_ERROR_WITH_ERRNO( + s2n_ktls_recvmsg(NULL, &recv_record_type, recv_buf, S2N_TEST_TO_SEND, &blocked, &bytes_read), + S2N_ERR_IO); + EXPECT_ERROR_WITH_ERRNO( + s2n_ktls_recvmsg(&ctx, NULL, recv_buf, S2N_TEST_TO_SEND, &blocked, &bytes_read), + S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO( + s2n_ktls_recvmsg(&ctx, &recv_record_type, NULL, S2N_TEST_TO_SEND, &blocked, &bytes_read), + S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO( + s2n_ktls_recvmsg(&ctx, &recv_record_type, recv_buf, S2N_TEST_TO_SEND, NULL, &bytes_read), + S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO( + s2n_ktls_recvmsg(&ctx, &recv_record_type, recv_buf, S2N_TEST_TO_SEND, &blocked, NULL), + S2N_ERR_NULL); + + size_t to_recv_zero = 0; + EXPECT_ERROR_WITH_ERRNO( + s2n_ktls_recvmsg(&ctx, &recv_record_type, recv_buf, to_recv_zero, &blocked, &bytes_read), + S2N_ERR_SAFETY); + }; + + /* Happy case: send/recv data using sendmsg/recvmsg */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair io_pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(server, client, &io_pair)); + + struct iovec msg_iov = { .iov_base = test_data, .iov_len = S2N_TEST_TO_SEND }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + size_t bytes_written = 0; + EXPECT_OK(s2n_ktls_sendmsg(server->send_io_context, test_record_type, + &msg_iov, 1, &blocked, &bytes_written)); + EXPECT_EQUAL(bytes_written, S2N_TEST_TO_SEND); + + uint8_t recv_buf[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 }; + uint8_t recv_record_type = 0; + size_t bytes_read = 0; + EXPECT_OK(s2n_ktls_recvmsg(client->recv_io_context, &recv_record_type, + recv_buf, S2N_TEST_TO_SEND, &blocked, &bytes_read)); + EXPECT_BYTEARRAY_EQUAL(test_data, recv_buf, bytes_read); + EXPECT_EQUAL(bytes_read, bytes_written); + + EXPECT_EQUAL(io_pair.client_in.sendmsg_invoked_count, 1); + EXPECT_EQUAL(io_pair.client_in.recvmsg_invoked_count, 1); + }; + + /* Simulate blocked and handle a S2N_ERR_IO_BLOCKED error */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair io_pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(server, client, &io_pair)); + + uint8_t recv_buf[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + size_t blocked_invoked_count = 5; + uint8_t recv_record_type = 0; + size_t bytes_read = 0; + /* recv should block since there is no data */ + for (size_t i = 0; i < blocked_invoked_count; i++) { + EXPECT_ERROR_WITH_ERRNO( + s2n_ktls_recvmsg(client->recv_io_context, &recv_record_type, + recv_buf, S2N_TEST_TO_SEND, &blocked, &bytes_read), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + } + + /* send data to unblock */ + struct iovec msg_iov = { .iov_base = test_data, .iov_len = S2N_TEST_TO_SEND }; + size_t bytes_written = 0; + EXPECT_OK(s2n_ktls_sendmsg(server->send_io_context, test_record_type, + &msg_iov, 1, &blocked, &bytes_written)); + EXPECT_EQUAL(bytes_written, S2N_TEST_TO_SEND); + + EXPECT_OK(s2n_ktls_recvmsg(client->recv_io_context, &recv_record_type, + recv_buf, S2N_TEST_TO_SEND, &blocked, &bytes_read)); + EXPECT_BYTEARRAY_EQUAL(test_data, recv_buf, bytes_read); + EXPECT_EQUAL(bytes_read, bytes_written); + + /* recv should block again since we have read all the data */ + for (size_t i = 0; i < blocked_invoked_count; i++) { + EXPECT_ERROR_WITH_ERRNO( + s2n_ktls_recvmsg(client->recv_io_context, &recv_record_type, + recv_buf, S2N_TEST_TO_SEND, &blocked, &bytes_read), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + } + + EXPECT_EQUAL(io_pair.client_in.recvmsg_invoked_count, (blocked_invoked_count * 2) + 1); + EXPECT_EQUAL(io_pair.client_in.sendmsg_invoked_count, 1); + }; + + /* Both EWOULDBLOCK and EAGAIN should return a S2N_ERR_IO_BLOCKED error */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + struct s2n_test_ktls_io_fail_ctx io_ctx = { 0 }; + EXPECT_OK(s2n_ktls_set_recvmsg_cb(client, s2n_test_ktls_recvmsg_fail, &io_ctx)); + + uint8_t recv_buf[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + uint8_t recv_record_type = 0; + size_t bytes_read = 0; + + io_ctx.errno_code = EWOULDBLOCK; + EXPECT_ERROR_WITH_ERRNO( + s2n_ktls_recvmsg(client->recv_io_context, &recv_record_type, + recv_buf, S2N_TEST_TO_SEND, &blocked, &bytes_read), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + /* cppcheck-suppress redundantAssignment */ + io_ctx.errno_code = EAGAIN; + EXPECT_ERROR_WITH_ERRNO( + s2n_ktls_recvmsg(client->recv_io_context, &recv_record_type, + recv_buf, S2N_TEST_TO_SEND, &blocked, &bytes_read), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + EXPECT_EQUAL(io_ctx.invoked_count, 2); + }; + + /* Handle a S2N_ERR_IO error */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + struct s2n_test_ktls_io_fail_ctx io_ctx = { + .errno_code = EINVAL, + }; + EXPECT_OK(s2n_ktls_set_recvmsg_cb(client, s2n_test_ktls_recvmsg_fail, &io_ctx)); + + uint8_t recv_buf[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + uint8_t recv_record_type = 0; + size_t bytes_read = 0; + EXPECT_ERROR_WITH_ERRNO( + s2n_ktls_recvmsg(client->recv_io_context, &recv_record_type, + recv_buf, S2N_TEST_TO_SEND, &blocked, &bytes_read), + S2N_ERR_IO); + /* Blocked status intentionally not reset to preserve legacy s2n_send behavior */ + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + EXPECT_EQUAL(io_ctx.invoked_count, 1); + }; + + /* Simulate EOF and handle a S2N_ERR_CLOSED error */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + struct s2n_test_ktls_io_fail_ctx io_ctx = { 0 }; + EXPECT_OK(s2n_ktls_set_recvmsg_cb(client, s2n_test_ktls_recvmsg_eof, &io_ctx)); + + uint8_t recv_buf[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + uint8_t recv_record_type = 0; + size_t bytes_read = 0; + EXPECT_ERROR_WITH_ERRNO( + s2n_ktls_recvmsg(client->recv_io_context, &recv_record_type, + recv_buf, S2N_TEST_TO_SEND, &blocked, &bytes_read), + S2N_ERR_CLOSED); + /* Blocked status intentionally not reset to preserve legacy s2n_send behavior */ + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + EXPECT_EQUAL(io_ctx.invoked_count, 1); + }; + + /* Simulate control message truncated via MSG_CTRUNC flag and handle a S2N_ERR_KTLS_BAD_CMSG error */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair io_pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(server, client, &io_pair)); + /* override the client recvmsg callback to add a MSG_CTRUNC flag to msghdr before returning */ + EXPECT_OK(s2n_ktls_set_recvmsg_cb(client, s2n_test_ktls_recvmsg_io_stuffer_and_ctrunc, &io_pair.client_in)); + + struct iovec msg_iov = { .iov_base = test_data, .iov_len = S2N_TEST_TO_SEND }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + size_t bytes_written = 0; + EXPECT_OK(s2n_ktls_sendmsg(server->send_io_context, test_record_type, + &msg_iov, 1, &blocked, &bytes_written)); + EXPECT_EQUAL(bytes_written, S2N_TEST_TO_SEND); + + uint8_t recv_buf[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 }; + uint8_t recv_record_type = 0; + size_t bytes_read = 0; + EXPECT_ERROR_WITH_ERRNO( + s2n_ktls_recvmsg(client->recv_io_context, &recv_record_type, + recv_buf, S2N_TEST_TO_SEND, &blocked, &bytes_read), + S2N_ERR_KTLS_BAD_CMSG); + /* Blocked status intentionally not reset to preserve legacy s2n_send behavior */ + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + EXPECT_EQUAL(io_pair.client_in.sendmsg_invoked_count, 1); + EXPECT_EQUAL(io_pair.client_in.recvmsg_invoked_count, 1); + }; + }; + + /* Test s2n_ktls_send */ + { + const size_t test_iov_lens[] = { 10, 0, 1, 5, 100, 100, 10 }; + + /* Safety */ + { + struct s2n_connection conn = { 0 }; + s2n_blocked_status blocked = 0; + const struct iovec test_iovec = { .iov_base = &blocked, .iov_len = 1 }; + + EXPECT_FAILURE_WITH_ERRNO( + s2n_ktls_sendv_with_offset(NULL, &test_iovec, 1, 0, &blocked), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO( + s2n_ktls_sendv_with_offset(&conn, NULL, 1, 0, &blocked), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO( + s2n_ktls_sendv_with_offset(&conn, NULL, 1, 1, &blocked), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO( + s2n_ktls_sendv_with_offset(&conn, &test_iovec, 1, 0, NULL), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO( + s2n_ktls_sendv_with_offset(&conn, &test_iovec, -1, 0, &blocked), + S2N_ERR_INVALID_ARGUMENT); + EXPECT_FAILURE_WITH_ERRNO( + s2n_ktls_sendv_with_offset(&conn, &test_iovec, 1, -1, &blocked), + S2N_ERR_INVALID_ARGUMENT); + }; + + /* Test: Basic send with single iovec */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer out = { 0 }, + s2n_ktls_io_stuffer_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer_send(conn, &out)); + + const struct iovec test_iovec = { + .iov_base = test_data, + .iov_len = sizeof(test_data), + }; + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_EQUAL( + s2n_ktls_sendv_with_offset(conn, &test_iovec, 1, 0, &blocked), + sizeof(test_data)); + + EXPECT_EQUAL(out.sendmsg_invoked_count, 1); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_OK(s2n_test_validate_ancillary(&out, TLS_APPLICATION_DATA, sizeof(test_data))); + EXPECT_OK(s2n_test_validate_data(&out, test_data, sizeof(test_data))); + }; + + /* Test: Handle IO error from sendmsg */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + struct s2n_test_ktls_io_fail_ctx io_ctx = { .errno_code = EINVAL }; + EXPECT_OK(s2n_ktls_set_sendmsg_cb(conn, s2n_test_ktls_sendmsg_fail, &io_ctx)); + + const struct iovec test_iovec = { + .iov_base = test_data, + .iov_len = sizeof(test_data), + }; + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_FAILURE_WITH_ERRNO( + s2n_ktls_sendv_with_offset(conn, &test_iovec, 1, 0, &blocked), + S2N_ERR_IO); + EXPECT_EQUAL(io_ctx.invoked_count, 1); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + }; + + /* Test: Send nothing */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer out = { 0 }, + s2n_ktls_io_stuffer_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer_send(conn, &out)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + const struct iovec test_iovec = { + .iov_base = test_data, + .iov_len = 0, + }; + + /* Send nothing with zero-length iovec array */ + EXPECT_EQUAL(s2n_ktls_sendv_with_offset(conn, NULL, 0, 0, &blocked), 0); + EXPECT_EQUAL(out.sendmsg_invoked_count, 1); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_OK(s2n_test_records_in_ancillary(&out, 0)); + + /* Send nothing with iovec array with zero-length buffer */ + EXPECT_EQUAL(s2n_ktls_sendv_with_offset(conn, &test_iovec, 1, 0, &blocked), 0); + EXPECT_EQUAL(out.sendmsg_invoked_count, 2); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_OK(s2n_test_records_in_ancillary(&out, 0)); + }; + + /* Test: Send with multiple iovecs */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer out = { 0 }, + s2n_ktls_io_stuffer_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer_send(conn, &out)); + + DEFER_CLEANUP(struct s2n_test_iovecs test_iovecs = { 0 }, s2n_test_iovecs_free); + EXPECT_OK(s2n_test_new_iovecs(&test_iovecs, &test_data_blob, + test_iov_lens, s2n_array_len(test_iov_lens))); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t result = s2n_ktls_sendv_with_offset(conn, + test_iovecs.iovecs, test_iovecs.iovecs_count, 0, &blocked); + EXPECT_EQUAL(result, sizeof(test_data)); + + EXPECT_EQUAL(out.sendmsg_invoked_count, 1); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_OK(s2n_test_validate_ancillary(&out, TLS_APPLICATION_DATA, sizeof(test_data))); + EXPECT_OK(s2n_test_validate_data(&out, test_data, sizeof(test_data))); + }; + + /* Test: Send with offset */ + { + DEFER_CLEANUP(struct s2n_test_iovecs test_iovecs = { 0 }, s2n_test_iovecs_free); + EXPECT_OK(s2n_test_new_iovecs(&test_iovecs, &test_data_blob, + test_iov_lens, s2n_array_len(test_iov_lens))); + + size_t large_test_iov_lens[100] = { 0 }; + EXPECT_MEMCPY_SUCCESS(large_test_iov_lens, test_iov_lens, sizeof(test_iov_lens)); + + DEFER_CLEANUP(struct s2n_test_iovecs large_test_iovecs = { 0 }, + s2n_test_iovecs_free); + EXPECT_OK(s2n_test_new_iovecs(&large_test_iovecs, &test_data_blob, + large_test_iov_lens, s2n_array_len(large_test_iov_lens))); + + /* Test: Send with invalid / too large offset */ + { + const size_t bad_offset = sizeof(test_data) + 1; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer out = { 0 }, + s2n_ktls_io_stuffer_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer_send(conn, &out)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t result = s2n_ktls_sendv_with_offset(conn, large_test_iovecs.iovecs, + large_test_iovecs.iovecs_count, bad_offset, &blocked); + EXPECT_FAILURE_WITH_ERRNO(result, S2N_ERR_INVALID_ARGUMENT); + + EXPECT_EQUAL(out.sendmsg_invoked_count, 0); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_OK(s2n_test_records_in_ancillary(&out, 0)); + }; + + /* Test: Send with offset equal to total data size */ + { + const size_t offset = sizeof(test_data); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer out = { 0 }, + s2n_ktls_io_stuffer_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer_send(conn, &out)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + int written = s2n_ktls_sendv_with_offset(conn, large_test_iovecs.iovecs, + large_test_iovecs.iovecs_count, offset, &blocked); + EXPECT_EQUAL(written, 0); + + EXPECT_EQUAL(out.sendmsg_invoked_count, 1); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_OK(s2n_test_records_in_ancillary(&out, 0)); + }; + + /* Test: Send with small iovecs array and all possible valid offsets */ + for (size_t offset = 0; offset < sizeof(test_data); offset++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer out = { 0 }, + s2n_ktls_io_stuffer_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer_send(conn, &out)); + + const size_t expected_sent = sizeof(test_data) - offset; + EXPECT_TRUE(expected_sent > 0); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t result = s2n_ktls_sendv_with_offset(conn, + test_iovecs.iovecs, test_iovecs.iovecs_count, offset, &blocked); + EXPECT_EQUAL(result, expected_sent); + + EXPECT_EQUAL(out.sendmsg_invoked_count, 1); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_OK(s2n_test_validate_ancillary(&out, TLS_APPLICATION_DATA, expected_sent)); + EXPECT_OK(s2n_test_validate_data(&out, test_data + offset, expected_sent)); + } + + /* Test: Send with large iovecs array and all possible valid offsets */ + for (size_t offset = 0; offset < sizeof(test_data); offset++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer out = { 0 }, + s2n_ktls_io_stuffer_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer_send(conn, &out)); + + const size_t expected_sent = sizeof(test_data) - offset; + EXPECT_TRUE(expected_sent > 0); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t result = s2n_ktls_sendv_with_offset(conn, large_test_iovecs.iovecs, + large_test_iovecs.iovecs_count, offset, &blocked); + EXPECT_EQUAL(result, expected_sent); + + EXPECT_EQUAL(out.sendmsg_invoked_count, 1); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_OK(s2n_test_validate_ancillary(&out, TLS_APPLICATION_DATA, expected_sent)); + EXPECT_OK(s2n_test_validate_data(&out, test_data + offset, expected_sent)); + } + }; + + /* Test: Partial write */ + { + DEFER_CLEANUP(struct s2n_test_iovecs test_iovecs = { 0 }, s2n_test_iovecs_free); + EXPECT_OK(s2n_test_new_iovecs(&test_iovecs, &test_data_blob, + test_iov_lens, s2n_array_len(test_iov_lens))); + + /* Test with all possible partial write lengths */ + for (size_t size = 1; size < sizeof(test_data); size++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer out = { 0 }, + s2n_ktls_io_stuffer_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer_send(conn, &out)); + EXPECT_SUCCESS(s2n_stuffer_free(&out.data_buffer)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&out.data_buffer, size)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t result = s2n_ktls_sendv_with_offset(conn, + test_iovecs.iovecs, test_iovecs.iovecs_count, 0, &blocked); + EXPECT_EQUAL(result, size); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + EXPECT_EQUAL(out.sendmsg_invoked_count, 1); + EXPECT_OK(s2n_test_validate_data(&out, test_data, size)); + } + }; + + /* Test: IO would block */ + { + DEFER_CLEANUP(struct s2n_test_iovecs test_iovecs = { 0 }, s2n_test_iovecs_free); + EXPECT_OK(s2n_test_new_iovecs(&test_iovecs, &test_data_blob, + test_iov_lens, s2n_array_len(test_iov_lens))); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + struct s2n_test_ktls_io_fail_ctx io_ctx = { .errno_code = EAGAIN }; + EXPECT_OK(s2n_ktls_set_sendmsg_cb(conn, s2n_test_ktls_sendmsg_fail, &io_ctx)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t result = s2n_ktls_sendv_with_offset(conn, + test_iovecs.iovecs, test_iovecs.iovecs_count, 0, &blocked); + EXPECT_FAILURE_WITH_ERRNO(result, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(io_ctx.invoked_count, 1); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + }; + + /* Test: Memory usage */ + { + const size_t iov_lens[100] = { 10, 5, 0, 1, 100, 100, 10 }; + const size_t small_iov_lens_count = 10; + const size_t large_iov_lens_count = s2n_array_len(iov_lens); + + DEFER_CLEANUP(struct s2n_test_iovecs small_iovecs = { 0 }, s2n_test_iovecs_free); + EXPECT_OK(s2n_test_new_iovecs(&small_iovecs, &test_data_blob, + iov_lens, small_iov_lens_count)); + + DEFER_CLEANUP(struct s2n_test_iovecs large_iovecs = { 0 }, s2n_test_iovecs_free); + EXPECT_OK(s2n_test_new_iovecs(&large_iovecs, &test_data_blob, + iov_lens, large_iov_lens_count)); + + const size_t one_iovec_size = sizeof(struct iovec); + const size_t large_iovecs_size = large_iovecs.iovecs_count * one_iovec_size; + + struct { + struct s2n_test_iovecs *iovecs; + size_t offset; + uint32_t expected_malloc; + uint32_t expected_malloc_count; + } test_cases[] = { + /* Small iovecs never require an allocation */ + { + .iovecs = &small_iovecs, + .offset = 1, + .expected_malloc_count = 0, + }, + { + .iovecs = &small_iovecs, + .offset = iov_lens[0], + .expected_malloc_count = 0, + }, + { + .iovecs = &small_iovecs, + .offset = iov_lens[0] + 1, + .expected_malloc_count = 0, + }, + /* Large iovecs with offset evenly divisible by the iov_lens do + * not require an alloc. + * Example: { x, y, z }, offset=x -> { y, z } + */ + { + .iovecs = &large_iovecs, + .offset = iov_lens[0], + .expected_malloc_count = 0, + }, + { + .iovecs = &large_iovecs, + .offset = iov_lens[0] + iov_lens[1], + .expected_malloc_count = 0, + }, + /* Large iovecs with offset not evenly divisible by the iov_lens + * modify an entry so require an alloc. + * Example: { x, y, z }, offset=1 -> { x-1, y, z } + */ + { + .iovecs = &large_iovecs, + .offset = 1, + .expected_malloc_count = 1, + .expected_malloc = large_iovecs_size, + }, + { + .iovecs = &large_iovecs, + .offset = iov_lens[0] + 1, + .expected_malloc_count = 1, + .expected_malloc = large_iovecs_size - one_iovec_size, + }, + /* Large iovecs that become small iovecs when the offset + * is applied do not require an alloc. + */ + { + .iovecs = &large_iovecs, + .offset = sizeof(test_data) - 1, + .expected_malloc_count = 0, + }, + /* No alloc if the entire large iovec is skipped */ + { + .iovecs = &large_iovecs, + .offset = sizeof(test_data), + .expected_malloc_count = 0, + }, + }; + + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + struct iovec *iovecs = test_cases[i].iovecs->iovecs; + const size_t iovecs_count = test_cases[i].iovecs->iovecs_count; + const size_t offset = test_cases[i].offset; + + const size_t expected_send = sizeof(test_data) - offset; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer out = { 0 }, + s2n_ktls_io_stuffer_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer_send(conn, &out)); + + /* Preemptively allocate sendmsg memory to avoid false positives */ + EXPECT_SUCCESS(s2n_stuffer_resize(&out.data_buffer, expected_send)); + EXPECT_SUCCESS(s2n_stuffer_resize(&out.ancillary_buffer, 100)); + + DEFER_CLEANUP(struct s2n_mem_test_cb_scope scope = { 0 }, + s2n_mem_test_free_callbacks); + EXPECT_OK(s2n_mem_test_init_callbacks(&scope)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + ssize_t result = s2n_ktls_sendv_with_offset(conn, iovecs, iovecs_count, + offset, &blocked); + EXPECT_EQUAL(result, sizeof(test_data) - offset); + + size_t malloc_count = test_cases[i].expected_malloc_count; + EXPECT_OK(s2n_mem_test_assert_malloc_count(malloc_count)); + if (malloc_count) { + EXPECT_OK(s2n_mem_test_assert_malloc(test_cases[i].expected_malloc)); + } + EXPECT_OK(s2n_mem_test_assert_all_freed()); + } + }; + }; + + /* Test: s2n_ktls_send_cb */ + { + /* It's safe to reuse a connection across tests because the connection + * isn't actually used by s2n_ktls_send_cb. It's just required for test + * setup methods. + */ + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + /* Safety */ + { + struct s2n_test_ktls_io_stuffer ctx = { 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_ktls_send_cb(NULL, test_data, 1), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_ktls_send_cb(&ctx, NULL, 1), S2N_ERR_NULL); + }; + + /* Test: Basic write succeeds */ + { + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer ctx = { 0 }, + s2n_ktls_io_stuffer_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer_send(conn, &ctx)); + + EXPECT_SUCCESS(s2n_ktls_send_cb(&ctx, test_data, sizeof(test_data))); + EXPECT_EQUAL(ctx.sendmsg_invoked_count, 1); + EXPECT_OK(s2n_test_validate_ancillary(&ctx, TLS_ALERT, sizeof(test_data))); + EXPECT_OK(s2n_test_validate_data(&ctx, test_data, sizeof(test_data))); + }; + + /* Test: Errors passed on to caller */ + { + struct s2n_test_ktls_io_fail_ctx ctx = { 0 }; + EXPECT_OK(s2n_ktls_set_sendmsg_cb(conn, s2n_test_ktls_sendmsg_fail, &ctx)); + + ctx.errno_code = 1; + EXPECT_FAILURE_WITH_ERRNO(s2n_ktls_send_cb(&ctx, test_data, sizeof(test_data)), + S2N_ERR_IO); + + ctx.errno_code = EINVAL; + EXPECT_FAILURE_WITH_ERRNO(s2n_ktls_send_cb(&ctx, test_data, sizeof(test_data)), + S2N_ERR_IO); + + ctx.errno_code = EAGAIN; + EXPECT_FAILURE_WITH_ERRNO(s2n_ktls_send_cb(&ctx, test_data, sizeof(test_data)), + S2N_ERR_IO_BLOCKED); + + ctx.errno_code = EWOULDBLOCK; + EXPECT_FAILURE_WITH_ERRNO(s2n_ktls_send_cb(&ctx, test_data, sizeof(test_data)), + S2N_ERR_IO_BLOCKED); + }; + }; + + /* Test: s2n_ktls_record_writev */ + { + const size_t to_write = 10; + + /* Safety */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + struct iovec iov = { 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_ktls_record_writev(NULL, 0, &iov, 1, 1, 1), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_ktls_record_writev(conn, 0, NULL, 1, 1, 1), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_ktls_record_writev(conn, 0, &iov, -1, 1, 1), + S2N_ERR_INVALID_ARGUMENT); + }; + + /* Test: Basic write succeeds */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + struct iovec iov = { + .iov_base = test_data, + .iov_len = sizeof(test_data), + }; + EXPECT_EQUAL(s2n_ktls_record_writev(conn, TLS_ALERT, &iov, 1, 0, to_write), to_write); + EXPECT_EQUAL(conn->out.blob.allocated, to_write); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->out), to_write); + uint8_t *in_out = s2n_stuffer_raw_read(&conn->out, to_write); + EXPECT_BYTEARRAY_EQUAL(in_out, test_data, to_write); + }; + + /* Test: Only alerts currently supported */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + struct iovec iov = { + .iov_base = test_data, + .iov_len = sizeof(test_data), + }; + EXPECT_FAILURE_WITH_ERRNO( + s2n_ktls_record_writev(conn, TLS_HANDSHAKE, &iov, 1, 0, to_write), + S2N_ERR_UNIMPLEMENTED); + }; + }; + + /* Test: s2n_ktls_read_full_record */ + { + const struct iovec test_iovec = { + .iov_base = test_data, + .iov_len = sizeof(test_data), + }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + const size_t max_frag_len = S2N_DEFAULT_FRAGMENT_LENGTH; + /* Our test assumptions are wrong if this isn't true */ + EXPECT_TRUE(max_frag_len < sizeof(test_data)); + + /* Safety */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + uint8_t record_type = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_ktls_read_full_record(NULL, &record_type), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_ktls_read_full_record(conn, NULL), + S2N_ERR_NULL); + }; + + /* Test: Basic read succeeds */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; + + size_t written = 0; + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_ALERT, &test_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, sizeof(test_data)); + + uint8_t record_type = 0; + EXPECT_SUCCESS(s2n_ktls_read_full_record(conn, &record_type)); + EXPECT_EQUAL(record_type, TLS_ALERT); + + EXPECT_EQUAL(conn->in.blob.allocated, max_frag_len); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), max_frag_len); + uint8_t *read = s2n_stuffer_raw_read(&conn->in, max_frag_len); + EXPECT_BYTEARRAY_EQUAL(read, test_data, max_frag_len); + }; + + /* Test: Receive does not completely fill the output buffer */ + { + const size_t small_frag_len = 10; + EXPECT_TRUE(small_frag_len < max_frag_len); + EXPECT_TRUE(small_frag_len < sizeof(test_data)); + struct iovec small_test_iovec = test_iovec; + small_test_iovec.iov_len = small_frag_len; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; + + size_t written = 0; + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_ALERT, &small_test_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, small_frag_len); + + uint8_t record_type = 0; + EXPECT_SUCCESS(s2n_ktls_read_full_record(conn, &record_type)); + EXPECT_EQUAL(record_type, TLS_ALERT); + + /* Verify that conn->in reflects the correct size of the "record" + * read and doesn't just assume the maximum read size. + */ + EXPECT_EQUAL(conn->in.blob.allocated, max_frag_len); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), small_frag_len); + uint8_t *read = s2n_stuffer_raw_read(&conn->in, small_frag_len); + EXPECT_BYTEARRAY_EQUAL(read, test_data, small_frag_len); + }; + + /* Test: Receive drains conn->in before calling recvmsg again */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; + + /* Write half the test data into conn->in */ + const size_t offset = sizeof(test_data) / 2; + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->in, test_data, offset)); + + /* Write the other half into a new record */ + size_t written = 0; + struct iovec offset_iovec = { + .iov_base = test_data + offset, + .iov_len = sizeof(test_data) - offset, + }; + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_ALERT, &offset_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, offset_iovec.iov_len); + + uint8_t record_type = 0; + uint8_t *read = NULL; + + /* Verify that our first read returns conn->in, not the new record */ + EXPECT_SUCCESS(s2n_ktls_read_full_record(conn, &record_type)); + EXPECT_EQUAL(record_type, TLS_APPLICATION_DATA); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), offset); + read = s2n_stuffer_raw_read(&conn->in, offset); + EXPECT_BYTEARRAY_EQUAL(read, test_data, offset); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + + /* Verify a second read returns the new record */ + EXPECT_SUCCESS(s2n_ktls_read_full_record(conn, &record_type)); + EXPECT_EQUAL(record_type, TLS_ALERT); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), offset_iovec.iov_len); + read = s2n_stuffer_raw_read(&conn->in, offset_iovec.iov_len); + EXPECT_BYTEARRAY_EQUAL(read, offset_iovec.iov_base, offset_iovec.iov_len); + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_ktls_mode_test.c b/tests/unit/s2n_ktls_mode_test.c new file mode 100644 index 00000000000..04bad8526e2 --- /dev/null +++ b/tests/unit/s2n_ktls_mode_test.c @@ -0,0 +1,38 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_ktls.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Default connection kTLS mode */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + EXPECT_FALSE(conn->ktls_recv_enabled); + EXPECT_FALSE(conn->ktls_send_enabled); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_ktls_test.c b/tests/unit/s2n_ktls_test.c new file mode 100644 index 00000000000..b3294332920 --- /dev/null +++ b/tests/unit/s2n_ktls_test.c @@ -0,0 +1,564 @@ +/* + * 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 "tls/s2n_ktls.h" + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_preferences.h" +#include "tls/s2n_ktls_parameters.h" +#include "utils/s2n_random.h" +#include "utils/s2n_socket.h" + +#define S2N_TEST_SEND_FD 66 +#define S2N_TEST_RECV_FD 55 + +static int s2n_test_setsockopt_noop(int fd, int level, int optname, const void *optval, socklen_t optlen) +{ + return S2N_SUCCESS; +} + +static int s2n_test_setsockopt_tcp_error(int fd, int level, int optname, const void *optval, socklen_t optlen) +{ + if (level == S2N_SOL_TCP) { + errno = EINVAL; + return -1; + } + return 0; +} + +static int s2n_test_setsockopt_tls_error(int fd, int level, int optname, const void *optval, socklen_t optlen) +{ + if (level == S2N_SOL_TLS) { + errno = EINVAL; + return -1; + } + return 0; +} + +static int s2n_test_setsockopt_aes128_tx(int fd, int level, int optname, const void *optval, socklen_t optlen) +{ + POSIX_ENSURE_EQ(fd, S2N_TEST_SEND_FD); + if (level == S2N_SOL_TLS) { + POSIX_ENSURE_EQ(optname, S2N_TLS_TX); + POSIX_ENSURE_EQ(optlen, sizeof(s2n_ktls_crypto_info_tls12_aes_gcm_128)); + } else if (level == S2N_SOL_TCP) { + POSIX_ENSURE_EQ(optname, S2N_TCP_ULP); + POSIX_ENSURE_EQ(optlen, S2N_TLS_ULP_NAME_SIZE); + } else { + POSIX_BAIL(S2N_ERR_SAFETY); + } + return S2N_SUCCESS; +} + +static int s2n_test_setsockopt_aes128_rx(int fd, int level, int optname, const void *optval, socklen_t optlen) +{ + POSIX_ENSURE_EQ(fd, S2N_TEST_RECV_FD); + if (level == S2N_SOL_TLS) { + POSIX_ENSURE_EQ(optname, S2N_TLS_RX); + POSIX_ENSURE_EQ(optlen, sizeof(s2n_ktls_crypto_info_tls12_aes_gcm_128)); + } else if (level == S2N_SOL_TCP) { + POSIX_ENSURE_EQ(optname, S2N_TCP_ULP); + POSIX_ENSURE_EQ(optlen, S2N_TLS_ULP_NAME_SIZE); + } else { + POSIX_BAIL(S2N_ERR_SAFETY); + } + return S2N_SUCCESS; +} + +struct s2n_test_setsockopt_expected_struct { + size_t count; + int fd; + int optname; + struct s2n_ktls_crypto_info_inputs crypto_info_inputs; +} s2n_test_setsockopt_expected; + +static int s2n_test_setsockopt_tls_cb(int fd, int level, + int optname, const void *optval, socklen_t optlen) +{ + if (level == S2N_SOL_TLS) { + POSIX_ENSURE_EQ(0, s2n_test_setsockopt_expected.count); + POSIX_ENSURE_EQ(fd, s2n_test_setsockopt_expected.fd); + POSIX_ENSURE_EQ(optname, s2n_test_setsockopt_expected.optname); + POSIX_ENSURE_EQ(optval, &s2n_test_setsockopt_expected.crypto_info_inputs); + POSIX_ENSURE_EQ(optlen, sizeof(s2n_test_setsockopt_expected.crypto_info_inputs)); + s2n_test_setsockopt_expected.count++; + } + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_test_setsockopt_set_ktls_info(struct s2n_ktls_crypto_info_inputs *inputs, + struct s2n_ktls_crypto_info *crypto_info) +{ + S2N_BLOB_EXPECT_EQUAL(inputs->iv, s2n_test_setsockopt_expected.crypto_info_inputs.iv); + S2N_BLOB_EXPECT_EQUAL(inputs->key, s2n_test_setsockopt_expected.crypto_info_inputs.key); + S2N_BLOB_EXPECT_EQUAL(inputs->seq, s2n_test_setsockopt_expected.crypto_info_inputs.seq); + RESULT_GUARD_POSIX(s2n_blob_init(&crypto_info->value, + (void *) &s2n_test_setsockopt_expected.crypto_info_inputs, + sizeof(s2n_test_setsockopt_expected.crypto_info_inputs))); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_test_configure_connection_for_ktls(struct s2n_connection *conn) +{ + RESULT_ENSURE_REF(conn); + + RESULT_GUARD(s2n_ktls_set_setsockopt_cb(s2n_test_setsockopt_noop)); + + /* config I/O */ + RESULT_GUARD_POSIX(s2n_connection_set_write_fd(conn, S2N_TEST_SEND_FD)); + RESULT_GUARD_POSIX(s2n_connection_set_read_fd(conn, S2N_TEST_RECV_FD)); + conn->ktls_send_enabled = false; + conn->ktls_recv_enabled = false; + + /* set kTLS supported cipher */ + conn->secure->cipher_suite = &s2n_rsa_with_aes_128_gcm_sha256; + conn->actual_protocol_version = S2N_TLS12; + /* configure connection so that the handshake is complete */ + RESULT_GUARD(s2n_skip_handshake(conn)); + + return S2N_RESULT_OK; +} + +static int s2n_test_reneg_cb(struct s2n_connection *conn, void *context, + s2n_renegotiate_response *response) +{ + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_ktls_is_supported_on_platform()) { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_OK(s2n_test_configure_connection_for_ktls(server_conn)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_send(server_conn), S2N_ERR_KTLS_UNSUPPORTED_PLATFORM); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_recv(server_conn), S2N_ERR_KTLS_UNSUPPORTED_PLATFORM); + + END_TEST(); + } + + /* Test set_ktls_info for ciphers */ + { + struct s2n_crypto_parameters crypto_params = { 0 }; + + struct s2n_blob test_iv = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&test_iv, crypto_params.client_implicit_iv, + sizeof(crypto_params.client_implicit_iv))); + EXPECT_OK(s2n_get_public_random_data(&test_iv)); + + struct s2n_blob test_seq = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&test_seq, crypto_params.client_sequence_number, + sizeof(crypto_params.client_sequence_number))); + EXPECT_OK(s2n_get_public_random_data(&test_seq)); + + /* s2n_aes128_gcm */ + { + DEFER_CLEANUP(struct s2n_blob test_key = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&test_key, s2n_aes128_gcm.key_material_size)); + EXPECT_OK(s2n_get_public_random_data(&test_key)); + + struct s2n_ktls_crypto_info_inputs inputs = { + .key = test_key, + .iv = test_iv, + .seq = test_seq, + }; + + struct s2n_ktls_crypto_info crypto_info = { 0 }; + EXPECT_OK(s2n_aes128_gcm.set_ktls_info(&inputs, &crypto_info)); + EXPECT_EQUAL(crypto_info.value.size, sizeof(crypto_info.ciphers.aes_gcm_128)); + EXPECT_EQUAL(crypto_info.value.data, (uint8_t *) &crypto_info.ciphers.aes_gcm_128); + s2n_ktls_crypto_info_tls12_aes_gcm_128 *value = + (s2n_ktls_crypto_info_tls12_aes_gcm_128 *) crypto_info.value.data; + + EXPECT_EQUAL(test_key.size, sizeof(value->key)); + EXPECT_BYTEARRAY_EQUAL(test_key.data, value->key, sizeof(value->key)); + + EXPECT_TRUE(test_iv.size >= sizeof(value->iv)); + EXPECT_BYTEARRAY_EQUAL(test_iv.data, value->iv, sizeof(value->iv)); + + EXPECT_TRUE(test_iv.size >= sizeof(value->salt)); + EXPECT_BYTEARRAY_EQUAL(test_iv.data, value->salt, sizeof(value->salt)); + + EXPECT_TRUE(test_seq.size >= sizeof(value->rec_seq)); + EXPECT_BYTEARRAY_EQUAL(test_seq.data, value->rec_seq, sizeof(value->rec_seq)); + }; + + /* s2n_aes256_gcm */ + { + DEFER_CLEANUP(struct s2n_blob test_key = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&test_key, s2n_aes256_gcm.key_material_size)); + EXPECT_OK(s2n_get_public_random_data(&test_key)); + + struct s2n_ktls_crypto_info_inputs inputs = { + .key = test_key, + .iv = test_iv, + .seq = test_seq, + }; + + struct s2n_ktls_crypto_info crypto_info = { 0 }; + EXPECT_OK(s2n_aes256_gcm.set_ktls_info(&inputs, &crypto_info)); + EXPECT_EQUAL(crypto_info.value.size, sizeof(crypto_info.ciphers.aes_gcm_256)); + EXPECT_EQUAL(crypto_info.value.data, (uint8_t *) &crypto_info.ciphers.aes_gcm_256); + s2n_ktls_crypto_info_tls12_aes_gcm_256 *value = + (s2n_ktls_crypto_info_tls12_aes_gcm_256 *) crypto_info.value.data; + + EXPECT_EQUAL(test_key.size, sizeof(value->key)); + EXPECT_BYTEARRAY_EQUAL(test_key.data, value->key, sizeof(value->key)); + + EXPECT_TRUE(test_iv.size >= sizeof(value->iv)); + EXPECT_BYTEARRAY_EQUAL(test_iv.data, value->iv, sizeof(value->iv)); + + EXPECT_TRUE(test_iv.size >= sizeof(value->salt)); + EXPECT_BYTEARRAY_EQUAL(test_iv.data, value->salt, sizeof(value->salt)); + + EXPECT_TRUE(test_seq.size >= sizeof(value->rec_seq)); + EXPECT_BYTEARRAY_EQUAL(test_seq.data, value->rec_seq, sizeof(value->rec_seq)); + }; + }; + + /* Test s2n_connection_ktls_enable_recv/send */ + { + /* enable TX/RX */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_OK(s2n_test_configure_connection_for_ktls(server_conn)); + + const struct s2n_cipher *cipher = NULL; + EXPECT_OK(s2n_connection_get_secure_cipher(server_conn, &cipher)); + EXPECT_EQUAL(cipher, &s2n_aes128_gcm); + + EXPECT_OK(s2n_ktls_set_setsockopt_cb(s2n_test_setsockopt_aes128_tx)); + EXPECT_SUCCESS(s2n_connection_ktls_enable_send(server_conn)); + EXPECT_TRUE(server_conn->ktls_send_enabled); + + EXPECT_OK(s2n_ktls_set_setsockopt_cb(s2n_test_setsockopt_aes128_rx)); + EXPECT_SUCCESS(s2n_connection_ktls_enable_recv(server_conn)); + EXPECT_TRUE(server_conn->ktls_recv_enabled); + }; + + /* handle setsockopt error for S2N_SOL_TCP */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_OK(s2n_test_configure_connection_for_ktls(server_conn)); + EXPECT_OK(s2n_ktls_set_setsockopt_cb(s2n_test_setsockopt_tcp_error)); + + /* The error does not prevent us from enabling ktls */ + EXPECT_SUCCESS(s2n_connection_ktls_enable_send(server_conn)); + EXPECT_SUCCESS(s2n_connection_ktls_enable_recv(server_conn)); + }; + + /* handle setsockopt error for S2N_SOL_TLS */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_OK(s2n_test_configure_connection_for_ktls(server_conn)); + EXPECT_OK(s2n_ktls_set_setsockopt_cb(s2n_test_setsockopt_tls_error)); + + /* The error prevents us from enabling ktls */ + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_send(server_conn), + S2N_ERR_KTLS_ENABLE); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_recv(server_conn), + S2N_ERR_KTLS_ENABLE); + }; + + /* Noop if kTLS is already enabled */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_OK(s2n_test_configure_connection_for_ktls(server_conn)); + + server_conn->ktls_send_enabled = true; + EXPECT_SUCCESS(s2n_connection_ktls_enable_send(server_conn)); + + server_conn->ktls_recv_enabled = true; + EXPECT_SUCCESS(s2n_connection_ktls_enable_recv(server_conn)); + }; + + /* Fail if handshake is not complete */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_OK(s2n_test_configure_connection_for_ktls(server_conn)); + server_conn->handshake.message_number = 0; + + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_send(server_conn), S2N_ERR_HANDSHAKE_NOT_COMPLETE); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_recv(server_conn), S2N_ERR_HANDSHAKE_NOT_COMPLETE); + }; + + /* Fail if unsupported protocols */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_OK(s2n_test_configure_connection_for_ktls(server_conn)); + + server_conn->actual_protocol_version = S2N_TLS13; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_send(server_conn), S2N_ERR_KTLS_UNSUPPORTED_CONN); + + server_conn->actual_protocol_version = S2N_TLS11; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_send(server_conn), S2N_ERR_KTLS_UNSUPPORTED_CONN); + }; + + /* Fail if unsupported ciphers */ + { + /* set kTLS unsupported cipher */ + struct s2n_cipher ktls_temp_unsupported_cipher = { + .set_ktls_info = NULL, + }; + struct s2n_record_algorithm ktls_temp_unsupported_record_alg = { + .cipher = &ktls_temp_unsupported_cipher, + }; + struct s2n_cipher_suite ktls_temp_unsupported_cipher_suite = { + .record_alg = &ktls_temp_unsupported_record_alg, + }; + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_OK(s2n_test_configure_connection_for_ktls(server_conn)); + + server_conn->secure->cipher_suite = &ktls_temp_unsupported_cipher_suite; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_send(server_conn), S2N_ERR_KTLS_UNSUPPORTED_CONN); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_recv(server_conn), S2N_ERR_KTLS_UNSUPPORTED_CONN); + }; + + /* Fail if buffers are not drained */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_OK(s2n_test_configure_connection_for_ktls(server_conn)); + + uint8_t write_byte = 8; + uint8_t read_byte = 0; + /* write to conn->out buffer and assert error */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&server_conn->out, write_byte)); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_send(server_conn), S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); + /* drain conn->out buffer and assert success case */ + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&server_conn->out, &read_byte, 1)); + EXPECT_SUCCESS(s2n_connection_ktls_enable_send(server_conn)); + + /* write to conn->in buffer and assert error */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&server_conn->in, write_byte)); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_recv(server_conn), S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); + /* drain conn->in buffer and assert success case */ + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&server_conn->in, &read_byte, 1)); + EXPECT_SUCCESS(s2n_connection_ktls_enable_recv(server_conn)); + }; + + /* Fail if not using managed IO for send */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_OK(s2n_test_configure_connection_for_ktls(server_conn)); + + /* expect failure if connection is using custom IO */ + server_conn->managed_send_io = false; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_send(server_conn), S2N_ERR_KTLS_MANAGED_IO); + + /* expect success if connection is NOT using custom IO */ + server_conn->managed_send_io = true; + EXPECT_SUCCESS(s2n_connection_ktls_enable_send(server_conn)); + }; + + /* Fail if not using managed IO for recv */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_OK(s2n_test_configure_connection_for_ktls(server_conn)); + + /* recv managed io */ + server_conn->managed_recv_io = false; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_recv(server_conn), S2N_ERR_KTLS_MANAGED_IO); + + /* expect success if connection is NOT using custom IO */ + server_conn->managed_recv_io = true; + EXPECT_SUCCESS(s2n_connection_ktls_enable_recv(server_conn)); + }; + + /* Fail if renegotiation potentially supported */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_OK(s2n_test_configure_connection_for_ktls(client)); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_OK(s2n_test_configure_connection_for_ktls(server)); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, s2n_test_reneg_cb, NULL)); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_ktls_enable_recv(client), S2N_ERR_KTLS_RENEG); + EXPECT_SUCCESS(s2n_connection_ktls_enable_recv(server)); + } + + /* Call setsockopt correctly to configure tls crypto */ + { + struct s2n_cipher_suite test_cipher_suite = s2n_rsa_with_aes_256_gcm_sha384; + struct s2n_record_algorithm test_record_alg = *test_cipher_suite.record_alg; + test_cipher_suite.record_alg = &test_record_alg; + struct s2n_cipher test_cipher = *test_record_alg.cipher; + test_record_alg.cipher = &test_cipher; + test_cipher.set_ktls_info = s2n_test_setsockopt_set_ktls_info; + + struct s2n_crypto_parameters crypto_params = { 0 }; + crypto_params.cipher_suite = &test_cipher_suite; + + struct s2n_blob server_iv = { 0 }, client_iv = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&server_iv, crypto_params.server_implicit_iv, + sizeof(crypto_params.server_implicit_iv))); + EXPECT_SUCCESS(s2n_blob_init(&client_iv, crypto_params.client_implicit_iv, + sizeof(crypto_params.client_implicit_iv))); + EXPECT_OK(s2n_get_public_random_data(&server_iv)); + EXPECT_OK(s2n_get_public_random_data(&client_iv)); + + struct s2n_blob server_seq = { 0 }, client_seq = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&server_seq, crypto_params.server_sequence_number, + sizeof(crypto_params.server_sequence_number))); + EXPECT_SUCCESS(s2n_blob_init(&client_seq, crypto_params.client_sequence_number, + sizeof(crypto_params.client_sequence_number))); + EXPECT_OK(s2n_get_public_random_data(&server_seq)); + EXPECT_OK(s2n_get_public_random_data(&client_seq)); + + /* Test server */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_OK(s2n_test_configure_connection_for_ktls(server)); + EXPECT_OK(s2n_ktls_set_setsockopt_cb(s2n_test_setsockopt_tls_cb)); + EXPECT_OK(s2n_crypto_parameters_free(&server->secure)); + server->secure = &crypto_params; + + struct s2n_key_material key_material = { 0 }; + EXPECT_OK(s2n_prf_generate_key_material(server, &key_material)); + + /* Test server receive */ + s2n_test_setsockopt_expected = (struct s2n_test_setsockopt_expected_struct){ + .fd = S2N_TEST_RECV_FD, + .optname = S2N_TLS_RX, + .crypto_info_inputs = { + .key = key_material.client_key, + .iv = client_iv, + .seq = client_seq, + }, + }; + EXPECT_SUCCESS(s2n_connection_ktls_enable_recv(server)); + EXPECT_EQUAL(s2n_test_setsockopt_expected.count, 1); + + /* Test server send */ + s2n_test_setsockopt_expected = (struct s2n_test_setsockopt_expected_struct){ + .fd = S2N_TEST_SEND_FD, + .optname = S2N_TLS_TX, + .crypto_info_inputs = { + .key = key_material.server_key, + .iv = server_iv, + .seq = server_seq, + }, + }; + EXPECT_SUCCESS(s2n_connection_ktls_enable_send(server)); + EXPECT_EQUAL(s2n_test_setsockopt_expected.count, 1); + server->secure = NULL; + }; + + /* Test client */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_OK(s2n_test_configure_connection_for_ktls(client)); + EXPECT_OK(s2n_ktls_set_setsockopt_cb(s2n_test_setsockopt_tls_cb)); + EXPECT_OK(s2n_crypto_parameters_free(&client->secure)); + client->secure = &crypto_params; + + struct s2n_key_material key_material = { 0 }; + EXPECT_OK(s2n_prf_generate_key_material(client, &key_material)); + + /* Test client receive */ + s2n_test_setsockopt_expected = (struct s2n_test_setsockopt_expected_struct){ + .fd = S2N_TEST_RECV_FD, + .optname = S2N_TLS_RX, + .crypto_info_inputs = { + .key = key_material.server_key, + .iv = server_iv, + .seq = server_seq, + }, + }; + EXPECT_SUCCESS(s2n_connection_ktls_enable_recv(client)); + EXPECT_EQUAL(s2n_test_setsockopt_expected.count, 1); + + /* Test client send */ + s2n_test_setsockopt_expected = (struct s2n_test_setsockopt_expected_struct){ + .fd = S2N_TEST_SEND_FD, + .optname = S2N_TLS_TX, + .crypto_info_inputs = { + .key = key_material.client_key, + .iv = client_iv, + .seq = client_seq, + }, + }; + EXPECT_SUCCESS(s2n_connection_ktls_enable_send(client)); + EXPECT_EQUAL(s2n_test_setsockopt_expected.count, 1); + client->secure = NULL; + }; + }; + }; + + /* selftalk: Success case with a real TLS1.2 negotiated server and client */ + { + DEFER_CLEANUP(struct s2n_cert_chain_and_key * chain_and_key, + s2n_cert_chain_and_key_ptr_free); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + + /* setup config */ + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20170210")); + EXPECT_OK(s2n_ktls_set_setsockopt_cb(s2n_test_setsockopt_noop)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* setup IO and negotiate */ + DEFER_CLEANUP(struct s2n_test_io_pair test_io_pair = { 0 }, + s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&test_io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &test_io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + + /* enable kTLS send */ + EXPECT_SUCCESS(s2n_connection_ktls_enable_send(server_conn)); + EXPECT_TRUE(server_conn->ktls_send_enabled); + EXPECT_NOT_EQUAL(server_conn->send, s2n_socket_write); + + /* enable kTLS recv */ + EXPECT_SUCCESS(s2n_connection_ktls_enable_recv(server_conn)); + EXPECT_TRUE(server_conn->ktls_recv_enabled); + EXPECT_NOT_EQUAL(server_conn->recv, s2n_socket_read); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_ktls_test_utils_test.c b/tests/unit/s2n_ktls_test_utils_test.c new file mode 100644 index 00000000000..965ba6437bf --- /dev/null +++ b/tests/unit/s2n_ktls_test_utils_test.c @@ -0,0 +1,634 @@ +/* + * 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 "testlib/s2n_ktls_test_utils.h" + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_random.h" + +#define S2N_TEST_TO_SEND 10 +#define S2N_CONTROL_BUF_SIZE 100 +#define S2N_TEST_MSG_IOVLEN 5 + +S2N_RESULT s2n_ktls_set_control_data(struct msghdr *msg, char *buf, size_t buf_size, + int cmsg_type, uint8_t record_type); +S2N_RESULT s2n_ktls_get_control_data(struct msghdr *msg, int cmsg_type, uint8_t *record_type); + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + const uint8_t test_record_type = 43; + /* test data */ + uint8_t test_data[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 }; + struct s2n_blob test_data_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&test_data_blob, test_data, sizeof(test_data))); + EXPECT_OK(s2n_get_public_random_data(&test_data_blob)); + + /* Test the sendmsg mock IO stuffer implementation */ + { + /* Happy case: server sends a single record */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair io_pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(server, client, &io_pair)); + + struct iovec send_msg_iov = { .iov_base = test_data, .iov_len = S2N_TEST_TO_SEND }; + struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1 }; + char control_buf[S2N_CONTROL_BUF_SIZE] = { 0 }; + EXPECT_OK(s2n_ktls_set_control_data(&send_msg, control_buf, sizeof(control_buf), + S2N_TLS_SET_RECORD_TYPE, test_record_type)); + ssize_t bytes_written = s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg); + EXPECT_EQUAL(bytes_written, S2N_TEST_TO_SEND); + + /* confirm sent data */ + EXPECT_OK(s2n_test_validate_data(&io_pair.client_in, test_data, S2N_TEST_TO_SEND)); + EXPECT_OK(s2n_test_validate_ancillary(&io_pair.client_in, test_record_type, S2N_TEST_TO_SEND)); + + EXPECT_EQUAL(io_pair.client_in.sendmsg_invoked_count, 1); + EXPECT_EQUAL(io_pair.server_in.sendmsg_invoked_count, 0); + }; + + /* Happy case: client sends a single record */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair io_pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(server, client, &io_pair)); + + struct iovec send_msg_iov = { .iov_base = test_data, .iov_len = S2N_TEST_TO_SEND }; + struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1 }; + char control_buf[S2N_CONTROL_BUF_SIZE] = { 0 }; + EXPECT_OK(s2n_ktls_set_control_data(&send_msg, control_buf, sizeof(control_buf), + S2N_TLS_SET_RECORD_TYPE, test_record_type)); + ssize_t bytes_written = s2n_test_ktls_sendmsg_io_stuffer(client->send_io_context, &send_msg); + EXPECT_EQUAL(bytes_written, S2N_TEST_TO_SEND); + + /* confirm sent data */ + EXPECT_OK(s2n_test_validate_data(&io_pair.server_in, test_data, S2N_TEST_TO_SEND)); + EXPECT_OK(s2n_test_validate_ancillary(&io_pair.server_in, test_record_type, S2N_TEST_TO_SEND)); + + EXPECT_EQUAL(io_pair.client_in.sendmsg_invoked_count, 0); + EXPECT_EQUAL(io_pair.server_in.sendmsg_invoked_count, 1); + }; + + /* Send 0 bytes */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair io_pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(server, client, &io_pair)); + + size_t send_zero = 0; + struct iovec send_msg_iov = { .iov_base = test_data, .iov_len = send_zero }; + struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1 }; + char control_buf[S2N_CONTROL_BUF_SIZE] = { 0 }; + EXPECT_OK(s2n_ktls_set_control_data(&send_msg, control_buf, sizeof(control_buf), + S2N_TLS_SET_RECORD_TYPE, test_record_type)); + ssize_t bytes_written = s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg); + EXPECT_EQUAL(bytes_written, send_zero); + + /* confirm no records were sent */ + EXPECT_EQUAL(s2n_stuffer_data_available(&io_pair.client_in.ancillary_buffer), 0); + + EXPECT_EQUAL(io_pair.client_in.sendmsg_invoked_count, 1); + }; + + /* Send msg_iovlen > 1 */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair io_pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(server, client, &io_pair)); + + size_t total_sent = 0; + struct iovec send_msg_iov[S2N_TEST_MSG_IOVLEN] = { 0 }; + for (size_t i = 0; i < S2N_TEST_MSG_IOVLEN; i++) { + send_msg_iov[i].iov_base = test_data + total_sent; + send_msg_iov[i].iov_len = S2N_TEST_TO_SEND; + + total_sent += S2N_TEST_TO_SEND; + } + struct msghdr send_msg = { .msg_iov = send_msg_iov, .msg_iovlen = S2N_TEST_MSG_IOVLEN }; + char control_buf[S2N_CONTROL_BUF_SIZE] = { 0 }; + EXPECT_OK(s2n_ktls_set_control_data(&send_msg, control_buf, sizeof(control_buf), + S2N_TLS_SET_RECORD_TYPE, test_record_type)); + ssize_t bytes_written = s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg); + EXPECT_EQUAL(bytes_written, total_sent); + + /* confirm sent data */ + EXPECT_OK(s2n_test_validate_data(&io_pair.client_in, test_data, total_sent)); + EXPECT_OK(s2n_test_validate_ancillary(&io_pair.client_in, test_record_type, total_sent)); + /* validate only 1 record was sent */ + EXPECT_EQUAL(s2n_stuffer_data_available(&io_pair.client_in.ancillary_buffer), S2N_TEST_KTLS_MOCK_HEADER_SIZE); + EXPECT_EQUAL(io_pair.client_in.sendmsg_invoked_count, 1); + }; + + /* Send multiple records of same type */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair io_pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(server, client, &io_pair)); + + size_t records_to_send = 5; + struct iovec send_msg_iov = { .iov_len = S2N_TEST_TO_SEND }; + struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1 }; + char control_buf[S2N_CONTROL_BUF_SIZE] = { 0 }; + + size_t total_sent = 0; + for (size_t i = 0; i < records_to_send; i++) { + /* increment test data ptr */ + send_msg_iov.iov_base = test_data + total_sent; + + EXPECT_OK(s2n_ktls_set_control_data(&send_msg, control_buf, sizeof(control_buf), + S2N_TLS_SET_RECORD_TYPE, test_record_type)); + ssize_t bytes_written = s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg); + EXPECT_EQUAL(bytes_written, S2N_TEST_TO_SEND); + total_sent += bytes_written; + } + + /* confirm sent data */ + EXPECT_OK(s2n_test_validate_data(&io_pair.client_in, test_data, total_sent)); + /* validate `records_to_send` records were sent */ + EXPECT_EQUAL(s2n_stuffer_data_available(&io_pair.client_in.ancillary_buffer), records_to_send * S2N_TEST_KTLS_MOCK_HEADER_SIZE); + /* validate ancillary header */ + for (size_t i = 0; i < records_to_send; i++) { + EXPECT_OK(s2n_test_validate_ancillary(&io_pair.client_in, test_record_type, S2N_TEST_TO_SEND)); + /* consume the header in order to then validate the next header */ + EXPECT_NOT_NULL(s2n_stuffer_raw_read(&io_pair.client_in.ancillary_buffer, S2N_TEST_KTLS_MOCK_HEADER_SIZE)); + } + + EXPECT_EQUAL(io_pair.client_in.sendmsg_invoked_count, records_to_send); + }; + + /* Send multiple records of different types */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair io_pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(server, client, &io_pair)); + + size_t records_to_send = 5; + struct iovec send_msg_iov = { .iov_len = S2N_TEST_TO_SEND }; + struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1 }; + char control_buf[S2N_CONTROL_BUF_SIZE] = { 0 }; + + size_t total_sent = 0; + for (size_t i = 0; i < records_to_send; i++) { + /* increment test data ptr */ + send_msg_iov.iov_base = test_data + total_sent; + + EXPECT_OK(s2n_ktls_set_control_data(&send_msg, control_buf, sizeof(control_buf), + S2N_TLS_SET_RECORD_TYPE, i)); + ssize_t bytes_written = s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg); + EXPECT_EQUAL(bytes_written, S2N_TEST_TO_SEND); + total_sent += bytes_written; + } + + /* confirm sent data */ + EXPECT_OK(s2n_test_validate_data(&io_pair.client_in, test_data, total_sent)); + /* validate `records_to_send` records were sent */ + EXPECT_EQUAL(s2n_stuffer_data_available(&io_pair.client_in.ancillary_buffer), records_to_send * S2N_TEST_KTLS_MOCK_HEADER_SIZE); + /* validate ancillary header */ + for (size_t i = 0; i < records_to_send; i++) { + EXPECT_OK(s2n_test_validate_ancillary(&io_pair.client_in, i, S2N_TEST_TO_SEND)); + /* consume the header in order to then validate the next header */ + EXPECT_NOT_NULL(s2n_stuffer_raw_read(&io_pair.client_in.ancillary_buffer, S2N_TEST_KTLS_MOCK_HEADER_SIZE)); + } + + EXPECT_EQUAL(io_pair.client_in.sendmsg_invoked_count, records_to_send); + }; + + /* Attempt send and expect EAGAIN error */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair io_pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(server, client, &io_pair)); + /* disable growable to simulate blocked/network buffer full */ + io_pair.client_in.data_buffer.growable = false; + + size_t to_send = 1; + struct iovec send_msg_iov = { .iov_base = test_data, .iov_len = to_send }; + struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1 }; + char control_buf[S2N_CONTROL_BUF_SIZE] = { 0 }; + + /* attempt sendmsg and expect EAGAIN */ + size_t blocked_invoked_count = 5; + for (size_t i = 0; i < blocked_invoked_count; i++) { + EXPECT_OK(s2n_ktls_set_control_data(&send_msg, control_buf, sizeof(control_buf), + S2N_TLS_SET_RECORD_TYPE, test_record_type)); + EXPECT_EQUAL(s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg), S2N_FAILURE); + EXPECT_EQUAL(errno, EAGAIN); + } + + /* enable growable to unblock write */ + /* cppcheck-suppress redundantAssignment */ + io_pair.client_in.data_buffer.growable = true; + /* attempt sendmsg again and expect success */ + ssize_t bytes_written = s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg); + EXPECT_EQUAL(bytes_written, to_send); + + /* confirm sent data */ + EXPECT_OK(s2n_test_validate_data(&io_pair.client_in, test_data, to_send)); + EXPECT_OK(s2n_test_validate_ancillary(&io_pair.client_in, test_record_type, to_send)); + /* validate only 1 record was sent */ + EXPECT_EQUAL(s2n_stuffer_data_available(&io_pair.client_in.ancillary_buffer), S2N_TEST_KTLS_MOCK_HEADER_SIZE); + + EXPECT_EQUAL(io_pair.client_in.sendmsg_invoked_count, blocked_invoked_count + 1); + }; + + /* Partial write with iov_len > 1 */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair io_pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(server, client, &io_pair)); + /* disable growable and alloc enough space for only 1 iov buffer */ + io_pair.client_in.data_buffer.growable = false; + EXPECT_SUCCESS(s2n_stuffer_alloc(&io_pair.client_in.data_buffer, S2N_TEST_TO_SEND)); + + uint8_t *test_data_ptr = test_data; + struct iovec send_msg_iov[S2N_TEST_MSG_IOVLEN] = { 0 }; + for (size_t i = 0; i < S2N_TEST_MSG_IOVLEN; i++) { + send_msg_iov[i].iov_base = (void *) test_data_ptr; + send_msg_iov[i].iov_len = S2N_TEST_TO_SEND; + test_data_ptr += S2N_TEST_TO_SEND; + } + + struct msghdr send_msg = { .msg_iov = send_msg_iov, .msg_iovlen = S2N_TEST_MSG_IOVLEN }; + char control_buf[S2N_CONTROL_BUF_SIZE] = { 0 }; + EXPECT_OK(s2n_ktls_set_control_data(&send_msg, control_buf, sizeof(control_buf), + S2N_TLS_SET_RECORD_TYPE, test_record_type)); + EXPECT_EQUAL(s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg), + S2N_TEST_TO_SEND); + + EXPECT_EQUAL(io_pair.client_in.sendmsg_invoked_count, 1); + EXPECT_OK(s2n_test_validate_ancillary(&io_pair.client_in, test_record_type, S2N_TEST_TO_SEND)); + EXPECT_OK(s2n_test_validate_data(&io_pair.client_in, test_data, S2N_TEST_TO_SEND)); + }; + }; + + /* Test the recvmsg mock IO stuffer implementation */ + { + /* Happy case: test send/recv non-zero values. Sending 0 is a special case and tested separately */ + for (size_t to_send = 1; to_send < S2N_TLS_MAXIMUM_FRAGMENT_LENGTH; to_send++) { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair io_pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(server, client, &io_pair)); + + struct iovec send_msg_iov = { .iov_base = test_data, .iov_len = to_send }; + struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1 }; + char send_ctrl_buf[S2N_CONTROL_BUF_SIZE] = { 0 }; + EXPECT_OK(s2n_ktls_set_control_data(&send_msg, send_ctrl_buf, sizeof(send_ctrl_buf), + S2N_TLS_SET_RECORD_TYPE, test_record_type)); + ssize_t bytes_written = s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg); + EXPECT_EQUAL(bytes_written, to_send); + + uint8_t recv_buffer[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 }; + char recv_ctrl_buf[S2N_CONTROL_BUF_SIZE] = { 0 }; + struct iovec recv_msg_iov = { .iov_base = recv_buffer, .iov_len = to_send }; + struct msghdr recv_msg = { + .msg_iov = &recv_msg_iov, + .msg_iovlen = 1, + .msg_control = recv_ctrl_buf, + .msg_controllen = sizeof(recv_ctrl_buf), + }; + ssize_t bytes_read = s2n_test_ktls_recvmsg_io_stuffer(client->recv_io_context, &recv_msg); + EXPECT_EQUAL(bytes_read, to_send); + /* confirm read data */ + EXPECT_BYTEARRAY_EQUAL(test_data, recv_buffer, to_send); + uint8_t recv_record_type = 0; + EXPECT_OK(s2n_ktls_get_control_data(&recv_msg, S2N_TLS_GET_RECORD_TYPE, &recv_record_type)); + EXPECT_EQUAL(recv_record_type, test_record_type); + + EXPECT_EQUAL(io_pair.client_in.sendmsg_invoked_count, 1); + EXPECT_EQUAL(io_pair.client_in.recvmsg_invoked_count, 1); + }; + + /* Attempt read and expect EAGAIN error */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair io_pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(server, client, &io_pair)); + + size_t to_send = 1; + uint8_t recv_buffer[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 }; + struct iovec recv_msg_iov = { .iov_base = recv_buffer, .iov_len = to_send }; + char recv_ctrl_buf[S2N_CONTROL_BUF_SIZE] = { 0 }; + struct msghdr recv_msg = { + .msg_iov = &recv_msg_iov, + .msg_iovlen = 1, + .msg_control = recv_ctrl_buf, + .msg_controllen = sizeof(recv_ctrl_buf), + }; + /* attempting to recv data when nothing has been sent blocks */ + EXPECT_EQUAL(s2n_test_ktls_recvmsg_io_stuffer(client->recv_io_context, &recv_msg), S2N_FAILURE); + EXPECT_EQUAL(errno, EAGAIN); + + struct iovec send_msg_iov = { .iov_base = test_data, .iov_len = to_send }; + struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1 }; + char send_ctrl_buf[S2N_CONTROL_BUF_SIZE] = { 0 }; + EXPECT_OK(s2n_ktls_set_control_data(&send_msg, send_ctrl_buf, sizeof(send_ctrl_buf), + S2N_TLS_SET_RECORD_TYPE, test_record_type)); + ssize_t bytes_written = s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg); + EXPECT_EQUAL(bytes_written, to_send); + + /* recv all the sent data */ + ssize_t bytes_read = 0; + bytes_read = s2n_test_ktls_recvmsg_io_stuffer(client->recv_io_context, &recv_msg); + EXPECT_EQUAL(bytes_read, to_send); + /* confirm read data */ + EXPECT_BYTEARRAY_EQUAL(test_data, recv_buffer, to_send); + uint8_t recv_record_type = 0; + EXPECT_OK(s2n_ktls_get_control_data(&recv_msg, S2N_TLS_GET_RECORD_TYPE, &recv_record_type)); + EXPECT_EQUAL(recv_record_type, test_record_type); + + size_t blocked_invoked_count = 5; + for (size_t i = 0; i < blocked_invoked_count; i++) { + /* attempting to recv more data blocks */ + EXPECT_EQUAL(s2n_test_ktls_recvmsg_io_stuffer(client->recv_io_context, &recv_msg), S2N_FAILURE); + EXPECT_EQUAL(errno, EAGAIN); + } + + EXPECT_EQUAL(io_pair.client_in.sendmsg_invoked_count, 1); + EXPECT_EQUAL(io_pair.client_in.recvmsg_invoked_count, blocked_invoked_count + 2); + }; + + /* Read partial data: request < total sent */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair io_pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(server, client, &io_pair)); + + size_t to_send = 10; + size_t to_recv = 3; + size_t remaining_len = to_send - to_recv; + + struct iovec send_msg_iov = { .iov_base = test_data, .iov_len = to_send }; + struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1 }; + char send_ctrl_buf[S2N_CONTROL_BUF_SIZE] = { 0 }; + EXPECT_OK(s2n_ktls_set_control_data(&send_msg, send_ctrl_buf, sizeof(send_ctrl_buf), + S2N_TLS_SET_RECORD_TYPE, test_record_type)); + ssize_t bytes_written = s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg); + EXPECT_EQUAL(bytes_written, to_send); + + uint8_t recv_buffer[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 }; + char recv_ctrl_buf[S2N_CONTROL_BUF_SIZE] = { 0 }; + struct iovec recv_msg_iov = { .iov_base = recv_buffer, .iov_len = to_recv }; + struct msghdr recv_msg = { + .msg_iov = &recv_msg_iov, + .msg_iovlen = 1, + .msg_control = recv_ctrl_buf, + .msg_controllen = sizeof(recv_ctrl_buf), + }; + ssize_t bytes_read = s2n_test_ktls_recvmsg_io_stuffer(client->recv_io_context, &recv_msg); + EXPECT_EQUAL(bytes_read, to_recv); + /* confirm read data */ + EXPECT_BYTEARRAY_EQUAL(test_data, recv_buffer, to_recv); + uint8_t recv_record_type_1 = 0; + EXPECT_OK(s2n_ktls_get_control_data(&recv_msg, S2N_TLS_GET_RECORD_TYPE, &recv_record_type_1)); + EXPECT_EQUAL(recv_record_type_1, test_record_type); + + /* confirm that a single records still exists; data len is updated on partial reads */ + EXPECT_EQUAL(s2n_stuffer_data_available(&io_pair.client_in.ancillary_buffer), S2N_TEST_KTLS_MOCK_HEADER_SIZE); + + /* offset and recv remaining data of the same record type */ + recv_msg_iov.iov_base = recv_buffer + to_recv; + recv_msg_iov.iov_len = remaining_len; + bytes_read = s2n_test_ktls_recvmsg_io_stuffer(client->recv_io_context, &recv_msg); + EXPECT_EQUAL(bytes_read, remaining_len); + /* confirm read data */ + uint8_t recv_record_type_2 = 0; + EXPECT_OK(s2n_ktls_get_control_data(&recv_msg, S2N_TLS_GET_RECORD_TYPE, &recv_record_type_2)); + EXPECT_EQUAL(recv_record_type_2, test_record_type); + + /* validate all sent/recv data */ + EXPECT_BYTEARRAY_EQUAL(test_data, recv_buffer, to_send); + /* confirm no more records are available for reading */ + EXPECT_EQUAL(s2n_stuffer_data_available(&io_pair.client_in.ancillary_buffer), 0); + + EXPECT_EQUAL(io_pair.client_in.sendmsg_invoked_count, 1); + EXPECT_EQUAL(io_pair.client_in.recvmsg_invoked_count, 2); + }; + + /* Read partial data: request > total sent */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair io_pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(server, client, &io_pair)); + + size_t to_send = 10; + size_t to_recv = 15; + + struct iovec send_msg_iov = { .iov_base = test_data, .iov_len = to_send }; + struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1 }; + char send_ctrl_buf[S2N_CONTROL_BUF_SIZE] = { 0 }; + EXPECT_OK(s2n_ktls_set_control_data(&send_msg, send_ctrl_buf, sizeof(send_ctrl_buf), + S2N_TLS_SET_RECORD_TYPE, test_record_type)); + ssize_t bytes_written = s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg); + EXPECT_EQUAL(bytes_written, to_send); + + uint8_t recv_buffer[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 }; + struct iovec recv_msg_iov = { .iov_base = recv_buffer, .iov_len = to_recv }; + char recv_ctrl_buf[S2N_CONTROL_BUF_SIZE] = { 0 }; + struct msghdr recv_msg = { + .msg_iov = &recv_msg_iov, + .msg_iovlen = 1, + .msg_control = recv_ctrl_buf, + .msg_controllen = sizeof(recv_ctrl_buf), + }; + ssize_t bytes_read = s2n_test_ktls_recvmsg_io_stuffer(client->recv_io_context, &recv_msg); + + /* confirm read data: minimum of sent and requested (to_send) */ + EXPECT_EQUAL(bytes_read, to_send); + EXPECT_BYTEARRAY_EQUAL(test_data, recv_buffer, to_send); + uint8_t recv_record_type = 0; + EXPECT_OK(s2n_ktls_get_control_data(&recv_msg, S2N_TLS_GET_RECORD_TYPE, &recv_record_type)); + EXPECT_EQUAL(recv_record_type, test_record_type); + + EXPECT_EQUAL(io_pair.client_in.sendmsg_invoked_count, 1); + EXPECT_EQUAL(io_pair.client_in.recvmsg_invoked_count, 1); + }; + + /* Read coalesced records of same type */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair io_pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(server, client, &io_pair)); + + /* send 2 records and recv one. send and recv total of 10 bytes */ + size_t records_to_send = 2; + size_t to_send = 5; + size_t to_recv = 10; + + struct iovec send_msg_iov = { .iov_base = test_data, .iov_len = to_send }; + struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1 }; + char send_ctrl_buf[S2N_CONTROL_BUF_SIZE] = { 0 }; + EXPECT_OK(s2n_ktls_set_control_data(&send_msg, send_ctrl_buf, sizeof(send_ctrl_buf), + S2N_TLS_SET_RECORD_TYPE, test_record_type)); + size_t total_sent = 0; + for (size_t i = 0; i < records_to_send; i++) { + /* increment test data ptr */ + send_msg_iov.iov_base = test_data + total_sent; + + ssize_t bytes_written = s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg); + EXPECT_EQUAL(bytes_written, to_send); + total_sent += bytes_written; + } + + uint8_t recv_buffer[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 }; + char recv_ctrl_buf[S2N_CONTROL_BUF_SIZE] = { 0 }; + struct iovec recv_msg_iov = { .iov_base = recv_buffer, .iov_len = to_recv }; + struct msghdr recv_msg = { + .msg_iov = &recv_msg_iov, + .msg_iovlen = 1, + .msg_control = recv_ctrl_buf, + .msg_controllen = sizeof(recv_ctrl_buf), + }; + ssize_t bytes_read = s2n_test_ktls_recvmsg_io_stuffer(client->recv_io_context, &recv_msg); + EXPECT_EQUAL(bytes_read, to_recv); + uint8_t recv_record_type = 0; + EXPECT_OK(s2n_ktls_get_control_data(&recv_msg, S2N_TLS_GET_RECORD_TYPE, &recv_record_type)); + EXPECT_EQUAL(recv_record_type, test_record_type); + + /* validate all data was received */ + EXPECT_EQUAL(bytes_read, total_sent); + EXPECT_BYTEARRAY_EQUAL(test_data, recv_buffer, bytes_read); + + EXPECT_EQUAL(io_pair.client_in.sendmsg_invoked_count, 2); + EXPECT_EQUAL(io_pair.client_in.recvmsg_invoked_count, 1); + }; + + /* Read doesn't coalesce records of different types */ + { + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair io_pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(server, client, &io_pair)); + + /* send 2 records of different types and recv 2 records in separate calls. send and recv total of 10 bytes */ + uint8_t record_type_1 = 1; + uint8_t record_type_2 = 2; + size_t to_send = 5; + size_t to_recv = 10; + size_t total_sent = 0; + size_t total_recv = 0; + + struct iovec send_msg_iov = { .iov_base = test_data, .iov_len = to_send }; + struct msghdr send_msg = { .msg_iov = &send_msg_iov, .msg_iovlen = 1 }; + char send_ctrl_buf[S2N_CONTROL_BUF_SIZE] = { 0 }; + /* sendmsg record_type_1 */ + EXPECT_OK(s2n_ktls_set_control_data(&send_msg, send_ctrl_buf, sizeof(send_ctrl_buf), + S2N_TLS_SET_RECORD_TYPE, record_type_1)); + ssize_t bytes_written = s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg); + EXPECT_EQUAL(bytes_written, to_send); + total_sent += bytes_written; + /* sendmsg record_type_2 */ + EXPECT_OK(s2n_ktls_set_control_data(&send_msg, send_ctrl_buf, sizeof(send_ctrl_buf), + S2N_TLS_SET_RECORD_TYPE, record_type_2)); + send_msg_iov.iov_base = test_data + total_sent; + bytes_written = s2n_test_ktls_sendmsg_io_stuffer(server->send_io_context, &send_msg); + EXPECT_EQUAL(bytes_written, to_send); + total_sent += bytes_written; + + uint8_t recv_buffer[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 }; + char recv_ctrl_buf[S2N_CONTROL_BUF_SIZE] = { 0 }; + struct iovec recv_msg_iov = { .iov_base = recv_buffer, .iov_len = to_recv }; + struct msghdr recv_msg = { + .msg_iov = &recv_msg_iov, + .msg_iovlen = 1, + .msg_control = recv_ctrl_buf, + .msg_controllen = sizeof(recv_ctrl_buf), + }; + /* only recv record_type_1 even though we request more data */ + ssize_t bytes_read = s2n_test_ktls_recvmsg_io_stuffer(client->recv_io_context, &recv_msg); + EXPECT_EQUAL(bytes_read, to_send); + uint8_t recv_record_type_1 = 0; + EXPECT_OK(s2n_ktls_get_control_data(&recv_msg, S2N_TLS_GET_RECORD_TYPE, &recv_record_type_1)); + EXPECT_EQUAL(recv_record_type_1, record_type_1); + total_recv += bytes_read; + /* only recv record_type_2; which is all that remains */ + recv_msg_iov.iov_base = recv_buffer + bytes_read; + bytes_read = s2n_test_ktls_recvmsg_io_stuffer(client->recv_io_context, &recv_msg); + EXPECT_EQUAL(bytes_read, to_send); + uint8_t recv_record_type_2 = 0; + EXPECT_OK(s2n_ktls_get_control_data(&recv_msg, S2N_TLS_GET_RECORD_TYPE, &recv_record_type_2)); + EXPECT_EQUAL(recv_record_type_2, record_type_2); + total_recv += bytes_read; + + /* validate all data was received (we offset the test_data/recv_buffer so the + * data ends up contiguous and easier to validate) */ + EXPECT_EQUAL(s2n_stuffer_data_available(&io_pair.client_in.ancillary_buffer), 0); + EXPECT_EQUAL(total_recv, total_sent); + EXPECT_BYTEARRAY_EQUAL(test_data, recv_buffer, total_sent); + + EXPECT_EQUAL(io_pair.client_in.sendmsg_invoked_count, 2); + EXPECT_EQUAL(io_pair.client_in.recvmsg_invoked_count, 2); + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_locking_test.c b/tests/unit/s2n_locking_test.c new file mode 100644 index 00000000000..4ae66ed6114 --- /dev/null +++ b/tests/unit/s2n_locking_test.c @@ -0,0 +1,124 @@ +/* + * 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 "crypto/s2n_locking.h" + +#include + +#include "s2n_test.h" + +#define LOCK_N 1 + +#if !(S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0)) + +static void *s2n_test_thread(void *arg) +{ + bool *lock_was_acquired = (bool *) arg; + CRYPTO_lock(CRYPTO_LOCK, LOCK_N, NULL, 0); + *lock_was_acquired = true; + CRYPTO_lock(CRYPTO_UNLOCK, LOCK_N, NULL, 0); + return NULL; +} + +static void s2n_test_locking_cb(int mode, int n, char *file, int line) +{ + return; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Test: libcrypto locks should actually lock. + * + * If libcrypto locking is not configured properly, then + * libcrypto locks will be no-ops and will not block threads. + */ + { + /* Lock one of the libcrypto locks */ + CRYPTO_lock(CRYPTO_LOCK, LOCK_N, NULL, 0); + + /* Create a new thread which will try to take the lock held by this thread + * in order to set a flag. + */ + bool lock_was_acquired = false; + pthread_t thread = { 0 }; + EXPECT_EQUAL(pthread_create(&thread, NULL, s2n_test_thread, &lock_was_acquired), 0); + + /* Expect the flag NOT to be set because we still hold the lock */ + sleep(1); + EXPECT_FALSE(lock_was_acquired); + + /* Release the libcrypto lock */ + CRYPTO_lock(CRYPTO_UNLOCK, LOCK_N, NULL, 0); + + /* Expect the flag to be set because the new thread took the lock */ + void *retval = NULL; + EXPECT_EQUAL(pthread_join(thread, &retval), 0); + EXPECT_TRUE(lock_was_acquired); + }; + + /* Test: basic lifecycle */ + { + EXPECT_OK(s2n_locking_cleanup()); + EXPECT_OK(s2n_locking_init()); + }; + + /* Test: s2n-tls should not override locking configured by the application */ + { + /* The callback should have already been set by BEGIN_TEST() + * or a later call to s2n_locking_init(). + */ + EXPECT_NOT_EQUAL(CRYPTO_get_locking_callback(), NULL); + + /* Manually override the existing callback. + * Applications might set their callback after calling s2n_init. + */ + CRYPTO_set_locking_callback((void (*)()) s2n_test_locking_cb); + EXPECT_EQUAL(CRYPTO_get_locking_callback(), (void (*)()) s2n_test_locking_cb); + + /* Cleaning up does not affect the application-set callback */ + EXPECT_OK(s2n_locking_cleanup()); + EXPECT_EQUAL(CRYPTO_get_locking_callback(), (void (*)()) s2n_test_locking_cb); + + /* Initializing again doesn't override the application-set callback. + * Applications might set their callback before calling s2n_init. + */ + EXPECT_OK(s2n_locking_init()); + EXPECT_EQUAL(CRYPTO_get_locking_callback(), (void (*)()) s2n_test_locking_cb); + EXPECT_OK(s2n_locking_cleanup()); + EXPECT_EQUAL(CRYPTO_get_locking_callback(), (void (*)()) s2n_test_locking_cb); + + /* Reset the callback */ + CRYPTO_set_locking_callback(NULL); + EXPECT_EQUAL(CRYPTO_get_locking_callback(), NULL); + + /* Initializing now sets the missing callback */ + EXPECT_OK(s2n_locking_init()); + EXPECT_NOT_EQUAL(CRYPTO_get_locking_callback(), NULL); + }; + + END_TEST(); +} + +#else + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + END_TEST(); +} + +#endif diff --git a/tests/unit/s2n_malformed_handshake_test.c b/tests/unit/s2n_malformed_handshake_test.c new file mode 100644 index 00000000000..78f636aa80d --- /dev/null +++ b/tests/unit/s2n_malformed_handshake_test.c @@ -0,0 +1,539 @@ +/* + * 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 +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" + +#define TLS_ALERT 21 +#define TLS_HANDSHAKE 22 +#define TLS_HEARTBEAT 24 + +#define ZERO_TO_THIRTY_ONE 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, \ + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F + +static uint8_t zero_to_thirty_one[] = { ZERO_TO_THIRTY_ONE }; + +static uint8_t server_hello_message[] = { + /* SERVER HELLO */ + 0x02, + + /* Length */ + 0x00, 0x00, 0x46, + + /* Protocol version */ + 0x03, 0x03, + + /* Server random */ + ZERO_TO_THIRTY_ONE, + + /* SessionID len - 32 bytes */ + 0x20, + + /* Session ID */ + ZERO_TO_THIRTY_ONE, + + /* Cipher suite - TLS_RSA_WITH_AES_256_CBC_SHA256 */ + 0x00, 0x3D, + + /* Compression method: none */ + 0x00 +}; + +static uint8_t good_certificate_list[] = { + /* SERVER CERT */ + 0x0B, + + /* Length of the handshake message */ + 0x00, 0x03, 0x38, + + /* Length of all certificates */ + 0x00, 0x03, 0x35, + + /* Length of the first cert */ + 0x00, 0x03, 0x32, + + /* Certificate data - via openssl x509 -in cert.pem -outform DER | xxd -i */ + 0x30, 0x82, 0x03, 0x2e, 0x30, 0x82, 0x02, 0x16, 0x02, 0x09, 0x00, 0xcb, + 0xd6, 0x5a, 0xfa, 0x37, 0xcf, 0xe0, 0xbf, 0x30, 0x0d, 0x06, 0x09, 0x2a, + 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x05, 0x05, 0x00, 0x30, 0x59, + 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, 0x13, 0x02, 0x41, + 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, 0x08, 0x13, 0x0a, + 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, 0x65, 0x31, 0x21, + 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, 0x49, 0x6e, 0x74, + 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, 0x67, 0x69, 0x74, + 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, 0x31, 0x12, 0x30, + 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x09, 0x6c, 0x6f, 0x63, 0x61, + 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x30, 0x1e, 0x17, 0x0d, 0x31, 0x34, 0x30, + 0x35, 0x31, 0x30, 0x31, 0x37, 0x30, 0x38, 0x32, 0x33, 0x5a, 0x17, 0x0d, + 0x32, 0x34, 0x30, 0x35, 0x30, 0x37, 0x31, 0x37, 0x30, 0x38, 0x32, 0x33, + 0x5a, 0x30, 0x59, 0x31, 0x0b, 0x30, 0x09, 0x06, 0x03, 0x55, 0x04, 0x06, + 0x13, 0x02, 0x41, 0x55, 0x31, 0x13, 0x30, 0x11, 0x06, 0x03, 0x55, 0x04, + 0x08, 0x13, 0x0a, 0x53, 0x6f, 0x6d, 0x65, 0x2d, 0x53, 0x74, 0x61, 0x74, + 0x65, 0x31, 0x21, 0x30, 0x1f, 0x06, 0x03, 0x55, 0x04, 0x0a, 0x13, 0x18, + 0x49, 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x65, 0x74, 0x20, 0x57, 0x69, 0x64, + 0x67, 0x69, 0x74, 0x73, 0x20, 0x50, 0x74, 0x79, 0x20, 0x4c, 0x74, 0x64, + 0x31, 0x12, 0x30, 0x10, 0x06, 0x03, 0x55, 0x04, 0x03, 0x13, 0x09, 0x6c, + 0x6f, 0x63, 0x61, 0x6c, 0x68, 0x6f, 0x73, 0x74, 0x30, 0x82, 0x01, 0x22, + 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, + 0x01, 0x05, 0x00, 0x03, 0x82, 0x01, 0x0f, 0x00, 0x30, 0x82, 0x01, 0x0a, + 0x02, 0x82, 0x01, 0x01, 0x00, 0xc8, 0x96, 0xd6, 0x94, 0x98, 0x78, 0x3e, + 0x1b, 0xb2, 0x1d, 0x6b, 0x65, 0xc2, 0xb4, 0x44, 0x31, 0xd5, 0x87, 0x96, + 0x0d, 0x7e, 0x35, 0x53, 0x6c, 0xc1, 0x29, 0xb6, 0x34, 0x95, 0x3f, 0x9a, + 0xb9, 0x77, 0xfc, 0xd5, 0xc4, 0xf7, 0x75, 0x84, 0xdd, 0x7c, 0x96, 0xe5, + 0x6f, 0xeb, 0xf8, 0x09, 0x35, 0x9a, 0x18, 0xda, 0x1f, 0xa2, 0x07, 0x33, + 0x79, 0xb9, 0xbc, 0x07, 0x6f, 0xce, 0x17, 0xd1, 0x7e, 0x59, 0x69, 0x0a, + 0x00, 0x98, 0x4a, 0xb1, 0x33, 0xc0, 0x13, 0xbf, 0xd2, 0x34, 0x07, 0x62, + 0xe0, 0x4a, 0xaf, 0xe0, 0x57, 0xcd, 0x6d, 0x62, 0xa4, 0x19, 0xbe, 0x31, + 0x69, 0xcc, 0x71, 0x6f, 0x83, 0xc7, 0xd9, 0x73, 0xfd, 0x57, 0x70, 0xa1, + 0x27, 0xa9, 0x4c, 0x48, 0x8d, 0xd5, 0xeb, 0xc1, 0x66, 0x11, 0xfe, 0x24, + 0x70, 0x43, 0x75, 0xe1, 0x5f, 0x2f, 0xb9, 0xf2, 0x02, 0xe4, 0x71, 0x3f, + 0x2d, 0x3e, 0x20, 0x08, 0xf0, 0xc9, 0xe1, 0x47, 0xd4, 0x51, 0xb0, 0x20, + 0x12, 0x14, 0x9e, 0x6d, 0x3e, 0xab, 0xfc, 0xa1, 0x58, 0x07, 0x94, 0xf7, + 0x01, 0xe0, 0xdc, 0xd5, 0x57, 0x67, 0x69, 0xa4, 0x5b, 0x96, 0xb3, 0xfa, + 0x2b, 0x03, 0x38, 0xe6, 0xf4, 0xec, 0xd0, 0x88, 0xb4, 0xf7, 0xf6, 0x2b, + 0x97, 0x30, 0x71, 0x69, 0x33, 0xcc, 0x8c, 0xb1, 0x82, 0x29, 0xaf, 0x09, + 0x32, 0xff, 0x0f, 0x5b, 0x64, 0x74, 0x53, 0xd5, 0x82, 0xa8, 0x79, 0xb3, + 0x04, 0x7f, 0x96, 0xdd, 0x0f, 0x71, 0x3e, 0xb7, 0xe1, 0x08, 0x89, 0xe6, + 0xe0, 0x95, 0xa8, 0x6f, 0xc5, 0xa0, 0x33, 0x53, 0x6e, 0x89, 0x8b, 0xb3, + 0x14, 0x1d, 0x02, 0x35, 0xa4, 0x1c, 0x74, 0xc4, 0xbb, 0x87, 0x46, 0x99, + 0x10, 0x05, 0x67, 0x6b, 0x28, 0x50, 0xf7, 0xaf, 0xcf, 0x69, 0xda, 0x63, + 0x28, 0xd1, 0x34, 0x2e, 0xea, 0xfd, 0x9d, 0x4c, 0x5b, 0x02, 0x03, 0x01, + 0x00, 0x01, 0x30, 0x0d, 0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, + 0x01, 0x01, 0x05, 0x05, 0x00, 0x03, 0x82, 0x01, 0x01, 0x00, 0x64, 0x55, + 0xef, 0x5b, 0x91, 0xb7, 0xfd, 0x5d, 0x00, 0x3b, 0x0c, 0x0f, 0xd7, 0xe0, + 0x26, 0xfc, 0xd6, 0xf3, 0xd8, 0xc5, 0x00, 0xdf, 0x3b, 0x85, 0x70, 0x91, + 0x85, 0x35, 0xb1, 0x7d, 0x78, 0x58, 0x33, 0x39, 0x27, 0xc4, 0x9e, 0x56, + 0x31, 0xbd, 0x89, 0x02, 0x56, 0x8c, 0x73, 0xf8, 0x13, 0xa6, 0x20, 0xe2, + 0x40, 0x19, 0x1b, 0xbc, 0x1f, 0xa2, 0x25, 0xee, 0x40, 0x7a, 0x98, 0x10, + 0x59, 0xbc, 0xb1, 0x3c, 0x93, 0x6d, 0x4a, 0x50, 0x3d, 0x19, 0xf2, 0x81, + 0xcf, 0x52, 0x0d, 0x47, 0x97, 0x05, 0xb0, 0xe2, 0xf6, 0xed, 0x5a, 0xc1, + 0xa0, 0xc6, 0x07, 0x31, 0xaa, 0x25, 0xbd, 0xe7, 0xac, 0x95, 0xcd, 0x40, + 0x5b, 0x61, 0xdf, 0x06, 0xd5, 0xd6, 0x5d, 0xe5, 0x92, 0x10, 0x5e, 0xc5, + 0x40, 0xd8, 0x32, 0x7b, 0xc6, 0x43, 0x3c, 0xdc, 0xde, 0x49, 0x64, 0x88, + 0xd1, 0x5c, 0x8a, 0xdb, 0xbe, 0xb6, 0xc2, 0xc4, 0xe0, 0x4e, 0xe5, 0x21, + 0x1c, 0x06, 0x89, 0xe3, 0x9e, 0xba, 0xd1, 0xe5, 0xf9, 0xef, 0xe7, 0xbc, + 0x22, 0xf6, 0x8c, 0xef, 0x13, 0x84, 0x7c, 0x13, 0xc3, 0x29, 0x8b, 0x54, + 0xd1, 0xad, 0xbc, 0x66, 0xe8, 0x6f, 0x4a, 0xbd, 0x9a, 0x90, 0x9b, 0x46, + 0x0b, 0x07, 0x2c, 0xd8, 0x9e, 0xab, 0xb3, 0xa2, 0x3e, 0xad, 0x5f, 0x38, + 0x52, 0x4b, 0x43, 0xc4, 0x50, 0xbd, 0x2d, 0x47, 0xb3, 0x06, 0x8f, 0x03, + 0xf4, 0x59, 0x0c, 0x3c, 0xba, 0x0f, 0x28, 0xa3, 0x47, 0xd5, 0xd5, 0xd1, + 0xe8, 0xb3, 0xbc, 0x18, 0xe9, 0x2a, 0x59, 0x4a, 0xe1, 0x3c, 0x81, 0x26, + 0x7f, 0x2f, 0x4a, 0x61, 0xeb, 0x37, 0xab, 0x66, 0x57, 0xea, 0xcb, 0xe4, + 0xe2, 0xbc, 0x01, 0xb6, 0x89, 0xa6, 0x1d, 0x1b, 0xf7, 0xd2, 0x43, 0xf1, + 0x9e, 0x75, 0x35, 0x61, 0x7b, 0x79, 0xd9, 0x18, 0xbe, 0x5d, 0xcc, 0xce, + 0xc0, 0x4b +}; + +static uint8_t empty_certificate_list[] = { + /* SERVER CERT */ + 0x0B, + + /* Length of the handshake message */ + 0x00, 0x03, 0x03, + + /* Length of all certificates */ + 0x00, 0x03, 0x00 +}; + +static uint8_t empty_certificate[] = { + /* SERVER CERT */ + 0x0B, + + /* Length of the handshake message */ + 0x00, 0x00, 0x06, + + /* Length of all certificates */ + 0x00, 0x00, 0x03, + + /* Length of the first certificate */ + 0x00, 0x00, 0x00 +}; + +static uint8_t certificate_list_too_large[] = { + /* SERVER CERT */ + 0x0B, + + /* Length of the handshake message */ + 0x00, 0x00, 0x06, + + /* Length of all certificates */ + 0x00, 0x00, 0x10, + + /* Length the first certificate */ + 0x00, 0x00, 0x00 +}; + +static uint8_t certificate_too_large[] = { + /* SERVER CERT */ + 0x0B, + + /* Length of the handshake message */ + 0x00, 0x00, 0x06, + + /* Length of all certificates */ + 0x00, 0x00, 0x03, + + /* Length the first certificate */ + 0x00, 0x00, 0x10 +}; + +message_type_t s2n_conn_get_current_message_type(struct s2n_connection *conn); + +void send_messages(int write_fd, uint8_t *server_hello, uint32_t server_hello_len, uint8_t *server_cert, uint32_t server_cert_len) +{ + uint8_t record_header[5] = { TLS_HANDSHAKE, 0x03, 0x03, (server_hello_len >> 8), server_hello_len & 0xff }; + + if (write(write_fd, record_header, 5) != 5) { + _exit(100); + } + + if (write(write_fd, server_hello, server_hello_len) != server_hello_len) { + _exit(100); + } + + record_header[3] = (server_cert_len >> 8); + record_header[4] = server_cert_len & 0xff; + + if (write(write_fd, record_header, 5) != 5) { + _exit(100); + } + + if (write(write_fd, server_cert, server_cert_len) != server_cert_len) { + _exit(100); + } + + /* Close the pipe and exit */ + close(write_fd); +} + +int main(int argc, char **argv) +{ + s2n_blocked_status blocked; + int status; + pid_t pid; + int p[2]; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Test a good certificate list */ + + /* Create a pipe */ + EXPECT_SUCCESS(pipe(p)); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_read_fd(conn, p[0])); + + /* Pretend the client hello has already been set */ + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; + conn->handshake.message_number = SERVER_HELLO; + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the child process, close the read end of the pipe */ + EXPECT_SUCCESS(close(p[0])); + + send_messages(p[1], server_hello_message, sizeof(server_hello_message), good_certificate_list, sizeof(good_certificate_list)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + exit(0); + } + + /* This is the parent process, close the write end of the pipe */ + EXPECT_SUCCESS(close(p[1])); + + /* Negotiate the handshake. This will fail due to EOF, but that's ok. Turn off Blinding before negotiation so that + * server doesn't delay its response and test finishes quickly. */ + EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); + + /* Verify that the data is as we expect it */ + EXPECT_EQUAL(memcmp(conn->handshake_params.server_random, zero_to_thirty_one, 32), 0); + + /* Check that the server hello message was processed .. we should be HELLO DONE */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_HELLO_DONE); + + /* Clean up */ + EXPECT_EQUAL(waitpid(pid, &status, 0), pid); + EXPECT_EQUAL(status, 0); + EXPECT_SUCCESS(close(p[0])); + + /* Test an empty certificate list */ + + /* Create a pipe */ + EXPECT_SUCCESS(pipe(p)); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_read_fd(conn, p[0])); + + /* Pretend the client hello has already been set */ + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; + conn->handshake.message_number = SERVER_HELLO; + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the child process, close the read end of the pipe */ + EXPECT_SUCCESS(close(p[0])); + + send_messages(p[1], server_hello_message, sizeof(server_hello_message), empty_certificate_list, sizeof(empty_certificate_list)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + exit(0); + } + + /* This is the parent process, close the write end of the pipe */ + EXPECT_SUCCESS(close(p[1])); + + /* Negotiate the handshake. This will fail due to EOF, but that's ok. Turn off Blinding before negotiation so that + * server doesn't delay its response and test finishes quickly. */ + EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); + + /* Verify that the data is as we expect it */ + EXPECT_EQUAL(memcmp(conn->handshake_params.server_random, zero_to_thirty_one, 32), 0); + + /* Check that the server hello message was not processed, we're stuck in SERVER_CERT */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_CERT); + + /* Clean up */ + EXPECT_EQUAL(waitpid(pid, &status, 0), pid); + EXPECT_EQUAL(status, 0); + EXPECT_SUCCESS(close(p[0])); + + /* Test an empty certificate */ + + /* Create a pipe */ + EXPECT_SUCCESS(pipe(p)); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_read_fd(conn, p[0])); + + /* Pretend the client hello has already been set */ + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; + conn->handshake.message_number = SERVER_HELLO; + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the child process, close the read end of the pipe */ + EXPECT_SUCCESS(close(p[0])); + + send_messages(p[1], server_hello_message, sizeof(server_hello_message), empty_certificate, sizeof(empty_certificate)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + exit(0); + } + + /* This is the parent process, close the write end of the pipe */ + EXPECT_SUCCESS(close(p[1])); + + /* Negotiate the handshake. This will fail due to EOF, but that's ok. Turn off Blinding before negotiation so that + * server doesn't delay its response and test finishes quickly. */ + EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); + + /* Verify that the data is as we expect it */ + EXPECT_EQUAL(memcmp(conn->handshake_params.server_random, zero_to_thirty_one, 32), 0); + + /* Check that the server hello message was not processed, we're stuck in SERVER_CERT */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_CERT); + + /* Clean up */ + EXPECT_EQUAL(waitpid(pid, &status, 0), pid); + EXPECT_EQUAL(status, 0); + EXPECT_SUCCESS(close(p[0])); + + /* Test an 'too large' certificate list */ + + /* Create a pipe */ + EXPECT_SUCCESS(pipe(p)); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_read_fd(conn, p[0])); + + /* Pretend the client hello has already been set */ + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; + conn->handshake.message_number = SERVER_HELLO; + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the child process, close the read end of the pipe */ + EXPECT_SUCCESS(close(p[0])); + + send_messages(p[1], server_hello_message, sizeof(server_hello_message), certificate_list_too_large, sizeof(certificate_list_too_large)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + exit(0); + } + + /* This is the parent process, close the write end of the pipe */ + EXPECT_SUCCESS(close(p[1])); + + /* Negotiate the handshake. This will fail due to EOF, but that's ok. Turn off Blinding before negotiation so that + * server doesn't delay its response and test finishes quickly. */ + EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); + + /* Verify that the data is as we expect it */ + EXPECT_EQUAL(memcmp(conn->handshake_params.server_random, zero_to_thirty_one, 32), 0); + + /* Check that the server hello message was not processed, we're stuck in SERVER_CERT */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_CERT); + + /* Clean up */ + EXPECT_EQUAL(waitpid(pid, &status, 0), pid); + EXPECT_EQUAL(status, 0); + EXPECT_SUCCESS(close(p[0])); + + /* Test an 'too large' certificate */ + + /* Create a pipe */ + EXPECT_SUCCESS(pipe(p)); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_read_fd(conn, p[0])); + + /* Pretend the client hello has already been set */ + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; + conn->handshake.message_number = SERVER_HELLO; + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the child process, close the read end of the pipe */ + EXPECT_SUCCESS(close(p[0])); + + send_messages(p[1], server_hello_message, sizeof(server_hello_message), certificate_too_large, sizeof(certificate_too_large)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + exit(0); + } + + /* This is the parent process, close the write end of the pipe */ + EXPECT_SUCCESS(close(p[1])); + + /* Negotiate the handshake. This will fail due to EOF, but that's ok. Turn off Blinding before negotiation so that + * server doesn't delay its response and test finishes quickly. */ + EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); + + /* Verify that the data is as we expect it */ + EXPECT_EQUAL(memcmp(conn->handshake_params.server_random, zero_to_thirty_one, 32), 0); + + /* Check that the server hello message was not processed, we're stuck in SERVER_CERT */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_CERT); + + /* Clean up */ + EXPECT_EQUAL(waitpid(pid, &status, 0), pid); + EXPECT_EQUAL(status, 0); + EXPECT_SUCCESS(close(p[0])); + + /* Test an 'too large' handshake */ + + /* Create a pipe */ + EXPECT_SUCCESS(pipe(p)); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_read_fd(conn, p[0])); + + /* Pretend the client hello has already been set */ + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; + conn->handshake.message_number = SERVER_HELLO; + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the child process, close the read end of the pipe */ + EXPECT_SUCCESS(close(p[0])); + + send_messages(p[1], server_hello_message, sizeof(server_hello_message), certificate_list_too_large, sizeof(certificate_list_too_large)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + exit(0); + } + + /* This is the parent process, close the write end of the pipe */ + EXPECT_SUCCESS(close(p[1])); + + /* Negotiate the handshake. This will fail due to EOF, but that's ok. Turn off Blinding before negotiation so that + * server doesn't delay its response and test finishes quickly. */ + EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); + + /* Verify that the data is as we expect it */ + EXPECT_EQUAL(memcmp(conn->handshake_params.server_random, zero_to_thirty_one, 32), 0); + + /* Check that the server hello message was not processed, we're stuck in SERVER_CERT */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_CERT); + + /* Clean up */ + EXPECT_EQUAL(waitpid(pid, &status, 0), pid); + EXPECT_EQUAL(status, 0); + EXPECT_SUCCESS(close(p[0])); + + END_TEST(); +} diff --git a/tests/unit/s2n_map_test.c b/tests/unit/s2n_map_test.c new file mode 100644 index 00000000000..8f1d8bb8437 --- /dev/null +++ b/tests/unit/s2n_map_test.c @@ -0,0 +1,177 @@ +/* + * 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 "utils/s2n_map.h" + +#include + +#include "api/s2n.h" +#include "s2n_test.h" + +int main(int argc, char **argv) +{ + char keystr[sizeof("ffff")]; + char valstr[sizeof("16384")]; + struct s2n_map *empty, *map; + struct s2n_blob key = { 0 }; + struct s2n_blob val = { 0 }; + bool key_found; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_NOT_NULL(empty = s2n_map_new()); + + /* Try a lookup on an empty map. Expect an error because the map is still mutable. */ + EXPECT_SUCCESS(snprintf(keystr, sizeof(keystr), "%04x", 1234)); + key.data = (void *) keystr; + key.size = strlen(keystr) + 1; + EXPECT_ERROR(s2n_map_lookup(empty, &key, &val, &key_found)); + + EXPECT_SUCCESS(snprintf(valstr, sizeof(valstr), "%05d", 1234)); + val.data = (void *) valstr; + val.size = strlen(valstr) + 1; + + /* Try to add/put key with zero-size data. Expect failures */ + key.size = 0; + EXPECT_ERROR(s2n_map_add(empty, &key, &val)); + EXPECT_ERROR(s2n_map_put(empty, &key, &val)); + key.size = strlen(keystr) + 1; + + /* Make the empty map complete */ + EXPECT_OK(s2n_map_complete(empty)); + + /* Lookup and expect no result */ + EXPECT_OK(s2n_map_lookup(empty, &key, &val, &key_found)); + EXPECT_EQUAL(key_found, false); + + /* Done with the empty map */ + EXPECT_OK(s2n_map_free(empty)); + + /* Expect failure since initial map size is zero */ + EXPECT_NULL(map = s2n_map_new_with_initial_capacity(0)); + + /* Create map with the smallest initial size */ + EXPECT_NOT_NULL(map = s2n_map_new_with_initial_capacity(1)); + + /* Insert 8k key value pairs of the form hex(i) -> dec(i) */ + for (int i = 0; i < 8192; i++) { + EXPECT_SUCCESS(snprintf(keystr, sizeof(keystr), "%04x", i)); + EXPECT_SUCCESS(snprintf(valstr, sizeof(valstr), "%05d", i)); + + key.data = (void *) keystr; + key.size = strlen(keystr) + 1; + val.data = (void *) valstr; + val.size = strlen(valstr) + 1; + + EXPECT_OK(s2n_map_add(map, &key, &val)); + } + + /* Try adding some duplicates */ + for (int i = 0; i < 10; i++) { + EXPECT_SUCCESS(snprintf(keystr, sizeof(keystr), "%04x", i)); + EXPECT_SUCCESS(snprintf(valstr, sizeof(valstr), "%05d", i)); + + key.data = (void *) keystr; + key.size = strlen(keystr) + 1; + val.data = (void *) valstr; + val.size = strlen(valstr) + 1; + + EXPECT_ERROR(s2n_map_add(map, &key, &val)); + } + + /* Try replacing some entries */ + for (int i = 0; i < 10; i++) { + EXPECT_SUCCESS(snprintf(keystr, sizeof(keystr), "%04x", i)); + EXPECT_SUCCESS(snprintf(valstr, sizeof(valstr), "%05d", i + 1)); + + key.data = (void *) keystr; + key.size = strlen(keystr) + 1; + val.data = (void *) valstr; + val.size = strlen(valstr) + 1; + + EXPECT_OK(s2n_map_put(map, &key, &val)); + } + + /* Try a lookup before the map is complete: should fail */ + EXPECT_SUCCESS(snprintf(keystr, sizeof(keystr), "%04x", 1)); + EXPECT_ERROR(s2n_map_lookup(map, &key, &val, &key_found)); + + /* Make the map complete */ + EXPECT_OK(s2n_map_complete(map)); + + /* Make sure that add-after-complete fails */ + EXPECT_SUCCESS(snprintf(keystr, sizeof(keystr), "%04x", 8193)); + EXPECT_SUCCESS(snprintf(valstr, sizeof(valstr), "%05d", 8193)); + + key.data = (void *) keystr; + key.size = strlen(keystr) + 1; + val.data = (void *) valstr; + val.size = strlen(valstr) + 1; + + EXPECT_ERROR(s2n_map_add(map, &key, &val)); + + /* Check for equivalence */ + for (int i = 0; i < 8192; i++) { + if (i >= 10) { + EXPECT_SUCCESS(snprintf(keystr, sizeof(keystr), "%04x", i)); + EXPECT_SUCCESS(snprintf(valstr, sizeof(valstr), "%05d", i)); + } else { + /* The first 10 entries were overwritten with i+1 */ + EXPECT_SUCCESS(snprintf(keystr, sizeof(keystr), "%04x", i)); + EXPECT_SUCCESS(snprintf(valstr, sizeof(valstr), "%05d", i + 1)); + } + + key.data = (void *) keystr; + key.size = strlen(keystr) + 1; + + EXPECT_OK(s2n_map_lookup(map, &key, &val, &key_found)); + EXPECT_EQUAL(key_found, true); + + EXPECT_SUCCESS(memcmp(val.data, valstr, strlen(valstr) + 1)); + } + + /* Check for a key that shouldn't be there */ + EXPECT_SUCCESS(snprintf(keystr, sizeof(keystr), "%04x", 8193)); + key.data = (void *) keystr; + key.size = strlen(keystr) + 1; + EXPECT_OK(s2n_map_lookup(map, &key, &val, &key_found)); + EXPECT_EQUAL(key_found, false); + + /* Make the map mutable */ + EXPECT_OK(s2n_map_unlock(map)); + /* Make sure that add-after-unlock succeeds */ + EXPECT_SUCCESS(snprintf(keystr, sizeof(keystr), "%04x", 8193)); + EXPECT_SUCCESS(snprintf(valstr, sizeof(valstr), "%05d", 8193)); + + key.data = (void *) keystr; + key.size = strlen(keystr) + 1; + val.data = (void *) valstr; + val.size = strlen(valstr) + 1; + + EXPECT_OK(s2n_map_add(map, &key, &val)); + + /* Complete the map again */ + EXPECT_OK(s2n_map_complete(map)); + + /* Check the element added after map unlock */ + EXPECT_OK(s2n_map_lookup(map, &key, &val, &key_found)); + EXPECT_EQUAL(key_found, true); + EXPECT_SUCCESS(memcmp(val.data, valstr, strlen(valstr) + 1)); + + EXPECT_OK(s2n_map_free(map)); + + END_TEST(); +} diff --git a/tests/unit/s2n_mem_allocator_test.c b/tests/unit/s2n_mem_allocator_test.c index 5f1615cf63f..423fddcb2ca 100644 --- a/tests/unit/s2n_mem_allocator_test.c +++ b/tests/unit/s2n_mem_allocator_test.c @@ -95,10 +95,7 @@ void mock_client(struct s2n_test_io_pair *io_pair) s2n_connection_set_io_pair(conn, io_pair); - fprintf(stdout, "client s2n_negotiate\n"); - int result = s2n_negotiate(conn, &blocked); - fprintf(stdout, "client s2n_negotiate: %i (%s %s)\n", - result, s2n_strerror_name(s2n_errno), s2n_strerror_debug(s2n_errno, NULL)); + s2n_negotiate(conn, &blocked); s2n_connection_free_handshake(conn); @@ -109,9 +106,7 @@ void mock_client(struct s2n_test_io_pair *io_pair) for (int j = 0; j < i; j++) { buffer[j] = 33; } - result = s2n_send(conn, buffer, i, &blocked); - fprintf(stdout, "client s2n_send: %i (%s %s)\n", - result, s2n_strerror_name(s2n_errno), s2n_strerror_debug(s2n_errno, NULL)); + s2n_send(conn, buffer, i, &blocked); } for (int j = 0; j < i; j++) { @@ -122,30 +117,21 @@ void mock_client(struct s2n_test_io_pair *io_pair) s2n_connection_release_buffers(conn); /* Simulate timeout second conneciton inactivity and tolerate 50 ms error */ - fprintf(stdout, "client nanosleep\n"); struct timespec sleep_time = { .tv_sec = timeout, .tv_nsec = 50000000 }; int r; do { r = nanosleep(&sleep_time, &sleep_time); - fprintf(stdout, "client nanosleep: %i (%i)\n", r, errno); } while (r != 0); /* Active application bytes consumed is reset to 0 in before writing data. */ /* Its value should equal to bytes written after writing */ ssize_t bytes_written = s2n_send(conn, buffer, i, &blocked); - fprintf(stdout, "client s2n_send again: %li (%s %s)\n", - bytes_written, s2n_strerror_name(s2n_errno), s2n_strerror_debug(s2n_errno, NULL)); if ((uint64_t) bytes_written != conn->active_application_bytes_consumed) { - fprintf(stdout, "client exit early\n"); exit(0); } - fprintf(stdout, "client s2n_shutdown\n"); int shutdown_rc = -1; while (shutdown_rc != 0) { - fprintf(stdout, "client s2n_shutdown poll\n"); shutdown_rc = s2n_shutdown(conn, &blocked); - fprintf(stdout, "client s2n_shutdown: %i (%s %s)\n", - shutdown_rc, s2n_strerror_name(s2n_errno), s2n_strerror_debug(s2n_errno, NULL)); } s2n_connection_free(conn); @@ -156,13 +142,11 @@ void mock_client(struct s2n_test_io_pair *io_pair) s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); - fprintf(stdout, "client exit\n"); exit(0); } int main(int argc, char **argv) { - fprintf(stdout, "TEST START\n"); s2n_blocked_status blocked; int status; pid_t pid; @@ -191,17 +175,14 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); /* Create a child process */ - fprintf(stdout, "About to fork\n"); pid = fork(); if (pid == 0) { - fprintf(stdout, "Forked: client\n"); /* This is the client process, close the server end of the pipe */ EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); /* Write the fragmented hello message */ mock_client(&io_pair); } - fprintf(stdout, "Forked: server\n"); EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); @@ -238,10 +219,8 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); /* Negotiate the handshake. */ - fprintf(stdout, "server s2n_negotiate\n"); EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); - fprintf(stdout, "server s2n_recv\n"); char buffer[0xffff]; for (int i = 1; i < 0xffff; i += 100) { char *ptr = buffer; @@ -249,12 +228,10 @@ int main(int argc, char **argv) do { int bytes_read = 0; - fprintf(stdout, "server s2n_recv poll: i=%i\n", i); EXPECT_SUCCESS(bytes_read = s2n_recv(conn, ptr, size, &blocked)); size -= bytes_read; ptr += bytes_read; - fprintf(stdout, "server s2n_recv poll: bytes_read=%i size=%i\n", bytes_read, size); } while (size); for (int j = 0; j < i; j++) { @@ -265,10 +242,8 @@ int main(int argc, char **argv) EXPECT_SUCCESS(s2n_connection_release_buffers(conn)); } - fprintf(stdout, "server s2n_shutdown\n"); int shutdown_rc = -1; do { - fprintf(stdout, "server calling s2n_shutdown\n"); shutdown_rc = s2n_shutdown(conn, &blocked); EXPECT_TRUE(shutdown_rc == 0 || (errno == EAGAIN && blocked)); } while (shutdown_rc != 0); @@ -278,10 +253,8 @@ int main(int argc, char **argv) } /* Clean up */ - fprintf(stdout, "Waiting for %i\n", pid); EXPECT_EQUAL(waitpid(-1, &status, 0), pid); EXPECT_EQUAL(status, 0); - fprintf(stdout, "server s2n_io_pair_close_one_end\n"); EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); free(cert_chain_pem); free(private_key_pem); diff --git a/tests/unit/s2n_mem_test.c b/tests/unit/s2n_mem_test.c new file mode 100644 index 00000000000..0c522bc0adf --- /dev/null +++ b/tests/unit/s2n_mem_test.c @@ -0,0 +1,89 @@ +/* + * 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 "s2n_test.h" +#include "utils/s2n_safety.h" + +/* Required to override memory callbacks at runtime */ +#include "utils/s2n_mem.c" + +int s2n_strict_mem_free_cb(void *ptr, uint32_t size) +{ + POSIX_ENSURE_REF(ptr); + POSIX_ENSURE_GT(size, 0); + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Test safety of all mem free methods */ + { + /* Test: no-op for empty blob */ + { + struct s2n_blob blob = { 0 }; + EXPECT_SUCCESS(s2n_free(&blob)); + EXPECT_SUCCESS(s2n_free_without_wipe(&blob)); + EXPECT_SUCCESS(s2n_free_or_wipe(&blob)); + }; + + /* Test: no-op for already freed blob */ + { + struct s2n_blob blob = { 0 }; + EXPECT_SUCCESS(s2n_alloc(&blob, 10)); + EXPECT_SUCCESS(s2n_free(&blob)); + + EXPECT_SUCCESS(s2n_free(&blob)); + EXPECT_SUCCESS(s2n_free_without_wipe(&blob)); + EXPECT_SUCCESS(s2n_free_or_wipe(&blob)); + }; + + /* Test: error for NULL */ + { + EXPECT_FAILURE_WITH_ERRNO(s2n_free(NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_free_without_wipe(NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_free_or_wipe(NULL), S2N_ERR_NULL); + }; + + /* Test: faulty / overly strict free implementation + * + * A correct implementation of free() should be a no-op for a NULL pointer. + * However, we have encountered cases where faulty implementations of free() + * seg faulted on NULL: our Rust bindings initially incorrectly assumed that + * Rust's dealloc() handled NULLs like C's free(). + * + * As an easy way to avoid this, we should just never call the free mem callback for NULL. + */ + { + /* Save real free callback */ + s2n_mem_free_callback saved_free_cb = s2n_mem_free_cb; + + /* Set callback that won't accepts NULLs / zeros */ + s2n_mem_free_cb = s2n_strict_mem_free_cb; + + /* No-op for empty blob */ + struct s2n_blob blob = { 0 }; + EXPECT_SUCCESS(s2n_free(&blob)); + EXPECT_SUCCESS(s2n_free_without_wipe(&blob)); + EXPECT_SUCCESS(s2n_free_or_wipe(&blob)); + + /* Restore real free callback */ + s2n_mem_free_cb = saved_free_cb; + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_mem_testlib_test.c b/tests/unit/s2n_mem_testlib_test.c new file mode 100644 index 00000000000..a44e822e379 --- /dev/null +++ b/tests/unit/s2n_mem_testlib_test.c @@ -0,0 +1,198 @@ +/* +* 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 "testlib/s2n_mem_testlib.h" + +#include "s2n_test.h" + +int main() +{ + BEGIN_TEST(); + + /* Test: No mallocs */ + for (size_t init = 0; init < 2; init++) { + /* The getters and asserts should behave the same whether the callbacks + * were never invoked or never initialized at all + */ + if (init) { + DEFER_CLEANUP(struct s2n_mem_test_cb_scope scope = { 0 }, + s2n_mem_test_free_callbacks); + EXPECT_OK(s2n_mem_test_init_callbacks(&scope)); + } + + /* Test: s2n_mem_test_assert_malloc_count */ + { + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + EXPECT_ERROR_WITH_ERRNO(s2n_mem_test_assert_malloc_count(1), + S2N_ERR_TEST_ASSERTION); + }; + + /* Test: s2n_mem_test_assert_malloc */ + { + EXPECT_ERROR_WITH_ERRNO(s2n_mem_test_assert_malloc(0), S2N_ERR_TEST_ASSERTION); + EXPECT_ERROR_WITH_ERRNO(s2n_mem_test_assert_malloc(1), S2N_ERR_TEST_ASSERTION); + EXPECT_ERROR_WITH_ERRNO(s2n_mem_test_assert_malloc(UINT32_MAX), + S2N_ERR_TEST_ASSERTION); + }; + + /* Test: s2n_mem_test_assert_all_freed */ + EXPECT_OK(s2n_mem_test_assert_all_freed()); + }; + + /* Test: Single malloc */ + { + DEFER_CLEANUP(struct s2n_mem_test_cb_scope scope = { 0 }, + s2n_mem_test_free_callbacks); + EXPECT_OK(s2n_mem_test_init_callbacks(&scope)); + + const size_t requested = 17; + struct s2n_blob mem = { 0 }; + EXPECT_SUCCESS(s2n_alloc(&mem, requested)); + EXPECT_NOT_NULL(mem.data); + EXPECT_EQUAL(mem.size, requested); + + /* Test: s2n_mem_test_assert_malloc_count */ + { + EXPECT_OK(s2n_mem_test_assert_malloc_count(1)); + EXPECT_ERROR_WITH_ERRNO(s2n_mem_test_assert_malloc_count(0), + S2N_ERR_TEST_ASSERTION); + EXPECT_ERROR_WITH_ERRNO(s2n_mem_test_assert_malloc_count(2), + S2N_ERR_TEST_ASSERTION); + }; + + /* Test: s2n_mem_test_assert_malloc */ + { + EXPECT_OK(s2n_mem_test_assert_malloc(requested)); + EXPECT_ERROR_WITH_ERRNO(s2n_mem_test_assert_malloc(0), S2N_ERR_TEST_ASSERTION); + EXPECT_ERROR_WITH_ERRNO(s2n_mem_test_assert_malloc(1), S2N_ERR_TEST_ASSERTION); + EXPECT_ERROR_WITH_ERRNO(s2n_mem_test_assert_malloc(UINT32_MAX), + S2N_ERR_TEST_ASSERTION); + }; + + /* Test: s2n_mem_test_assert_all_freed */ + { + EXPECT_ERROR_WITH_ERRNO(s2n_mem_test_assert_all_freed(), S2N_ERR_TEST_ASSERTION); + EXPECT_SUCCESS(s2n_free(&mem)); + EXPECT_OK(s2n_mem_test_assert_all_freed()); + }; + }; + + /* Test: Multiple mallocs */ + { + DEFER_CLEANUP(struct s2n_mem_test_cb_scope scope = { 0 }, + s2n_mem_test_free_callbacks); + EXPECT_OK(s2n_mem_test_init_callbacks(&scope)); + + const size_t requested[] = { 10, 1, 17, 17, 111 }; + const size_t count = s2n_array_len(requested); + struct s2n_blob mem = { 0 }; + for (size_t i = 0; i < count; i++) { + EXPECT_SUCCESS(s2n_alloc(&mem, requested[i])); + EXPECT_NOT_NULL(mem.data); + EXPECT_EQUAL(mem.size, requested[i]); + EXPECT_SUCCESS(s2n_free(&mem)); + } + + /* Test: s2n_mem_test_assert_malloc_count */ + { + EXPECT_OK(s2n_mem_test_assert_malloc_count(count)); + EXPECT_ERROR_WITH_ERRNO(s2n_mem_test_assert_malloc_count(0), + S2N_ERR_TEST_ASSERTION); + EXPECT_ERROR_WITH_ERRNO(s2n_mem_test_assert_malloc_count(count - 1), + S2N_ERR_TEST_ASSERTION); + EXPECT_ERROR_WITH_ERRNO(s2n_mem_test_assert_malloc_count(count + 1), + S2N_ERR_TEST_ASSERTION); + }; + + /* Test: s2n_mem_test_assert_malloc */ + { + for (size_t i = 0; i < count; i++) { + EXPECT_OK(s2n_mem_test_assert_malloc(requested[i])); + } + EXPECT_ERROR_WITH_ERRNO(s2n_mem_test_assert_malloc(0), S2N_ERR_TEST_ASSERTION); + EXPECT_ERROR_WITH_ERRNO(s2n_mem_test_assert_malloc(UINT32_MAX), + S2N_ERR_TEST_ASSERTION); + }; + + /* Test: s2n_mem_test_assert_all_freed */ + { + EXPECT_OK(s2n_mem_test_assert_all_freed()); + EXPECT_SUCCESS(s2n_alloc(&mem, 1)); + EXPECT_ERROR_WITH_ERRNO(s2n_mem_test_assert_all_freed(), S2N_ERR_TEST_ASSERTION); + EXPECT_SUCCESS(s2n_free(&mem)); + EXPECT_OK(s2n_mem_test_assert_all_freed()); + }; + }; + + /* Test: s2n_mem_test_wipe_callbacks */ + { + DEFER_CLEANUP(struct s2n_mem_test_cb_scope scope = { 0 }, + s2n_mem_test_free_callbacks); + EXPECT_OK(s2n_mem_test_init_callbacks(&scope)); + + DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&mem, 1)); + EXPECT_NOT_NULL(mem.data); + EXPECT_OK(s2n_mem_test_assert_malloc_count(1)); + + EXPECT_OK(s2n_mem_test_wipe_callbacks()); + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + }; + + /* Test: s2n_mem_test_init_callbacks */ + { + EXPECT_OK(s2n_mem_test_init_callbacks(NULL)); + for (size_t i = 0; i < 5; i++) { + DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&mem, 1)); + EXPECT_NOT_NULL(mem.data); + EXPECT_OK(s2n_mem_test_assert_malloc_count(1)); + + /* If already initialized, we just wipe the state */ + EXPECT_OK(s2n_mem_test_init_callbacks(NULL)); + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + } + EXPECT_OK(s2n_mem_test_free_callbacks(NULL)); + }; + + /* Test: s2n_mem_test_free_callbacks */ + { + for (size_t i = 1; i < 5; i++) { + EXPECT_OK(s2n_mem_test_free_callbacks(NULL)); + + DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&mem, 1)); + EXPECT_NOT_NULL(mem.data); + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + } + + EXPECT_OK(s2n_mem_test_init_callbacks(NULL)); + DEFER_CLEANUP(struct s2n_blob mem = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&mem, 1)); + EXPECT_OK(s2n_mem_test_assert_malloc_count(1)); + + for (size_t i = 1; i < 5; i++) { + EXPECT_OK(s2n_mem_test_free_callbacks(NULL)); + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + + DEFER_CLEANUP(struct s2n_blob mem2 = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&mem2, 1)); + EXPECT_NOT_NULL(mem2.data); + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + } + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_mem_usage_test.c b/tests/unit/s2n_mem_usage_test.c new file mode 100644 index 00000000000..1180c655889 --- /dev/null +++ b/tests/unit/s2n_mem_usage_test.c @@ -0,0 +1,287 @@ +/* + * 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. + */ + +#ifdef __FreeBSD__ + /* FreeBSD requires POSIX compatibility off for its syscalls (enables __BSD_VISIBLE) + * Without the below line, cannot be imported (it requires __BSD_VISIBLE) */ + #undef _POSIX_C_SOURCE + /* clang-format off */ + #include + #include + /* clang-format on */ + #include +#elif defined(__OpenBSD__) + #undef _POSIX_C_SOURCE + #include + /* clang-format off */ + #include + #include + /* clang-format on */ + #include +#endif + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +/* The number of connection pairs to allocate before measuring memory + * usage. The greater the value, the more accurate the end result. */ +#define MAX_CONNECTIONS 1000 + +/* This is roughly the current memory usage per connection, in KB */ +#ifdef __FreeBSD__ + #define MEM_PER_CONNECTION 57 +#elif defined(__OpenBSD__) + #define MEM_PER_CONNECTION 60 +#else + #define MEM_PER_CONNECTION 49 +#endif + +/* This is the maximum memory per connection including 4KB of slack */ +#define TEST_SLACK 4 +#define MAX_MEM_PER_CONNECTION \ + ((MEM_PER_CONNECTION + TEST_SLACK) * 1024) + +/* This is the total maximum memory allowed */ +#define MAX_MEM_ALLOWED(num_connections) \ + (2 * (num_connections) *MAX_MEM_PER_CONNECTION) + +/* This is the correct value of MEM_PER_CONNECTION based on test results. + * Basically, this calculation should reverse MAX_MEM_ALLOWED */ +#define ACTUAL_MEM_PER_CONNECTION(num_connections, max_mem) \ + ((((max_mem) / 2 / (num_connections)) / 1024) - TEST_SLACK) + +ssize_t get_vm_data_size() +{ +#ifdef __linux__ + long page_size; + ssize_t size, resident, share, text, lib, data, dt; + + page_size = sysconf(_SC_PAGESIZE); + if (page_size < 0) { + return -1; + } + + FILE *status_file = fopen("/proc/self/statm", "r"); + if (fscanf(status_file, "%zd %zd %zd %zd %zd %zd %zd", &size, &resident, &share, &text, &lib, &data, &dt) < 7) { + fclose(status_file); + return -1; + } + fclose(status_file); + + return data * page_size; + +#elif defined(__FreeBSD__) + pid_t ppid = getpid(); + int pidinfo[4]; + pidinfo[0] = CTL_KERN; + pidinfo[1] = KERN_PROC; + pidinfo[2] = KERN_PROC_PID; + pidinfo[3] = (int) ppid; + + struct kinfo_proc procinfo = { 0 }; + + size_t len = sizeof(procinfo); + + sysctl(pidinfo, nitems(pidinfo), &procinfo, &len, NULL, 0); + + /* Taken from linprocfs implementation + * https://github.com/freebsd/freebsd-src/blob/779fd05344662aeec79c29470258bf657318eab3/sys/compat/linprocfs/linprocfs.c#L1019 */ + segsz_t lsize = (procinfo.ki_size >> PAGE_SHIFT) - procinfo.ki_dsize - procinfo.ki_ssize - procinfo.ki_tsize - 1; + + return lsize << PAGE_SHIFT; + +#elif defined(__OpenBSD__) + struct kinfo_proc *procinfo; + kvm_t *kd; + pid_t ppid; + long page_size; + ssize_t size; + int nentries; + + kd = kvm_open(NULL, NULL, NULL, KVM_NO_FILES, NULL); + ppid = getpid(); + procinfo = kvm_getprocs(kd, KERN_PROC_PID, ppid, sizeof(*procinfo), &nentries); + if (procinfo == NULL || nentries == 0) { + return -1; + } + + /* Taken from ps(1)'s calculation of vsize + * https://github.com/openbsd/src/blob/329e3480337617df4d195c9a400c3f186254b137/bin/ps/print.c#L603 */ + size = procinfo->p_vm_dsize + procinfo->p_vm_ssize + procinfo->p_vm_tsize; + + page_size = sysconf(_SC_PAGESIZE); + if (page_size < 0) { + return -1; + } + kvm_close(kd); + + return (size * page_size); +#else + /* Not implemented for other platforms */ + return 0; +#endif +} + +int main(int argc, char **argv) +{ + size_t connectionsToUse = MAX_CONNECTIONS; + + char *cert_chain; + char *private_key; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + /* Skip the test when running under valgrind or address sanitizer, as those tools + * impact the memory usage. */ + if (getenv("S2N_VALGRIND") != NULL || getenv("S2N_ADDRESS_SANITIZER") != NULL) { + END_TEST(); + } + + struct rlimit file_limit; + EXPECT_SUCCESS(getrlimit(RLIMIT_NOFILE, &file_limit)); + /* 4 fds per connection: {client,server} {write,read} fd + * and reserve 16 fds for libraries, stdin/stdout/stderr and so on */ + if (4 * connectionsToUse + 16 > file_limit.rlim_cur) { + connectionsToUse = MAX(1, (file_limit.rlim_cur - 16) / 4); + } + + const ssize_t maxAllowedMemDiff = MAX_MEM_ALLOWED(connectionsToUse); + const ssize_t minAllowedMemDiff = maxAllowedMemDiff * 0.75; + + struct s2n_connection **clients = calloc(connectionsToUse, sizeof(struct s2n_connection *)); + struct s2n_connection **servers = calloc(connectionsToUse, sizeof(struct s2n_connection *)); + + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + + struct s2n_config *client_config; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + + struct s2n_cert_chain_and_key *chain_and_key; + struct s2n_config *server_config; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + ssize_t vm_data_initial = get_vm_data_size(); + EXPECT_NOT_EQUAL(vm_data_initial, -1); + + /* Allocate all connections */ + for (size_t i = 0; i < connectionsToUse; i++) { + struct s2n_connection *client_conn; + 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_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + clients[i] = client_conn; + + struct s2n_connection *server_conn; + 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_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + servers[i] = server_conn; + } + + ssize_t vm_data_after_allocation = get_vm_data_size(); + EXPECT_NOT_EQUAL(vm_data_after_allocation, -1); + + for (size_t i = 0; i < connectionsToUse; i++) { + EXPECT_SUCCESS(s2n_connections_set_io_pair(clients[i], servers[i], &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(servers[i], clients[i])); + } + + ssize_t vm_data_after_handshakes = get_vm_data_size(); + EXPECT_NOT_EQUAL(vm_data_after_handshakes, -1); + + for (int i = 0; i < connectionsToUse; i++) { + EXPECT_SUCCESS(s2n_connection_free_handshake(servers[i])); + EXPECT_SUCCESS(s2n_connection_free_handshake(clients[i])); + } + ssize_t vm_data_after_free_handshake = get_vm_data_size(); + EXPECT_NOT_EQUAL(vm_data_after_free_handshake, -1); + + for (int i = 0; i < connectionsToUse; i++) { + EXPECT_SUCCESS(s2n_connection_release_buffers(servers[i])); + EXPECT_SUCCESS(s2n_connection_release_buffers(clients[i])); + } + ssize_t vm_data_after_release_buffers = get_vm_data_size(); + EXPECT_NOT_EQUAL(vm_data_after_release_buffers, -1); + + for (int i = 0; i < connectionsToUse; i++) { + EXPECT_SUCCESS(s2n_connection_free(clients[i])); + EXPECT_SUCCESS(s2n_connection_free(servers[i])); + } + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + + free(cert_chain); + free(private_key); + free(clients); + free(servers); + + TEST_DEBUG_PRINT("\n"); + TEST_DEBUG_PRINT("VmData initial: %10zd\n", vm_data_initial); + TEST_DEBUG_PRINT("VmData after allocations: %10zd\n", vm_data_after_allocation); + TEST_DEBUG_PRINT("VmData after handshakes: %10zd\n", vm_data_after_handshakes); + TEST_DEBUG_PRINT("VmData after free handshake: %10zd\n", vm_data_after_free_handshake); + TEST_DEBUG_PRINT("VmData after release: %10zd\n", vm_data_after_release_buffers); + TEST_DEBUG_PRINT("Max VmData diff allowed: %10zd\n", maxAllowedMemDiff); + TEST_DEBUG_PRINT("Number of connections used: %10zu\n", connectionsToUse); + + EXPECT_TRUE(vm_data_after_free_handshake <= vm_data_after_handshakes); + EXPECT_TRUE(vm_data_after_release_buffers <= vm_data_after_free_handshake); + + ssize_t handshake_diff = (vm_data_after_handshakes - vm_data_initial); + ssize_t allocation_diff = (vm_data_after_allocation - vm_data_initial); + + /* + * get_vm_data_size is required for this test to succeed. + * Any platform that doesn't implement get_vm_data_size should be excluded here. + */ +#ifndef __APPLE__ + if (allocation_diff > maxAllowedMemDiff + || handshake_diff > maxAllowedMemDiff + || handshake_diff < minAllowedMemDiff) { + fprintf(stdout, "\nActual KB per connection: %i\n", + (int) ACTUAL_MEM_PER_CONNECTION(connectionsToUse, handshake_diff)); + FAIL_MSG("Unexpected memory usage. If expected, update MEM_PER_CONNECTION."); + } +#endif + + END_TEST(); +} diff --git a/tests/unit/s2n_mutual_auth_test.c b/tests/unit/s2n_mutual_auth_test.c new file mode 100644 index 00000000000..46f6b487fc3 --- /dev/null +++ b/tests/unit/s2n_mutual_auth_test.c @@ -0,0 +1,316 @@ +/* + * 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 "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_security_policies.h" +#include "utils/s2n_safety.h" + +struct host_verify_data { + uint8_t callback_invoked; + uint8_t allow; +}; + +static uint8_t verify_host_fn(const char *host_name, size_t host_name_len, void *data) +{ + struct host_verify_data *verify_data = (struct host_verify_data *) data; + verify_data->callback_invoked = 1; + return verify_data->allow; +} + +int main(int argc, char **argv) +{ + struct s2n_config *config; + const struct s2n_security_policy *default_security_policy; + const struct s2n_cipher_preferences *default_cipher_preferences; + char *cert_chain_pem; + char *private_key_pem; + char *dhparams_pem; + struct s2n_cert_chain_and_key *chain_and_key; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + + /* + * Test Mutual Auth using **s2n_connection_set_client_auth_type** + */ + + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + /* Later versions of the default security policies include ECDSA, which this test does not handle. + * We can't just add an ECDSA certificate to the test, because only one cert is allowed in client mode. + * Freeze the version of the security policy used by this test. */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20170210")); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); + EXPECT_NOT_NULL(default_security_policy = config->security_policy); + EXPECT_NOT_NULL(default_cipher_preferences = default_security_policy->cipher_preferences); + + struct host_verify_data verify_data = { .allow = 1, .callback_invoked = 0 }; + EXPECT_SUCCESS(s2n_config_set_verify_host_callback(config, verify_host_fn, &verify_data)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* Verify that a handshake succeeds for every cipher in the default list. */ + for (size_t cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { + verify_data.callback_invoked = 0; + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_security_policy server_security_policy; + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + struct s2n_stuffer client_to_server = { 0 }; + struct s2n_stuffer server_to_client = { 0 }; + + /* Craft a cipher preference with a cipher_idx cipher */ + EXPECT_MEMCPY_SUCCESS(&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; + + EXPECT_MEMCPY_SUCCESS(&server_security_policy, default_security_policy, sizeof(server_security_policy)); + server_security_policy.cipher_preferences = &server_cipher_preferences; + + config->security_policy = &server_security_policy; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that both connections negotiated Mutual Auth */ + EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); + EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); + EXPECT_TRUE(verify_data.callback_invoked); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); + EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); + } + + /* + * Test Mutual Auth using **s2n_config_set_client_auth_type** + */ + + EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_REQUIRED)); + + /* Verify that a handshake succeeds for every cipher in the default list. */ + for (size_t cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_security_policy server_security_policy; + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + struct s2n_stuffer client_to_server = { 0 }; + struct s2n_stuffer server_to_client = { 0 }; + + /* Craft a cipher preference with a cipher_idx cipher */ + EXPECT_MEMCPY_SUCCESS(&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; + EXPECT_MEMCPY_SUCCESS(&server_security_policy, default_security_policy, sizeof(server_security_policy)); + server_security_policy.cipher_preferences = &server_cipher_preferences; + config->security_policy = &server_security_policy; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that both connections negotiated Mutual Auth */ + EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); + EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); + EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); + } + + /* + * Test Mutual Auth using connection override of **s2n_config_set_client_auth_type** + */ + + EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_NONE)); + + /* Verify that a handshake succeeds for every cipher in the default list. */ + for (size_t cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_security_policy server_security_policy; + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + struct s2n_stuffer client_to_server = { 0 }; + struct s2n_stuffer server_to_client = { 0 }; + + /* Craft a cipher preference with a cipher_idx cipher */ + EXPECT_MEMCPY_SUCCESS(&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; + + EXPECT_MEMCPY_SUCCESS(&server_security_policy, default_security_policy, sizeof(server_security_policy)); + server_security_policy.cipher_preferences = &server_cipher_preferences; + + config->security_policy = &server_security_policy; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); + + /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that both connections negotiated Mutual Auth */ + EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); + EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); + EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); + } + + /* + * Test Mutual Auth using connection override of **s2n_config_set_client_auth_type** only on one side of the + * connection and verify that a connection is not established + */ + + EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_NONE)); + + /* Verify that a handshake succeeds for every cipher in the default list. */ + for (size_t cipher_idx = 0; cipher_idx < default_cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_security_policy server_security_policy; + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + struct s2n_stuffer client_to_server = { 0 }; + struct s2n_stuffer server_to_client = { 0 }; + + /* Craft a cipher preference with a cipher_idx cipher */ + EXPECT_MEMCPY_SUCCESS(&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; + + EXPECT_MEMCPY_SUCCESS(&server_security_policy, default_security_policy, sizeof(server_security_policy)); + server_security_policy.cipher_preferences = &server_cipher_preferences; + + config->security_policy = &server_security_policy; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + /* Only set S2N_CERT_AUTH_REQUIRED on the server and not the client so that the connection fails */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + + /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + + EXPECT_FAILURE(s2n_negotiate_test_server_and_client(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)); + EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); + EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); + } + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(config)); + free(cert_chain_pem); + free(private_key_pem); + free(dhparams_pem); + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_next_protocol_test.c b/tests/unit/s2n_next_protocol_test.c new file mode 100644 index 00000000000..a2e6d16dcee --- /dev/null +++ b/tests/unit/s2n_next_protocol_test.c @@ -0,0 +1,221 @@ +/* + * 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 "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_safety.h" + +S2N_RESULT s2n_calculate_padding(uint8_t protocol_len, uint8_t *padding_len); +S2N_RESULT s2n_write_npn_protocol(struct s2n_connection *conn, struct s2n_stuffer *out); +S2N_RESULT s2n_read_npn_protocol(struct s2n_connection *conn, struct s2n_stuffer *in); + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + const char *protocols[] = { "http/1.1", "spdy/1", "spdy/2" }; + + /* Test s2n_next_protocol_send */ + { + /* Safety checks */ + EXPECT_FAILURE(s2n_next_protocol_send(NULL)); + + /* Should fail for >= TLS1.3 */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + + /* Fails for TLS1.3 */ + client_conn->actual_protocol_version = S2N_TLS13; + EXPECT_FAILURE_WITH_ERRNO(s2n_next_protocol_send(client_conn), S2N_ERR_BAD_MESSAGE); + + /* Succeeds for TLS1.2 */ + client_conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_next_protocol_send(client_conn)); + }; + }; + + /* Test s2n_next_protocol_recv */ + { + /* Safety checks */ + EXPECT_FAILURE(s2n_next_protocol_recv(NULL)); + + /* Should parse s2n_next_protocol_send */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + + client_conn->actual_protocol_version = S2N_TLS12; + + char protocol[] = { "http/1.1" }; + uint8_t protocol_len = strlen(protocol); + EXPECT_MEMCPY_SUCCESS(client_conn->application_protocol, protocol, protocol_len + 1); + + struct s2n_stuffer *stuffer = &client_conn->handshake.io; + + EXPECT_SUCCESS(s2n_next_protocol_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(stuffer, &server_conn->handshake.io, s2n_stuffer_data_available(stuffer))); + + /* Fails for TLS1.3 */ + server_conn->actual_protocol_version = S2N_TLS13; + EXPECT_FAILURE_WITH_ERRNO(s2n_next_protocol_recv(server_conn), S2N_ERR_BAD_MESSAGE); + + /* Succeeds for TLS1.2 */ + server_conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_next_protocol_recv(server_conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(&server_conn->handshake.io), 0); + + EXPECT_BYTEARRAY_EQUAL(client_conn->application_protocol, server_conn->application_protocol, protocol_len); + }; + }; + + /* s2n_write_npn_protocol */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + + uint8_t first_protocol_len = strlen(protocols[0]); + EXPECT_MEMCPY_SUCCESS(client_conn->application_protocol, protocols[0], first_protocol_len + 1); + + DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_OK(s2n_write_npn_protocol(client_conn, &out)); + + uint8_t protocol_len = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&out, &protocol_len)); + + uint8_t protocol[UINT8_MAX] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&out, protocol, protocol_len)); + EXPECT_BYTEARRAY_EQUAL(protocol, protocols[0], protocol_len); + + uint8_t padding_len = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&out, &padding_len)); + EXPECT_TRUE(padding_len > 0); + + for (size_t i = 0; i < padding_len; i++) { + uint8_t byte = UINT8_MAX; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&out, &byte)); + EXPECT_EQUAL(byte, 0); + } + EXPECT_EQUAL(s2n_stuffer_data_available(&out), 0); + }; + + /* s2n_read_npn_protocol can parse s2n_write_npn_protocol */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + uint8_t first_protocol_len = strlen(protocols[0]); + EXPECT_MEMCPY_SUCCESS(client_conn->application_protocol, protocols[0], first_protocol_len + 1); + + DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_OK(s2n_write_npn_protocol(client_conn, &out)); + EXPECT_OK(s2n_read_npn_protocol(server_conn, &out)); + + EXPECT_NOT_NULL(s2n_get_application_protocol(server_conn)); + EXPECT_BYTEARRAY_EQUAL(s2n_get_application_protocol(server_conn), protocols[0], strlen(protocols[0])); + }; + + /* s2n_read_npn_protocol can read empty message */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + + /* Zero-length protocol */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&out, 0)); + uint8_t padding_len = 0; + EXPECT_OK(s2n_calculate_padding(0, &padding_len)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&out, padding_len)); + for (size_t i = 0; i < padding_len; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&out, 0)); + } + + EXPECT_OK(s2n_read_npn_protocol(server_conn, &out)); + EXPECT_NULL(s2n_get_application_protocol(server_conn)); + }; + + /* s2n_read_npn_protocol errors on malformed message */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + uint8_t wire_bytes[] = { + /* Incorrect length of protocol */ + 0x10, + }; + + DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&out, wire_bytes, sizeof(wire_bytes))); + EXPECT_ERROR_WITH_ERRNO(s2n_read_npn_protocol(server_conn, &out), S2N_ERR_NULL); + }; + + /* s2n_read_npn_protocol errors on malformed padding */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + uint8_t wire_bytes[] = { + /* Zero-length protocol */ + 0x00, + 0x00, + /* Padding character is not zero */ + 0x01, + 0xFF, + }; + + DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&out, wire_bytes, sizeof(wire_bytes))); + EXPECT_ERROR_WITH_ERRNO(s2n_read_npn_protocol(server_conn, &out), S2N_ERR_SAFETY); + }; + + /* + *= https://datatracker.ietf.org/doc/id/draft-agl-tls-nextprotoneg-03#section-3 + *= type=test + *# The length of "padding" SHOULD be 32 - ((len(selected_protocol) + 2) % 32). + */ + { + struct { + uint8_t protocol_len; + uint8_t expected_padding; + } test_cases[] = { + { .protocol_len = 0, .expected_padding = 30 }, + { .protocol_len = 32, .expected_padding = 30 }, + { .protocol_len = 17, .expected_padding = 13 }, + { .protocol_len = 2, .expected_padding = 28 }, + { .protocol_len = UINT8_MAX, .expected_padding = 31 }, + { .protocol_len = 30, .expected_padding = 32 }, + }; + + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + uint8_t output = 0; + EXPECT_OK(s2n_calculate_padding(test_cases[i].protocol_len, &output)); + EXPECT_EQUAL(output, test_cases[i].expected_padding); + } + }; + END_TEST(); +} diff --git a/tests/unit/s2n_npn_extension_test.c b/tests/unit/s2n_npn_extension_test.c new file mode 100644 index 00000000000..b9403d5bdbe --- /dev/null +++ b/tests/unit/s2n_npn_extension_test.c @@ -0,0 +1,289 @@ +/* + * 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 "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tests/s2n_test.h" +#include "tls/extensions/s2n_client_alpn.h" +#include "tls/extensions/s2n_npn.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_safety.h" + +#define HTTP11 0x68, 0x74, 0x74, 0x70, 0x2f, 0x31, 0x2e, 0x31 +#define SPDY1 0x73, 0x70, 0x64, 0x79, 0x2f, 0x31 +#define SPDY2 0x73, 0x70, 0x64, 0x79, 0x2f, 0x32 +#define SPDY3 0x73, 0x70, 0x64, 0x79, 0x2f, 0x33 + +S2N_RESULT s2n_calculate_padding(uint8_t protocol_len, uint8_t *padding_len); + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + const char *protocols[] = { "http/1.1", "spdy/1", "spdy/2" }; + const uint8_t protocols_count = s2n_array_len(protocols); + + /* Should-send tests on the client side */ + { + /* No connection */ + EXPECT_FALSE(s2n_client_npn_extension.should_send(NULL)); + + /* No config */ + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_FALSE(s2n_client_npn_extension.should_send(client_conn)); + + /* No application protocols set */ + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_FALSE(s2n_client_npn_extension.should_send(client_conn)); + + /* Application protocols set but NPN not supported. In this case the ALPN extension will be sent. */ + EXPECT_SUCCESS(s2n_config_set_protocol_preferences(config, protocols, protocols_count)); + EXPECT_FALSE(s2n_client_npn_extension.should_send(client_conn)); + EXPECT_TRUE(s2n_client_alpn_extension.should_send(client_conn)); + + /* Both ALPN and NPN extensions will be sent */ + client_conn->config->npn_supported = true; + EXPECT_TRUE(s2n_client_npn_extension.should_send(client_conn)); + EXPECT_TRUE(s2n_client_alpn_extension.should_send(client_conn)); + + /* + *= https://datatracker.ietf.org/doc/id/draft-agl-tls-nextprotoneg-03#section-3 + *= type=test + *# For the same reasons, after a handshake has been performed for a + *# given connection, renegotiations on the same connection MUST NOT + *# include the "next_protocol_negotiation" extension. + */ + client_conn->handshake.renegotiation = true; + EXPECT_FALSE(s2n_client_npn_extension.should_send(client_conn)); + EXPECT_TRUE(s2n_client_alpn_extension.should_send(client_conn)); + }; + + /* s2n_client_npn_recv */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_protocol_preferences(config, protocols, protocols_count)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_stuffer extension = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension, 0)); + + /* NPN not supported */ + EXPECT_SUCCESS(s2n_client_npn_extension.recv(server_conn, &extension)); + EXPECT_FALSE(server_conn->npn_negotiated); + + /* NPN supported */ + server_conn->config->npn_supported = true; + EXPECT_SUCCESS(s2n_client_npn_extension.recv(server_conn, &extension)); + EXPECT_TRUE(server_conn->npn_negotiated); + + /* Server has already negotiated a protocol with the ALPN extension */ + uint8_t first_protocol_len = strlen(protocols[0]); + EXPECT_MEMCPY_SUCCESS(server_conn->application_protocol, protocols[0], first_protocol_len + 1); + server_conn->npn_negotiated = false; + EXPECT_SUCCESS(s2n_client_npn_extension.recv(server_conn, &extension)); + EXPECT_FALSE(server_conn->npn_negotiated); + }; + + /* Should-send tests on the server side */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_protocol_preferences(config, protocols, protocols_count)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* NPN not negotiated */ + EXPECT_FALSE(s2n_server_npn_extension.should_send(server_conn)); + + /* NPN negotiated */ + server_conn->npn_negotiated = true; + EXPECT_TRUE(s2n_server_npn_extension.should_send(server_conn)); + }; + + /* s2n_server_npn_send */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_protocol_preferences(config, protocols, protocols_count)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + + EXPECT_SUCCESS(s2n_server_npn_extension.send(server_conn, &out)); + + uint8_t protocol_len = 0; + uint8_t protocol[UINT8_MAX] = { 0 }; + for (size_t i = 0; i < protocols_count; i++) { + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&out, &protocol_len)); + EXPECT_EQUAL(protocol_len, strlen(protocols[i])); + + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&out, protocol, protocol_len)); + EXPECT_BYTEARRAY_EQUAL(protocol, protocols[i], protocol_len); + } + + EXPECT_EQUAL(s2n_stuffer_data_available(&out), 0); + }; + + /* s2n_server_npn_recv */ + { + /* Client has no application protocols configured. Not sure how this + * could happen, but added to be thorough. */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + DEFER_CLEANUP(struct s2n_stuffer extension = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension, 0)); + + EXPECT_SUCCESS(s2n_server_npn_extension.recv(client_conn, &extension)); + EXPECT_NULL(s2n_get_application_protocol(client_conn)); + }; + + /* NPN recv extension can read NPN send extension */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_protocol_preferences(config, protocols, protocols_count)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_stuffer extension = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension, 0)); + + EXPECT_SUCCESS(s2n_server_npn_extension.send(client_conn, &extension)); + EXPECT_SUCCESS(s2n_server_npn_extension.recv(client_conn, &extension)); + + /* Server sent the same list that the client configured so the first protocol in the list is chosen */ + EXPECT_NOT_NULL(s2n_get_application_protocol(client_conn)); + EXPECT_BYTEARRAY_EQUAL(s2n_get_application_protocol(client_conn), protocols[0], strlen(protocols[0])); + }; + + /* No match exists */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_protocol_preferences(config, protocols, protocols_count)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_stuffer extension = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension, 0)); + + uint8_t protocol[] = { SPDY3 }; + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension, sizeof(protocol))); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&extension, protocol, sizeof(protocol))); + + /* + *= https://datatracker.ietf.org/doc/id/draft-agl-tls-nextprotoneg-03#section-4 + *= type=test + *# In the event that the client doesn't support any of server's protocols, or + *# the server doesn't advertise any, it SHOULD select the first protocol + *# that it supports. + */ + EXPECT_SUCCESS(s2n_server_npn_extension.recv(client_conn, &extension)); + EXPECT_NOT_NULL(s2n_get_application_protocol(client_conn)); + EXPECT_BYTEARRAY_EQUAL(s2n_get_application_protocol(client_conn), protocols[0], strlen(protocols[0])); + }; + + /* Server sends empty list */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_protocol_preferences(config, protocols, protocols_count)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_stuffer extension = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension, 0)); + + /* + *= https://datatracker.ietf.org/doc/id/draft-agl-tls-nextprotoneg-03#section-4 + *= type=test + *# In the event that the client doesn't support any of server's protocols, or + *# the server doesn't advertise any, it SHOULD select the first protocol + *# that it supports. + */ + EXPECT_SUCCESS(s2n_server_npn_extension.recv(client_conn, &extension)); + EXPECT_NOT_NULL(s2n_get_application_protocol(client_conn)); + EXPECT_BYTEARRAY_EQUAL(s2n_get_application_protocol(client_conn), protocols[0], strlen(protocols[0])); + }; + + /* Multiple matches exist and server's preferred choice is selected */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_protocol_preferences(config, protocols, protocols_count)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_stuffer extension = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension, 0)); + + uint8_t wire_bytes[] = { + /* Size and bytes of first protocol */ + 0x06, + SPDY1, + /* Size and bytes of second protocol */ + 0x08, + HTTP11, + /* Size and bytes of second protocol */ + 0x06, + SPDY2, + }; + + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&extension, wire_bytes, sizeof(wire_bytes))); + EXPECT_SUCCESS(s2n_server_npn_extension.recv(client_conn, &extension)); + + EXPECT_NOT_NULL(s2n_get_application_protocol(client_conn)); + + /* Client's second protocol is selected because the server prefers it over client's first protocol */ + EXPECT_BYTEARRAY_EQUAL(s2n_get_application_protocol(client_conn), protocols[1], strlen(protocols[1])); + }; + }; + + /* Check application protocol array can hold the largest uint8_t value. + * + * We frequently copy a uint8_t's worth of data into this array. Adding + * checks to ensure that the array will be large enough causes compilers + * to give warnings that the check will always be true. + * This test will fail if we ever make that array smaller, so we remember + * to go back and add those checks. + */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + /* Not <= because the application protocol is a string, which needs to + * be terminated by a null character */ + EXPECT_TRUE(UINT8_MAX < sizeof(server_conn->application_protocol)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_nst_early_data_indication_test.c b/tests/unit/s2n_nst_early_data_indication_test.c new file mode 100644 index 00000000000..1a12a4bd5fb --- /dev/null +++ b/tests/unit/s2n_nst_early_data_indication_test.c @@ -0,0 +1,98 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_early_data_indication.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Test s2n_nst_early_data_indication_should_send */ + { + /* Safety check */ + EXPECT_FALSE(s2n_nst_early_data_indication_extension.should_send(NULL)); + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + + /* Should not send if max_early_data_size not set */ + EXPECT_FALSE(s2n_nst_early_data_indication_extension.should_send(conn)); + + /* Should not send if max_early_data_size set to 0 */ + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(conn, 0)); + EXPECT_FALSE(s2n_nst_early_data_indication_extension.should_send(conn)); + + /* Should send if max_early_data_size set to non-zero */ + uint32_t server_max = 13; + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(conn, server_max)); + EXPECT_TRUE(s2n_nst_early_data_indication_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_nst_early_data_indiction_send */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS13; + + struct s2n_stuffer output = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + + const uint32_t expected_max_early_data_size = 13; + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(conn, expected_max_early_data_size)); + + /* Safety checks */ + EXPECT_FAILURE_WITH_ERRNO(s2n_nst_early_data_indication_extension.send(conn, NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_nst_early_data_indication_extension.send(NULL, &output), S2N_ERR_NULL); + + uint32_t actual_max_early_data_size = 0; + EXPECT_SUCCESS(s2n_nst_early_data_indication_extension.send(conn, &output)); + EXPECT_SUCCESS(s2n_stuffer_read_uint32(&output, &actual_max_early_data_size)); + EXPECT_EQUAL(expected_max_early_data_size, actual_max_early_data_size); + + EXPECT_SUCCESS(s2n_stuffer_free(&output)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_nst_early_data_indiction_recv */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS13; + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + + /* Safety checks */ + EXPECT_FAILURE_WITH_ERRNO(s2n_nst_early_data_indication_extension.recv(conn, NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_nst_early_data_indication_extension.recv(NULL, &input), S2N_ERR_NULL); + + const uint32_t expected_max_early_data_size = 13; + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(conn, expected_max_early_data_size)); + EXPECT_SUCCESS(s2n_nst_early_data_indication_extension.send(conn, &input)); + + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(conn, 0)); + EXPECT_SUCCESS(s2n_nst_early_data_indication_extension.recv(conn, &input)); + EXPECT_EQUAL(conn->server_max_early_data_size, expected_max_early_data_size); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_openssl_test.c b/tests/unit/s2n_openssl_test.c new file mode 100644 index 00000000000..11b14fa26a8 --- /dev/null +++ b/tests/unit/s2n_openssl_test.c @@ -0,0 +1,41 @@ +/* + * 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 "crypto/s2n_openssl.h" + +#include "s2n_test.h" + +int main(int argc, char** argv) +{ + BEGIN_TEST(); + + const char* env_libcrypto = getenv("S2N_LIBCRYPTO"); + if (env_libcrypto == NULL) { + END_TEST(); + } + + if (strcmp(env_libcrypto, "boringssl") == 0) { + EXPECT_FALSE(s2n_libcrypto_is_awslc()); + EXPECT_TRUE(s2n_libcrypto_is_boringssl()); + } else if (strstr(env_libcrypto, "awslc") != NULL) { + EXPECT_TRUE(s2n_libcrypto_is_awslc()); + EXPECT_FALSE(s2n_libcrypto_is_boringssl()); + } else { + EXPECT_FALSE(s2n_libcrypto_is_awslc()); + EXPECT_FALSE(s2n_libcrypto_is_boringssl()); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_optional_client_auth_test.c b/tests/unit/s2n_optional_client_auth_test.c new file mode 100644 index 00000000000..498b659efb1 --- /dev/null +++ b/tests/unit/s2n_optional_client_auth_test.c @@ -0,0 +1,472 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_security_policies.h" + +int main(int argc, char **argv) +{ + struct s2n_config *client_config; + struct s2n_config *server_config; + const struct s2n_security_policy *default_security_policy; + const struct s2n_cipher_preferences *default_cipher_preferences; + char *cert_chain_pem; + char *private_key_pem; + char *dhparams_pem; + struct s2n_cert_chain_and_key *chain_and_key; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + + /* Setup baseline server config and certs. */ + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_chain)); + + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + EXPECT_NOT_NULL(default_security_policy = server_config->security_policy); + EXPECT_NOT_NULL(default_cipher_preferences = default_security_policy->cipher_preferences); + + /* + * Test optional client auth using **s2n_config_set_client_auth_type** with a valid client cert provided. + */ + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + + /* Server requires optional client auth and accepts the client cert. */ + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_OPTIONAL)); + 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_security_policy->cipher_preferences->count; cipher_idx++) { + struct s2n_cipher_preferences server_cipher_preferences; + struct s2n_security_policy security_policy; + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + + /* Craft a cipher preference with a cipher_idx cipher. */ + EXPECT_MEMCPY_SUCCESS(&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; + + EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); + security_policy.cipher_preferences = &server_cipher_preferences; + + client_config->security_policy = &security_policy; + server_config->security_policy = &security_policy; + + /* Create nonblocking pipes. */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Verify the handshake was successful. */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that both connections negotiated mutual auth. */ + EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); + EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + 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_to_store(client_config, chain_and_key)); + 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_security_policy security_policy; + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + + /* Craft a cipher preference with a cipher_idx cipher. */ + EXPECT_MEMCPY_SUCCESS(&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; + + EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); + security_policy.cipher_preferences = &server_cipher_preferences; + + client_config->security_policy = &security_policy; + server_config->security_policy = &security_policy; + + /* Create nonblocking pipes. */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Verify the handshake was successful. */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(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)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + EXPECT_SUCCESS(s2n_config_free(client_config)); + + /* + * Test optional client auth using **s2n_config_set_client_auth_type** with no client cert provided. + */ + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + + /* Server requires optional client auth and accepts the client cert. */ + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_OPTIONAL)); + 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_security_policy security_policy; + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + + /* Craft a cipher preference with a cipher_idx cipher. */ + EXPECT_MEMCPY_SUCCESS(&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; + + EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); + security_policy.cipher_preferences = &server_cipher_preferences; + + client_config->security_policy = &security_policy; + server_config->security_policy = &security_policy; + + /* Create nonblocking pipes. */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Verify the handshake was successful. */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that neither connection 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)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + EXPECT_SUCCESS(s2n_config_free(client_config)); + + /* + * Test optional client auth using **s2n_connection_set_client_auth_type** with a valid client cert provided. + */ + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_REQUIRED)); + + /* Server requires no client auth but the connection will. */ + 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_security_policy security_policy; + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + + /* Craft a cipher preference with a cipher_idx cipher. */ + EXPECT_MEMCPY_SUCCESS(&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; + + EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); + security_policy.cipher_preferences = &server_cipher_preferences; + + client_config->security_policy = &security_policy; + server_config->security_policy = &security_policy; + + /* Create nonblocking pipes. */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + /* Override the config setting on the connection. */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Override the config setting on the connection. */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); + + /* Verify the handshake was successful. */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that both connections negotiated mutual auth. */ + EXPECT_TRUE(s2n_connection_client_cert_used(server_conn)); + EXPECT_TRUE(s2n_connection_client_cert_used(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + EXPECT_SUCCESS(s2n_config_free(client_config)); + + /* + * Test optional client auth using **s2n_connection_set_client_auth_type** with no client cert provided. + */ + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_REQUIRED)); + + /* Server requires client auth but the connection will allow an empty client cert. */ + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + 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_security_policy security_policy; + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + + /* Craft a cipher preference with a cipher_idx cipher. */ + EXPECT_MEMCPY_SUCCESS(&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; + + EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); + security_policy.cipher_preferences = &server_cipher_preferences; + + client_config->security_policy = &security_policy; + server_config->security_policy = &security_policy; + + /* Create nonblocking pipes. */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + /* Override the config setting on the connection. */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_OPTIONAL)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Override the config setting on the connection. */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); + + /* Verify the handshake was successful. */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that neither connection 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)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + EXPECT_SUCCESS(s2n_config_free(client_config)); + + /* + * Test optional client auth using **s2n_config_set_client_auth_type** with an incorrect client + * cert provided fails negotiation, allowing the user to fatally kill the handshake if they want. + * https://tools.ietf.org/html/rfc5246#section-7.4.6 + */ + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + + /* Server requires optional client auth but will reject the client cert. We need to reset the config, to turn validation back on*/ + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dhparams_pem)); + EXPECT_NOT_NULL(default_security_policy = server_config->security_policy); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_OPTIONAL)); + + /* Verify that a handshake fails 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_security_policy security_policy; + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + + /* Craft a cipher preference with a cipher_idx cipher. */ + EXPECT_MEMCPY_SUCCESS(&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; + + EXPECT_MEMCPY_SUCCESS(&security_policy, default_security_policy, sizeof(security_policy)); + security_policy.cipher_preferences = &server_cipher_preferences; + + client_config->security_policy = &security_policy; + server_config->security_policy = &security_policy; + + /* Create nonblocking pipes. */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Verify the handshake failed. Blinding is disabled for the failure case to speed up tests. */ + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_FAILURE(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that neither connection 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)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + EXPECT_SUCCESS(s2n_config_free(client_config)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + free(cert_chain_pem); + free(private_key_pem); + free(dhparams_pem); + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_override_openssl_random_test.c b/tests/unit/s2n_override_openssl_random_test.c new file mode 100644 index 00000000000..823cecf9315 --- /dev/null +++ b/tests/unit/s2n_override_openssl_random_test.c @@ -0,0 +1,147 @@ +/* + * 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_dhe.h" +#include "crypto/s2n_drbg.h" +#include "crypto/s2n_ecc_evp.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" + +#if S2N_LIBCRYPTO_SUPPORTS_CUSTOM_RAND +const char reference_entropy_hex[] = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000" + "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000"; + +/** + * This test verifies that s2n can override OpenSSL's RNG with s2n's RNG implementation. We do that by initializing s2n's + * RNG with all zeros, overriding OpenSSL's RNG with s2n's, generating a DH Param, and verifying that the DH param + * matches a known hex string. + */ +const char expected_dhe_key_hex[] = "0100cb5fa155609f350a0f07e340ef7dc854e38d97c7c2ba68b3f7375146ed61cd56b6caf1ac7944aa05b9fa934150ef23040fac395d640a0c2d33da6d0523f04" + "f13702351fb8fcc4606a930dff73419d8bcf8a0037dd12b9d96e3a8121611f7d7046c29f44f8781bc47fd214b5ccd7519ff08fb83319b186d3b74b7d3f8298244" + "9c428e1ae8b1e9c9833b9cc92ee3b756e86e053ae892a480c366ee1258e3f9e14792d64c2cd9cb36108761ccd959382b966a20ba63fe7d12e496134363d58713f" + "e52ef3e8480acffc56f33bd83ce78cf673b9f0038a98c2ec2b10e12eb1fde71996e16d6dbf994ef1c8e429d89a403027af8549619a6500e2f1b81eac593d456c3" + "00010201001d255a7d1afbf0c706fd776a51e34074e0c0b86a1fdbafd6b893ea7e71ffe91de204f787836592c20bbafc71bfcfb38478827826e2fc76db25e263a" + "3c8e1c74d46344d3ef8939ec663e29de34698138d0a28fcf00bc0a65380c1ac58ee7d2d94f343bd94cb558bb6b30d24ca6465cae259239487b2e8796a9e54b518" + "4f4c78f3c31c27e091530da9e261d407b42da97718b6b44c9ca8a4cc74d3b6c43573051a97ec2cbf938f32fbb108e9f3cb397471fc2d3edaef46225e63720564b" + "ddbaa47646a497793e0a8e129e00e4fcd4b11b68897afb0987a48f51e3a3079e3d0573d340597c2c7b8ec839ea608a341c8d3ae8fb8a30c2d80e7083f64adf790" + "18a19c"; + +struct s2n_stuffer test_entropy = { 0 }; +int s2n_entropy_generator(void *data, uint32_t size) +{ + struct s2n_blob blob = { 0 }; + POSIX_GUARD(s2n_blob_init(&blob, data, size)); + POSIX_GUARD(s2n_stuffer_read(&test_entropy, &blob)); + return 0; +} + +int s2n_entropy_init_cleanup(void) +{ + return 0; +} + +int main(int argc, char **argv) +{ + struct s2n_stuffer dhparams_in = { 0 }, dhparams_out = { 0 }; + struct s2n_dh_params dh_params = { 0 }; + struct s2n_blob b = { 0 }; + char *dhparams_pem = NULL; + uint64_t bytes_used = 0; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_OK(s2n_get_private_random_bytes_used(&bytes_used)); + EXPECT_EQUAL(bytes_used, 0); + + /* Parse the DH params */ + EXPECT_SUCCESS(s2n_blob_init(&b, (uint8_t *) dhparams_pem, strlen(dhparams_pem) + 1)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&dhparams_in, b.size)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&dhparams_out, b.size)); + EXPECT_SUCCESS(s2n_stuffer_write(&dhparams_in, &b)); + EXPECT_SUCCESS(s2n_stuffer_dhparams_from_pem(&dhparams_in, &dhparams_out)); + uint32_t available_size = s2n_stuffer_data_available(&dhparams_out); + EXPECT_SUCCESS(s2n_blob_init(&b, s2n_stuffer_raw_read(&dhparams_out, available_size), available_size)); + EXPECT_SUCCESS(s2n_pkcs3_to_dh_params(&dh_params, &b)); + + EXPECT_SUCCESS(s2n_dh_generate_ephemeral_key(&dh_params)); + + /* Verify that our DRBG is called and that over-riding works */ + EXPECT_OK(s2n_get_private_random_bytes_used(&bytes_used)); + EXPECT_NOT_EQUAL(bytes_used, 0); + + /* Setup for the second test */ + EXPECT_SUCCESS(s2n_dh_params_free(&dh_params)); + EXPECT_SUCCESS(s2n_pkcs3_to_dh_params(&dh_params, &b)); + + /* Set s2n_random to use a new fixed DRBG to test that other known answer tests with s2n_random and OpenSSL are deterministic */ + EXPECT_SUCCESS(s2n_stuffer_alloc_ro_from_hex_string(&test_entropy, reference_entropy_hex)); + struct s2n_drbg drbg; + EXPECT_SUCCESS(s2n_rand_set_callbacks(s2n_entropy_init_cleanup, s2n_entropy_init_cleanup, s2n_entropy_generator, s2n_entropy_generator)); + + s2n_stack_blob(personalization_string, 32, 32); + EXPECT_OK(s2n_drbg_instantiate(&drbg, &personalization_string, S2N_AES_256_CTR_NO_DF_PR)); + EXPECT_OK(s2n_set_private_drbg_for_test(drbg)); + /* Verify we switched to a new DRBG */ + EXPECT_OK(s2n_get_private_random_bytes_used(&bytes_used)); + EXPECT_EQUAL(bytes_used, 0); + + DEFER_CLEANUP(struct s2n_stuffer out_stuffer = { 0 }, s2n_stuffer_free); + struct s2n_blob out_blob = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_alloc(&out_stuffer, 4096)); + POSIX_GUARD(s2n_dh_generate_ephemeral_key(&dh_params)); + POSIX_GUARD(s2n_dh_params_to_p_g_Ys(&dh_params, &out_stuffer, &out_blob)); + + EXPECT_OK(s2n_get_private_random_bytes_used(&bytes_used)); + EXPECT_EQUAL(bytes_used, 352); + + DEFER_CLEANUP(struct s2n_stuffer dhe_key_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_alloc_ro_from_hex_string(&dhe_key_stuffer, expected_dhe_key_hex)); + EXPECT_EQUAL(dhe_key_stuffer.blob.size, 519); + + EXPECT_EQUAL(out_blob.size, 519); + + EXPECT_EQUAL(0, memcmp(out_blob.data, dhe_key_stuffer.blob.data, out_blob.size)); + + EXPECT_SUCCESS(s2n_dh_params_free(&dh_params)); + EXPECT_SUCCESS(s2n_stuffer_free(&dhparams_out)); + EXPECT_SUCCESS(s2n_stuffer_free(&dhparams_in)); + EXPECT_SUCCESS(s2n_stuffer_free(&test_entropy)); + free(dhparams_pem); + + END_TEST(); +} + +#else + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + END_TEST(); +} + +#endif diff --git a/tests/unit/s2n_pem_rsa_dhe_test.c b/tests/unit/s2n_pem_rsa_dhe_test.c new file mode 100644 index 00000000000..edea5b16674 --- /dev/null +++ b/tests/unit/s2n_pem_rsa_dhe_test.c @@ -0,0 +1,205 @@ +/* + * 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 "crypto/s2n_dhe.h" +#include "crypto/s2n_fips.h" +#include "crypto/s2n_rsa.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" + +static uint8_t unmatched_private_key[] = + "-----BEGIN RSA PRIVATE KEY-----\n" + "MIIEpQIBAAKCAQEA6ddvtO6DbX5GLrWeXD2jufmR6lvYuzwpIHbDf0RQr2AT50wf\n" + "vV+sMYVa/d4g68tqtihH691tqzKevVc25LXM0a0f0NfnrNibyA1/66CwFQsyy1j2\n" + "U90BdguI/ZxqEV58ee/PzerOoS5d/rgBALzGwwYwyvlzr35KGjfRZ4XOFoToKnkN\n" + "0mR44firCvb7dJ53QRXoPbkYpQQQ3vMWMOtDZoPsvP0dJJ50B7LaFL6nbOyJmZ2T\n" + "evaAov1Nuk5QSrZf2icpuQVXxuu2xLmTNL25OUiV3t7ZjiLtrOZrmrIzv8/sLcWp\n" + "VJcFWFoKizmjvpDfD086a9c81vo4kqgD/RZ9dQIDAQABAoIBAQDBNUzJ3MxgupW4\n" + "YD2BDzjpH2jNj6fKRBHjDd3HmKVl0eeAE2iiKpt2qy2cVl0zFfaMnUmXe3PyoLeB\n" + "z76+R+v8TqPcBZgZOzuzllvcTv9N09vbIh0c+50KcMt2aDdHNJ96jIdRJzIlAM+O\n" + "9290sYU0fDfybRuFo74MXZQ6idbWyNMjXEv2oPrmvMmOHm10sz9BCXWFUpDpDFKD\n" + "8xgw1GZ1QeHITWoQXMI2uJUFFj2hYbRAl6f8cMXVjUipmMNpfJk4CbtEdSMPkjrz\n" + "XgIVrMCorCiaLuVQgSQCHB01n8ETM6R9PYSgMSg3RYYqKq4N0nvREUf4VvKV8JOY\n" + "EGzNgq3BAoGBAPvHPoCKQawLEr4jE5ITMqhZXlBXh8aU7LetjSY1BfnsiH43eg1n\n" + "186lfOX0r1I2KkVZTehZohkEo8xeEpc+XMdux1z0mX2uz1tfmHl838sRaTBs1JZ0\n" + "slkBrASfDdOnLlHCMhRmFEEnA0eelNYCiy/Ak0UX7NpuhZ3bh2jGemixAoGBAO3D\n" + "MupFbCl64AFEuLCA3wSFRhG82AScYAF1txoTM01V/kqy66j2jJsqMXkVcyBcLBkJ\n" + "5ozW4kIKkh0dYtIjDpsnXKBK/kVcvJN+60hwVnAMFFJPlRCdejkKSsSe9xod+FzU\n" + "DvwWLpLaO3sFtykGHelFbzMDB6FaZQFSfSMsNhIFAoGBAKaNafor+z9s38wpdfPG\n" + "gVc+LxakoGur7l+fDeU9ZCOs5ang1vtxOyA29sVDtIqEzDet2MygJou4NwalIFUu\n" + "ar9+t6D1KWgrsH24YivTgFNbxCLFi2ev8J7SbVFtSf8983UgKnK2CCYFQbUp4Tkk\n" + "26AOGx20svjX7cm8A/o6eZUxAoGBANtedXSnNuOSpmklMc5QKPRvzrWA6kJe0Umn\n" + "hZf+TSA2jlf3eu07BYIITPst6jnaMSms89XQUZOjUyqfuVSu2cQXbiPK7Y2rwaXI\n" + "vWbplybsTjefi6Z31ZQZReDh1pV3P3bOhUDban898RFRtauZJDHdSXrkeb7Ku1Sb\n" + "+i9glEbNAoGAJngXJZ2djyzCxYtSNyUFP8AZfmSy+vmHVCCri1FCoFfj9D1HTw4h\n" + "G0guyN3/Qm51gJVeBTOu/dtoA5JAZi6CRyNuhClLCqV+jGsUr9xyMzA+t/JHVr+0\n" + "XQhx4wqVYQs2852qaJg+wYUi50FtRT6hLyDQmg9ttvS4YIwMTdVzl2Q=\n" + "-----END RSA PRIVATE KEY-----\n"; + +int main(int argc, char **argv) +{ + struct s2n_stuffer certificate_in = { 0 }, certificate_out = { 0 }; + struct s2n_stuffer dhparams_in = { 0 }, dhparams_out = { 0 }; + struct s2n_stuffer rsa_key_in = { 0 }, rsa_key_out = { 0 }; + struct s2n_blob b = { 0 }; + char *leaf_cert_pem = NULL; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + char *dhparams_pem = NULL; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(leaf_cert_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_in, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_out, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&dhparams_in, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&dhparams_out, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&rsa_key_in, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&rsa_key_out, S2N_MAX_TEST_PEM_SIZE)); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_LEAF_CERT, leaf_cert_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + + EXPECT_SUCCESS(s2n_blob_init(&b, (uint8_t *) leaf_cert_pem, strlen(leaf_cert_pem) + 1)); + EXPECT_SUCCESS(s2n_stuffer_write(&certificate_in, &b)); + + EXPECT_SUCCESS(s2n_blob_init(&b, (uint8_t *) private_key_pem, strlen(private_key_pem) + 1)); + EXPECT_SUCCESS(s2n_stuffer_write(&rsa_key_in, &b)); + + EXPECT_SUCCESS(s2n_blob_init(&b, (uint8_t *) dhparams_pem, strlen(dhparams_pem) + 1)); + EXPECT_SUCCESS(s2n_stuffer_write(&dhparams_in, &b)); + + int type = 0; + EXPECT_SUCCESS(s2n_stuffer_certificate_from_pem(&certificate_in, &certificate_out)); + EXPECT_SUCCESS(s2n_stuffer_private_key_from_pem(&rsa_key_in, &rsa_key_out, &type)); + EXPECT_SUCCESS(s2n_stuffer_dhparams_from_pem(&dhparams_in, &dhparams_out)); + EXPECT_EQUAL(type, EVP_PKEY_RSA); + + struct s2n_pkey priv_key = { 0 }; + struct s2n_pkey pub_key = { 0 }; + s2n_pkey_type pkey_type = { 0 }; + + uint32_t available_size = 0; + available_size = s2n_stuffer_data_available(&certificate_out); + EXPECT_SUCCESS(s2n_blob_init(&b, s2n_stuffer_raw_read(&certificate_out, available_size), available_size)); + EXPECT_SUCCESS(s2n_asn1der_to_public_key_and_type(&pub_key, &pkey_type, &b)); + + /* Test without a type hint */ + int wrong_type = 0; + EXPECT_NOT_EQUAL(wrong_type, EVP_PKEY_RSA); + + available_size = s2n_stuffer_data_available(&rsa_key_out); + EXPECT_SUCCESS(s2n_blob_init(&b, s2n_stuffer_raw_read(&rsa_key_out, available_size), available_size)); + EXPECT_SUCCESS(s2n_asn1der_to_private_key(&priv_key, &b, wrong_type)); + + EXPECT_SUCCESS(s2n_pkey_match(&pub_key, &priv_key)); + + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + struct s2n_dh_params dh_params = { 0 }; + available_size = s2n_stuffer_data_available(&dhparams_out); + EXPECT_SUCCESS(s2n_blob_init(&b, s2n_stuffer_raw_read(&dhparams_out, available_size), available_size)); + EXPECT_SUCCESS(s2n_pkcs3_to_dh_params(&dh_params, &b)); + + EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); + + /* Try signing and verification with RSA */ + uint8_t inputpad[] = "Hello world!"; + struct s2n_blob signature = { 0 }; + struct s2n_hash_state tls10_one = { 0 }; + struct s2n_hash_state tls10_two = { 0 }; + struct s2n_hash_state tls12_one = { 0 }; + struct s2n_hash_state tls12_two = { 0 }; + + uint32_t maximum_signature_length = 0; + EXPECT_OK(s2n_pkey_size(&pub_key, &maximum_signature_length)); + EXPECT_SUCCESS(s2n_alloc(&signature, maximum_signature_length)); + + if (s2n_hash_is_available(S2N_HASH_MD5_SHA1)) { + /* TLS 1.0 use of RSA with DHE is not permitted when FIPS mode is set */ + EXPECT_SUCCESS(s2n_hash_new(&tls10_one)); + EXPECT_SUCCESS(s2n_hash_new(&tls10_two)); + + EXPECT_SUCCESS(s2n_hash_init(&tls10_one, S2N_HASH_MD5_SHA1)); + EXPECT_SUCCESS(s2n_hash_init(&tls10_two, S2N_HASH_MD5_SHA1)); + + EXPECT_SUCCESS(s2n_hash_update(&tls10_one, inputpad, sizeof(inputpad))); + EXPECT_SUCCESS(s2n_hash_update(&tls10_two, inputpad, sizeof(inputpad))); + EXPECT_SUCCESS(s2n_pkey_sign(&priv_key, S2N_SIGNATURE_RSA, &tls10_one, &signature)); + EXPECT_SUCCESS(s2n_pkey_verify(&pub_key, S2N_SIGNATURE_RSA, &tls10_two, &signature)); + + EXPECT_SUCCESS(s2n_hash_free(&tls10_one)); + EXPECT_SUCCESS(s2n_hash_free(&tls10_two)); + } + + /* TLS 1.2 use of RSA with DHE is permitted for FIPS and non-FIPS */ + EXPECT_SUCCESS(s2n_hash_new(&tls12_one)); + EXPECT_SUCCESS(s2n_hash_new(&tls12_two)); + + EXPECT_SUCCESS(s2n_hash_init(&tls12_one, S2N_HASH_SHA1)); + EXPECT_SUCCESS(s2n_hash_init(&tls12_two, S2N_HASH_SHA1)); + + EXPECT_SUCCESS(s2n_hash_update(&tls12_one, inputpad, sizeof(inputpad))); + EXPECT_SUCCESS(s2n_hash_update(&tls12_two, inputpad, sizeof(inputpad))); + EXPECT_SUCCESS(s2n_pkey_sign(&priv_key, S2N_SIGNATURE_RSA, &tls12_one, &signature)); + EXPECT_SUCCESS(s2n_pkey_verify(&pub_key, S2N_SIGNATURE_RSA, &tls12_two, &signature)); + + EXPECT_SUCCESS(s2n_hash_free(&tls12_one)); + EXPECT_SUCCESS(s2n_hash_free(&tls12_two)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(config)); + + /* Mismatched public/private key should fail */ + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, (char *) unmatched_private_key)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(config)); + + EXPECT_SUCCESS(s2n_dh_params_free(&dh_params)); + EXPECT_SUCCESS(s2n_pkey_free(&priv_key)); + EXPECT_SUCCESS(s2n_pkey_free(&pub_key)); + EXPECT_SUCCESS(s2n_free(&signature)); + EXPECT_SUCCESS(s2n_stuffer_free(&certificate_in)); + EXPECT_SUCCESS(s2n_stuffer_free(&certificate_out)); + EXPECT_SUCCESS(s2n_stuffer_free(&dhparams_in)); + EXPECT_SUCCESS(s2n_stuffer_free(&dhparams_out)); + EXPECT_SUCCESS(s2n_stuffer_free(&rsa_key_in)); + EXPECT_SUCCESS(s2n_stuffer_free(&rsa_key_out)); + free(cert_chain_pem); + free(leaf_cert_pem); + free(private_key_pem); + free(dhparams_pem); + + END_TEST(); +} diff --git a/tests/unit/s2n_pem_test.c b/tests/unit/s2n_pem_test.c new file mode 100644 index 00000000000..7035a4445bb --- /dev/null +++ b/tests/unit/s2n_pem_test.c @@ -0,0 +1,96 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_safety.h" + +/* The ECDSA private key is missing the "publicKey" field, which is optional. + * The missing field makes the cert type difficult to detect via ASN1 parsing */ +#define S2N_MISSING_ECDSA_PUB_CERT_KEY "../pems/missing_public_key_ecdsa_key.pem" +#define S2N_MISSING_ECDSA_PUB_CERT_CERT_CHAIN "../pems/missing_public_key_ecdsa_cert.pem" + +static const char *valid_pem_pairs[][2] = { + { S2N_RSA_2048_PKCS8_CERT_CHAIN, S2N_RSA_2048_PKCS8_KEY }, + { S2N_RSA_2048_PKCS1_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY }, + { S2N_RSA_2048_PKCS1_LEAF_CERT, S2N_RSA_2048_PKCS1_KEY }, + { S2N_RSA_CERT_CHAIN_CRLF, S2N_RSA_KEY_CRLF }, + /* PEMs with no-op data before/after entries are still valid */ + { S2N_LEAF_WHITESPACE_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY }, + { S2N_INTERMEDIATE_WHITESPACE_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY }, + { S2N_ROOT_WHITESPACE_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY }, + { S2N_TRAILING_WHITESPACE_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY }, + { S2N_LEADING_COMMENT_TEXT_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY }, + { S2N_LONG_BASE64_LINES_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY }, + { S2N_MISSING_LINE_ENDINGS_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY }, + { S2N_MISSING_ECDSA_PUB_CERT_CERT_CHAIN, S2N_MISSING_ECDSA_PUB_CERT_KEY }, + + /* Technically Invalid according to RFC, but that we are lenient towards */ + { S2N_INVALID_HEADER_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY }, + { S2N_INVALID_TRAILER_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY }, + { S2N_RSA_2048_PKCS1_CERT_CHAIN, S2N_INVALID_TRAILER_KEY }, + { S2N_WEIRD_DASHES_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY }, +}; + +static const char *invalid_pem_pairs[][2] = { + /* Invalid cert PEMs and valid key PEMs */ + { S2N_UNKNOWN_KEYWORD_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY }, + /* Valid cert PEMs and invalid key PEMs */ + { S2N_RSA_2048_PKCS1_CERT_CHAIN, S2N_INVALID_HEADER_KEY }, + { S2N_RSA_2048_PKCS1_CERT_CHAIN, S2N_UNKNOWN_KEYWORD_KEY }, + /* For good measure an invalid cert and invalid key */ + { S2N_UNKNOWN_KEYWORD_CERT_CHAIN, S2N_UNKNOWN_KEYWORD_KEY }, + { S2N_NO_DASHES_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY }, +}; + +int main(int argc, char **argv) +{ + struct s2n_config *config; + char *cert_chain_pem; + char *private_key_pem; + struct s2n_cert_chain_and_key *chain_and_key; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + + for (size_t i = 0; i < s2n_array_len(valid_pem_pairs); i++) { + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_read_test_pem(valid_pem_pairs[i][0], cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(valid_pem_pairs[i][1], private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(config)); + } + + for (size_t i = 0; i < s2n_array_len(invalid_pem_pairs); i++) { + EXPECT_SUCCESS(s2n_read_test_pem(invalid_pem_pairs[i][0], cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(invalid_pem_pairs[i][1], private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + } + + free(cert_chain_pem); + free(private_key_pem); + END_TEST(); +} diff --git a/tests/unit/s2n_pkey_test.c b/tests/unit/s2n_pkey_test.c new file mode 100644 index 00000000000..015c1a3ddc2 --- /dev/null +++ b/tests/unit/s2n_pkey_test.c @@ -0,0 +1,171 @@ +/* + * 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 "crypto/s2n_rsa_pss.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Test each combination of s2n_pkey_types to validate that only keys of + * the same type can be compared */ + { + struct s2n_cert_chain_and_key *chain_and_key; + char rsa_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char rsa_pss_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char ecdsa_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char rsa_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char rsa_pss_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + char ecdsa_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_CERT_CHAIN, rsa_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_CERT, rsa_pss_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, ecdsa_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_KEY, rsa_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_KEY, rsa_pss_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, ecdsa_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + + /* Keys of the same type can be compared */ + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_cert_chain_pem, rsa_private_key_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + if (s2n_is_rsa_pss_certs_supported()) { + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS( + s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_pss_cert_chain_pem, rsa_pss_private_key_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + } + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, ecdsa_cert_chain_pem, ecdsa_private_key_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + /* Keys of different types cannot be compared */ + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_cert_chain_pem, ecdsa_private_key_pem), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem(chain_and_key, ecdsa_cert_chain_pem, rsa_private_key_pem), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + if (s2n_is_rsa_pss_certs_supported()) { + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO( + s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_cert_chain_pem, rsa_pss_private_key_pem), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_pss_cert_chain_pem, rsa_private_key_pem), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem(chain_and_key, rsa_pss_cert_chain_pem, ecdsa_private_key_pem), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem(chain_and_key, ecdsa_cert_chain_pem, rsa_pss_private_key_pem), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + } + }; + + /* Test the same as above but with non null terminated chain and key and + * api that accepts length */ + { + struct s2n_cert_chain_and_key *chain_and_key; + uint8_t rsa_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint8_t rsa_pss_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint8_t ecdsa_cert_chain_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint8_t rsa_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint8_t rsa_pss_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + uint8_t ecdsa_private_key_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + + uint32_t rsa_cert_chain_len = 0; + uint32_t rsa_pss_cert_chain_len = 0; + uint32_t ecdsa_cert_chain_len = 0; + uint32_t rsa_private_key_len = 0; + uint32_t rsa_pss_private_key_len = 0; + uint32_t ecdsa_private_key_len = 0; + + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_2048_PKCS1_CERT_CHAIN, rsa_cert_chain_pem, &rsa_cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_PSS_2048_SHA256_CA_CERT, rsa_pss_cert_chain_pem, &rsa_pss_cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, ecdsa_cert_chain_pem, &ecdsa_cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_2048_PKCS1_KEY, rsa_private_key_pem, &rsa_private_key_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_RSA_PSS_2048_SHA256_CA_KEY, rsa_pss_private_key_pem, &rsa_pss_private_key_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_KEY, ecdsa_private_key_pem, &ecdsa_private_key_len, S2N_MAX_TEST_PEM_SIZE)); + + /* Keys of the same type can be compared */ + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_cert_chain_pem, rsa_cert_chain_len, rsa_private_key_pem, rsa_private_key_len)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + if (s2n_is_rsa_pss_certs_supported()) { + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS( + s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_pss_cert_chain_pem, rsa_pss_cert_chain_len, rsa_pss_private_key_pem, rsa_pss_private_key_len)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + } + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, ecdsa_cert_chain_pem, ecdsa_cert_chain_len, ecdsa_private_key_pem, ecdsa_private_key_len)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + /* Keys of different types cannot be compared */ + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_cert_chain_pem, rsa_cert_chain_len, ecdsa_private_key_pem, ecdsa_private_key_len), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, ecdsa_cert_chain_pem, ecdsa_cert_chain_len, rsa_private_key_pem, rsa_private_key_len), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + if (s2n_is_rsa_pss_certs_supported()) { + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO( + s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_cert_chain_pem, rsa_cert_chain_len, rsa_pss_private_key_pem, rsa_pss_private_key_len), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_pss_cert_chain_pem, rsa_pss_cert_chain_len, rsa_private_key_pem, rsa_private_key_len), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, rsa_pss_cert_chain_pem, rsa_pss_cert_chain_len, ecdsa_private_key_pem, ecdsa_private_key_len), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_FAILURE_WITH_ERRNO(s2n_cert_chain_and_key_load_pem_bytes(chain_and_key, ecdsa_cert_chain_pem, ecdsa_cert_chain_len, rsa_pss_private_key_pem, rsa_pss_private_key_len), + S2N_ERR_KEY_MISMATCH); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + } + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_post_handshake_recv_test.c b/tests/unit/s2n_post_handshake_recv_test.c new file mode 100644 index 00000000000..9789c785bff --- /dev/null +++ b/tests/unit/s2n_post_handshake_recv_test.c @@ -0,0 +1,516 @@ +/* + * 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/unstable/renegotiate.h" +#include "error/s2n_errno.h" +#include "s2n_test.h" +#include "testlib/s2n_mem_testlib.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_key_update.h" +#include "tls/s2n_post_handshake.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13_handshake.h" +#include "utils/s2n_safety.h" + +#define S2N_TEST_MESSAGE_COUNT 5 + +int s2n_key_update_write(struct s2n_blob *out); + +size_t tickets_count = 0; +static int s2n_ticket_count_cb(struct s2n_connection *conn, void *ctx, struct s2n_session_ticket *ticket) +{ + tickets_count++; + return S2N_SUCCESS; +} + +size_t hello_request_count = 0; +static int s2n_hello_request_cb(struct s2n_connection *conn, void *ctx, s2n_renegotiate_response *response) +{ + hello_request_count++; + *response = S2N_RENEGOTIATE_IGNORE; + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_test_send_records(struct s2n_connection *conn, struct s2n_stuffer messages, uint32_t fragment_size) +{ + conn->max_outgoing_fragment_length = fragment_size; + + DEFER_CLEANUP(struct s2n_blob record_data = { 0 }, s2n_free); + RESULT_GUARD_POSIX(s2n_alloc(&record_data, fragment_size)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + uint32_t remaining = 0; + while ((remaining = s2n_stuffer_data_available(&messages)) > 0) { + record_data.size = MIN(record_data.size, remaining); + RESULT_GUARD_POSIX(s2n_stuffer_read(&messages, &record_data)); + RESULT_GUARD(s2n_record_write(conn, TLS_HANDSHAKE, &record_data)); + RESULT_GUARD_POSIX(s2n_flush(conn, &blocked)); + }; + + return S2N_RESULT_OK; +} + +/* + * Verify that the receiver can receive a byte sent by the sender. + * In the process, we also verify that the receiver can receive all previous + * data sent by the sender, since TCP / TLS messages have a guaranteed order. + */ +static S2N_RESULT s2n_test_basic_recv(struct s2n_connection *sender, struct s2n_connection *receiver) +{ + uint8_t app_data[1] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + int send_ret = s2n_send(sender, app_data, sizeof(app_data), &blocked); + RESULT_GUARD_POSIX(send_ret); + RESULT_ENSURE_EQ(send_ret, sizeof(app_data)); + + /* Reset all counters */ + RESULT_GUARD(s2n_mem_test_wipe_callbacks()); + tickets_count = 0; + hello_request_count = 0; + + int recv_ret = s2n_recv(receiver, app_data, sizeof(app_data), &blocked); + RESULT_GUARD_POSIX(recv_ret); + RESULT_ENSURE_EQ(recv_ret, sizeof(app_data)); + + return S2N_RESULT_OK; +} + +/* Like s2n_test_basic_recv, + * but we make only one byte of data available at a time. + * This forces us to call s2n_recv repeatedly and verifies that s2n_recv + * can resume across s2n_recv calls while handling fragmented post-handshake messages. + */ +static S2N_RESULT s2n_test_blocking_recv(struct s2n_connection *sender, struct s2n_connection *receiver, + struct s2n_test_io_stuffer_pair *io_pair) +{ + uint8_t app_data[1] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + int send_ret = s2n_send(sender, app_data, sizeof(app_data), &blocked); + RESULT_GUARD_POSIX(send_ret); + RESULT_ENSURE_EQ(send_ret, sizeof(app_data)); + + /* Reset all counters */ + RESULT_GUARD(s2n_mem_test_wipe_callbacks()); + tickets_count = 0; + hello_request_count = 0; + + /* Modify the stuffer's write_cursor to make only one byte + * of the socket / input data available at a time. + */ + struct s2n_stuffer *in = &io_pair->client_in; + if (receiver->mode == S2N_SERVER) { + in = &io_pair->server_in; + } + uint32_t *write_cursor = &in->write_cursor; + uint32_t *read_cursor = &in->read_cursor; + RESULT_ENSURE_GT(write_cursor, read_cursor); + uint32_t saved_write_cursor = *write_cursor; + RESULT_ENSURE_GT(saved_write_cursor, 0); + *write_cursor = *read_cursor + 1; + + while (s2n_recv(receiver, app_data, sizeof(app_data), &blocked) < 0) { + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + (*write_cursor)++; + RESULT_ENSURE_LTE(*write_cursor, saved_write_cursor); + } + RESULT_ENSURE_EQ(*write_cursor, saved_write_cursor); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_test_init_sender_and_receiver(struct s2n_config *config, + struct s2n_connection *sender, struct s2n_connection *receiver, + struct s2n_test_io_stuffer_pair *io_pair) +{ + RESULT_GUARD_POSIX(s2n_connection_set_config(sender, config)); + RESULT_GUARD_POSIX(s2n_connection_set_all_protocol_versions(sender, S2N_TLS13)); + RESULT_GUARD(s2n_connection_set_secrets(sender)); + RESULT_GUARD_POSIX(s2n_connection_set_blinding(sender, S2N_SELF_SERVICE_BLINDING)); + + RESULT_GUARD_POSIX(s2n_connection_set_config(receiver, config)); + RESULT_GUARD_POSIX(s2n_connection_set_all_protocol_versions(receiver, S2N_TLS13)); + RESULT_GUARD(s2n_connection_set_secrets(receiver)); + RESULT_GUARD_POSIX(s2n_connection_set_blinding(receiver, S2N_SELF_SERVICE_BLINDING)); + + RESULT_GUARD(s2n_io_stuffer_pair_init(io_pair)); + if (sender->mode == S2N_SERVER) { + RESULT_GUARD(s2n_connections_set_io_stuffer_pair(receiver, sender, io_pair)); + } else { + RESULT_GUARD(s2n_connections_set_io_stuffer_pair(sender, receiver, io_pair)); + } + + /* Send and receive to initialize io buffers */ + EXPECT_OK(s2n_test_basic_recv(sender, receiver)); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + const uint8_t unknown_message_type = UINT8_MAX; + const uint32_t test_large_message_size = 3001; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_ticket_count_cb, NULL)); + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(config, s2n_hello_request_cb, NULL)); + + /* Some tests require sending and receiving tickets. + * Setup the config to handle tickets, but don't send any by default. + */ + uint8_t ticket_key_name[16] = "key name"; + uint8_t ticket_key[] = "key data"; + uint64_t current_time = 0; + EXPECT_SUCCESS(config->wall_clock(config->sys_clock_ctx, ¤t_time)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, sizeof(ticket_key_name), + ticket_key, sizeof(ticket_key), current_time / ONE_SEC_IN_NANOS)); + config->initial_tickets_to_send = 0; + + const uint32_t fragment_sizes[] = { + 1, + 2, + S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE, + TLS_HANDSHAKE_HEADER_LENGTH, + TLS_HANDSHAKE_HEADER_LENGTH + 1, + S2N_DEFAULT_FRAGMENT_LENGTH, + S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, + }; + const uint8_t modes[] = { S2N_CLIENT, S2N_SERVER }; + + /* Test: client and server receive small post-handshake messages (KeyUpdates) */ + for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { + uint32_t fragment_size = fragment_sizes[frag_i]; + + for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { + uint8_t mode = modes[mode_i]; + + DEFER_CLEANUP(struct s2n_connection *receiver = s2n_connection_new(mode), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *sender = s2n_connection_new(S2N_PEER_MODE(mode)), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, sender, receiver, &io_pair)); + + /* Write KeyUpdate message */ + struct s2n_stuffer message = { 0 }; + DEFER_CLEANUP(struct s2n_blob message_blob = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&message_blob, S2N_KEY_UPDATE_MESSAGE_SIZE)); + for (size_t i = 0; i < S2N_TEST_MESSAGE_COUNT; i++) { + EXPECT_SUCCESS(s2n_key_update_write(&message_blob)); + EXPECT_SUCCESS(s2n_stuffer_init(&message, &message_blob)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&message, S2N_KEY_UPDATE_MESSAGE_SIZE)); + + /* The TLS1.3 RFC says "Handshake messages MUST NOT span key changes". + * Because KeyUpdate messages trigger key changes, we cannot include multiple in one record. + * We must send individual KeyUpdate messages. + */ + EXPECT_OK(s2n_test_send_records(sender, message, fragment_size)); + + /* Update the traffic keys for the next records */ + EXPECT_SUCCESS(s2n_update_application_traffic_keys(sender, sender->mode, SENDING)); + } + + /* + * We have no mechanism to count KeyUpdates, but we can assume they are processed + * if we successfully decrypt all records. If they were not processed, + * then we would try to use the wrong key to decrypt the next record. + */ + DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); + EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); + EXPECT_OK(s2n_test_basic_recv(sender, receiver)); + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + } + } + + /* Test: client receives large post-handshake messages (NewSessionTickets) + * + * There is no server version of this test because there are no large post-handshake messages + * valid for the server to accept. + */ + for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { + uint32_t fragment_size = fragment_sizes[frag_i]; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, server, client, &io_pair)); + + /* Write NewSessionTicket message */ + DEFER_CLEANUP(struct s2n_stuffer messages = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&messages, 0)); + for (size_t i = 0; i < S2N_TEST_MESSAGE_COUNT; i++) { + server->tickets_to_send++; + EXPECT_OK(s2n_tls13_server_nst_write(server, &messages)); + } + + EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); + EXPECT_OK(s2n_test_basic_recv(server, client)); + EXPECT_EQUAL(tickets_count, S2N_TEST_MESSAGE_COUNT); + + EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); + EXPECT_OK(s2n_test_blocking_recv(server, client, &io_pair)); + EXPECT_EQUAL(tickets_count, S2N_TEST_MESSAGE_COUNT); + } + + /* Test: client receives large post-handshake messages of different sizes (NewSessionTickets) + * + * There is no server version of this test because there are no large post-handshake messages + * valid for the server to accept. + */ + for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { + uint32_t fragment_size = fragment_sizes[frag_i]; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, server, client, &io_pair)); + server->server_max_early_data_size = 10; + + size_t total_size = 0; + DEFER_CLEANUP(struct s2n_stuffer messages = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&messages, 0)); + for (size_t i = 0; i < 3; i++) { + /* Write a basic NewSessionTicket */ + server->server_max_early_data_size_overridden = false; + server->tickets_to_send++; + EXPECT_OK(s2n_tls13_server_nst_write(server, &messages)); + size_t min_length = s2n_stuffer_data_available(&messages) - total_size; + total_size += min_length; + + /* Write a NewSesionTicket with early data enabled + * so that the early_data_indication extension is included + * and the message is therefore longer. + */ + server->server_max_early_data_size_overridden = true; + server->tickets_to_send++; + EXPECT_OK(s2n_tls13_server_nst_write(server, &messages)); + size_t max_length = s2n_stuffer_data_available(&messages) - total_size; + EXPECT_TRUE(max_length > min_length); + total_size += max_length; + } + + EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); + EXPECT_OK(s2n_test_basic_recv(server, client)); + EXPECT_EQUAL(tickets_count, server->tickets_to_send); + + EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); + EXPECT_OK(s2n_test_blocking_recv(server, client, &io_pair)); + EXPECT_EQUAL(tickets_count, server->tickets_to_send); + } + + /* Test: server rejects known, invalid post-handshake messages (NewSessionTickets) + * + * There is no client version of this test because the client accepts all supported + * post-handshake messages. + */ + for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { + uint32_t fragment_size = fragment_sizes[frag_i]; + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, client, server, &io_pair)); + + /* Send NewSessionTicket message */ + DEFER_CLEANUP(struct s2n_stuffer messages = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&messages, 0)); + client->tickets_to_send = 1; + EXPECT_OK(s2n_tls13_server_nst_write(client, &messages)); + EXPECT_OK(s2n_test_send_records(client, messages, fragment_size)); + + DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); + EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); + EXPECT_ERROR_WITH_ERRNO(s2n_test_basic_recv(client, server), S2N_ERR_BAD_MESSAGE); + EXPECT_EQUAL(tickets_count, 0); + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + } + + /* Test: server rejects fragmented post-handshake message (KeyUpdate) with an invalid size + * + * This response is unique to the server because we want to prevent a malicious + * client from forcing the server to allocate large amounts of memory. + * + * While we could extend the same validation to the client, the client accepts + * a variable-sized message (NewSessionTicket) so can't really be protected. + */ + { + /* This test is only interesting if the message is fragmented */ + uint32_t fragment_size = 2; + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, client, server, &io_pair)); + + /* Write large KeyUpdate messages */ + DEFER_CLEANUP(struct s2n_stuffer message = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&message, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&message, TLS_KEY_UPDATE)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&message, test_large_message_size)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&message, test_large_message_size)); + EXPECT_OK(s2n_test_send_records(client, message, fragment_size)); + EXPECT_SUCCESS(s2n_update_application_traffic_keys(client, client->mode, SENDING)); + + DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); + EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); + EXPECT_ERROR_WITH_ERRNO(s2n_test_basic_recv(client, server), S2N_ERR_BAD_MESSAGE); + + /* No post-handshake message should trigger the server to allocate memory */ + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + }; + + /* Test: client receives empty post-handshake messages (HelloRequests) + * + * There is no server version of this test because there are no empty post-handshake messages + * valid for the server to accept. + */ + for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { + uint32_t fragment_size = fragment_sizes[frag_i]; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, server, client, &io_pair)); + + /* HelloRequests are ignored if secure_renegotiation isn't set */ + client->secure_renegotiation = true; + + /* Write HelloRequest messages */ + DEFER_CLEANUP(struct s2n_stuffer messages = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&messages, 0)); + for (size_t i = 0; i < S2N_TEST_MESSAGE_COUNT; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&messages, TLS_HELLO_REQUEST)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&messages, 0)); + } + + DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); + EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); + + EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); + EXPECT_OK(s2n_test_basic_recv(server, client)); + EXPECT_EQUAL(hello_request_count, S2N_TEST_MESSAGE_COUNT); + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + + EXPECT_OK(s2n_test_send_records(server, messages, fragment_size)); + EXPECT_OK(s2n_test_blocking_recv(server, client, &io_pair)); + EXPECT_EQUAL(hello_request_count, S2N_TEST_MESSAGE_COUNT); + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + } + + /* Test: client and server reject known, invalid messages (ClientHellos) */ + for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { + uint32_t fragment_size = fragment_sizes[frag_i]; + + for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { + uint8_t mode = modes[mode_i]; + + DEFER_CLEANUP(struct s2n_connection *receiver = s2n_connection_new(mode), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *sender = s2n_connection_new(S2N_PEER_MODE(mode)), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, sender, receiver, &io_pair)); + + /* Send fake ClientHello messages */ + DEFER_CLEANUP(struct s2n_stuffer message = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&message, TLS_HANDSHAKE_HEADER_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&message, TLS_CLIENT_HELLO)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&message, test_large_message_size)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&message, test_large_message_size)); + EXPECT_OK(s2n_test_send_records(sender, message, fragment_size)); + + DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); + EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); + EXPECT_ERROR_WITH_ERRNO(s2n_test_basic_recv(sender, receiver), S2N_ERR_BAD_MESSAGE); + + /* No post-handshake message should trigger the server to allocate memory */ + if (mode == S2N_SERVER) { + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + } + } + } + + /* Test: client and server reject unknown messages */ + for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { + uint32_t fragment_size = fragment_sizes[frag_i]; + + for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { + uint8_t mode = modes[mode_i]; + + DEFER_CLEANUP(struct s2n_connection *receiver = s2n_connection_new(mode), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *sender = s2n_connection_new(S2N_PEER_MODE(mode)), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, sender, receiver, &io_pair)); + + /* Send unknown message */ + DEFER_CLEANUP(struct s2n_stuffer message = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&message, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&message, unknown_message_type)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&message, test_large_message_size)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&message, test_large_message_size)); + EXPECT_OK(s2n_test_send_records(sender, message, fragment_size)); + + DEFER_CLEANUP(struct s2n_mem_test_cb_scope mem_ctx = { 0 }, s2n_mem_test_free_callbacks); + EXPECT_OK(s2n_mem_test_init_callbacks(&mem_ctx)); + EXPECT_ERROR_WITH_ERRNO(s2n_test_basic_recv(sender, receiver), S2N_ERR_BAD_MESSAGE); + + /* No post-handshake message should trigger the server to allocate memory */ + if (mode == S2N_SERVER) { + EXPECT_OK(s2n_mem_test_assert_malloc_count(0)); + } + } + } + + /** + *= https://tools.ietf.org/rfc/rfc8446#section-5.1 + *= type=test + *# - Handshake messages MUST NOT be interleaved with other record + *# types. That is, if a handshake message is split over two or more + *# records, there MUST NOT be any other records between them. + */ + for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { + uint8_t mode = modes[mode_i]; + + /* This test is only interesting if the message is fragmented */ + uint32_t fragment_size = 2; + + DEFER_CLEANUP(struct s2n_connection *receiver = s2n_connection_new(mode), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *sender = s2n_connection_new(S2N_PEER_MODE(mode)), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_sender_and_receiver(config, sender, receiver, &io_pair)); + + /* Write a partial message */ + DEFER_CLEANUP(struct s2n_stuffer message = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&message, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&message, TLS_KEY_UPDATE)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&message, S2N_KEY_UPDATE_LENGTH)); + /* Don't write the actual message body -- we want the message to be incomplete */ + + /* Verify we can't receive the records: s2n_test_send_records does not send + * the complete handshake message, so we receive the application data sent by + * s2n_test_basic_recv in the middle of the handshake message. + */ + EXPECT_OK(s2n_test_send_records(sender, message, fragment_size)); + EXPECT_ERROR_WITH_ERRNO(s2n_test_basic_recv(sender, receiver), S2N_ERR_BAD_MESSAGE); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_post_handshake_send_test.c b/tests/unit/s2n_post_handshake_send_test.c new file mode 100644 index 00000000000..967af96e064 --- /dev/null +++ b/tests/unit/s2n_post_handshake_send_test.c @@ -0,0 +1,235 @@ +/* + * 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 "error/s2n_errno.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_post_handshake.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_safety.h" + +#define S2N_FRAG_LEN_SMALLER_THAN_NST 50 + +static S2N_RESULT s2n_get_expected_record_count(uint32_t nst_size, uint32_t fragment_size, uint8_t tickets_to_send, + uint64_t *expected_record_count) +{ + uint32_t app_data_records = 1; + uint32_t records_per_nst = ceil((1.0 * nst_size) / fragment_size); + uint32_t nst_records = records_per_nst * tickets_to_send; + *expected_record_count = app_data_records + nst_records; + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_get_actual_record_count(uint8_t *cur_seq_num_bytes, + uint64_t *last_seq_num, uint64_t *actual_record_count) +{ + uint64_t cur_seq_num = 0; + struct s2n_blob blob = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&blob, cur_seq_num_bytes, S2N_TLS_SEQUENCE_NUM_LEN)); + RESULT_GUARD_POSIX(s2n_stuffer_init(&stuffer, &blob)); + RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&stuffer, S2N_TLS_SEQUENCE_NUM_LEN)); + RESULT_GUARD_POSIX(s2n_stuffer_read_uint64(&stuffer, &cur_seq_num)); + + *actual_record_count = cur_seq_num - *last_seq_num; + *last_seq_num = cur_seq_num; + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_get_nst_message_size(struct s2n_connection *conn, uint32_t *size) +{ + DEFER_CLEANUP(struct s2n_stuffer nst_message = { 0 }, s2n_stuffer_free); + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&nst_message, 0)); + RESULT_GUARD(s2n_tls13_server_nst_write(conn, &nst_message)); + *size = s2n_stuffer_data_available(&nst_message); + EXPECT_TRUE(*size > S2N_FRAG_LEN_SMALLER_THAN_NST); + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + + /* These tests require sending tickets. + * Setup the config to handle tickets, but don't send any by default. + */ + uint8_t ticket_key_name[16] = "key name"; + uint8_t ticket_key[] = "key data"; + uint64_t current_time = 0; + EXPECT_SUCCESS(config->wall_clock(config->sys_clock_ctx, ¤t_time)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, sizeof(ticket_key_name), + ticket_key, sizeof(ticket_key), current_time / ONE_SEC_IN_NANOS)); + config->initial_tickets_to_send = 0; + + const uint32_t fragment_sizes[] = { + 1, + 2, + TLS_HANDSHAKE_HEADER_LENGTH, + TLS_HANDSHAKE_HEADER_LENGTH + 1, + S2N_FRAG_LEN_SMALLER_THAN_NST, + S2N_DEFAULT_FRAGMENT_LENGTH, + S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, + }; + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + const uint8_t send_data[1] = { 'k' }; + const uint8_t tickets_to_send = 3; + + /* Test: send fragmented post-handshake message (NewSessionTicket) */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + EXPECT_OK(s2n_connection_set_secrets(server_conn)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(client_conn, S2N_TLS13)); + EXPECT_OK(s2n_connection_set_secrets(client_conn)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &io_pair)); + + /* Calculate the size of the NewSessionTicket message */ + uint32_t nst_size = 0; + EXPECT_OK(s2n_get_nst_message_size(server_conn, &nst_size)); + + /* Test: the messages are fragmented into the number of records expected */ + uint64_t write_seq_num = 0; + for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { + const uint32_t fragment_size = fragment_sizes[frag_i]; + + server_conn->max_outgoing_fragment_length = fragment_size; + server_conn->tickets_sent = 0; + server_conn->tickets_to_send = tickets_to_send; + + EXPECT_SUCCESS(s2n_send(server_conn, send_data, sizeof(send_data), &blocked)); + EXPECT_EQUAL(server_conn->tickets_sent, tickets_to_send); + EXPECT_TRUE(s2n_stuffer_is_freed(&server_conn->handshake.io)); + + uint64_t actual_record_count = 0; + uint64_t expected_record_count = 0; + uint8_t *seq_num = server_conn->server->server_sequence_number; + EXPECT_OK(s2n_get_actual_record_count(seq_num, &write_seq_num, &actual_record_count)); + EXPECT_OK(s2n_get_expected_record_count(nst_size, fragment_size, tickets_to_send, &expected_record_count)); + EXPECT_EQUAL(actual_record_count, expected_record_count); + } + + /* Test: the full messages can be parsed by the client */ + uint64_t read_seq_num = 0; + for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { + const uint32_t fragment_size = fragment_sizes[frag_i]; + + uint8_t recv_data[sizeof(send_data)] = { 0 }; + EXPECT_SUCCESS(s2n_recv(client_conn, recv_data, sizeof(recv_data), &blocked)); + EXPECT_BYTEARRAY_EQUAL(send_data, recv_data, sizeof(recv_data)); + + uint64_t actual_record_count = 0; + uint64_t expected_record_count = 0; + uint8_t *seq_num = client_conn->server->server_sequence_number; + EXPECT_OK(s2n_get_actual_record_count(seq_num, &read_seq_num, &actual_record_count)); + EXPECT_OK(s2n_get_expected_record_count(nst_size, fragment_size, tickets_to_send, &expected_record_count)); + EXPECT_EQUAL(actual_record_count, expected_record_count); + } + }; + + /* Test: send fragmented post-handshake messages (NewSessionTicket) when IO blocks */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + EXPECT_OK(s2n_connection_set_secrets(server_conn)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(client_conn, S2N_TLS13)); + EXPECT_OK(s2n_connection_set_secrets(client_conn)); + + DEFER_CLEANUP(struct s2n_test_io_stuffer_pair io_pair = { 0 }, s2n_io_stuffer_pair_free); + EXPECT_OK(s2n_io_stuffer_pair_init(&io_pair)); + EXPECT_OK(s2n_connections_set_io_stuffer_pair(client_conn, server_conn, &io_pair)); + + /* Calculate the size of the NewSessionTicket message */ + uint32_t nst_size = 0; + EXPECT_OK(s2n_get_nst_message_size(server_conn, &nst_size)); + + /* Free the client_in (server_out) so that we can later trigger blocking */ + struct s2n_stuffer *server_out = &io_pair.client_in; + EXPECT_SUCCESS(s2n_stuffer_free(server_out)); + EXPECT_SUCCESS(s2n_stuffer_alloc(server_out, 0)); + + /* Test: the messages can be sent despite constant IO blocking */ + uint64_t write_seq_num = 0; + for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { + const uint32_t fragment_size = fragment_sizes[frag_i]; + + server_conn->max_outgoing_fragment_length = fragment_size; + server_conn->tickets_sent = 0; + server_conn->tickets_to_send = tickets_to_send; + + while (s2n_send(server_conn, send_data, sizeof(send_data), &blocked) < S2N_SUCCESS) { + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + + /* Resize server_out. + * However, we don't want s2n_send to resize the buffer itself, + * so we need to lie about the stuffer not being growable. + */ + server_out->growable = true; + size_t existing_size = s2n_stuffer_data_available(server_out); + size_t new_data_size = s2n_stuffer_data_available(&server_conn->out); + EXPECT_SUCCESS(s2n_stuffer_resize(server_out, existing_size + new_data_size)); + server_out->growable = false; + } + EXPECT_EQUAL(server_conn->tickets_sent, tickets_to_send); + EXPECT_TRUE(s2n_stuffer_is_freed(&server_conn->handshake.io)); + + uint64_t actual_record_count = 0; + uint64_t expected_record_count = 0; + uint8_t *seq_num = server_conn->server->server_sequence_number; + EXPECT_OK(s2n_get_actual_record_count(seq_num, &write_seq_num, &actual_record_count)); + EXPECT_OK(s2n_get_expected_record_count(nst_size, fragment_size, tickets_to_send, &expected_record_count)); + EXPECT_EQUAL(actual_record_count, expected_record_count); + } + + /* Test: the full messages can be parsed by the client */ + uint64_t read_seq_num = 0; + for (size_t frag_i = 0; frag_i < s2n_array_len(fragment_sizes); frag_i++) { + const uint32_t fragment_size = fragment_sizes[frag_i]; + + uint8_t recv_data[sizeof(send_data)] = { 0 }; + EXPECT_SUCCESS(s2n_recv(client_conn, recv_data, sizeof(recv_data), &blocked)); + EXPECT_BYTEARRAY_EQUAL(send_data, recv_data, sizeof(recv_data)); + + uint64_t actual_record_count = 0; + uint64_t expected_record_count = 0; + uint8_t *seq_num = client_conn->server->server_sequence_number; + EXPECT_OK(s2n_get_actual_record_count(seq_num, &read_seq_num, &actual_record_count)); + EXPECT_OK(s2n_get_expected_record_count(nst_size, fragment_size, tickets_to_send, &expected_record_count)); + EXPECT_EQUAL(actual_record_count, expected_record_count); + } + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_post_handshake_test.c b/tests/unit/s2n_post_handshake_test.c new file mode 100644 index 00000000000..b8098050c42 --- /dev/null +++ b/tests/unit/s2n_post_handshake_test.c @@ -0,0 +1,269 @@ +/* + * 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 "tls/s2n_post_handshake.h" + +#include "error/s2n_errno.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_key_update.h" +#include "utils/s2n_safety.h" + +/* Include to get access to the handshake state machine + * to verify we don't allow its messages post-handshake. + */ +#include "tls/s2n_handshake_io.c" + +#define KEY_UPDATE_MESSAGE_SIZE sizeof(uint8_t) + /* message id */ \ + SIZEOF_UINT24 + /* message len */ \ + sizeof(uint8_t) /* message */ + +int s2n_key_update_write(struct s2n_blob *out); + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* s2n_post_handshake_recv */ + { + /* post_handshake_recv processes a key update requested message */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); + + /* Write key update requested to conn->in */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&conn->in, TLS_KEY_UPDATE)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&conn->in, S2N_KEY_UPDATE_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&conn->in, S2N_KEY_UPDATE_REQUESTED)); + + EXPECT_OK(s2n_post_handshake_recv(conn)); + EXPECT_TRUE(s2n_atomic_flag_test(&conn->key_update_pending)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* post_handshake_recv rejects an unknown post handshake message */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); + + /* Write key update requested to conn->in */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&conn->in, -1)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&conn->in, S2N_KEY_UPDATE_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&conn->in, S2N_KEY_UPDATE_REQUESTED)); + + EXPECT_ERROR_WITH_ERRNO(s2n_post_handshake_recv(conn), S2N_ERR_BAD_MESSAGE); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* post_handshake_recv processes a malformed post handshake message */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); + + /* Write key update requested to conn->in */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&conn->in, TLS_KEY_UPDATE)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&conn->in, S2N_KEY_UPDATE_LENGTH)); + + EXPECT_ERROR(s2n_post_handshake_recv(conn)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Functional test: Multiple post handshake messages can be received in the same record */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + uint8_t num_key_updates = 3; + + /* Write three key update messages in one record. We cannot call s2n_post_handshake_send + * multiple times here because s2n only sends one handshake message per record */ + for (size_t i = 0; i < num_key_updates; i++) { + uint8_t data[KEY_UPDATE_MESSAGE_SIZE] = { 0 }; + struct s2n_blob key_update_message = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&key_update_message, data, sizeof(data))); + EXPECT_SUCCESS(s2n_key_update_write(&key_update_message)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->in, key_update_message.data, key_update_message.size)); + } + + EXPECT_OK(s2n_post_handshake_recv(conn)); + + /* All three key update messages have been read */ + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* HELLO_REQUEST messages can be received post-handshake. */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&conn->in, TLS_HELLO_REQUEST)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&conn->in, 0)); + EXPECT_OK(s2n_post_handshake_recv(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* No non-post handshake messages can be received. + * This means that no handshake message that appears in the handshake state machine + * should be allowed. + */ + { + /* For TLS1.2 */ + for (size_t i = 0; i < s2n_array_len(state_machine); i++) { + if (state_machine[i].record_type != TLS_HANDSHAKE) { + break; + } + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS13; + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&conn->in, state_machine[i].message_type)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&conn->in, 0)); + EXPECT_ERROR_WITH_ERRNO(s2n_post_handshake_recv(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* For TLS1.3 */ + for (size_t i = 0; i < s2n_array_len(tls13_state_machine); i++) { + if (tls13_state_machine[i].record_type != TLS_HANDSHAKE) { + break; + } + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS13; + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&conn->in, tls13_state_machine[i].message_type)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&conn->in, 0)); + EXPECT_ERROR_WITH_ERRNO(s2n_post_handshake_recv(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + }; + }; + + /* post_handshake_send */ + { + /* Post handshake messages can be sent */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + EXPECT_SUCCESS(s2n_connection_set_send_io_stuffer(&output, conn)); + + conn->actual_protocol_version = S2N_TLS13; + s2n_atomic_flag_set(&conn->key_update_pending); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_SUCCESS(s2n_post_handshake_send(conn, &blocked)); + + EXPECT_TRUE(s2n_stuffer_data_available(&output) > 0); + }; + + /* No messages sent if no post-handshake messages required */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + conn->actual_protocol_version = S2N_TLS13; + s2n_atomic_flag_clear(&conn->key_update_pending); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_SUCCESS(s2n_post_handshake_send(conn, &blocked)); + + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->out), 0); + }; + + /* No messages sent if actual_protocol_version = S2N_TLS12; + s2n_atomic_flag_set(&conn->key_update_pending); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_SUCCESS(s2n_post_handshake_send(conn, &blocked)); + + EXPECT_TRUE(s2n_atomic_flag_test(&conn->key_update_pending)); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->out), 0); + }; + }; + + /* Errors while processing post-handshake messages close the connection */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + + DEFER_CLEANUP(struct s2n_stuffer io_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&io_stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&io_stuffer, &io_stuffer, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&io_stuffer, &io_stuffer, server_conn)); + + /* Send just the ClientHello */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + /* Try to read the ClientHello as a post-handshake message */ + uint8_t output_buffer[10] = { 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, output_buffer, sizeof(output_buffer), &blocked), S2N_ERR_BAD_MESSAGE); + + /* Error closed connection */ + EXPECT_TRUE(s2n_connection_check_io_status(server_conn, S2N_IO_CLOSED)); + + /* Error triggers blinding */ + EXPECT_NOT_EQUAL(s2n_connection_get_delay(server_conn), 0); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_pq_kem_kat_kyber_r3_test.c b/tests/unit/s2n_pq_kem_kat_kyber_r3_test.c new file mode 100644 index 00000000000..66e72027608 --- /dev/null +++ b/tests/unit/s2n_pq_kem_kat_kyber_r3_test.c @@ -0,0 +1,52 @@ +/* + * 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 "pq-crypto/s2n_pq.h" +#include "s2n_test.h" +#include "tests/testlib/s2n_testlib.h" + +static const struct s2n_kem_kat_test_vector test_vectors[] = { + { + .kem = &s2n_kyber_512_r3, + .kat_file = "kats/kyber_r3.kat", + .asm_is_enabled = s2n_pq_no_asm_available, + .enable_asm = s2n_pq_noop_asm, + .disable_asm = s2n_pq_noop_asm, + }, + { + .kem = &s2n_kyber_512_r3, + .kat_file = "kats/kyber_r3.kat", + .asm_is_enabled = s2n_kyber512r3_is_avx2_bmi2_enabled, + .enable_asm = s2n_try_enable_kyber512r3_opt_avx2_bmi2, + .disable_asm = s2n_disable_kyber512r3_opt_avx2_bmi2, + }, +}; + +int main() +{ + BEGIN_TEST(); + if (!s2n_pq_is_enabled() || s2n_libcrypto_supports_kyber()) { + /* The KAT tests rely on the low-level PQ crypto functions; + * there is nothing to test if PQ is disabled. + * + * In the case where we are using AWS-LC backed PQ, we rely on the + * KAT tests implemented in the AWS-LC repository. Implementing these + * tests within S2N is impossible due to the lack of AWS-LC interfaces + * for initializing the RNG. */ + END_TEST(); + } + EXPECT_OK(s2n_pq_kem_kat_test(test_vectors, s2n_array_len(test_vectors))); + END_TEST(); +} diff --git a/tests/unit/s2n_pq_kem_test.c b/tests/unit/s2n_pq_kem_test.c new file mode 100644 index 00000000000..06959bbbc90 --- /dev/null +++ b/tests/unit/s2n_pq_kem_test.c @@ -0,0 +1,118 @@ +/* + * 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 "crypto/s2n_fips.h" +#include "crypto/s2n_openssl.h" +#include "pq-crypto/s2n_pq.h" +#include "s2n_test.h" +#include "tests/testlib/s2n_testlib.h" +#include "tls/s2n_kem.h" +#include "utils/s2n_safety.h" + +struct s2n_kem_test_vector { + const struct s2n_kem *kem; + bool (*asm_is_enabled)(); + S2N_RESULT (*enable_asm)(); + S2N_RESULT (*disable_asm)(); +}; + +static const struct s2n_kem_test_vector test_vectors[] = { + { + .kem = &s2n_kyber_512_r3, + .asm_is_enabled = s2n_pq_no_asm_available, + .enable_asm = s2n_pq_noop_asm, + .disable_asm = s2n_pq_noop_asm, + }, + { + .kem = &s2n_kyber_512_r3, + .asm_is_enabled = s2n_kyber512r3_is_avx2_bmi2_enabled, + .enable_asm = s2n_try_enable_kyber512r3_opt_avx2_bmi2, + .disable_asm = s2n_disable_kyber512r3_opt_avx2_bmi2, + }, +}; + +/* EXPECT_SUCCESS checks explicitly function_call != -1; the PQ KEM functions may return + * any non-zero int to indicate failure.*/ +#define EXPECT_PQ_KEM_SUCCESS(function_call) EXPECT_EQUAL((function_call), 0) +#define EXPECT_PQ_KEM_FAILURE(function_call) EXPECT_NOT_EQUAL((function_call), 0) + +int main() +{ + BEGIN_TEST(); + +#if defined(OPENSSL_IS_AWSLC) && defined(AWSLC_API_VERSION) + /* If using non-FIPS AWS-LC >= v1.6 (API vers. 21), expect Kyber512 KEM from AWS-LC */ + if (!s2n_libcrypto_is_fips() && AWSLC_API_VERSION >= 21) { + EXPECT_TRUE(s2n_libcrypto_supports_kyber()); + } +#endif + + for (size_t i = 0; i < s2n_array_len(test_vectors); i++) { + const struct s2n_kem_test_vector vector = test_vectors[i]; + const struct s2n_kem *kem = vector.kem; + + DEFER_CLEANUP(struct s2n_blob public_key = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&public_key, kem->public_key_length)); + + DEFER_CLEANUP(struct s2n_blob private_key = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&private_key, kem->private_key_length)); + + DEFER_CLEANUP(struct s2n_blob client_shared_secret = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&client_shared_secret, kem->shared_secret_key_length)); + + DEFER_CLEANUP(struct s2n_blob server_shared_secret = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&server_shared_secret, kem->shared_secret_key_length)); + + DEFER_CLEANUP(struct s2n_blob ciphertext = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&ciphertext, kem->ciphertext_length)); + + if (s2n_pq_is_enabled()) { + /* Run the tests for C and assembly implementations (where available) */ + s2n_result (*asm_toggle_funcs[])(void) = { vector.disable_asm, vector.enable_asm }; + + for (size_t j = 0; j < s2n_array_len(asm_toggle_funcs); j++) { + EXPECT_OK(asm_toggle_funcs[j]()); + + /* Test a successful round-trip: keygen->enc->dec */ + EXPECT_PQ_KEM_SUCCESS(kem->generate_keypair(kem, public_key.data, private_key.data)); + EXPECT_PQ_KEM_SUCCESS(kem->encapsulate(kem, ciphertext.data, client_shared_secret.data, public_key.data)); + EXPECT_PQ_KEM_SUCCESS(kem->decapsulate(kem, server_shared_secret.data, ciphertext.data, private_key.data)); + EXPECT_BYTEARRAY_EQUAL(server_shared_secret.data, client_shared_secret.data, kem->shared_secret_key_length); + + /* By design, if an invalid private key + ciphertext pair is provided to decapsulate(), + * the function should still succeed (return S2N_SUCCESS); however, the shared secret + * that was "decapsulated" will be a garbage random value. */ + ciphertext.data[0] ^= 1; /* Flip a bit to invalidate the ciphertext */ + + EXPECT_PQ_KEM_SUCCESS(kem->decapsulate(kem, server_shared_secret.data, ciphertext.data, private_key.data)); + EXPECT_BYTEARRAY_NOT_EQUAL(server_shared_secret.data, client_shared_secret.data, kem->shared_secret_key_length); + } + } else { +#if defined(S2N_NO_PQ) + EXPECT_FAILURE_WITH_ERRNO(kem->generate_keypair(kem, public_key.data, private_key.data), S2N_ERR_UNIMPLEMENTED); + EXPECT_FAILURE_WITH_ERRNO(kem->encapsulate(kem, ciphertext.data, client_shared_secret.data, public_key.data), S2N_ERR_UNIMPLEMENTED); + EXPECT_FAILURE_WITH_ERRNO(kem->decapsulate(kem, server_shared_secret.data, ciphertext.data, private_key.data), S2N_ERR_UNIMPLEMENTED); +#else + EXPECT_FAILURE_WITH_ERRNO(kem->generate_keypair(kem, public_key.data, private_key.data), S2N_ERR_PQ_DISABLED); + EXPECT_FAILURE_WITH_ERRNO(kem->encapsulate(kem, ciphertext.data, client_shared_secret.data, public_key.data), S2N_ERR_PQ_DISABLED); + EXPECT_FAILURE_WITH_ERRNO(kem->decapsulate(kem, server_shared_secret.data, ciphertext.data, private_key.data), S2N_ERR_PQ_DISABLED); +#endif + } + } + + END_TEST(); +} diff --git a/tests/unit/s2n_prf_key_material_test.c b/tests/unit/s2n_prf_key_material_test.c new file mode 100644 index 00000000000..2edbf61d431 --- /dev/null +++ b/tests/unit/s2n_prf_key_material_test.c @@ -0,0 +1,235 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_prf.h" +#include "utils/s2n_random.h" + +static S2N_RESULT s2n_test_validate_key_material(struct s2n_key_material *key_material, + struct s2n_blob *test_data_blob, uint8_t mac_size, uint8_t key_size, uint8_t iv_size) +{ + /* confirm that the data is copied to key_material */ + RESULT_ENSURE_EQ(test_data_blob->size, sizeof(key_material->key_block)); + RESULT_ENSURE_EQ(memcmp(test_data_blob->data, key_material->key_block, test_data_blob->size), 0); + + struct s2n_stuffer test_data_stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init_written(&test_data_stuffer, test_data_blob)); + + /* test that its possible to access data from s2n_key_material */ + /* client MAC */ + uint8_t *test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, mac_size); + RESULT_ENSURE_REF(test_ptr); + RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->client_mac.data, mac_size), 0); + /* server MAC */ + test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, mac_size); + RESULT_ENSURE_REF(test_ptr); + RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->server_mac.data, mac_size), 0); + + /* client KEY */ + test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, key_size); + RESULT_ENSURE_REF(test_ptr); + RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->client_key.data, key_size), 0); + /* server KEY */ + test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, key_size); + RESULT_ENSURE_REF(test_ptr); + RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->server_key.data, key_size), 0); + + /* client IV */ + test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, iv_size); + RESULT_ENSURE_REF(test_ptr); + RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->client_iv.data, iv_size), 0); + /* server IV */ + test_ptr = s2n_stuffer_raw_read(&test_data_stuffer, iv_size); + RESULT_ENSURE_REF(test_ptr); + RESULT_ENSURE_EQ(memcmp(test_ptr, key_material->server_iv.data, iv_size), 0); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* prepare test data */ + uint8_t test_data[S2N_MAX_KEY_BLOCK_LEN] = { 0 }; + struct s2n_blob test_data_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&test_data_blob, test_data, sizeof(test_data))); + EXPECT_OK(s2n_get_public_random_data(&test_data_blob)); + + /* fuzz s2n_key_material_init with different mac, key, iv sizes */ + { + for (uint8_t mac_size = 0; mac_size < SHA512_DIGEST_LENGTH; mac_size++) { + for (uint8_t key_size = 0; key_size < S2N_TLS_AES_256_GCM_KEY_LEN; key_size++) { + for (uint8_t iv_size = 0; iv_size < S2N_TLS_MAX_IV_LEN; iv_size++) { + if ((mac_size * 2 + key_size * 2 + iv_size * 2 > S2N_MAX_KEY_BLOCK_LEN)) { + continue; + } + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + + /* test varying size of mac, key and iv */ + conn->actual_protocol_version = S2N_TLS10; + struct s2n_cipher temp_cipher = { + .type = S2N_COMPOSITE, + .key_material_size = key_size, + .io.comp = { + /* interpreted as iv size for composite ciphers.. which makes + * it easy for testing */ + .block_size = iv_size, + .mac_key_size = mac_size, + }, + }; + struct s2n_record_algorithm temp_record_alg = { + .cipher = &temp_cipher, + }; + struct s2n_cipher_suite temp_cipher_suite = { + .record_alg = &temp_record_alg, + }; + conn->secure->cipher_suite = &temp_cipher_suite; + + /* init s2n_key_material */ + struct s2n_key_material key_material = { 0 }; + EXPECT_OK(s2n_key_material_init(&key_material, conn)); + + /* assert that sizes match */ + EXPECT_EQUAL(key_material.client_mac.size, mac_size); + EXPECT_EQUAL(key_material.client_key.size, key_size); + EXPECT_EQUAL(key_material.client_iv.size, iv_size); + EXPECT_EQUAL(key_material.server_mac.size, mac_size); + EXPECT_EQUAL(key_material.server_key.size, key_size); + EXPECT_EQUAL(key_material.server_iv.size, iv_size); + + /* copy data into key_material and validate accessing key_material is sound */ + EXPECT_EQUAL(sizeof(key_material.key_block), sizeof(test_data)); + POSIX_CHECKED_MEMCPY(key_material.key_block, test_data, sizeof(key_material.key_block)); + EXPECT_OK(s2n_test_validate_key_material(&key_material, &test_data_blob, mac_size, key_size, iv_size)); + } + } + } + } + + /* confirm that s2n_key_material can correctly handle all cipher suites */ + { + for (size_t i = 0; i < cipher_preferences_test_all.count; i++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + + struct s2n_cipher_suite *cipher_suite = cipher_preferences_test_all.suites[i]; + if (!cipher_suite->available) { + continue; + } + conn->secure->cipher_suite = cipher_suite; + + /* init s2n_key_material */ + struct s2n_key_material key_material = { 0 }; + EXPECT_OK(s2n_key_material_init(&key_material, conn)); + + /* copy data into key_material and validate accessing key_material is sound */ + EXPECT_EQUAL(sizeof(key_material.key_block), sizeof(test_data)); + POSIX_CHECKED_MEMCPY(key_material.key_block, test_data, sizeof(key_material.key_block)); + /* test that its possible to access mac, key and iv correctly from s2n_key_material */ + EXPECT_OK(s2n_test_validate_key_material( + &key_material, + &test_data_blob, + key_material.client_mac.size, + key_material.client_key.size, + key_material.client_iv.size)); + } + } + + /* AEAD cipher + * IV size should be the same regardless of protocol version + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + EXPECT_EQUAL(conn->secure->cipher_suite->record_alg->cipher->type, S2N_AEAD); + + struct s2n_key_material key_material = { 0 }; + + uint32_t mac = 0; + uint32_t key = S2N_TLS_AES_128_GCM_KEY_LEN; + uint32_t iv = S2N_TLS13_FIXED_IV_LEN; + + /* initialize s2n_key_material */ + conn->actual_protocol_version = S2N_TLS10; + EXPECT_OK(s2n_key_material_init(&key_material, conn)); + + EXPECT_EQUAL(key_material.client_mac.size, mac); + EXPECT_EQUAL(key_material.client_key.size, key); + EXPECT_EQUAL(key_material.client_iv.size, iv); + EXPECT_EQUAL(key_material.server_mac.size, mac); + EXPECT_EQUAL(key_material.server_key.size, key); + EXPECT_EQUAL(key_material.server_iv.size, iv); + + /* re-initialize s2n_key_material */ + conn->actual_protocol_version = S2N_TLS11; + EXPECT_OK(s2n_key_material_init(&key_material, conn)); + /* assert same IV size regardless of protocol version */ + EXPECT_EQUAL(key_material.client_mac.size, mac); + EXPECT_EQUAL(key_material.client_key.size, key); + EXPECT_EQUAL(key_material.client_iv.size, iv); + EXPECT_EQUAL(key_material.server_mac.size, mac); + EXPECT_EQUAL(key_material.server_key.size, key); + EXPECT_EQUAL(key_material.server_iv.size, iv); + } + + /* NON AEAD cipher + * IV size depends on protocol version + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + + conn->secure->cipher_suite = &s2n_rsa_with_aes_128_cbc_sha256; + const struct s2n_cipher *cipher = conn->secure->cipher_suite->record_alg->cipher; + /* assert that the cipher chosen is non AEAD */ + EXPECT_TRUE(cipher->type == S2N_COMPOSITE || cipher->type == S2N_CBC); + + struct s2n_key_material key_material = { 0 }; + + uint32_t mac = SHA256_DIGEST_LENGTH; + uint32_t key = S2N_TLS_AES_128_GCM_KEY_LEN; + uint32_t iv = 16; + + /* initialize s2n_key_material */ + conn->actual_protocol_version = S2N_TLS10; + EXPECT_OK(s2n_key_material_init(&key_material, conn)); + /* assert IV of non 0 if protocol version <= S2N_TLS10 */ + EXPECT_EQUAL(key_material.client_mac.size, mac); + EXPECT_EQUAL(key_material.client_key.size, key); + EXPECT_EQUAL(key_material.client_iv.size, iv); + EXPECT_EQUAL(key_material.server_mac.size, mac); + EXPECT_EQUAL(key_material.server_key.size, key); + EXPECT_EQUAL(key_material.server_iv.size, iv); + + /* re-initialize s2n_key_material */ + conn->actual_protocol_version = S2N_TLS11; + EXPECT_OK(s2n_key_material_init(&key_material, conn)); + /* assert IV of size == 0 if protocol version > S2N_TLS10 */ + iv = 0; + EXPECT_EQUAL(key_material.client_mac.size, mac); + EXPECT_EQUAL(key_material.client_key.size, key); + EXPECT_EQUAL(key_material.client_iv.size, iv); + EXPECT_EQUAL(key_material.server_mac.size, mac); + EXPECT_EQUAL(key_material.server_key.size, key); + EXPECT_EQUAL(key_material.server_iv.size, iv); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_protocol_preferences_test.c b/tests/unit/s2n_protocol_preferences_test.c new file mode 100644 index 00000000000..3edf757e302 --- /dev/null +++ b/tests/unit/s2n_protocol_preferences_test.c @@ -0,0 +1,284 @@ +/* + * 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 "tls/s2n_protocol_preferences.h" + +#include "api/s2n.h" +#include "s2n_test.h" +#include "tls/extensions/s2n_client_alpn.h" +#include "tls/s2n_connection.h" + +#define LEN_PREFIX 1 + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* input values */ + const uint8_t protocol1[] = "protocol1"; + const size_t protocol1_len = strlen((const char *) protocol1); + const uint8_t protocol2[] = "protocol abc 2"; + const size_t protocol2_len = strlen((const char *) protocol2); + + const uint8_t large_value[255] = { 0 }; + + /* Test config append */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_EQUAL(config->application_protocols.size, 0); + size_t prev_size = 0; + + /* should grow the blob with the provided value */ + EXPECT_SUCCESS(s2n_config_append_protocol_preference(config, protocol1, sizeof(protocol1))); + EXPECT_EQUAL(config->application_protocols.size, LEN_PREFIX + sizeof(protocol1)); + prev_size = config->application_protocols.size; + + /* should grow the blob even more */ + EXPECT_SUCCESS(s2n_config_append_protocol_preference(config, protocol2, sizeof(protocol2))); + EXPECT_EQUAL(config->application_protocols.size, prev_size + LEN_PREFIX + sizeof(protocol2)); + prev_size = config->application_protocols.size; + + /* should reallocate the blob with large values */ + EXPECT_SUCCESS(s2n_config_append_protocol_preference(config, large_value, sizeof(large_value))); + EXPECT_EQUAL(config->application_protocols.size, prev_size + LEN_PREFIX + sizeof(large_value)); + prev_size = config->application_protocols.size; + + /* should not allow empty protocol values */ + EXPECT_FAILURE(s2n_config_append_protocol_preference(config, large_value, 0)); + EXPECT_EQUAL(config->application_protocols.size, prev_size); + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test connection append */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_EQUAL(conn->application_protocols_overridden.size, 0); + size_t prev_size = 0; + + /* should grow the blob with the provided value */ + EXPECT_SUCCESS(s2n_connection_append_protocol_preference(conn, protocol1, sizeof(protocol1))); + EXPECT_EQUAL(conn->application_protocols_overridden.size, LEN_PREFIX + sizeof(protocol1)); + prev_size = conn->application_protocols_overridden.size; + + /* should grow the blob even more */ + EXPECT_SUCCESS(s2n_connection_append_protocol_preference(conn, protocol2, sizeof(protocol2))); + EXPECT_EQUAL(conn->application_protocols_overridden.size, prev_size + LEN_PREFIX + sizeof(protocol2)); + prev_size = conn->application_protocols_overridden.size; + + /* should reallocate the blob with large values */ + EXPECT_SUCCESS(s2n_connection_append_protocol_preference(conn, large_value, sizeof(large_value))); + EXPECT_EQUAL(conn->application_protocols_overridden.size, prev_size + LEN_PREFIX + sizeof(large_value)); + prev_size = conn->application_protocols_overridden.size; + + /* should not allow empty protocol values */ + EXPECT_FAILURE(s2n_connection_append_protocol_preference(conn, large_value, 0)); + EXPECT_EQUAL(conn->application_protocols_overridden.size, prev_size); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + const char *protocols[] = { (const char *) protocol1, (const char *) protocol2 }; + const uint8_t protocols_count = s2n_array_len(protocols); + + char oversized_value[257] = { 0 }; + memset(&oversized_value, 1, 256); + EXPECT_EQUAL(strlen(oversized_value), 256); + const char *oversized[] = { oversized_value }; + const uint8_t oversized_count = s2n_array_len(oversized); + + /* Test config set */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_EQUAL(config->application_protocols.size, 0); + + /* should copy the preference list */ + EXPECT_SUCCESS(s2n_config_set_protocol_preferences(config, protocols, protocols_count)); + EXPECT_EQUAL(config->application_protocols.size, LEN_PREFIX + protocol1_len + LEN_PREFIX + protocol2_len); + + /* should correctly free the old list list */ + EXPECT_SUCCESS(s2n_config_set_protocol_preferences(config, protocols, 1)); + EXPECT_EQUAL(config->application_protocols.size, LEN_PREFIX + protocol1_len); + + /* should clear the preference list */ + EXPECT_SUCCESS(s2n_config_set_protocol_preferences(config, NULL, protocols_count)); + EXPECT_EQUAL(config->application_protocols.size, 0); + + EXPECT_SUCCESS(s2n_config_set_protocol_preferences(config, protocols, protocols_count)); + EXPECT_EQUAL(config->application_protocols.size, LEN_PREFIX + protocol1_len + LEN_PREFIX + protocol2_len); + EXPECT_SUCCESS(s2n_config_set_protocol_preferences(config, protocols, 0)); + EXPECT_EQUAL(config->application_protocols.size, 0); + + /* should limit the length of the protocol value */ + EXPECT_FAILURE(s2n_config_set_protocol_preferences(config, oversized, oversized_count)); + EXPECT_EQUAL(config->application_protocols.size, 0); + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test connection set */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_EQUAL(conn->application_protocols_overridden.size, 0); + + /* should copy the preference list */ + EXPECT_SUCCESS(s2n_connection_set_protocol_preferences(conn, protocols, protocols_count)); + EXPECT_EQUAL(conn->application_protocols_overridden.size, LEN_PREFIX + protocol1_len + LEN_PREFIX + protocol2_len); + + /* should correctly free the old list */ + EXPECT_SUCCESS(s2n_connection_set_protocol_preferences(conn, protocols, 1)); + EXPECT_EQUAL(conn->application_protocols_overridden.size, LEN_PREFIX + protocol1_len); + + /* should clear the preference list */ + EXPECT_SUCCESS(s2n_connection_set_protocol_preferences(conn, NULL, protocols_count)); + EXPECT_EQUAL(conn->application_protocols_overridden.size, 0); + + EXPECT_SUCCESS(s2n_connection_set_protocol_preferences(conn, protocols, protocols_count)); + EXPECT_EQUAL(conn->application_protocols_overridden.size, LEN_PREFIX + protocol1_len + LEN_PREFIX + protocol2_len); + EXPECT_SUCCESS(s2n_connection_set_protocol_preferences(conn, protocols, 0)); + EXPECT_EQUAL(conn->application_protocols_overridden.size, 0); + + /* should limit the length of the protocol value */ + + EXPECT_FAILURE(s2n_connection_set_protocol_preferences(conn, oversized, oversized_count)); + EXPECT_EQUAL(conn->application_protocols_overridden.size, 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_protocol_preferences_read */ + { + /* Safety checks */ + { + struct s2n_stuffer stuffer = { 0 }; + struct s2n_blob blob = { 0 }; + EXPECT_ERROR_WITH_ERRNO(s2n_protocol_preferences_read(NULL, &blob), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_protocol_preferences_read(&stuffer, NULL), S2N_ERR_NULL); + }; + + /* Fail to read zero-length protocol */ + { + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&input, 0)); + + struct s2n_blob result = { 0 }; + EXPECT_ERROR_WITH_ERRNO(s2n_protocol_preferences_read(&input, &result), S2N_ERR_SAFETY); + EXPECT_EQUAL(result.size, 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + }; + + /* Read valid value */ + { + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&input, sizeof(protocol1))); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&input, protocol1, sizeof(protocol1))); + + struct s2n_blob result = { 0 }; + EXPECT_OK(s2n_protocol_preferences_read(&input, &result)); + EXPECT_EQUAL(result.size, sizeof(protocol1)); + EXPECT_BYTEARRAY_EQUAL(result.data, protocol1, sizeof(protocol1)); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + }; + + /* Read what we write */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + EXPECT_SUCCESS(s2n_connection_append_protocol_preference(conn, protocol1, sizeof(protocol1))); + EXPECT_SUCCESS(s2n_client_alpn_extension.send(conn, &conn->handshake.io)); + + /* Skip list size */ + EXPECT_SUCCESS(s2n_stuffer_skip_read(&conn->handshake.io, sizeof(uint16_t))); + + struct s2n_blob result = { 0 }; + EXPECT_OK(s2n_protocol_preferences_read(&conn->handshake.io, &result)); + EXPECT_EQUAL(result.size, sizeof(protocol1)); + EXPECT_BYTEARRAY_EQUAL(result.data, protocol1, sizeof(protocol1)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* s2n_protocol_preferences_contain */ + { + uint8_t protocol3[] = "protocol3"; + struct s2n_blob protocol3_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&protocol3_blob, protocol3, sizeof(protocol3))); + + /* Safety checks */ + { + struct s2n_blob blob = { 0 }; + bool match = false; + EXPECT_ERROR_WITH_ERRNO(s2n_protocol_preferences_contain(NULL, &blob, &match), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_protocol_preferences_contain(&blob, NULL, &match), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_protocol_preferences_contain(&blob, &blob, NULL), S2N_ERR_NULL); + }; + + /* No supported protocols */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + + bool result = true; + EXPECT_OK(s2n_protocol_preferences_contain(&conn->application_protocols_overridden, &protocol3_blob, &result)); + EXPECT_FALSE(result); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* No match */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + + EXPECT_SUCCESS(s2n_connection_append_protocol_preference(conn, protocol1, sizeof(protocol1))); + EXPECT_SUCCESS(s2n_connection_append_protocol_preference(conn, protocol2, sizeof(protocol2))); + + bool result = true; + EXPECT_OK(s2n_protocol_preferences_contain(&conn->application_protocols_overridden, &protocol3_blob, &result)); + EXPECT_FALSE(result); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Match */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + + EXPECT_SUCCESS(s2n_connection_append_protocol_preference(conn, protocol1, sizeof(protocol1))); + EXPECT_SUCCESS(s2n_connection_append_protocol_preference(conn, protocol2, sizeof(protocol2))); + EXPECT_SUCCESS(s2n_connection_append_protocol_preference(conn, protocol3, sizeof(protocol3))); + + bool result = false; + EXPECT_OK(s2n_protocol_preferences_contain(&conn->application_protocols_overridden, &protocol3_blob, &result)); + EXPECT_TRUE(result); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_psk_key_exchange_modes_extension_test.c b/tests/unit/s2n_psk_key_exchange_modes_extension_test.c new file mode 100644 index 00000000000..e3e25cff37b --- /dev/null +++ b/tests/unit/s2n_psk_key_exchange_modes_extension_test.c @@ -0,0 +1,291 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_psk_key_exchange_modes.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Test: s2n_psk_key_exchange_modes_send */ + { + struct s2n_stuffer out = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_psk_key_exchange_modes_extension.send(conn, &out)); + + uint8_t psk_ke_modes_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&out, &psk_ke_modes_size)); + EXPECT_EQUAL(psk_ke_modes_size, PSK_KEY_EXCHANGE_MODE_SIZE); + + uint8_t psk_ke_mode = S2N_PSK_KE_UNKNOWN; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&out, &psk_ke_mode)); + EXPECT_EQUAL(psk_ke_mode, TLS_PSK_DHE_KE_MODE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + }; + + /* Test: s2n_psk_key_exchange_modes_recv */ + { + /* Receive an extension when running TLS1.2 */ + { + struct s2n_stuffer out = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_EQUAL(conn->psk_params.psk_ke_mode, S2N_PSK_KE_UNKNOWN); + + /* Incorrect protocol version */ + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&out, PSK_KEY_EXCHANGE_MODE_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&out, TLS_PSK_DHE_KE_MODE)); + + EXPECT_SUCCESS(s2n_extension_recv(&s2n_psk_key_exchange_modes_extension, conn, &out)); + EXPECT_EQUAL(conn->psk_params.psk_ke_mode, S2N_PSK_KE_UNKNOWN); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + }; + + /* Length of extension is greater than contents of extension */ + { + struct s2n_stuffer out = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_EQUAL(conn->psk_params.psk_ke_mode, S2N_PSK_KE_UNKNOWN); + + conn->actual_protocol_version = S2N_TLS13; + + /* Incorrect length */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&out, PSK_KEY_EXCHANGE_MODE_SIZE + 1)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&out, TLS_PSK_DHE_KE_MODE)); + + EXPECT_SUCCESS(s2n_psk_key_exchange_modes_extension.recv(conn, &out)); + EXPECT_EQUAL(conn->psk_params.psk_ke_mode, S2N_PSK_KE_UNKNOWN); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + }; + + /* Receives valid psk_ke mode */ + { + struct s2n_stuffer out = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_EQUAL(conn->psk_params.psk_ke_mode, S2N_PSK_KE_UNKNOWN); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&out, PSK_KEY_EXCHANGE_MODE_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&out, TLS_PSK_KE_MODE)); + + EXPECT_SUCCESS(s2n_psk_key_exchange_modes_extension.recv(conn, &out)); + + /* s2n currently does not support the psk_ke mode */ + EXPECT_EQUAL(conn->psk_params.psk_ke_mode, S2N_PSK_KE_UNKNOWN); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + }; + + /* Receives list of supported and unsupported psk key exchange modes */ + { + struct s2n_stuffer out = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_EQUAL(conn->psk_params.psk_ke_mode, S2N_PSK_KE_UNKNOWN); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&out, PSK_KEY_EXCHANGE_MODE_SIZE * 2)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&out, TLS_PSK_KE_MODE)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&out, TLS_PSK_DHE_KE_MODE)); + + EXPECT_SUCCESS(s2n_psk_key_exchange_modes_extension.recv(conn, &out)); + + /* s2n chooses the only currently supported psk key exchange mode */ + EXPECT_EQUAL(conn->psk_params.psk_ke_mode, S2N_PSK_DHE_KE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + }; + + /* Server receives GREASE values. + * + *= https://www.rfc-editor.org/rfc/rfc8701#section-3.1 + *= type=test + *# A client MAY select one or more GREASE PskKeyExchangeMode values + *# and advertise them in the "psk_key_exchange_modes" extension, if + *# sent. + * + *= https://www.rfc-editor.org/rfc/rfc8701#section-3.2 + *= type=test + *# When processing a ClientHello, servers MUST NOT treat GREASE values + *# differently from any unknown value. Servers MUST NOT negotiate any + *# GREASE value when offered in a ClientHello. Servers MUST correctly + *# ignore unknown values in a ClientHello and attempt to negotiate with + *# one of the remaining parameters. + **/ + { + DEFER_CLEANUP(struct s2n_stuffer extension = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension, 0)); + + struct s2n_stuffer_reservation modes_size = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint8(&extension, &modes_size)); + + /* + *= https://www.rfc-editor.org/rfc/rfc8701#section-2 + *= type=test + *# The following values are reserved as GREASE values for + *# PskKeyExchangeModes: + *# + *# 0x0B + *# + *# 0x2A + *# + *# 0x49 + *# + *# 0x68 + *# + *# 0x87 + *# + *# 0xA6 + *# + *# 0xC5 + *# + *# 0xE4 + */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension, 0x0B)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension, 0x2A)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension, 0x49)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension, 0x68)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension, 0x87)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension, 0xA6)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension, 0xC5)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension, 0xE4)); + + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&modes_size)); + + /* No valid non-GREASE option */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS13; + + EXPECT_SUCCESS(s2n_psk_key_exchange_modes_extension.recv(conn, &extension)); + EXPECT_EQUAL(conn->psk_params.psk_ke_mode, S2N_PSK_KE_UNKNOWN); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_reread(&extension)); + }; + + /* Valid non-GREASE option */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS13; + + /* Add the valid option and rewrite size */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension, TLS_PSK_DHE_KE_MODE)); + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&modes_size)); + + EXPECT_SUCCESS(s2n_psk_key_exchange_modes_extension.recv(conn, &extension)); + EXPECT_EQUAL(conn->psk_params.psk_ke_mode, S2N_PSK_DHE_KE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_reread(&extension)); + }; + }; + }; + + /* Test: s2n_psk_key_exchange_modes_should_send */ + { + /* When neither resumption nor PSKs are enabled, the extension should not be sent. */ + { + DEFER_CLEANUP(struct s2n_config *no_resumption_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(no_resumption_config); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(no_resumption_config, false)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(conn, no_resumption_config)); + + EXPECT_FALSE(s2n_psk_key_exchange_modes_extension.should_send(conn)); + }; + + /* When session resumption is enabled, the extension should be sent. */ + { + DEFER_CLEANUP(struct s2n_config *resumption_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(resumption_config); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(resumption_config, true)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(conn, resumption_config)); + + EXPECT_TRUE(s2n_psk_key_exchange_modes_extension.should_send(conn)); + }; + + /* When a client is using out-of-band PSKs, the extension should be sent. */ + { + DEFER_CLEANUP(struct s2n_config *psk_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(psk_config); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(psk_config, false)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(conn, psk_config)); + + DEFER_CLEANUP(struct s2n_psk *psk = s2n_test_psk_new(conn), s2n_psk_free); + EXPECT_SUCCESS(s2n_connection_append_psk(conn, psk)); + + EXPECT_TRUE(s2n_psk_key_exchange_modes_extension.should_send(conn)); + }; + }; + + /* Functional test */ + { + struct s2n_stuffer out = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + + struct s2n_connection *server_conn; + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_EQUAL(server_conn->psk_params.psk_ke_mode, S2N_PSK_KE_UNKNOWN); + + server_conn->actual_protocol_version = S2N_TLS13; + client_conn->actual_protocol_version = S2N_TLS13; + + EXPECT_SUCCESS(s2n_psk_key_exchange_modes_extension.send(client_conn, &out)); + EXPECT_SUCCESS(s2n_psk_key_exchange_modes_extension.recv(server_conn, &out)); + + EXPECT_EQUAL(server_conn->psk_params.psk_ke_mode, S2N_PSK_DHE_KE); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_psk_offered_test.c b/tests/unit/s2n_psk_offered_test.c new file mode 100644 index 00000000000..de01cd25b1e --- /dev/null +++ b/tests/unit/s2n_psk_offered_test.c @@ -0,0 +1,632 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_client_psk.h" + +/* Include source to test static methods. */ +#include "tls/s2n_psk.c" + +#define MILLIS_TO_NANOS(millis) (millis * (uint64_t) ONE_MILLISEC_IN_NANOS) + +static int s2n_setup_ticket_key(struct s2n_config *config) +{ + /** + *= https://tools.ietf.org/rfc/rfc5869#appendix-A.1 + *# PRK = 0x077709362c2e32df0ddc3f0dc47bba63 + *# 90b6c73bb50f9c3122ec844ad7c2b3e5 (32 octets) + **/ + S2N_BLOB_FROM_HEX(ticket_key, + "077709362c2e32df0ddc3f0dc47bba63" + "90b6c73bb50f9c3122ec844ad7c2b3e5"); + + /* Set up encryption key */ + uint64_t current_time = 0; + uint8_t ticket_key_name[16] = "2016.07.26.15\0"; + + POSIX_GUARD(s2n_config_set_session_tickets_onoff(config, 1)); + POSIX_GUARD(config->wall_clock(config->sys_clock_ctx, ¤t_time)); + POSIX_GUARD(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), + ticket_key.data, ticket_key.size, current_time / ONE_SEC_IN_NANOS)); + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_setup_encrypted_ticket(struct s2n_connection *conn, struct s2n_stuffer *output) +{ + conn->tls13_ticket_fields = (struct s2n_ticket_fields){ 0 }; + uint8_t test_secret_data[] = "test secret"; + RESULT_GUARD_POSIX(s2n_alloc(&conn->tls13_ticket_fields.session_secret, sizeof(test_secret_data))); + RESULT_CHECKED_MEMCPY(conn->tls13_ticket_fields.session_secret.data, test_secret_data, sizeof(test_secret_data)); + + /* Create a valid resumption psk identity */ + RESULT_GUARD_POSIX(s2n_encrypt_session_ticket(conn, output)); + output->blob.size = s2n_stuffer_data_available(output); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_write_test_identity(struct s2n_stuffer *out, const uint8_t *identity, uint16_t identity_size) +{ + RESULT_GUARD_POSIX(s2n_stuffer_write_uint16(out, identity_size)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(out, identity, identity_size)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint32(out, 0)); + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + const uint8_t wire_identity_1[] = { "one" }; + const uint8_t wire_identity_2[] = { "two" }; + + /* Test s2n_offered_psk_list_has_next */ + { + struct s2n_offered_psk_list psk_list = { 0 }; + + /* Safety check */ + EXPECT_FALSE(s2n_offered_psk_list_has_next(NULL)); + + /* Empty list */ + EXPECT_FALSE(s2n_offered_psk_list_has_next(&psk_list)); + + /* Contains data */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&psk_list.wire_data, 0)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&psk_list.wire_data, 1)); + EXPECT_TRUE(s2n_offered_psk_list_has_next(&psk_list)); + + /* Out of data */ + EXPECT_SUCCESS(s2n_stuffer_skip_read(&psk_list.wire_data, 1)); + EXPECT_FALSE(s2n_offered_psk_list_has_next(&psk_list)); + + EXPECT_SUCCESS(s2n_stuffer_free(&psk_list.wire_data)); + }; + + /* Test s2n_offered_psk_list_next */ + { + /* Safety checks */ + { + struct s2n_offered_psk_list psk_list = { 0 }; + struct s2n_offered_psk psk = { 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_psk_list_next(&psk_list, NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_psk_list_next(NULL, &psk), S2N_ERR_NULL); + }; + + /* Empty list */ + { + struct s2n_offered_psk_list psk_list = { 0 }; + struct s2n_offered_psk psk = { 0 }; + + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_psk_list_next(&psk_list, &psk), S2N_ERR_STUFFER_OUT_OF_DATA); + EXPECT_EQUAL(psk.identity.size, 0); + EXPECT_EQUAL(psk.identity.data, NULL); + + /* Calling again produces same result */ + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_psk_list_next(&psk_list, &psk), S2N_ERR_STUFFER_OUT_OF_DATA); + EXPECT_EQUAL(psk.identity.size, 0); + EXPECT_EQUAL(psk.identity.data, NULL); + }; + + /* Parses only element in list */ + { + struct s2n_offered_psk psk = { 0 }; + struct s2n_offered_psk_list psk_list = { 0 }; + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + psk_list.conn = conn; + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&psk_list.wire_data, 0)); + EXPECT_OK(s2n_write_test_identity(&psk_list.wire_data, wire_identity_1, sizeof(wire_identity_1))); + + EXPECT_SUCCESS(s2n_offered_psk_list_next(&psk_list, &psk)); + EXPECT_EQUAL(psk.identity.size, sizeof(wire_identity_1)); + EXPECT_BYTEARRAY_EQUAL(psk.identity.data, wire_identity_1, sizeof(wire_identity_1)); + + /* Trying to retrieve a second element fails */ + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_psk_list_next(&psk_list, &psk), S2N_ERR_STUFFER_OUT_OF_DATA); + EXPECT_EQUAL(psk.identity.size, 0); + EXPECT_EQUAL(psk.identity.data, NULL); + + EXPECT_SUCCESS(s2n_stuffer_free(&psk_list.wire_data)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Fails to parse zero-length identities */ + { + struct s2n_offered_psk psk = { 0 }; + struct s2n_offered_psk_list psk_list = { 0 }; + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&psk_list.wire_data, 0)); + EXPECT_OK(s2n_write_test_identity(&psk_list.wire_data, wire_identity_1, 0)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_psk_list_next(&psk_list, &psk), S2N_ERR_BAD_MESSAGE); + EXPECT_EQUAL(psk.identity.size, 0); + EXPECT_EQUAL(psk.identity.data, NULL); + + EXPECT_SUCCESS(s2n_stuffer_free(&psk_list.wire_data)); + }; + + /* Fails to parse partial identities */ + { + struct s2n_offered_psk psk = { 0 }; + struct s2n_offered_psk_list psk_list = { 0 }; + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&psk_list.wire_data, 0)); + EXPECT_OK(s2n_write_test_identity(&psk_list.wire_data, wire_identity_1, 0)); + EXPECT_SUCCESS(s2n_stuffer_wipe_n(&psk_list.wire_data, 1)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_psk_list_next(&psk_list, &psk), S2N_ERR_BAD_MESSAGE); + EXPECT_EQUAL(psk.identity.size, 0); + EXPECT_EQUAL(psk.identity.data, NULL); + + EXPECT_SUCCESS(s2n_stuffer_free(&psk_list.wire_data)); + }; + + /* Parses multiple elements from list */ + { + struct s2n_offered_psk psk = { 0 }; + struct s2n_offered_psk_list psk_list = { 0 }; + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + psk_list.conn = conn; + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&psk_list.wire_data, 0)); + EXPECT_OK(s2n_write_test_identity(&psk_list.wire_data, wire_identity_1, sizeof(wire_identity_1))); + EXPECT_OK(s2n_write_test_identity(&psk_list.wire_data, wire_identity_2, sizeof(wire_identity_2))); + + EXPECT_SUCCESS(s2n_offered_psk_list_next(&psk_list, &psk)); + EXPECT_EQUAL(psk.identity.size, sizeof(wire_identity_1)); + EXPECT_BYTEARRAY_EQUAL(psk.identity.data, wire_identity_1, sizeof(wire_identity_1)); + + EXPECT_SUCCESS(s2n_offered_psk_list_next(&psk_list, &psk)); + EXPECT_EQUAL(psk.identity.size, sizeof(wire_identity_2)); + EXPECT_BYTEARRAY_EQUAL(psk.identity.data, wire_identity_2, sizeof(wire_identity_2)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_psk_list_next(&psk_list, &psk), S2N_ERR_STUFFER_OUT_OF_DATA); + EXPECT_EQUAL(psk.identity.size, 0); + EXPECT_EQUAL(psk.identity.data, NULL); + + EXPECT_SUCCESS(s2n_stuffer_free(&psk_list.wire_data)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test s2n_offered_psk_list_read_next */ + { + /* Safety check */ + { + struct s2n_offered_psk psk = { 0 }; + struct s2n_offered_psk_list psk_list = { 0 }; + EXPECT_ERROR_WITH_ERRNO(s2n_offered_psk_list_read_next(&psk_list, NULL), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_offered_psk_list_read_next(NULL, &psk), S2N_ERR_NULL); + }; + + /** + *= https://tools.ietf.org/rfc/rfc8446#section-4.2.11 + *= type=test + *# For identities established externally, an obfuscated_ticket_age of 0 SHOULD be + *# used, and servers MUST ignore the value. + */ + { + struct s2n_offered_psk psk = { 0 }; + struct s2n_offered_psk_list psk_list = { 0 }; + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; + psk_list.conn = conn; + uint32_t obfuscated_ticket_age = 100; + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&psk_list.wire_data, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&psk_list.wire_data, sizeof(wire_identity_1))); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&psk_list.wire_data, wire_identity_1, sizeof(wire_identity_1))); + EXPECT_SUCCESS(s2n_stuffer_write_uint32(&psk_list.wire_data, obfuscated_ticket_age)); + + EXPECT_OK(s2n_offered_psk_list_read_next(&psk_list, &psk)); + + EXPECT_EQUAL(psk.obfuscated_ticket_age, 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&psk_list.wire_data)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Resumption PSKs do not skip obfuscated ticket age parsing */ + { + struct s2n_offered_psk psk = { 0 }; + struct s2n_offered_psk_list psk_list = { 0 }; + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + conn->psk_params.type = S2N_PSK_TYPE_RESUMPTION; + psk_list.conn = conn; + uint32_t obfuscated_ticket_age = 100; + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&psk_list.wire_data, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&psk_list.wire_data, sizeof(wire_identity_1))); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&psk_list.wire_data, wire_identity_1, sizeof(wire_identity_1))); + EXPECT_SUCCESS(s2n_stuffer_write_uint32(&psk_list.wire_data, obfuscated_ticket_age)); + + EXPECT_OK(s2n_offered_psk_list_read_next(&psk_list, &psk)); + + EXPECT_EQUAL(psk.obfuscated_ticket_age, obfuscated_ticket_age); + + EXPECT_SUCCESS(s2n_stuffer_free(&psk_list.wire_data)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + }; + + /* Test s2n_offered_psk_list_reread */ + { + /* Safety check */ + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_psk_list_reread(NULL), S2N_ERR_NULL); + + /* No-op on empty list */ + { + struct s2n_offered_psk psk = { 0 }; + struct s2n_offered_psk_list psk_list = { 0 }; + + EXPECT_SUCCESS(s2n_offered_psk_list_reread(&psk_list)); + EXPECT_SUCCESS(s2n_offered_psk_list_reread(&psk_list)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_psk_list_next(&psk_list, &psk), S2N_ERR_STUFFER_OUT_OF_DATA); + }; + + /* Resets non-empty list */ + { + struct s2n_offered_psk psk = { 0 }; + struct s2n_offered_psk_list psk_list = { 0 }; + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + psk_list.conn = conn; + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&psk_list.wire_data, 0)); + EXPECT_OK(s2n_write_test_identity(&psk_list.wire_data, wire_identity_1, sizeof(wire_identity_1))); + + EXPECT_SUCCESS(s2n_offered_psk_list_next(&psk_list, &psk)); + EXPECT_EQUAL(psk.identity.size, sizeof(wire_identity_1)); + EXPECT_BYTEARRAY_EQUAL(psk.identity.data, wire_identity_1, sizeof(wire_identity_1)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_psk_list_next(&psk_list, &psk), S2N_ERR_STUFFER_OUT_OF_DATA); + + EXPECT_SUCCESS(s2n_offered_psk_list_reread(&psk_list)); + + EXPECT_SUCCESS(s2n_offered_psk_list_next(&psk_list, &psk)); + EXPECT_EQUAL(psk.identity.size, sizeof(wire_identity_1)); + EXPECT_BYTEARRAY_EQUAL(psk.identity.data, wire_identity_1, sizeof(wire_identity_1)); + + EXPECT_SUCCESS(s2n_stuffer_free(&psk_list.wire_data)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test s2n_offered_psk_new */ + { + struct s2n_offered_psk zeroed_psk = { 0 }; + POSIX_CHECKED_MEMSET(&zeroed_psk, 0, sizeof(struct s2n_offered_psk)); + DEFER_CLEANUP(struct s2n_offered_psk *new_psk = s2n_offered_psk_new(), s2n_offered_psk_free); + EXPECT_NOT_NULL(new_psk); + + /* _new equivalent to a zero-inited structure */ + EXPECT_BYTEARRAY_EQUAL(&zeroed_psk, new_psk, sizeof(struct s2n_offered_psk)); + }; + + /* Test s2n_offered_psk_free */ + { + EXPECT_SUCCESS(s2n_offered_psk_free(NULL)); + + struct s2n_offered_psk *new_psk = s2n_offered_psk_new(); + EXPECT_NOT_NULL(new_psk); + EXPECT_SUCCESS(s2n_offered_psk_free(&new_psk)); + EXPECT_NULL(new_psk); + }; + + /* Test s2n_offered_psk_get_identity */ + { + /* Safety checks */ + { + struct s2n_offered_psk psk = { 0 }; + uint8_t *data = NULL; + uint16_t size = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_psk_get_identity(NULL, &data, &size), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_psk_get_identity(&psk, NULL, &size), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_psk_get_identity(&psk, &data, NULL), S2N_ERR_NULL); + }; + + /* Empty identity */ + { + DEFER_CLEANUP(struct s2n_offered_psk *psk = s2n_offered_psk_new(), s2n_offered_psk_free); + + uint8_t *data = NULL; + uint16_t size = 0; + EXPECT_SUCCESS(s2n_offered_psk_get_identity(psk, &data, &size)); + EXPECT_EQUAL(size, 0); + EXPECT_EQUAL(data, NULL); + }; + + /* Valid identity */ + { + uint8_t wire_identity[] = "identity"; + DEFER_CLEANUP(struct s2n_offered_psk *psk = s2n_offered_psk_new(), s2n_offered_psk_free); + EXPECT_SUCCESS(s2n_blob_init(&psk->identity, wire_identity, sizeof(wire_identity))); + + uint8_t *data = NULL; + uint16_t size = 0; + EXPECT_SUCCESS(s2n_offered_psk_get_identity(psk, &data, &size)); + EXPECT_EQUAL(size, sizeof(wire_identity)); + EXPECT_BYTEARRAY_EQUAL(data, wire_identity, sizeof(wire_identity)); + }; + }; + + /* Test: s2n_validate_ticket_lifetime */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + uint32_t ticket_age_add = 0; + uint32_t obfuscated_ticket_age = 0; + + /* Ticket age is smaller than session lifetime */ + conn->config->session_state_lifetime_in_nanos = MILLIS_TO_NANOS(100); + EXPECT_OK(s2n_validate_ticket_lifetime(conn, obfuscated_ticket_age, ticket_age_add)); + + /* Ticket age is greater than session lifetime */ + obfuscated_ticket_age = 101; + conn->config->session_state_lifetime_in_nanos = MILLIS_TO_NANOS(100); + EXPECT_ERROR_WITH_ERRNO(s2n_validate_ticket_lifetime(conn, obfuscated_ticket_age, ticket_age_add), S2N_ERR_INVALID_SESSION_TICKET); + + /* ticket_age_add is greater than obfuscated ticket age and ticket age + * is smaller than session lifetime */ + obfuscated_ticket_age = 100; + ticket_age_add = 1000; + conn->config->session_state_lifetime_in_nanos = MILLIS_TO_NANOS(UINT32_MAX); + EXPECT_OK(s2n_validate_ticket_lifetime(conn, obfuscated_ticket_age, ticket_age_add)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: s2n_offered_psk_list_choose_psk */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + struct s2n_array *known_psks = &conn->psk_params.psk_list; + + const uint16_t wire_index = 5; + uint8_t wire_identity_bytes[] = "wire_identity_bytes"; + struct s2n_blob wire_identity = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&wire_identity, wire_identity_bytes, sizeof(wire_identity_bytes))); + struct s2n_offered_psk offered_psk = { .identity = wire_identity, .wire_index = wire_index }; + + struct s2n_offered_psk_list offered_psk_list = { .conn = conn }; + conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; + + /* Safety */ + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_psk_list_choose_psk(NULL, &offered_psk), S2N_ERR_NULL); + + /* Test: No known PSKs */ + { + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_psk_list_choose_psk(&offered_psk_list, &offered_psk), S2N_ERR_NULL); + EXPECT_NULL(conn->psk_params.chosen_psk); + }; + + /* Test: No match exists */ + { + struct s2n_psk *different_identity = NULL; + EXPECT_OK(s2n_array_pushback(known_psks, (void **) &different_identity)); + EXPECT_OK(s2n_psk_init(different_identity, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(different_identity, wire_identity_2, sizeof(wire_identity_2))); + + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_psk_list_choose_psk(&offered_psk_list, &offered_psk), S2N_ERR_NULL); + EXPECT_NULL(conn->psk_params.chosen_psk); + }; + + struct s2n_psk *expected_match = NULL; + + /* Test: Match exists */ + { + EXPECT_OK(s2n_array_pushback(known_psks, (void **) &expected_match)); + EXPECT_OK(s2n_psk_init(expected_match, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(expected_match, wire_identity_bytes, sizeof(wire_identity_bytes))); + + EXPECT_SUCCESS(s2n_offered_psk_list_choose_psk(&offered_psk_list, &offered_psk)); + EXPECT_EQUAL(conn->psk_params.chosen_psk, expected_match); + EXPECT_EQUAL(conn->psk_params.chosen_psk_wire_index, wire_index); + }; + + /* Test: set NULL PSK */ + { + EXPECT_SUCCESS(s2n_offered_psk_list_choose_psk(&offered_psk_list, NULL)); + EXPECT_NULL(conn->psk_params.chosen_psk); + }; + + /* Test: Multiple matches exist */ + { + struct s2n_psk *another_match = NULL; + EXPECT_OK(s2n_array_pushback(known_psks, (void **) &another_match)); + EXPECT_OK(s2n_psk_init(another_match, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(another_match, wire_identity_bytes, sizeof(wire_identity_bytes))); + + EXPECT_SUCCESS(s2n_offered_psk_list_choose_psk(&offered_psk_list, &offered_psk)); + EXPECT_EQUAL(conn->psk_params.chosen_psk, expected_match); + EXPECT_EQUAL(conn->psk_params.chosen_psk_wire_index, wire_index); + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + + /* Test: Valid resumption psk is received */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_setup_ticket_key(config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + server_conn->psk_params.type = S2N_PSK_TYPE_RESUMPTION; + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + DEFER_CLEANUP(struct s2n_stuffer psk_identity = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&psk_identity, 0)); + EXPECT_OK(s2n_setup_encrypted_ticket(server_conn, &psk_identity)); + + struct s2n_offered_psk_list identity_list = { .conn = server_conn }; + + DEFER_CLEANUP(struct s2n_stuffer test_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&test_stuffer, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&test_stuffer, psk_identity.blob.data, psk_identity.blob.size)); + test_stuffer.blob.size = s2n_stuffer_data_available(&psk_identity); + + struct s2n_offered_psk client_psk = { .identity = test_stuffer.blob, .wire_index = wire_index }; + + EXPECT_SUCCESS(s2n_offered_psk_list_choose_psk(&identity_list, &client_psk)); + + EXPECT_EQUAL(server_conn->psk_params.chosen_psk_wire_index, wire_index); + EXPECT_NOT_NULL(server_conn->psk_params.chosen_psk); + + /* Sanity check psk creation is correct */ + EXPECT_EQUAL(server_conn->psk_params.chosen_psk->hmac_alg, s2n_tls13_aes_128_gcm_sha256.prf_alg); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Resumption psk has expired */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_setup_ticket_key(config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + server_conn->psk_params.type = S2N_PSK_TYPE_RESUMPTION; + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + DEFER_CLEANUP(struct s2n_stuffer psk_identity = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&psk_identity, 0)); + EXPECT_OK(s2n_setup_encrypted_ticket(server_conn, &psk_identity)); + + struct s2n_offered_psk_list identity_list = { .conn = server_conn }; + + DEFER_CLEANUP(struct s2n_stuffer test_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&test_stuffer, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&test_stuffer, psk_identity.blob.data, psk_identity.blob.size)); + test_stuffer.blob.size = s2n_stuffer_data_available(&psk_identity); + + /* Obfuscated ticket age is larger than the session lifetime */ + struct s2n_offered_psk client_psk = { .identity = psk_identity.blob, .wire_index = wire_index, .obfuscated_ticket_age = 100 }; + server_conn->config->session_state_lifetime_in_nanos = 0; + + EXPECT_FAILURE_WITH_ERRNO(s2n_offered_psk_list_choose_psk(&identity_list, &client_psk), S2N_ERR_INVALID_SESSION_TICKET); + + EXPECT_EQUAL(server_conn->psk_params.chosen_psk_wire_index, 0); + EXPECT_NULL(server_conn->psk_params.chosen_psk); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Invalid resumption psk is received */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + + server_conn->psk_params.type = S2N_PSK_TYPE_RESUMPTION; + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + struct s2n_offered_psk_list identity_list = { .conn = server_conn }; + struct s2n_offered_psk client_psk = { .identity = wire_identity, .wire_index = wire_index }; + + EXPECT_FAILURE(s2n_offered_psk_list_choose_psk(&identity_list, &client_psk)); + + EXPECT_EQUAL(server_conn->psk_params.chosen_psk_wire_index, 0); + EXPECT_NULL(server_conn->psk_params.chosen_psk); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + }; + + /* Functional test: Process the output of sending the psk extension */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; + + const uint8_t test_secret[] = "secret"; + + struct s2n_psk *psk1 = NULL; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk1)); + EXPECT_OK(s2n_psk_init(psk1, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(psk1, wire_identity_1, sizeof(wire_identity_1))); + EXPECT_SUCCESS(s2n_psk_set_secret(psk1, test_secret, sizeof(test_secret))); + + struct s2n_psk *psk2 = NULL; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk2)); + EXPECT_OK(s2n_psk_init(psk2, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(psk2, wire_identity_2, sizeof(wire_identity_2))); + EXPECT_SUCCESS(s2n_psk_set_secret(psk2, test_secret, sizeof(test_secret))); + + EXPECT_SUCCESS(s2n_client_psk_extension.send(conn, &conn->handshake.io)); + + /* Skip identity list size */ + EXPECT_SUCCESS(s2n_stuffer_skip_read(&conn->handshake.io, sizeof(uint16_t))); + + struct s2n_offered_psk_list identity_list = { .conn = conn }; + const size_t size = s2n_stuffer_data_available(&conn->handshake.io); + EXPECT_SUCCESS(s2n_stuffer_alloc(&identity_list.wire_data, size)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&identity_list.wire_data, + s2n_stuffer_raw_read(&conn->handshake.io, size), size)); + + uint8_t *data = NULL; + uint16_t data_size = 0; + struct s2n_offered_psk psk = { 0 }; + + EXPECT_TRUE(s2n_offered_psk_list_has_next(&identity_list)); + EXPECT_SUCCESS(s2n_offered_psk_list_next(&identity_list, &psk)); + EXPECT_SUCCESS(s2n_offered_psk_get_identity(&psk, &data, &data_size)); + EXPECT_EQUAL(data_size, sizeof(wire_identity_1)); + EXPECT_BYTEARRAY_EQUAL(data, wire_identity_1, sizeof(wire_identity_1)); + + EXPECT_TRUE(s2n_offered_psk_list_has_next(&identity_list)); + EXPECT_SUCCESS(s2n_offered_psk_list_next(&identity_list, &psk)); + EXPECT_SUCCESS(s2n_offered_psk_get_identity(&psk, &data, &data_size)); + EXPECT_EQUAL(data_size, sizeof(wire_identity_2)); + EXPECT_BYTEARRAY_EQUAL(data, wire_identity_2, sizeof(wire_identity_2)); + + EXPECT_SUCCESS(s2n_offered_psk_list_choose_psk(&identity_list, &psk)); + EXPECT_EQUAL(conn->psk_params.chosen_psk, psk2); + EXPECT_EQUAL(conn->psk_params.chosen_psk_wire_index, 1); + + EXPECT_SUCCESS(s2n_offered_psk_list_reread(&identity_list)); + + EXPECT_TRUE(s2n_offered_psk_list_has_next(&identity_list)); + EXPECT_SUCCESS(s2n_offered_psk_list_next(&identity_list, &psk)); + EXPECT_SUCCESS(s2n_offered_psk_get_identity(&psk, &data, &data_size)); + EXPECT_EQUAL(data_size, sizeof(wire_identity_1)); + EXPECT_BYTEARRAY_EQUAL(data, wire_identity_1, sizeof(wire_identity_1)); + + EXPECT_SUCCESS(s2n_offered_psk_list_choose_psk(&identity_list, &psk)); + EXPECT_EQUAL(conn->psk_params.chosen_psk, psk1); + EXPECT_EQUAL(conn->psk_params.chosen_psk_wire_index, 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&identity_list.wire_data)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_psk_test.c b/tests/unit/s2n_psk_test.c new file mode 100644 index 00000000000..e9718c94602 --- /dev/null +++ b/tests/unit/s2n_psk_test.c @@ -0,0 +1,1107 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" + +/* Include source to test static intermediate functions */ +#include "tls/s2n_psk.c" + +#define TEST_VALUE_1 "test value" +#define TEST_VALUE_2 "another" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Test s2n_psk_init */ + { + EXPECT_ERROR_WITH_ERRNO(s2n_psk_init(NULL, S2N_PSK_TYPE_EXTERNAL), + S2N_ERR_NULL); + + DEFER_CLEANUP(struct s2n_psk psk, s2n_psk_wipe); + + EXPECT_OK(s2n_psk_init(&psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_EQUAL(psk.type, S2N_PSK_TYPE_EXTERNAL); + EXPECT_EQUAL(psk.hmac_alg, S2N_HMAC_SHA256); + EXPECT_EQUAL(psk.ticket_age_add, 0); + EXPECT_EQUAL(psk.ticket_issue_time, 0); + EXPECT_EQUAL(psk.early_data_config.max_early_data_size, 0); + + EXPECT_OK(s2n_psk_init(&psk, S2N_PSK_TYPE_RESUMPTION)); + EXPECT_EQUAL(psk.type, S2N_PSK_TYPE_RESUMPTION); + EXPECT_EQUAL(psk.hmac_alg, S2N_HMAC_SHA256); + EXPECT_EQUAL(psk.ticket_age_add, 0); + EXPECT_EQUAL(psk.ticket_issue_time, 0); + EXPECT_EQUAL(psk.early_data_config.max_early_data_size, 0); + }; + + /* Test s2n_external_psk_new */ + { + DEFER_CLEANUP(struct s2n_psk inited_psk, s2n_psk_wipe); + EXPECT_OK(s2n_psk_init(&inited_psk, S2N_PSK_TYPE_EXTERNAL)); + + DEFER_CLEANUP(struct s2n_psk *new_psk = s2n_external_psk_new(), s2n_psk_free); + EXPECT_NOT_NULL(new_psk); + + EXPECT_BYTEARRAY_EQUAL(new_psk, &inited_psk, sizeof(struct s2n_psk)); + }; + + /* Test s2n_psk_set_identity */ + { + DEFER_CLEANUP(struct s2n_psk psk, s2n_psk_wipe); + EXPECT_OK(s2n_psk_init(&psk, S2N_PSK_TYPE_EXTERNAL)); + + uint8_t test_value_1[] = TEST_VALUE_1; + + EXPECT_FAILURE_WITH_ERRNO(s2n_psk_set_identity(NULL, test_value_1, 1), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_psk_set_identity(&psk, NULL, 1), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_psk_set_identity(&psk, test_value_1, 0), + S2N_ERR_INVALID_ARGUMENT); + + EXPECT_SUCCESS(s2n_psk_set_identity(&psk, test_value_1, sizeof(test_value_1))); + EXPECT_EQUAL(psk.identity.size, sizeof(TEST_VALUE_1)); + EXPECT_BYTEARRAY_EQUAL(psk.identity.data, TEST_VALUE_1, sizeof(TEST_VALUE_1)); + + /* source can be modified without affecting psk */ + test_value_1[0] = 'A'; + EXPECT_BYTEARRAY_EQUAL(psk.identity.data, TEST_VALUE_1, sizeof(TEST_VALUE_1)); + + /* method can be called again to replace identity */ + uint8_t test_value_2[] = TEST_VALUE_2; + EXPECT_SUCCESS(s2n_psk_set_identity(&psk, test_value_2, sizeof(test_value_2))); + EXPECT_EQUAL(psk.identity.size, sizeof(TEST_VALUE_2)); + EXPECT_BYTEARRAY_EQUAL(psk.identity.data, TEST_VALUE_2, sizeof(TEST_VALUE_2)); + }; + + /* Test s2n_psk_set_secret */ + { + DEFER_CLEANUP(struct s2n_psk psk, s2n_psk_wipe); + EXPECT_OK(s2n_psk_init(&psk, S2N_PSK_TYPE_EXTERNAL)); + + uint8_t test_value_1[] = TEST_VALUE_1; + + EXPECT_FAILURE_WITH_ERRNO(s2n_psk_set_secret(NULL, test_value_1, 1), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_psk_set_secret(&psk, NULL, 1), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_psk_set_secret(&psk, test_value_1, 0), + S2N_ERR_INVALID_ARGUMENT); + + EXPECT_SUCCESS(s2n_psk_set_secret(&psk, test_value_1, sizeof(test_value_1))); + EXPECT_EQUAL(psk.secret.size, sizeof(TEST_VALUE_1)); + EXPECT_BYTEARRAY_EQUAL(psk.secret.data, TEST_VALUE_1, sizeof(TEST_VALUE_1)); + + /* source identity can be modified without affecting psk */ + test_value_1[0] = 'A'; + EXPECT_BYTEARRAY_EQUAL(psk.secret.data, TEST_VALUE_1, sizeof(TEST_VALUE_1)); + + /* method can be called again to replace secret */ + uint8_t test_value_2[] = TEST_VALUE_2; + EXPECT_SUCCESS(s2n_psk_set_secret(&psk, test_value_2, sizeof(test_value_2))); + EXPECT_EQUAL(psk.secret.size, sizeof(TEST_VALUE_2)); + EXPECT_BYTEARRAY_EQUAL(psk.secret.data, TEST_VALUE_2, sizeof(TEST_VALUE_2)); + }; + + /* Test s2n_psk_clone */ + { + const uint8_t test_bad_value[] = "wrong"; + const uint8_t test_identity[] = "identity"; + const uint8_t test_secret[] = "secret"; + const uint8_t test_early_secret[] = "early_secret"; + const s2n_hmac_algorithm test_hmac = S2N_HMAC_SHA384; + + struct s2n_psk *original = s2n_external_psk_new(); + EXPECT_NOT_NULL(original); + EXPECT_SUCCESS(s2n_psk_set_identity(original, test_identity, sizeof(test_identity))); + EXPECT_SUCCESS(s2n_psk_set_secret(original, test_secret, sizeof(test_secret))); + EXPECT_SUCCESS(s2n_alloc(&original->early_secret, sizeof(test_early_secret))); + EXPECT_MEMCPY_SUCCESS(original->early_secret.data, test_early_secret, original->early_secret.size); + original->hmac_alg = test_hmac; + + DEFER_CLEANUP(struct s2n_psk *clone = s2n_external_psk_new(), s2n_psk_free); + EXPECT_SUCCESS(s2n_psk_set_identity(clone, test_bad_value, sizeof(test_bad_value))); + EXPECT_SUCCESS(s2n_psk_set_secret(clone, test_bad_value, sizeof(test_bad_value))); + EXPECT_NOT_NULL(clone); + + /* Check that the blobs weren't shallow copied */ + EXPECT_NOT_EQUAL(original->identity.data, clone->identity.data); + EXPECT_NOT_EQUAL(original->secret.data, clone->secret.data); + EXPECT_NOT_EQUAL(original->early_secret.data, clone->early_secret.data); + + EXPECT_OK(s2n_psk_clone(clone, original)); + + /* Free the original to ensure they share no memory */ + EXPECT_SUCCESS(s2n_psk_free(&original)); + + /* existing identity is replaced by original's identity */ + EXPECT_EQUAL(clone->identity.size, sizeof(test_identity)); + EXPECT_BYTEARRAY_EQUAL(clone->identity.data, test_identity, sizeof(test_identity)); + + /* new secret is replaced by original's secret */ + EXPECT_EQUAL(clone->secret.size, sizeof(test_secret)); + EXPECT_BYTEARRAY_EQUAL(clone->secret.data, test_secret, sizeof(test_secret)); + + /* early secret is allocated for original's early secret */ + EXPECT_EQUAL(clone->early_secret.size, sizeof(test_early_secret)); + EXPECT_BYTEARRAY_EQUAL(clone->early_secret.data, test_early_secret, sizeof(test_early_secret)); + + /* other values are copied */ + EXPECT_EQUAL(clone->hmac_alg, test_hmac); + }; + + /* Test s2n_psk_wipe */ + { + const uint8_t test_value[] = TEST_VALUE_1; + struct s2n_psk psk = { 0 }; + EXPECT_OK(s2n_psk_init(&psk, S2N_PSK_TYPE_EXTERNAL)); + + /* No-op if blobs not allocated yet */ + EXPECT_OK(s2n_psk_wipe(&psk)); + + EXPECT_SUCCESS(s2n_psk_set_identity(&psk, test_value, sizeof(test_value))); + EXPECT_NOT_EQUAL(psk.identity.size, 0); + EXPECT_SUCCESS(s2n_psk_set_secret(&psk, test_value, sizeof(test_value))); + EXPECT_NOT_EQUAL(psk.secret.size, 0); + EXPECT_SUCCESS(s2n_alloc(&psk.early_secret, sizeof(test_value))); + EXPECT_NOT_EQUAL(psk.early_secret.size, 0); + + /* Frees all blobs */ + EXPECT_OK(s2n_psk_wipe(&psk)); + EXPECT_EQUAL(psk.identity.data, NULL); + EXPECT_EQUAL(psk.identity.size, 0); + EXPECT_EQUAL(psk.secret.data, NULL); + EXPECT_EQUAL(psk.secret.size, 0); + EXPECT_EQUAL(psk.early_secret.data, NULL); + EXPECT_EQUAL(psk.early_secret.size, 0); + + /* No-op if already freed */ + EXPECT_OK(s2n_psk_wipe(&psk)); + }; + + /* Test s2n_psk_free */ + { + EXPECT_SUCCESS(s2n_psk_free(NULL)); + + struct s2n_psk *new_psk = s2n_external_psk_new(); + EXPECT_NOT_NULL(new_psk); + EXPECT_SUCCESS(s2n_psk_free(&new_psk)); + EXPECT_NULL(new_psk); + }; + + /* Test s2n_psk_parameters_init */ + { + DEFER_CLEANUP(struct s2n_psk_parameters params, s2n_psk_parameters_wipe); + + EXPECT_ERROR_WITH_ERRNO(s2n_psk_parameters_init(NULL), S2N_ERR_NULL); + + EXPECT_OK(s2n_psk_parameters_init(¶ms)); + + /* Verify params are initialized. + * Only element_size should be set. */ + struct s2n_psk_parameters expected_params = { 0 }; + expected_params.psk_list.element_size = sizeof(struct s2n_psk); + EXPECT_BYTEARRAY_EQUAL(&expected_params, ¶ms, sizeof(struct s2n_psk_parameters)); + }; + + /* Test s2n_psk_parameters_wipe */ + { + uint8_t test_value[] = TEST_VALUE_1; + + DEFER_CLEANUP(struct s2n_psk_parameters params = { 0 }, s2n_psk_parameters_wipe); + EXPECT_OK(s2n_psk_parameters_init(¶ms)); + params.binder_list_size = 1; + params.chosen_psk_wire_index = 1; + + struct s2n_psk *chosen_psk = NULL; + EXPECT_OK(s2n_array_pushback(¶ms.psk_list, (void **) &chosen_psk)); + EXPECT_OK(s2n_psk_init(chosen_psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(chosen_psk, test_value, sizeof(test_value))); + params.chosen_psk = chosen_psk; + + struct s2n_psk *other_psk = NULL; + EXPECT_OK(s2n_array_pushback(¶ms.psk_list, (void **) &other_psk)); + EXPECT_OK(s2n_psk_init(other_psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(other_psk, test_value, sizeof(test_value))); + + EXPECT_ERROR_WITH_ERRNO(s2n_psk_parameters_wipe(NULL), S2N_ERR_NULL); + EXPECT_OK(s2n_psk_parameters_wipe(¶ms)); + + /* Verify params are wiped. + * The params should be back to their initial state. */ + struct s2n_psk_parameters expected_params = { 0 }; + EXPECT_OK(s2n_psk_parameters_init(&expected_params)); + EXPECT_BYTEARRAY_EQUAL(&expected_params, ¶ms, sizeof(struct s2n_psk_parameters)); + }; + + /* Test s2n_psk_parameters_wipe_secrets */ + { + /* Safety Check */ + EXPECT_ERROR_WITH_ERRNO(s2n_psk_parameters_wipe_secrets(NULL), S2N_ERR_NULL); + + uint8_t test_identity_data[] = "test identity data"; + uint8_t test_secret_data[] = "test secret data"; + struct s2n_psk_parameters params = { 0 }; + EXPECT_OK(s2n_psk_parameters_init(¶ms)); + params.binder_list_size = 1; + params.chosen_psk_wire_index = 1; + + struct s2n_psk *chosen_psk = NULL; + EXPECT_OK(s2n_array_pushback(¶ms.psk_list, (void **) &chosen_psk)); + EXPECT_OK(s2n_psk_init(chosen_psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(chosen_psk, test_identity_data, sizeof(test_identity_data))); + EXPECT_SUCCESS(s2n_psk_set_secret(chosen_psk, test_secret_data, sizeof(test_secret_data))); + params.chosen_psk = chosen_psk; + + struct s2n_psk *other_psk = NULL; + EXPECT_OK(s2n_array_pushback(¶ms.psk_list, (void **) &other_psk)); + EXPECT_OK(s2n_psk_init(other_psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(other_psk, test_identity_data, sizeof(test_identity_data))); + EXPECT_SUCCESS(s2n_psk_set_secret(other_psk, test_secret_data, sizeof(test_secret_data))); + + EXPECT_OK(s2n_psk_parameters_wipe_secrets(¶ms)); + + /* Verify secrets are wiped */ + for (size_t i = 0; i < params.psk_list.len; i++) { + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_get(¶ms.psk_list, i, (void **) &psk)); + EXPECT_NOT_NULL(psk->identity.data); + EXPECT_NULL(psk->secret.data); + EXPECT_EQUAL(psk->secret.size, 0); + EXPECT_NULL(psk->early_secret.data); + EXPECT_EQUAL(psk->early_secret.size, 0); + } + + EXPECT_OK(s2n_psk_parameters_wipe(¶ms)); + }; + + /* Test s2n_connection psk_parameters lifecycle. + * This test mostly exists to check for memory leaks. */ + { + uint8_t test_value[] = TEST_VALUE_1; + struct s2n_psk_parameters empty_psk_params = { 0 }; + EXPECT_OK(s2n_psk_parameters_init(&empty_psk_params)); + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->psk_params.binder_list_size = 1; + conn->psk_params.chosen_psk_wire_index = 1; + + struct s2n_psk *first_psk = NULL; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &first_psk)); + EXPECT_OK(s2n_psk_init(first_psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(first_psk, test_value, sizeof(test_value))); + conn->psk_params.chosen_psk = first_psk; + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_BYTEARRAY_EQUAL(&empty_psk_params, &conn->psk_params, sizeof(struct s2n_psk_parameters)); + + struct s2n_psk *second_psk = NULL; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &second_psk)); + EXPECT_OK(s2n_psk_init(second_psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(second_psk, test_value, sizeof(test_value))); + conn->psk_params.chosen_psk = second_psk; + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_BYTEARRAY_EQUAL(&empty_psk_params, &conn->psk_params, sizeof(struct s2n_psk_parameters)); + + struct s2n_psk *third_psk = NULL; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &third_psk)); + EXPECT_OK(s2n_psk_init(third_psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(third_psk, test_value, sizeof(test_value))); + conn->psk_params.chosen_psk = third_psk; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_psk_write_binder_list */ + { + uint8_t test_value[] = TEST_VALUE_1; + + struct s2n_blob client_hello_prefix = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&client_hello_prefix, test_value, sizeof(test_value))); + + /* Write two binders. + * There are no available test vectors for multiple PSKs, but we should at least + * verify that we write something relatively sane for this use case. */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer out = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + + struct s2n_psk_parameters *params = &conn->psk_params; + + struct s2n_psk *first_psk = NULL; + EXPECT_OK(s2n_array_pushback(¶ms->psk_list, (void **) &first_psk)); + EXPECT_OK(s2n_psk_init(first_psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_secret(first_psk, test_value, sizeof(test_value))); + + struct s2n_psk *second_psk = NULL; + EXPECT_OK(s2n_array_pushback(¶ms->psk_list, (void **) &second_psk)); + EXPECT_OK(s2n_psk_init(second_psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_secret(second_psk, test_value, sizeof(test_value))); + + EXPECT_OK(s2n_psk_write_binder_list(conn, &client_hello_prefix, &out)); + + uint16_t binder_list_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&out, &binder_list_size)); + EXPECT_EQUAL(binder_list_size, s2n_stuffer_data_available(&out)); + + /* After reading both binders, the buffer should be empty. */ + for (int i = 0; i < 2; i++) { + uint8_t binder_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&out, &binder_size)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&out, binder_size)); + } + EXPECT_EQUAL(s2n_stuffer_data_available(&out), 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + }; + + /* On a retry, do not write binders for PSKs that do not match the cipher suite. + * + *= https://tools.ietf.org/rfc/rfc8446#section-4.1.4 + *= type=test + *# In addition, in its updated ClientHello, the client SHOULD NOT offer + *# any pre-shared keys associated with a hash other than that of the + *# selected cipher suite. This allows the client to avoid having to + *# compute partial hash transcripts for multiple hashes in the second + *# ClientHello. + */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + conn->handshake.handshake_type = HELLO_RETRY_REQUEST; + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + struct s2n_stuffer out = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + + struct s2n_psk_parameters *params = &conn->psk_params; + + struct s2n_psk *other_psk = NULL; + EXPECT_OK(s2n_array_pushback(¶ms->psk_list, (void **) &other_psk)); + EXPECT_OK(s2n_psk_init(other_psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_secret(other_psk, test_value, sizeof(test_value))); + other_psk->hmac_alg = conn->secure->cipher_suite->prf_alg - 1; + + struct s2n_psk *matching_psk = NULL; + EXPECT_OK(s2n_array_pushback(¶ms->psk_list, (void **) &matching_psk)); + EXPECT_OK(s2n_psk_init(matching_psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_secret(matching_psk, test_value, sizeof(test_value))); + matching_psk->hmac_alg = conn->secure->cipher_suite->prf_alg; + + EXPECT_OK(s2n_psk_write_binder_list(conn, &client_hello_prefix, &out)); + + uint16_t binder_list_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&out, &binder_list_size)); + EXPECT_EQUAL(binder_list_size, s2n_stuffer_data_available(&out)); + + /* There should only be one binder in the list + * (the other PSK was ignored because it didn't match the cipher suite) + * so that one binder should fill the rest of the stuffer. */ + uint8_t binder_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&out, &binder_size)); + EXPECT_EQUAL(binder_size, s2n_stuffer_data_available(&out)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + } + }; + + /* Test binder calculations with known values */ + { + /* Test Vectors from https://tools.ietf.org/html/rfc8448#section-4 */ + S2N_BLOB_FROM_HEX(identity, + "2c035d829359ee5ff7af4ec900000000262a6494dc486d2c8a34cb33fa90bf1b00" + "70ad3c498883c9367c09a2be785abc55cd226097a3a982117283f82a03a143efd3" + "ff5dd36d64e861be7fd61d2827db279cce145077d454a3664d4e6da4d29ee03725" + "a6a4dafcd0fc67d2aea70529513e3da2677fa5906c5b3f7d8f92f228bda40dda72" + "1470f9fbf297b5aea617646fac5c03272e970727c621a79141ef5f7de6505e5bfb" + "c388e93343694093934ae4d357fad6aacb"); + S2N_BLOB_FROM_HEX(client_hello_prefix, + "010001fc03031bc3ceb6bbe39cff938355b5a50adb6db21b7a6af649d7b4bc419d" + "7876487d95000006130113031302010001cd0000000b0009000006736572766572" + "ff01000100000a00140012001d0017001800190100010101020103010400330026" + "0024001d0020e4ffb68ac05f8d96c99da26698346c6be16482badddafe051a66b4" + "f18d668f0b002a0000002b0003020304000d0020001e0403050306030203080408" + "05080604010501060102010402050206020202002d00020101001c000240010015" + "005700000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000002900dd00b800b22c03" + "5d829359ee5ff7af4ec900000000262a6494dc486d2c8a34cb33fa90bf1b0070ad" + "3c498883c9367c09a2be785abc55cd226097a3a982117283f82a03a143efd3ff5d" + "d36d64e861be7fd61d2827db279cce145077d454a3664d4e6da4d29ee03725a6a4" + "dafcd0fc67d2aea70529513e3da2677fa5906c5b3f7d8f92f228bda40dda721470" + "f9fbf297b5aea617646fac5c03272e970727c621a79141ef5f7de6505e5bfbc388" + "e93343694093934ae4d357fad6aacb"); + S2N_BLOB_FROM_HEX(full_client_hello, + "010001fc03031bc3ceb6bbe39cff938355b5a50adb6db21b7a6af649d7b4bc419d" + "7876487d95000006130113031302010001cd0000000b0009000006736572766572" + "ff01000100000a00140012001d0017001800190100010101020103010400330026" + "0024001d0020e4ffb68ac05f8d96c99da26698346c6be16482badddafe051a66b4" + "f18d668f0b002a0000002b0003020304000d0020001e0403050306030203080408" + "05080604010501060102010402050206020202002d00020101001c000240010015" + "005700000000000000000000000000000000000000000000000000000000000000" + "000000000000000000000000000000000000000000000000000000000000000000" + "0000000000000000000000000000000000000000000000002900dd00b800b22c03" + "5d829359ee5ff7af4ec900000000262a6494dc486d2c8a34cb33fa90bf1b0070ad" + "3c498883c9367c09a2be785abc55cd226097a3a982117283f82a03a143efd3ff5d" + "d36d64e861be7fd61d2827db279cce145077d454a3664d4e6da4d29ee03725a6a4" + "dafcd0fc67d2aea70529513e3da2677fa5906c5b3f7d8f92f228bda40dda721470" + "f9fbf297b5aea617646fac5c03272e970727c621a79141ef5f7de6505e5bfbc388" + "e93343694093934ae4d357fad6aacb0021203add4fb2d8fdf822a0ca3cf7678ef5" + "e88dae990141c5924d57bb6fa31b9e5f9d"); + S2N_BLOB_FROM_HEX(resumption_secret, + "4ecd0eb6ec3b4d87f5d6028f922ca4c5851a277fd41311c9e62d2c9492e1c4f3"); + S2N_BLOB_FROM_HEX(binder_hash, + "63224b2e4573f2d3454ca84b9d009a04f6be9e05711a8396473aefa01e924a14"); + S2N_BLOB_FROM_HEX(early_secret, + "9b2188e9b2fc6d64d71dc329900e20bb41915000f678aa839cbb797cb7d8332c"); + S2N_BLOB_FROM_HEX(finished_binder, + "3add4fb2d8fdf822a0ca3cf7678ef5e88dae990141c5924d57bb6fa31b9e5f9d"); + + /* Test s2n_psk_calculate_binder_hash with known values */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_blob hash_value = { 0 }; + uint8_t hash_value_data[SHA256_DIGEST_LENGTH]; + EXPECT_SUCCESS(s2n_blob_init(&hash_value, hash_value_data, sizeof(hash_value_data))); + + EXPECT_SUCCESS(s2n_psk_calculate_binder_hash(conn, S2N_HMAC_SHA256, &client_hello_prefix, &hash_value)); + S2N_BLOB_EXPECT_EQUAL(hash_value, binder_hash); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_psk_calculate_binder with known values */ + { + DEFER_CLEANUP(struct s2n_psk test_psk, s2n_psk_wipe); + EXPECT_OK(s2n_psk_init(&test_psk, S2N_PSK_TYPE_RESUMPTION)); + EXPECT_SUCCESS(s2n_psk_set_secret(&test_psk, resumption_secret.data, resumption_secret.size)); + + struct s2n_blob binder_value = { 0 }; + uint8_t binder_value_data[SHA256_DIGEST_LENGTH]; + EXPECT_SUCCESS(s2n_blob_init(&binder_value, binder_value_data, sizeof(binder_value_data))); + + EXPECT_SUCCESS(s2n_psk_calculate_binder(&test_psk, &binder_hash, &binder_value)); + S2N_BLOB_EXPECT_EQUAL(test_psk.early_secret, early_secret); + S2N_BLOB_EXPECT_EQUAL(binder_value, finished_binder); + }; + + /* Test s2n_psk_verify_binder with known values */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + DEFER_CLEANUP(struct s2n_psk test_psk, s2n_psk_wipe); + EXPECT_OK(s2n_psk_init(&test_psk, S2N_PSK_TYPE_RESUMPTION)); + EXPECT_SUCCESS(s2n_psk_set_secret(&test_psk, resumption_secret.data, resumption_secret.size)); + + struct s2n_blob binder_value = { 0 }; + uint8_t binder_value_data[SHA256_DIGEST_LENGTH]; + EXPECT_SUCCESS(s2n_blob_init(&binder_value, binder_value_data, sizeof(binder_value_data))); + + EXPECT_SUCCESS(s2n_psk_verify_binder(conn, &test_psk, &client_hello_prefix, &finished_binder)); + S2N_BLOB_EXPECT_EQUAL(test_psk.early_secret, early_secret); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_psk_verify_binder with incorrect binder */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + DEFER_CLEANUP(struct s2n_psk test_psk, s2n_psk_wipe); + EXPECT_OK(s2n_psk_init(&test_psk, S2N_PSK_TYPE_RESUMPTION)); + EXPECT_SUCCESS(s2n_psk_set_secret(&test_psk, resumption_secret.data, resumption_secret.size)); + + struct s2n_blob *incorrect_binder_value = &resumption_secret; + + struct s2n_blob binder_value = { 0 }; + uint8_t binder_value_data[SHA256_DIGEST_LENGTH]; + EXPECT_SUCCESS(s2n_blob_init(&binder_value, binder_value_data, sizeof(binder_value_data))); + + EXPECT_FAILURE(s2n_psk_verify_binder(conn, &test_psk, &client_hello_prefix, incorrect_binder_value)); + S2N_BLOB_EXPECT_EQUAL(test_psk.early_secret, early_secret); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_psk_write_binder with known values */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + DEFER_CLEANUP(struct s2n_psk psk = { 0 }, s2n_psk_wipe); + EXPECT_OK(s2n_psk_init(&psk, S2N_PSK_TYPE_RESUMPTION)); + EXPECT_SUCCESS(s2n_psk_set_secret(&psk, resumption_secret.data, resumption_secret.size)); + + struct s2n_stuffer out = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + + EXPECT_OK(s2n_psk_write_binder(conn, &psk, &binder_hash, &out)); + + uint8_t binder_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&out, &binder_size)); + EXPECT_EQUAL(binder_size, s2n_stuffer_data_available(&out)); + EXPECT_EQUAL(binder_size, finished_binder.size); + + uint8_t *binder_data; + EXPECT_NOT_NULL(binder_data = s2n_stuffer_raw_read(&out, binder_size)); + EXPECT_BYTEARRAY_EQUAL(binder_data, finished_binder.data, binder_size); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + }; + + /* Test s2n_psk_write_binder_list with known values */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); + EXPECT_OK(s2n_psk_init(psk, S2N_PSK_TYPE_RESUMPTION)); + EXPECT_SUCCESS(s2n_psk_set_identity(psk, identity.data, identity.size)); + EXPECT_SUCCESS(s2n_psk_set_secret(psk, resumption_secret.data, resumption_secret.size)); + + struct s2n_stuffer out = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + + EXPECT_OK(s2n_psk_write_binder_list(conn, &client_hello_prefix, &out)); + + uint16_t binder_list_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&out, &binder_list_size)); + EXPECT_EQUAL(binder_list_size, s2n_stuffer_data_available(&out)); + + uint8_t binder_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&out, &binder_size)); + EXPECT_EQUAL(binder_size, s2n_stuffer_data_available(&out)); + EXPECT_EQUAL(binder_size, finished_binder.size); + + uint8_t *binder_data; + EXPECT_NOT_NULL(binder_data = s2n_stuffer_raw_read(&out, binder_size)); + EXPECT_BYTEARRAY_EQUAL(binder_data, finished_binder.data, binder_size); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + }; + + /* Test s2n_psk_write_binder_list with multiple PSKs */ + { + const uint8_t psk_count = 5; + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + for (uint8_t i = 0; i < psk_count; i++) { + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); + EXPECT_OK(s2n_psk_init(psk, S2N_PSK_TYPE_RESUMPTION)); + EXPECT_SUCCESS(s2n_psk_set_identity(psk, identity.data, identity.size)); + EXPECT_SUCCESS(s2n_psk_set_secret(psk, resumption_secret.data, resumption_secret.size)); + } + + struct s2n_stuffer out = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + + EXPECT_OK(s2n_psk_write_binder_list(conn, &client_hello_prefix, &out)); + + uint16_t binder_list_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&out, &binder_list_size)); + EXPECT_EQUAL(binder_list_size, s2n_stuffer_data_available(&out)); + + for (uint8_t i = 0; i < psk_count; i++) { + uint8_t binder_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&out, &binder_size)); + EXPECT_EQUAL(binder_size, finished_binder.size); + + uint8_t *binder_data; + EXPECT_NOT_NULL(binder_data = s2n_stuffer_raw_read(&out, binder_size)); + EXPECT_BYTEARRAY_EQUAL(binder_data, finished_binder.data, binder_size); + } + + EXPECT_EQUAL(s2n_stuffer_data_available(&out), 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + }; + + /* Test s2n_psk_write_binder_list with multiple hash algs */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + for (s2n_hmac_algorithm hmac_alg = S2N_HMAC_SHA256; hmac_alg <= S2N_HMAC_SHA384; hmac_alg++) { + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); + EXPECT_OK(s2n_psk_init(psk, S2N_PSK_TYPE_RESUMPTION)); + EXPECT_SUCCESS(s2n_psk_set_identity(psk, identity.data, identity.size)); + EXPECT_SUCCESS(s2n_psk_set_secret(psk, resumption_secret.data, resumption_secret.size)); + psk->hmac_alg = hmac_alg; + } + + struct s2n_stuffer out = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + + EXPECT_OK(s2n_psk_write_binder_list(conn, &client_hello_prefix, &out)); + + uint16_t binder_list_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&out, &binder_list_size)); + EXPECT_EQUAL(binder_list_size, s2n_stuffer_data_available(&out)); + + for (s2n_hmac_algorithm hmac_alg = S2N_HMAC_SHA256; hmac_alg <= S2N_HMAC_SHA384; hmac_alg++) { + uint8_t hash_size = 0; + POSIX_GUARD(s2n_hmac_digest_size(hmac_alg, &hash_size)); + + uint8_t binder_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&out, &binder_size)); + EXPECT_EQUAL(binder_size, hash_size); + + uint8_t *binder_data; + EXPECT_NOT_NULL(binder_data = s2n_stuffer_raw_read(&out, binder_size)); + /* We can only actually verify the result for SHA256; we don't have known + * values for any other hash. */ + if (hmac_alg == S2N_HMAC_SHA256) { + EXPECT_BYTEARRAY_EQUAL(binder_data, finished_binder.data, binder_size); + } + } + + EXPECT_EQUAL(s2n_stuffer_data_available(&out), 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + }; + + /* Test s2n_finish_psk_extension with known values */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_stuffer_write(&conn->handshake.io, &client_hello_prefix)); + conn->psk_params.binder_list_size = full_client_hello.size - client_hello_prefix.size; + EXPECT_SUCCESS(s2n_stuffer_skip_write(&conn->handshake.io, conn->psk_params.binder_list_size)); + + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); + EXPECT_OK(s2n_psk_init(psk, S2N_PSK_TYPE_RESUMPTION)); + EXPECT_SUCCESS(s2n_psk_set_identity(psk, identity.data, identity.size)); + EXPECT_SUCCESS(s2n_psk_set_secret(psk, resumption_secret.data, resumption_secret.size)); + + EXPECT_OK(s2n_finish_psk_extension(conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->handshake.io), full_client_hello.size); + EXPECT_BYTEARRAY_EQUAL(conn->handshake.io.blob.data, full_client_hello.data, full_client_hello.size); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test s2n_connection_append_psk */ + { + uint8_t identity_0[] = "identity"; + uint8_t secret_0[] = "secret"; + + uint8_t identity_1[] = "identity 1"; + + uint8_t huge_identity[UINT16_MAX] = { 0 }; + + DEFER_CLEANUP(struct s2n_psk *input_psk = s2n_external_psk_new(), s2n_psk_free); + EXPECT_SUCCESS(s2n_psk_set_identity(input_psk, identity_0, sizeof(identity_0))); + EXPECT_SUCCESS(s2n_psk_set_secret(input_psk, secret_0, sizeof(secret_0))); + EXPECT_SUCCESS(s2n_psk_set_hmac(input_psk, S2N_PSK_HMAC_SHA384)); + + /* Safety checks */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_append_psk(NULL, input_psk), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_append_psk(conn, NULL), S2N_ERR_NULL); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Append valid PSK to empty list */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_append_psk(conn, input_psk)); + + struct s2n_psk *actual_psk = NULL; + EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, 0, (void **) &actual_psk)); + EXPECT_NOT_NULL(actual_psk); + + EXPECT_EQUAL(actual_psk->type, S2N_PSK_TYPE_EXTERNAL); + EXPECT_EQUAL(actual_psk->identity.size, input_psk->identity.size); + EXPECT_BYTEARRAY_EQUAL(actual_psk->identity.data, input_psk->identity.data, input_psk->identity.size); + EXPECT_EQUAL(actual_psk->secret.size, input_psk->secret.size); + EXPECT_BYTEARRAY_EQUAL(actual_psk->secret.data, input_psk->secret.data, input_psk->secret.size); + EXPECT_EQUAL(actual_psk->hmac_alg, S2N_HMAC_SHA384); + EXPECT_EQUAL(actual_psk->ticket_age_add, 0); + EXPECT_EQUAL(actual_psk->ticket_issue_time, 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Original PSK can be safely freed after being added to a connection */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_psk *original_psk = s2n_external_psk_new(); + EXPECT_SUCCESS(s2n_psk_set_identity(original_psk, identity_0, sizeof(identity_0))); + EXPECT_SUCCESS(s2n_psk_set_secret(original_psk, secret_0, sizeof(secret_0))); + EXPECT_SUCCESS(s2n_psk_set_hmac(original_psk, S2N_PSK_HMAC_SHA384)); + + EXPECT_SUCCESS(s2n_connection_append_psk(conn, original_psk)); + + /* Original PSK freed */ + EXPECT_SUCCESS(s2n_psk_free(&original_psk)); + EXPECT_NULL(original_psk); + + /* PSK on connection not freed */ + struct s2n_psk *expected_psk = NULL; + EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, 0, (void **) &expected_psk)); + EXPECT_NOT_NULL(expected_psk); + + /* PSK on connection's buffers not freed */ + EXPECT_EQUAL(expected_psk->identity.size, input_psk->identity.size); + EXPECT_BYTEARRAY_EQUAL(expected_psk->identity.data, input_psk->identity.data, input_psk->identity.size); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Invalid PSK not added to connection */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + /* PSK is invalid because it has no identity */ + DEFER_CLEANUP(struct s2n_psk *invalid_psk = s2n_external_psk_new(), s2n_psk_free); + EXPECT_SUCCESS(s2n_psk_set_secret(invalid_psk, secret_0, sizeof(secret_0))); + + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_append_psk(conn, invalid_psk), + S2N_ERR_INVALID_ARGUMENT); + EXPECT_EQUAL(conn->psk_params.psk_list.len, 0); + + /* Successful if identity added to PSK, making it valid */ + EXPECT_SUCCESS(s2n_psk_set_identity(invalid_psk, identity_0, sizeof(identity_0))); + EXPECT_SUCCESS(s2n_connection_append_psk(conn, invalid_psk)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Huge PSK not added to client connection */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + DEFER_CLEANUP(struct s2n_psk *invalid_psk = s2n_external_psk_new(), s2n_psk_free); + EXPECT_SUCCESS(s2n_psk_set_secret(invalid_psk, secret_0, sizeof(secret_0))); + + /* PSK is invalid because it will not fit in the PSK extension */ + uint16_t max_identity_size = UINT16_MAX + - S2N_EXTENSION_HEADER_LENGTH + - sizeof(uint16_t) /* identity list size */ + - sizeof(uint16_t) /* identity size */ + - sizeof(uint32_t) /* obfuscated age add */ + - sizeof(uint16_t) /* binder list size */ + - sizeof(uint8_t) /* binder size */ + - SHA256_DIGEST_LENGTH; /* binder */ + EXPECT_SUCCESS(s2n_psk_set_identity(invalid_psk, huge_identity, max_identity_size + 1)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_append_psk(conn, invalid_psk), + S2N_ERR_OFFERED_PSKS_TOO_LONG); + EXPECT_EQUAL(conn->psk_params.psk_list.len, 0); + + /* Successful if smaller identity used */ + EXPECT_SUCCESS(s2n_psk_set_identity(invalid_psk, huge_identity, max_identity_size)); + EXPECT_SUCCESS(s2n_connection_append_psk(conn, invalid_psk)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Huge PSK added to server connection */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + DEFER_CLEANUP(struct s2n_psk *invalid_psk = s2n_external_psk_new(), s2n_psk_free); + EXPECT_SUCCESS(s2n_psk_set_secret(invalid_psk, secret_0, sizeof(secret_0))); + EXPECT_EQUAL(sizeof(huge_identity), UINT16_MAX); + + EXPECT_SUCCESS(s2n_psk_set_identity(invalid_psk, huge_identity, sizeof(huge_identity))); + + EXPECT_SUCCESS(s2n_connection_append_psk(conn, invalid_psk)); + EXPECT_EQUAL(conn->psk_params.psk_list.len, 1); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* New PSK would make existing list too long for client */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + uint32_t offered_psks_size = 0; + struct s2n_psk *test_psk = NULL; + EXPECT_SUCCESS(s2n_connection_set_psk_mode(conn, S2N_PSK_MODE_EXTERNAL)); + while (offered_psks_size < UINT16_MAX) { + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &test_psk)); + EXPECT_NOT_NULL(test_psk); + + EXPECT_OK(s2n_psk_init(test_psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(test_psk, identity_1, sizeof(identity_1))); + EXPECT_OK(s2n_psk_parameters_offered_psks_size(&conn->psk_params, &offered_psks_size)); + } + + /* Delete the last PSK that caused the list to exceed the allowed size */ + EXPECT_OK(s2n_psk_wipe(test_psk)); + EXPECT_OK(s2n_array_remove(&conn->psk_params.psk_list, conn->psk_params.psk_list.len - 1)); + EXPECT_OK(s2n_psk_parameters_offered_psks_size(&conn->psk_params, &offered_psks_size)); + EXPECT_TRUE(offered_psks_size < UINT16_MAX); + + uint32_t original_psk_count = conn->psk_params.psk_list.len; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_append_psk(conn, input_psk), + S2N_ERR_OFFERED_PSKS_TOO_LONG); + EXPECT_EQUAL(conn->psk_params.psk_list.len, original_psk_count); + + /* Server allows an arbitrarily long list */ + conn->mode = S2N_SERVER; + EXPECT_SUCCESS(s2n_connection_append_psk(conn, input_psk)); + EXPECT_EQUAL(conn->psk_params.psk_list.len, original_psk_count + 1); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* PSK matches existing external PSK */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_append_psk(conn, input_psk)); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_append_psk(conn, input_psk), + S2N_ERR_DUPLICATE_PSK_IDENTITIES); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Can't mix resumption and external PSKs */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + DEFER_CLEANUP(struct s2n_psk *test_external_psk = s2n_test_psk_new(conn), s2n_psk_free); + test_external_psk->type = S2N_PSK_TYPE_EXTERNAL; + DEFER_CLEANUP(struct s2n_psk *test_resumption_psk = s2n_test_psk_new(conn), s2n_psk_free); + test_resumption_psk->type = S2N_PSK_TYPE_RESUMPTION; + + /* Add resumption to list that contains external */ + { + EXPECT_SUCCESS(s2n_connection_append_psk(conn, test_external_psk)); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_append_psk(conn, test_resumption_psk), + S2N_ERR_PSK_MODE); + }; + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + /* Add external to a list that contains resumption */ + { + EXPECT_SUCCESS(s2n_connection_append_psk(conn, test_resumption_psk)); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_append_psk(conn, test_external_psk), + S2N_ERR_PSK_MODE); + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test: s2n_psk_set_hmac */ + { + EXPECT_FAILURE_WITH_ERRNO(s2n_psk_set_hmac(NULL, S2N_PSK_HMAC_SHA256), S2N_ERR_NULL); + + DEFER_CLEANUP(struct s2n_psk psk, s2n_psk_wipe); + EXPECT_OK(s2n_psk_init(&psk, S2N_PSK_TYPE_EXTERNAL)); + + EXPECT_EQUAL(psk.hmac_alg, S2N_HMAC_SHA256); + EXPECT_FAILURE_WITH_ERRNO(s2n_psk_set_hmac(&psk, -1), S2N_ERR_HMAC_INVALID_ALGORITHM); + EXPECT_EQUAL(psk.hmac_alg, S2N_HMAC_SHA256); + + EXPECT_SUCCESS(s2n_psk_set_hmac(&psk, S2N_PSK_HMAC_SHA384)); + EXPECT_EQUAL(psk.hmac_alg, S2N_HMAC_SHA384); + + EXPECT_SUCCESS(s2n_psk_set_hmac(&psk, S2N_PSK_HMAC_SHA256)); + EXPECT_EQUAL(psk.hmac_alg, S2N_HMAC_SHA256); + }; + + /* Test: s2n_config_set_psk_mode */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_EQUAL(config->psk_mode, S2N_PSK_MODE_RESUMPTION); + + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_psk_mode(NULL, S2N_PSK_MODE_EXTERNAL), S2N_ERR_NULL); + EXPECT_EQUAL(config->psk_mode, S2N_PSK_MODE_RESUMPTION); + + EXPECT_SUCCESS(s2n_config_set_psk_mode(config, S2N_PSK_MODE_EXTERNAL)); + EXPECT_EQUAL(config->psk_mode, S2N_PSK_MODE_EXTERNAL); + + EXPECT_SUCCESS(s2n_config_set_psk_mode(config, S2N_PSK_MODE_RESUMPTION)); + EXPECT_EQUAL(config->psk_mode, S2N_PSK_MODE_RESUMPTION); + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test: s2n_connection_set_psk_mode */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_EQUAL(conn->psk_params.type, S2N_PSK_TYPE_RESUMPTION); + + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_set_psk_mode(NULL, S2N_PSK_MODE_EXTERNAL), S2N_ERR_NULL); + EXPECT_EQUAL(conn->psk_params.type, S2N_PSK_TYPE_RESUMPTION); + EXPECT_FALSE(conn->psk_mode_overridden); + + EXPECT_SUCCESS(s2n_connection_set_psk_mode(conn, S2N_PSK_MODE_RESUMPTION)); + EXPECT_EQUAL(conn->psk_params.type, S2N_PSK_TYPE_RESUMPTION); + EXPECT_TRUE(conn->psk_mode_overridden); + + EXPECT_SUCCESS(s2n_connection_set_psk_mode(conn, S2N_PSK_MODE_EXTERNAL)); + EXPECT_EQUAL(conn->psk_params.type, S2N_PSK_TYPE_EXTERNAL); + EXPECT_TRUE(conn->psk_mode_overridden); + + DEFER_CLEANUP(struct s2n_psk *test_external_psk = s2n_test_psk_new(conn), s2n_psk_free); + EXPECT_SUCCESS(s2n_connection_append_psk(conn, test_external_psk)); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_set_psk_mode(conn, S2N_PSK_MODE_RESUMPTION), S2N_ERR_PSK_MODE); + EXPECT_EQUAL(conn->psk_params.type, S2N_PSK_TYPE_EXTERNAL); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: s2n_connection_get_negotiated_psk_identity_length */ + { + const uint8_t psk_identity[] = "identity"; + struct s2n_connection *conn = NULL; + uint16_t identity_length = 0; + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_negotiated_psk_identity_length(NULL, &identity_length), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_negotiated_psk_identity_length(conn, NULL), S2N_ERR_NULL); + + EXPECT_NULL(conn->psk_params.chosen_psk); + EXPECT_SUCCESS(s2n_connection_get_negotiated_psk_identity_length(conn, &identity_length)); + EXPECT_EQUAL(identity_length, 0); + + DEFER_CLEANUP(struct s2n_psk *psk = s2n_external_psk_new(), s2n_psk_free); + EXPECT_SUCCESS(s2n_psk_set_identity(psk, psk_identity, sizeof(psk_identity))); + conn->psk_params.chosen_psk = psk; + EXPECT_SUCCESS(s2n_connection_get_negotiated_psk_identity_length(conn, &identity_length)); + EXPECT_EQUAL(identity_length, sizeof(psk_identity)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: s2n_connection_get_negotiated_psk_identity */ + { + const uint8_t psk_identity[] = "identity"; + const uint8_t empty_identity[sizeof(psk_identity)] = { 0 }; + struct s2n_connection *conn = NULL; + uint8_t identity[sizeof(psk_identity)] = { 0 }; + uint16_t max_identity_length = 0; + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_negotiated_psk_identity(NULL, identity, max_identity_length), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_negotiated_psk_identity(conn, NULL, max_identity_length), S2N_ERR_NULL); + + EXPECT_NULL(conn->psk_params.chosen_psk); + EXPECT_SUCCESS(s2n_connection_get_negotiated_psk_identity(conn, identity, max_identity_length)); + EXPECT_EQUAL(max_identity_length, 0); + EXPECT_BYTEARRAY_EQUAL(identity, empty_identity, sizeof(empty_identity)); + + DEFER_CLEANUP(struct s2n_psk *psk = s2n_external_psk_new(), s2n_psk_free); + EXPECT_SUCCESS(s2n_psk_set_identity(psk, psk_identity, sizeof(psk_identity))); + conn->psk_params.chosen_psk = psk; + EXPECT_SUCCESS(s2n_connection_get_negotiated_psk_identity_length(conn, &max_identity_length)); + EXPECT_SUCCESS(s2n_connection_get_negotiated_psk_identity(conn, identity, max_identity_length)); + EXPECT_BYTEARRAY_EQUAL(identity, psk_identity, sizeof(psk_identity)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_negotiated_psk_identity(conn, identity, 0), S2N_ERR_INSUFFICIENT_MEM_SIZE); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_psk_validate_keying_material */ + { + uint64_t current_time = 100; + + /* Safety */ + EXPECT_ERROR_WITH_ERRNO(s2n_psk_validate_keying_material(NULL), S2N_ERR_NULL); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_OK(s2n_config_mock_wall_clock(config, ¤t_time)); + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + DEFER_CLEANUP(struct s2n_psk *chosen_psk = s2n_test_psk_new(conn), s2n_psk_free); + + /* No-op if no chosen PSK */ + EXPECT_OK(s2n_psk_validate_keying_material(conn)); + + conn->psk_params.chosen_psk = chosen_psk; + + /* No-op if chosen PSK is external */ + chosen_psk->type = S2N_PSK_TYPE_EXTERNAL; + EXPECT_OK(s2n_psk_validate_keying_material(conn)); + + chosen_psk->type = S2N_PSK_TYPE_RESUMPTION; + + /* Okay if chosen PSK's material is not expired */ + chosen_psk->keying_material_expiration = UINT64_MAX; + EXPECT_OK(s2n_psk_validate_keying_material(conn)); + + /* Fails if chosen PSK's material is expired */ + chosen_psk->keying_material_expiration = 0; + EXPECT_ERROR_WITH_ERRNO(s2n_psk_validate_keying_material(conn), S2N_ERR_KEYING_MATERIAL_EXPIRED); + + /* Fails if chosen PSK's material expires at current_time */ + chosen_psk->keying_material_expiration = current_time; + EXPECT_ERROR_WITH_ERRNO(s2n_psk_validate_keying_material(conn), S2N_ERR_KEYING_MATERIAL_EXPIRED); + + /* Fails if chosen PSK's material has less than 1s left to live */ + chosen_psk->keying_material_expiration = current_time + 1; + EXPECT_ERROR_WITH_ERRNO(s2n_psk_validate_keying_material(conn), S2N_ERR_KEYING_MATERIAL_EXPIRED); + + /* Okay if chosen PSK's material has more than 1s left to live */ + chosen_psk->keying_material_expiration = current_time + ONE_SEC_IN_NANOS + 1; + EXPECT_OK(s2n_psk_validate_keying_material(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_quic_support_io_test.c b/tests/unit/s2n_quic_support_io_test.c new file mode 100644 index 00000000000..f6aa0d0f970 --- /dev/null +++ b/tests/unit/s2n_quic_support_io_test.c @@ -0,0 +1,526 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_quic_support.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_mem.h" + +/* We need access to some io logic */ +#include "tls/s2n_handshake_io.c" + +#define TEST_TICKET_AGE_ADD 0x01, 0x02, 0x03, 0x04 +#define TEST_LIFETIME 0x00, 0x01, 0x01, 0x01 +#define TEST_TICKET 0x01, 0xFF, 0x23 + +static const uint8_t TEST_DATA[] = "test"; +static const size_t TEST_DATA_SIZE = sizeof(TEST_DATA); + +struct s2n_stuffer input_stuffer, output_stuffer; + +static int s2n_test_session_ticket_cb(struct s2n_connection *conn, void *ctx, struct s2n_session_ticket *ticket) +{ + uint8_t *count = (uint8_t *) ctx; + (*count)++; + + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_setup_conn(struct s2n_connection *conn) +{ + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + RESULT_GUARD_POSIX(s2n_stuffer_wipe(&input_stuffer)); + RESULT_GUARD_POSIX(s2n_stuffer_wipe(&output_stuffer)); + RESULT_GUARD_POSIX(s2n_connection_set_io_stuffers(&input_stuffer, &output_stuffer, conn)); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_setup_conn_for_client_hello(struct s2n_connection *conn) +{ + RESULT_GUARD(s2n_setup_conn(conn)); + conn->handshake.handshake_type = INITIAL; + conn->handshake.message_number = 0; + RESULT_ENSURE_EQ(s2n_conn_get_current_message_type(conn), CLIENT_HELLO); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_setup_conn_for_server_hello(struct s2n_connection *conn) +{ + RESULT_GUARD(s2n_setup_conn(conn)); + + /* Use arbitrary cipher suite */ + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + /* Setup secrets */ + const struct s2n_ecc_preferences *ecc_preferences = NULL; + RESULT_GUARD_POSIX(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; + conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; + if (conn->kex_params.server_ecc_evp_params.evp_pkey == NULL) { + RESULT_GUARD_POSIX(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.server_ecc_evp_params)); + } + if (conn->kex_params.client_ecc_evp_params.evp_pkey == NULL) { + RESULT_GUARD_POSIX(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + } + + /* Set handshake to write message */ + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; + conn->handshake.message_number = 1; + RESULT_ENSURE_EQ(s2n_conn_get_current_message_type(conn), SERVER_HELLO); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_write_test_message(struct s2n_blob *out, message_type_t message_type) +{ + RESULT_GUARD_POSIX(s2n_alloc(out, TEST_DATA_SIZE + TLS_HANDSHAKE_HEADER_LENGTH)); + + struct s2n_stuffer stuffer = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_init(&stuffer, out)); + + RESULT_GUARD_POSIX(s2n_stuffer_write_uint8(&stuffer, message_type)); + RESULT_GUARD_POSIX(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE)); + + return S2N_RESULT_OK; +} + +static int s2n_test_write_handler(struct s2n_connection *conn) +{ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->handshake.io, TEST_DATA, TEST_DATA_SIZE)); + return S2N_SUCCESS; +} + +static int s2n_test_read_handler(struct s2n_connection *conn) +{ + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->handshake.io), TEST_DATA_SIZE); + EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&conn->handshake.io, TEST_DATA_SIZE), + TEST_DATA, TEST_DATA_SIZE); + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + /* Test: s2n_quic_write_handshake_message */ + { + /* Safety checks */ + EXPECT_ERROR(s2n_quic_write_handshake_message(NULL)); + + /* Writes handshake message */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + uint8_t message_data[] = "The client says hello"; + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->handshake.io, message_data, sizeof(message_data))); + + EXPECT_OK(s2n_quic_write_handshake_message(conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->out), sizeof(message_data)); + EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&conn->out, sizeof(message_data)), + message_data, sizeof(message_data)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test: s2n_quic_read_handshake_message */ + { + /* Safety checks */ + { + struct s2n_connection conn = { 0 }; + uint8_t message_type = 0; + + EXPECT_ERROR(s2n_quic_read_handshake_message(NULL, &message_type)); + EXPECT_ERROR(s2n_quic_read_handshake_message(&conn, NULL)); + }; + + /* Reads basic handshake message */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + uint8_t expected_message_type = 7; + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, expected_message_type)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE)); + + uint8_t actual_message_type = 0; + EXPECT_OK(s2n_quic_read_handshake_message(conn, &actual_message_type)); + + EXPECT_EQUAL(actual_message_type, expected_message_type); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->handshake.io), TLS_HANDSHAKE_HEADER_LENGTH); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->in), TEST_DATA_SIZE); + EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&conn->in, TEST_DATA_SIZE), + TEST_DATA, sizeof(TEST_DATA)); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Blocks on insufficient data for handshake message header */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); + + uint8_t actual_message_type = 0; + EXPECT_ERROR_WITH_ERRNO(s2n_quic_read_handshake_message(conn, &actual_message_type), + S2N_ERR_IO_BLOCKED); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Blocks on insufficient data for handshake message data */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE - 1)); + + uint8_t actual_message_type = 0; + EXPECT_ERROR_WITH_ERRNO(s2n_quic_read_handshake_message(conn, &actual_message_type), + S2N_ERR_IO_BLOCKED); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Fails for an impossibly large handshake message */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, 7)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, S2N_MAXIMUM_HANDSHAKE_MESSAGE_LENGTH + 1)); + + uint8_t actual_message_type = 0; + EXPECT_ERROR_WITH_ERRNO(s2n_quic_read_handshake_message(conn, &actual_message_type), + S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Functional Tests */ + { + s2n_blocked_status blocked_status; + + /* Use handler stubs to avoid executing complicated handler implementations */ + for (size_t i = 0; i < s2n_array_len(tls13_state_machine); i++) { + tls13_state_machine[i].handler[S2N_SERVER] = s2n_test_read_handler; + tls13_state_machine[i].handler[S2N_CLIENT] = s2n_test_write_handler; + } + + /* Write test message */ + DEFER_CLEANUP(struct s2n_blob server_hello, s2n_free); + EXPECT_OK(s2n_write_test_message(&server_hello, TLS_SERVER_HELLO)); + + /* Setup IO buffers */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input_stuffer, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output_stuffer, 0)); + + /* Setup config */ + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_enable_quic(config)); + + /* Functional: successfully reads full handshake message */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_setup_conn_for_server_hello(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_SUCCESS(s2n_stuffer_write(&input_stuffer, &server_hello)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); + + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), ENCRYPTED_EXTENSIONS); + EXPECT_EQUAL(s2n_stuffer_data_available(&input_stuffer), 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Functional: successfully reads fragmented handshake message */ + for (size_t i = 1; i < server_hello.size - 1; i++) { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_setup_conn_for_server_hello(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Write initial fragment */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&input_stuffer, server_hello.data, i)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); + + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_HELLO); + EXPECT_EQUAL(s2n_stuffer_data_available(&input_stuffer), 0); + + /* Write rest of message */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&input_stuffer, + server_hello.data + i, server_hello.size - i)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); + + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), ENCRYPTED_EXTENSIONS); + EXPECT_EQUAL(s2n_stuffer_data_available(&input_stuffer), 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Functional: successfully reads multiple handshake messages */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_setup_conn_for_server_hello(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + DEFER_CLEANUP(struct s2n_blob encrypted_extensions, s2n_free); + EXPECT_OK(s2n_write_test_message(&encrypted_extensions, TLS_ENCRYPTED_EXTENSIONS)); + + EXPECT_SUCCESS(s2n_stuffer_write(&input_stuffer, &server_hello)); + EXPECT_SUCCESS(s2n_stuffer_write(&input_stuffer, &encrypted_extensions)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); + + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_CERT); + EXPECT_EQUAL(s2n_stuffer_data_available(&input_stuffer), 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Function: fails to read record instead of handshake message */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_setup_conn_for_server_hello(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Write the record: record type, protocol version, + * handshake message size, handshake message */ + POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, TLS_HANDSHAKE)); + POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, 3)); + POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, 3)); + POSIX_GUARD(s2n_stuffer_write_uint16(&input_stuffer, server_hello.size)); + EXPECT_SUCCESS(s2n_stuffer_write(&input_stuffer, &server_hello)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Function: fails to read Change Cipher Spec record */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_setup_conn_for_server_hello(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Write the record: record type, protocol version, + * record data size, standard "0x01" record data */ + POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, TLS_CHANGE_CIPHER_SPEC)); + POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, 3)); + POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, 3)); + POSIX_GUARD(s2n_stuffer_write_uint16(&input_stuffer, 1)); + POSIX_GUARD(s2n_stuffer_write_uint8(&input_stuffer, 1)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + uint32_t client_hello_length = 0; + + /* Functional: successfully writes full handshake message */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_setup_conn_for_client_hello(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); + client_hello_length = s2n_stuffer_data_available(&output_stuffer); + + uint8_t actual_message_type; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&output_stuffer, &actual_message_type)); + EXPECT_EQUAL(actual_message_type, TLS_CLIENT_HELLO); + + uint32_t actual_message_size; + EXPECT_SUCCESS(s2n_stuffer_read_uint24(&output_stuffer, &actual_message_size)); + EXPECT_EQUAL(actual_message_size, TEST_DATA_SIZE); + + EXPECT_EQUAL(s2n_stuffer_data_available(&output_stuffer), TEST_DATA_SIZE); + EXPECT_BYTEARRAY_EQUAL(s2n_stuffer_raw_read(&output_stuffer, TEST_DATA_SIZE), + TEST_DATA, TEST_DATA_SIZE); + + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_HELLO); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Functional: successfully retries after blocked write */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_OK(s2n_setup_conn_for_client_hello(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Sabotage the output stuffer to block writing */ + struct s2n_stuffer bad_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input_stuffer, &bad_stuffer, conn)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), CLIENT_HELLO); + EXPECT_EQUAL(s2n_stuffer_data_available(&output_stuffer), 0); + + /* Fix the output stuffer */ + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input_stuffer, &output_stuffer, conn)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked_status), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_HELLO); + EXPECT_EQUAL(s2n_stuffer_data_available(&output_stuffer), client_hello_length); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + EXPECT_SUCCESS(s2n_stuffer_free(&input_stuffer)); + EXPECT_SUCCESS(s2n_stuffer_free(&output_stuffer)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test: s2n_recv_quic_post_handshake_message */ + { + /* Safety checks */ + s2n_blocked_status blocked = 0; + EXPECT_FAILURE(s2n_recv_quic_post_handshake_message(NULL, &blocked)); + + /* Parsable session ticket message */ + uint8_t ticket_message[] = { + TLS_SERVER_NEW_SESSION_TICKET, + 0x00, 0x00, 0x12, /* message size */ + TEST_LIFETIME, /* ticket lifetime */ + TEST_TICKET_AGE_ADD, /* ticket age add */ + 0x02, /* nonce len */ + 0x00, 0x00, /* nonce */ + 0x00, 0x03, /* ticket len */ + TEST_TICKET, /* ticket */ + 0x00, 0x00, /* extensions len */ + }; + + /* Test: fails to read post-handshake message that is not a ST */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + /* Create a post-handshake message that isn't supported by quic */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, TLS_KEY_UPDATE)); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, TEST_DATA_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, TEST_DATA, TEST_DATA_SIZE)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_recv_quic_post_handshake_message(conn, &blocked), S2N_ERR_UNSUPPORTED_WITH_QUIC); + }; + + /* Test: successfully reads and processes post-handshake message */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + uint8_t session_ticket_cb_count = 0; + EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_cb, &session_ticket_cb_count)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + /* Construct ST handshake message */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, ticket_message, sizeof(ticket_message))); + + EXPECT_SUCCESS(s2n_recv_quic_post_handshake_message(conn, &blocked)); + + /* Callback was triggered */ + EXPECT_EQUAL(session_ticket_cb_count, 1); + }; + + /* Test: successfully reads and processes fragmented post-handshake message */ + for (size_t i = 1; i < sizeof(ticket_message); i++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + uint8_t session_ticket_cb_count = 0; + EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_cb, &session_ticket_cb_count)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + /* Mock receiving a fragmented handshake message */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, ticket_message, i)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_recv_quic_post_handshake_message(conn, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + /* Callback was not triggered */ + EXPECT_EQUAL(session_ticket_cb_count, 0); + + /* "Write" the rest of the message */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, ticket_message + i, sizeof(ticket_message) - i)); + + EXPECT_SUCCESS(s2n_recv_quic_post_handshake_message(conn, &blocked)); + + /* Callback was triggered */ + EXPECT_EQUAL(session_ticket_cb_count, 1); + }; + } + + END_TEST(); +} diff --git a/tests/unit/s2n_quic_support_test.c b/tests/unit/s2n_quic_support_test.c new file mode 100644 index 00000000000..976d929cd2b --- /dev/null +++ b/tests/unit/s2n_quic_support_test.c @@ -0,0 +1,304 @@ +/* + * 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 "tls/s2n_quic_support.h" + +#include "s2n_test.h" +#include "tls/s2n_connection.h" + +static const uint8_t TEST_DATA[] = "test"; + +static int s2n_test_noop_secret_handler(void *context, struct s2n_connection *conn, + s2n_secret_type_t secret_type, uint8_t *secret, uint8_t secret_size) +{ + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Test QUIC is not allowed if TLS1.3 not fully supported. */ + if (!s2n_is_tls13_fully_supported()) { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_enable_quic(config)); + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_enable_quic(conn), S2N_ERR_RSA_PSS_NOT_SUPPORTED); + EXPECT_FALSE(conn->quic_enabled); + + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_set_config(conn, config), S2N_ERR_RSA_PSS_NOT_SUPPORTED); + EXPECT_NOT_EQUAL(config, conn->config); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + END_TEST(); + } + + /* Test s2n_config_enable_quic */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_FALSE(config->quic_enabled); + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_FALSE(s2n_connection_is_quic_enabled(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_FALSE(s2n_connection_is_quic_enabled(conn)); + + /* Check error handling */ + { + EXPECT_FAILURE_WITH_ERRNO(s2n_config_enable_quic(NULL), S2N_ERR_NULL); + EXPECT_FALSE(config->quic_enabled); + EXPECT_FALSE(s2n_connection_is_quic_enabled(conn)); + }; + + /* Check success */ + { + EXPECT_SUCCESS(s2n_config_enable_quic(config)); + EXPECT_TRUE(config->quic_enabled); + EXPECT_TRUE(s2n_connection_is_quic_enabled(conn)); + + /* Enabling QUIC again still succeeds */ + EXPECT_SUCCESS(s2n_config_enable_quic(config)); + EXPECT_TRUE(config->quic_enabled); + EXPECT_TRUE(s2n_connection_is_quic_enabled(conn)); + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test s2n_connection_enable_quic */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_FALSE(s2n_connection_is_quic_enabled(conn)); + + /* Check error handling */ + { + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_enable_quic(NULL), S2N_ERR_NULL); + EXPECT_FALSE(s2n_connection_is_quic_enabled(conn)); + }; + + /* Check success */ + { + EXPECT_SUCCESS(s2n_connection_enable_quic(conn)); + EXPECT_TRUE(s2n_connection_is_quic_enabled(conn)); + + /* Enabling QUIC again still succeeds */ + EXPECT_SUCCESS(s2n_connection_enable_quic(conn)); + EXPECT_TRUE(s2n_connection_is_quic_enabled(conn)); + }; + + /* Check with config enabled too */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_FALSE(config->quic_enabled); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_SUCCESS(s2n_config_enable_quic(config)); + EXPECT_TRUE(s2n_connection_is_quic_enabled(conn)); + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test that if a connection enables quic via the config, + * quic stays enabled for the connection even if the config changes. + */ + { + struct s2n_config *non_quic_config = s2n_config_new(); + EXPECT_NOT_NULL(non_quic_config); + + struct s2n_config *quic_config = s2n_config_new(); + EXPECT_NOT_NULL(quic_config); + EXPECT_SUCCESS(s2n_config_enable_quic(quic_config)); + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + EXPECT_FALSE(s2n_connection_is_quic_enabled(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, non_quic_config)); + EXPECT_FALSE(s2n_connection_is_quic_enabled(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, quic_config)); + EXPECT_TRUE(s2n_connection_is_quic_enabled(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, non_quic_config)); + EXPECT_TRUE(s2n_connection_is_quic_enabled(conn)); + + EXPECT_SUCCESS(s2n_config_free(non_quic_config)); + EXPECT_SUCCESS(s2n_config_free(quic_config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_connection_set_quic_transport_parameters */ + { + /* Safety checks */ + { + struct s2n_connection conn = { 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_set_quic_transport_parameters(NULL, TEST_DATA, sizeof(TEST_DATA)), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_set_quic_transport_parameters(&conn, NULL, sizeof(TEST_DATA)), + S2N_ERR_NULL); + EXPECT_SUCCESS(s2n_connection_set_quic_transport_parameters(&conn, TEST_DATA, 0)); + EXPECT_EQUAL(conn.our_quic_transport_parameters.size, 0); + }; + + /* Set transport data */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + s2n_connection_set_quic_transport_parameters(conn, TEST_DATA, sizeof(TEST_DATA)); + EXPECT_BYTEARRAY_EQUAL(conn->our_quic_transport_parameters.data, TEST_DATA, sizeof(TEST_DATA)); + + /* Set again */ + const uint8_t other_data[] = "other parameters"; + s2n_connection_set_quic_transport_parameters(conn, other_data, sizeof(other_data)); + EXPECT_BYTEARRAY_EQUAL(conn->our_quic_transport_parameters.data, other_data, sizeof(other_data)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test s2n_connection_get_quic_transport_parameters */ + { + /* Safety checks */ + { + struct s2n_connection conn = { 0 }; + const uint8_t *data_buffer = NULL; + uint16_t data_buffer_len = 0; + + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_quic_transport_parameters(NULL, &data_buffer, &data_buffer_len), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_quic_transport_parameters(&conn, NULL, &data_buffer_len), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_quic_transport_parameters(&conn, &data_buffer, NULL), + S2N_ERR_NULL); + }; + + /* Get empty transport parameters */ + { + const uint8_t *data_buffer = TEST_DATA; + uint16_t data_buffer_len = sizeof(TEST_DATA); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_get_quic_transport_parameters(conn, &data_buffer, &data_buffer_len)); + EXPECT_EQUAL(data_buffer, NULL); + EXPECT_EQUAL(data_buffer_len, 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Get transport parameters */ + { + const uint8_t *data_buffer = NULL; + uint16_t data_buffer_len = 0; + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_alloc(&conn->peer_quic_transport_parameters, sizeof(TEST_DATA))); + EXPECT_MEMCPY_SUCCESS(conn->peer_quic_transport_parameters.data, TEST_DATA, sizeof(TEST_DATA)); + + EXPECT_SUCCESS(s2n_connection_get_quic_transport_parameters(conn, &data_buffer, &data_buffer_len)); + EXPECT_EQUAL(data_buffer, conn->peer_quic_transport_parameters.data); + EXPECT_EQUAL(data_buffer_len, conn->peer_quic_transport_parameters.size); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test s2n_connection_set_secret_callback */ + { + uint8_t test_context; + + /* Safety checks */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_set_secret_callback(NULL, s2n_test_noop_secret_handler, &test_context), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_set_secret_callback(conn, NULL, &test_context), S2N_ERR_NULL); + + EXPECT_EQUAL(conn->secret_cb, NULL); + EXPECT_EQUAL(conn->secret_cb_context, NULL); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Succeeds with NULL context */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_EQUAL(conn->secret_cb, NULL); + EXPECT_EQUAL(conn->secret_cb_context, NULL); + + EXPECT_SUCCESS(s2n_connection_set_secret_callback(conn, s2n_test_noop_secret_handler, NULL)); + + EXPECT_EQUAL(conn->secret_cb, s2n_test_noop_secret_handler); + EXPECT_NULL(conn->secret_cb_context); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Succeeds with context */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_EQUAL(conn->secret_cb, NULL); + EXPECT_EQUAL(conn->secret_cb_context, NULL); + + EXPECT_SUCCESS(s2n_connection_set_secret_callback(conn, s2n_test_noop_secret_handler, &test_context)); + + EXPECT_EQUAL(conn->secret_cb, s2n_test_noop_secret_handler); + EXPECT_EQUAL(conn->secret_cb_context, &test_context); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test: no API that sends/receives application data is allowed when QUIC is enabled */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_enable_quic(config)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + uint8_t buffer[10] = { 0 }; + struct iovec iovec_buffer = { .iov_base = buffer, .iov_len = sizeof(buffer) }; + s2n_blocked_status blocked_status; + + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(conn, buffer, sizeof(buffer), &blocked_status), S2N_ERR_UNSUPPORTED_WITH_QUIC); + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, buffer, sizeof(buffer), &blocked_status), S2N_ERR_UNSUPPORTED_WITH_QUIC); + EXPECT_FAILURE_WITH_ERRNO(s2n_sendv(conn, &iovec_buffer, 1, &blocked_status), S2N_ERR_UNSUPPORTED_WITH_QUIC); + EXPECT_FAILURE_WITH_ERRNO(s2n_sendv_with_offset(conn, &iovec_buffer, 1, 0, &blocked_status), S2N_ERR_UNSUPPORTED_WITH_QUIC); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_quic_transport_params_extension_test.c b/tests/unit/s2n_quic_transport_params_extension_test.c new file mode 100644 index 00000000000..60b4eb1d534 --- /dev/null +++ b/tests/unit/s2n_quic_transport_params_extension_test.c @@ -0,0 +1,236 @@ +/* + * 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 "s2n_test.h" +#include "tests/testlib/s2n_testlib.h" +#include "tls/extensions/s2n_quic_transport_params.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_quic_support.h" +#include "tls/s2n_tls13.h" + +static const uint8_t TEST_DATA[] = "These are transport parameters"; + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + /* Test should_send */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Safety check */ + EXPECT_FALSE(s2n_quic_transport_parameters_extension.should_send(NULL)); + + /* Don't send if quic not enabled (default) */ + EXPECT_FALSE(s2n_quic_transport_parameters_extension.should_send(conn)); + + /* Send if quic enabled */ + EXPECT_SUCCESS(s2n_config_enable_quic(config)); + EXPECT_TRUE(s2n_quic_transport_parameters_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test if_missing */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Safety check */ + EXPECT_FAILURE_WITH_ERRNO(s2n_quic_transport_parameters_extension.if_missing(NULL), S2N_ERR_NULL); + + /* Succeeds if quic not enabled (default) */ + EXPECT_SUCCESS(s2n_quic_transport_parameters_extension.if_missing(conn)); + + /* Fails if quic enabled + * + *= https://tools.ietf.org/rfc/rfc9001.txt#8.2 + *= type=test + *# Endpoints MUST send the quic_transport_parameters extension; endpoints that receive + *# ClientHello or EncryptedExtensions messages without the quic_transport_parameters + *# extension MUST close the connection with an error of type 0x016d (equivalent to a fatal + *# TLS missing_extension alert + **/ + EXPECT_SUCCESS(s2n_config_enable_quic(config)); + EXPECT_FAILURE_WITH_ALERT(s2n_quic_transport_parameters_extension.if_missing(conn), + S2N_ERR_MISSING_EXTENSION, S2N_TLS_ALERT_MISSING_EXTENSION); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test send */ + { + /* Safety checks */ + { + struct s2n_stuffer out = { 0 }; + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_quic_transport_parameters_extension.send(NULL, &out), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_quic_transport_parameters_extension.send(conn, NULL), S2N_ERR_NULL); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Writes transport parameters */ + { + struct s2n_stuffer out = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_enable_quic(config)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_connection_set_quic_transport_parameters(conn, TEST_DATA, sizeof(TEST_DATA))); + + EXPECT_SUCCESS(s2n_quic_transport_parameters_extension.send(conn, &out)); + EXPECT_BYTEARRAY_EQUAL(out.blob.data, TEST_DATA, sizeof(TEST_DATA)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + }; + + /* Writes empty transport parameters */ + { + struct s2n_stuffer out = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_enable_quic(config)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_SUCCESS(s2n_quic_transport_parameters_extension.send(conn, &out)); + EXPECT_EQUAL(s2n_stuffer_data_available(&out), 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + }; + }; + + /* Test recv */ + { + /* Safety checks */ + { + struct s2n_stuffer extension = { 0 }; + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_quic_transport_parameters_extension.recv(NULL, &extension), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_quic_transport_parameters_extension.recv(conn, NULL), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_quic_transport_parameters_extension.recv(conn, &extension), + S2N_ERR_UNSUPPORTED_EXTENSION); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Save transport parameters */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_enable_quic(config)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_stuffer extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&extension, TEST_DATA, sizeof(TEST_DATA))); + + EXPECT_SUCCESS(s2n_quic_transport_parameters_extension.recv(conn, &extension)); + EXPECT_BYTEARRAY_EQUAL(conn->peer_quic_transport_parameters.data, TEST_DATA, sizeof(TEST_DATA)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + }; + + /* Save empty transport parameters */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_enable_quic(config)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_stuffer extension = { 0 }; + + EXPECT_SUCCESS(s2n_quic_transport_parameters_extension.recv(conn, &extension)); + EXPECT_EQUAL(conn->peer_quic_transport_parameters.size, 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* recv processes the output of send */ + { + struct s2n_stuffer out = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_enable_quic(config)); + + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_quic_transport_parameters(client_conn, TEST_DATA, sizeof(TEST_DATA))); + + EXPECT_SUCCESS(s2n_quic_transport_parameters_extension.send(client_conn, &out)); + EXPECT_EQUAL(server_conn->peer_quic_transport_parameters.size, 0); + EXPECT_SUCCESS(s2n_quic_transport_parameters_extension.recv(server_conn, &out)); + EXPECT_BYTEARRAY_EQUAL(server_conn->peer_quic_transport_parameters.data, TEST_DATA, sizeof(TEST_DATA)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_random_test.c b/tests/unit/s2n_random_test.c new file mode 100644 index 00000000000..658de06a5cb --- /dev/null +++ b/tests/unit/s2n_random_test.c @@ -0,0 +1,842 @@ +/* + * 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. + */ + +#ifdef __FreeBSD__ + /* FreeBSD requires POSIX compatibility off for its syscalls (enables __BSD_VISIBLE) + * Without the below line, cannot be imported (it requires __BSD_VISIBLE) */ + #undef _POSIX_C_SOURCE +#else + /* For clone() */ + #define _GNU_SOURCE +#endif + +#include "utils/s2n_random.h" + +#include +#include +#include +#include +#include + +#include "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "utils/s2n_fork_detection.h" + +#define MAX_NUMBER_OF_TEST_THREADS 2 + +#define CLONE_TEST_NO 0 +#define CLONE_TEST_YES 1 +#define CLONE_TEST_DETERMINE_AT_RUNTIME 2 + +#define RANDOM_GENERATE_DATA_SIZE 100 +#define MAX_RANDOM_GENERATE_DATA_SIZE 5120 + +#define NUMBER_OF_BOUNDS 10 +#define NUMBER_OF_RANGE_FUNCTION_CALLS 200 +#define MAX_REPEATED_OUTPUT 4 + +struct random_test_case { + const char *test_case_label; + int (*test_case_cb)(struct random_test_case *test_case); + int test_case_must_pass_clone_test; + int expected_return_status; +}; + +struct random_communication { + S2N_RESULT (*s2n_get_random_data_cb_1)(struct s2n_blob *blob); + S2N_RESULT (*s2n_get_random_data_cb_2)(struct s2n_blob *blob); + uint8_t thread_data[RANDOM_GENERATE_DATA_SIZE]; + int *pipes; +}; + +static void s2n_verify_child_exit_status(pid_t proc_pid, int expected_status) +{ + int status = 0; +#if defined(S2N_CLONE_SUPPORTED) + EXPECT_EQUAL(waitpid(proc_pid, &status, __WALL), proc_pid); +#else + /* __WALL is not relevant when clone() is not supported + * https://man7.org/linux/man-pages/man2/wait.2.html#NOTES + */ + EXPECT_EQUAL(waitpid(proc_pid, &status, 0), proc_pid); +#endif + /* Check that child exited with status = expected_status. If not, this + * indicates that an error was encountered in the unit tests executed in + * that child process. + */ + EXPECT_NOT_EQUAL(WIFEXITED(status), 0); + EXPECT_EQUAL(WEXITSTATUS(status), expected_status); +} + +static int s2n_init_cb(void) +{ + return S2N_SUCCESS; +} + +static int s2n_cleanup_cb(void) +{ + return S2N_SUCCESS; +} + +static int s2n_entropy_cb(void *ptr, uint32_t size) +{ + return S2N_SUCCESS; +} + +/* Generates random data (every size between 1 and 5120 bytes) and performs + * basic pattern tests on the resulting output + */ +static S2N_RESULT s2n_basic_pattern_tests(S2N_RESULT (*s2n_get_random_data_cb)(struct s2n_blob *blob)) +{ + uint8_t bits[8] = { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 }; + uint8_t bit_set_run[8]; + uint8_t data[MAX_RANDOM_GENERATE_DATA_SIZE]; + struct s2n_blob blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&blob, data, 0)); + int trailing_zeros[8] = { 0 }; + + for (int size = 0; size < MAX_RANDOM_GENERATE_DATA_SIZE; size++) { + blob.size = size; + EXPECT_OK(s2n_get_random_data_cb(&blob)); + + if (size >= 64) { + /* Set the run counts to 0 */ + memset(bit_set_run, 0, 8); + + /* Apply 8 monobit tests to the data. Basically, we're + * looking for successive runs where a given bit is set. + * If a run exists with any particular bit 64 times in + * a row, then the data doesn't look randomly generated. + */ + for (int j = 0; j < size; j++) { + for (int k = 0; k < 8; k++) { + if (data[j] & bits[k]) { + bit_set_run[k]++; + + if (j >= 64) { + RESULT_ENSURE_LT(bit_set_run[k], 64); + } + } else { + bit_set_run[k] = 0; + } + } + } + } + + /* A common mistake in array filling leaves the last bytes zero + * depending on the length + */ + int remainder = size % 8; + int non_zero_found = 0; + for (int t = size - remainder; t < size; t++) { + non_zero_found |= data[t]; + } + if (!non_zero_found) { + trailing_zeros[remainder]++; + } + } + for (int t = 1; t < 8; t++) { + RESULT_ENSURE_LT(trailing_zeros[t], 5120 / 16); + } + + return S2N_RESULT_OK; +} + +int qsort_comparator(const void *pval1, const void *pval2) +{ + const uint64_t val1 = *(const uint64_t *) pval1; + const uint64_t val2 = *(const uint64_t *) pval2; + + if (val1 < val2) { + return -1; + } else if (val1 > val2) { + return 1; + } else { + return 0; + } +} + +static S2N_RESULT s2n_tests_get_range(void) +{ + uint64_t range_results[NUMBER_OF_RANGE_FUNCTION_CALLS] = { 0 }; + uint64_t current_output = 0; + /* The type of the `bound` parameter in s2n_public_random() is signed */ + int64_t chosen_upper_bound = 0; + struct s2n_blob upper_bound_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&upper_bound_blob, (void *) &chosen_upper_bound, sizeof(chosen_upper_bound))); + + /* 0 is not a legal upper bound */ + chosen_upper_bound = 0; + EXPECT_ERROR_WITH_ERRNO(s2n_public_random(chosen_upper_bound, ¤t_output), S2N_ERR_SAFETY); + + /* For an upper bound of 1, 0 should be the only possible output */ + chosen_upper_bound = 1; + EXPECT_OK(s2n_public_random(chosen_upper_bound, ¤t_output)); + EXPECT_EQUAL(current_output, 0); + + /* For a upper bound of 2, 0 and 1 should be the only possible outputs */ + chosen_upper_bound = 1; + EXPECT_OK(s2n_public_random(chosen_upper_bound, ¤t_output)); + EXPECT_TRUE((current_output == 0) || (current_output == 1)); + + /* Test NUMBER_OF_BOUNDS upper bounds. For each resulting range, draw + * NUMBER_OF_RANGE_FUNCTION_CALLS numbers from s2n_public_random() and + * verify the output. Set 2^30 * NUMBER_OF_RANGE_FUNCTION_CALLS as the + * minimal value for the upper bound. The minimal upper bound value is + * chosen to make the likelihood of a false positive small - see below for + * probability calculations. + */ + int64_t minimal_upper_bound = (int64_t) 0x40000000 * (int64_t) NUMBER_OF_RANGE_FUNCTION_CALLS; + for (size_t bound_ctr = 0; bound_ctr < NUMBER_OF_BOUNDS; bound_ctr++) { + /* chosen_upper_bound is supposedly chosen uniformly at random and + * minimal_upper_bound is only 2^30 * NUMBER_OF_RANGE_FUNCTION_CALLS, so + * this should not iterate for too long + */ + do { + EXPECT_OK(s2n_get_private_random_data(&upper_bound_blob)); + } while (chosen_upper_bound < minimal_upper_bound); + + /* Pick NUMBER_OF_RANGE_FUNCTION_CALLS numbers in the given interval. + * While doing that, also verify that the upper bound is respected. + */ + for (size_t func_call_ctr = 0; func_call_ctr < NUMBER_OF_RANGE_FUNCTION_CALLS; func_call_ctr++) { + EXPECT_OK(s2n_public_random(chosen_upper_bound, &range_results[func_call_ctr])); + EXPECT_TRUE(range_results[func_call_ctr] < chosen_upper_bound); + } + + /* The probability of "at least MAX_REPEATED_OUTPUT repeated values" + * follows a binomial distribution. Hence, we can get an upper bound via + * Markov's inequality: + * P("at least MAX_REPEATED_OUTPUT repeated values") + * <= E("at least MAX_REPEATED_OUTPUT repeated values") / MAX_REPEATED_OUTPUT. + * = (NUMBER_OF_RANGE_FUNCTION_CALLS * 1/(2^30 * NUMBER_OF_RANGE_FUNCTION_CALLS)) / MAX_REPEATED_OUTPUT + * = 1/(2^30 * MAX_REPEATED_OUTPUT) + * + * With current parameters + * NUMBER_OF_BOUNDS = 10 + * MAX_REPEATED_OUTPUT = 4 + * this ends up with about a ~1/2^30 probability of failing this test + * with a false positive. + * + * qsort() complexity is not guaranteed, but + * NUMBER_OF_RANGE_FUNCTION_CALLS is very small, so no biggie. + * Sorting the array means that we can check for repeated numbers by + * just counting from left to right resetting the count when meeting a + * different value. + */ + qsort(range_results, NUMBER_OF_RANGE_FUNCTION_CALLS, sizeof(uint64_t), + qsort_comparator); + uint64_t current_value = range_results[0]; + uint64_t next_value = 0; + size_t repeat_count = 1; + for (size_t ctr = 1; ctr < NUMBER_OF_RANGE_FUNCTION_CALLS - 1; ctr++) { + next_value = range_results[ctr]; + + if (current_value == next_value) { + repeat_count = repeat_count + 1; + } else { + RESULT_ENSURE_LT(current_value, next_value); + current_value = next_value; + repeat_count = 1; + } + + EXPECT_TRUE(repeat_count < MAX_REPEATED_OUTPUT); + } + + /* Reset for next iteration */ + RESULT_CHECKED_MEMSET(&range_results[0], 0, sizeof(range_results)); + } + + return S2N_RESULT_OK; +} + +void *s2n_thread_test_cb(void *thread_comms) +{ + struct random_communication *thread_comms_ptr = (struct random_communication *) thread_comms; + + struct s2n_blob thread_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&thread_blob, thread_comms_ptr->thread_data, RANDOM_GENERATE_DATA_SIZE)); + + EXPECT_NOT_NULL(thread_comms_ptr->s2n_get_random_data_cb_1); + EXPECT_OK(thread_comms_ptr->s2n_get_random_data_cb_1(&thread_blob)); + + EXPECT_OK(s2n_rand_cleanup_thread()); + + return NULL; +} + +/* Creates two threads and generates random data in those two threads as well + * as the parent thread. Verifies that all three resulting data blobs are + * different. + */ +static S2N_RESULT s2n_thread_test( + S2N_RESULT (*s2n_get_random_data_cb)(struct s2n_blob *blob), + S2N_RESULT (*s2n_get_random_data_cb_thread)(struct s2n_blob *blob)) +{ + uint8_t data[RANDOM_GENERATE_DATA_SIZE]; + struct s2n_blob blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&blob, data, 0)); + pthread_t threads[MAX_NUMBER_OF_TEST_THREADS]; + + struct random_communication thread_communication_0 = { .s2n_get_random_data_cb_1 = s2n_get_random_data_cb_thread }; + struct random_communication thread_communication_1 = { .s2n_get_random_data_cb_1 = s2n_get_random_data_cb_thread }; + + /* Create two threads and have them each grab RANDOM_GENERATE_DATA_SIZE + * bytes. + */ + EXPECT_EQUAL(pthread_create(&threads[0], NULL, s2n_thread_test_cb, &thread_communication_0), 0); + EXPECT_EQUAL(pthread_create(&threads[1], NULL, s2n_thread_test_cb, &thread_communication_1), 0); + + /* Wait for those threads to finish */ + EXPECT_EQUAL(pthread_join(threads[0], NULL), 0); + EXPECT_EQUAL(pthread_join(threads[1], NULL), 0); + + /* Confirm that their random data differs from each other */ + EXPECT_BYTEARRAY_NOT_EQUAL(thread_communication_0.thread_data, thread_communication_1.thread_data, RANDOM_GENERATE_DATA_SIZE); + + /* Confirm that their random data differs from the parent thread */ + blob.size = RANDOM_GENERATE_DATA_SIZE; + EXPECT_OK(s2n_get_random_data_cb(&blob)); + EXPECT_BYTEARRAY_NOT_EQUAL(thread_communication_0.thread_data, data, RANDOM_GENERATE_DATA_SIZE); + EXPECT_BYTEARRAY_NOT_EQUAL(thread_communication_1.thread_data, data, RANDOM_GENERATE_DATA_SIZE); + + return S2N_RESULT_OK; +} + +static void s2n_fork_test_generate_randomness(int write_fd, S2N_RESULT (*s2n_get_random_data_cb)(struct s2n_blob *blob)) +{ + uint8_t data[RANDOM_GENERATE_DATA_SIZE]; + + struct s2n_blob blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&blob, data, RANDOM_GENERATE_DATA_SIZE)); + EXPECT_OK(s2n_get_random_data_cb(&blob)); + + /* Write the data we got to our pipe */ + if (write(write_fd, data, RANDOM_GENERATE_DATA_SIZE) != RANDOM_GENERATE_DATA_SIZE) { + _exit(EXIT_FAILURE); + } + + /* Close the pipe and exit */ + close(write_fd); + exit(EXIT_SUCCESS); +} + +static S2N_RESULT s2n_fork_test_verify_result(int *pipes, int proc_id, S2N_RESULT (*s2n_get_random_data_cb)(struct s2n_blob *blob)) +{ + uint8_t child_data[RANDOM_GENERATE_DATA_SIZE]; + uint8_t parent_data[RANDOM_GENERATE_DATA_SIZE]; + struct s2n_blob parent_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&parent_blob, parent_data, RANDOM_GENERATE_DATA_SIZE)); + + /* Quickly verify we are in the parent process and not the child */ + EXPECT_NOT_EQUAL(proc_id, 0); + + /* This is the parent process, close the write end of the pipe */ + EXPECT_SUCCESS(close(pipes[1])); + + /* Read the child's data from the pipe */ + EXPECT_EQUAL(read(pipes[0], child_data, RANDOM_GENERATE_DATA_SIZE), RANDOM_GENERATE_DATA_SIZE); + + /* Get RANDOM_GENERATE_DATA_SIZE bytes in this parent process */ + EXPECT_OK(s2n_get_random_data_cb(&parent_blob)); + + /* Confirm that their data differs from each other */ + EXPECT_BYTEARRAY_NOT_EQUAL(child_data, parent_data, RANDOM_GENERATE_DATA_SIZE); + + EXPECT_SUCCESS(close(pipes[0])); + + /* Also remember to verify that the child exited okay */ + s2n_verify_child_exit_status(proc_id, S2N_SUCCESS); + + return S2N_RESULT_OK; +} + +/* This function lists a number of stanzas performing various random data + * generation tests. Each stanza goes through a different combination of forking + * a process and threading. Each stanza must end with + * s2n_fork_test_verify_result() to verify the result and the exit code of the + * child process. + */ +static S2N_RESULT s2n_fork_test( + S2N_RESULT (*s2n_get_random_data_cb)(struct s2n_blob *blob), + S2N_RESULT (*s2n_get_random_data_cb_thread)(struct s2n_blob *blob)) +{ + pid_t proc_id; + int pipes[2]; + + /* A simple fork test. Generates random data in the parent and child, and + * verifies that the two resulting data blobs are different. + */ + EXPECT_SUCCESS(pipe(pipes)); + proc_id = fork(); + if (proc_id == 0) { + /* This is the child process, close the read end of the pipe */ + EXPECT_SUCCESS(close(pipes[0])); + s2n_fork_test_generate_randomness(pipes[1], s2n_get_random_data_cb); + } + EXPECT_OK(s2n_fork_test_verify_result(pipes, proc_id, s2n_get_random_data_cb)); + + /* Creates a fork, but immediately creates threads in the child process. See + * https://github.com/aws/s2n-tls/issues/3107 why this might be an issue. + */ + EXPECT_SUCCESS(pipe(pipes)); + proc_id = fork(); + if (proc_id == 0) { + /* This is the child process, close the read end of the pipe */ + EXPECT_SUCCESS(close(pipes[0])); + EXPECT_OK(s2n_thread_test(s2n_get_random_data_cb, s2n_get_random_data_cb_thread)); + s2n_fork_test_generate_randomness(pipes[1], s2n_get_random_data_cb); + } + EXPECT_OK(s2n_fork_test_verify_result(pipes, proc_id, s2n_get_random_data_cb)); + + /* Creates threads and generates random data but only after generating + * random data in the child process */ + EXPECT_SUCCESS(pipe(pipes)); + proc_id = fork(); + if (proc_id == 0) { + /* This is the child process, close the read end of the pipe */ + EXPECT_SUCCESS(close(pipes[0])); + s2n_fork_test_generate_randomness(pipes[1], s2n_get_random_data_cb); + EXPECT_OK(s2n_thread_test(s2n_get_random_data_cb, s2n_get_random_data_cb_thread)); + } + EXPECT_OK(s2n_fork_test_verify_result(pipes, proc_id, s2n_get_random_data_cb)); + + /* Creates threads in the parent process before generating random data */ + EXPECT_SUCCESS(pipe(pipes)); + proc_id = fork(); + if (proc_id == 0) { + /* This is the child process, close the read end of the pipe */ + EXPECT_SUCCESS(close(pipes[0])); + s2n_fork_test_generate_randomness(pipes[1], s2n_get_random_data_cb); + } + EXPECT_OK(s2n_thread_test(s2n_get_random_data_cb, s2n_get_random_data_cb_thread)); + EXPECT_OK(s2n_fork_test_verify_result(pipes, proc_id, s2n_get_random_data_cb)); + + /* Basic tests in the child process */ + EXPECT_SUCCESS(pipe(pipes)); + proc_id = fork(); + if (proc_id == 0) { + /* This is the child process, close the read end of the pipe */ + EXPECT_SUCCESS(close(pipes[0])); + EXPECT_OK(s2n_basic_pattern_tests(s2n_get_random_data_cb)); + s2n_fork_test_generate_randomness(pipes[1], s2n_get_random_data_cb); + } + EXPECT_OK(s2n_fork_test_verify_result(pipes, proc_id, s2n_get_random_data_cb)); + + return S2N_RESULT_OK; +} + +static int s2n_clone_tests_child_process(void *ipc) +{ + struct random_communication *ipc_ptr = (struct random_communication *) ipc; + + /* This is the child process, close the read end of the pipe */ + EXPECT_SUCCESS(close((int) ipc_ptr->pipes[0])); + EXPECT_NOT_NULL(ipc_ptr->s2n_get_random_data_cb_2); + s2n_fork_test_generate_randomness((int) ipc_ptr->pipes[1], ipc_ptr->s2n_get_random_data_cb_2); + + /* s2n_fork_test_generate_randomness() will exit. But we need a return + * statement because we are in a non-void return type function. */ + return EXIT_SUCCESS; +} + +#define PROCESS_CHILD_STACK_SIZE (1024 * 1024) /* Suggested by clone() man page... */ +static S2N_RESULT s2n_clone_tests( + S2N_RESULT (*s2n_get_random_data_cb)(struct s2n_blob *blob), + S2N_RESULT (*s2n_get_random_data_cb_clone)(struct s2n_blob *blob)) +{ +#if defined(S2N_CLONE_SUPPORTED) + + int proc_id; + int pipes[2]; + + EXPECT_SUCCESS(pipe(pipes)); + + /* Use stack memory for this... We don't exit unit_test_clone() before this + * memory has served its purpose. + * Why? Using dynamically allocated memory causes Valgrind to squat on the + * allocated memory when the child process exists. + */ + char process_child_stack[PROCESS_CHILD_STACK_SIZE]; + EXPECT_NOT_NULL(process_child_stack); + + struct random_communication ipc = { + .s2n_get_random_data_cb_1 = s2n_get_random_data_cb, + .s2n_get_random_data_cb_2 = s2n_get_random_data_cb_clone, + .pipes = (int *) pipes + }; + + proc_id = clone(s2n_clone_tests_child_process, (void *) (process_child_stack + PROCESS_CHILD_STACK_SIZE), 0, (void *) &ipc); + EXPECT_NOT_EQUAL(proc_id, -1); + EXPECT_OK(s2n_fork_test_verify_result(pipes, proc_id, ipc.s2n_get_random_data_cb_1)); +#endif + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_execute_clone_tests(void) +{ + EXPECT_OK(s2n_clone_tests(s2n_get_public_random_data, s2n_get_public_random_data)); + EXPECT_OK(s2n_clone_tests(s2n_get_private_random_data, s2n_get_private_random_data)); + EXPECT_OK(s2n_clone_tests(s2n_get_public_random_data, s2n_get_private_random_data)); + EXPECT_OK(s2n_clone_tests(s2n_get_private_random_data, s2n_get_public_random_data)); + + return S2N_RESULT_OK; +} + +/* Very basic test generating random data a few times and checking that the + * output is different + */ +static S2N_RESULT s2n_basic_generate_tests(void) +{ + uint8_t data1[RANDOM_GENERATE_DATA_SIZE]; + uint8_t data2[RANDOM_GENERATE_DATA_SIZE]; + struct s2n_blob blob1 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&blob1, data1, 0)); + struct s2n_blob blob2 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&blob2, data2, 0)); + + /* Generate two random data blobs and confirm that they are unique */ + blob1.size = RANDOM_GENERATE_DATA_SIZE; + blob2.size = RANDOM_GENERATE_DATA_SIZE; + EXPECT_OK(s2n_get_public_random_data(&blob1)); + EXPECT_OK(s2n_get_public_random_data(&blob2)); + EXPECT_BYTEARRAY_NOT_EQUAL(data1, data2, RANDOM_GENERATE_DATA_SIZE); + EXPECT_OK(s2n_get_private_random_data(&blob1)); + EXPECT_BYTEARRAY_NOT_EQUAL(data1, data2, RANDOM_GENERATE_DATA_SIZE); + EXPECT_OK(s2n_get_private_random_data(&blob2)); + EXPECT_BYTEARRAY_NOT_EQUAL(data1, data2, RANDOM_GENERATE_DATA_SIZE); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_random_implementation_test(void) +{ + uint8_t random_data[RANDOM_GENERATE_DATA_SIZE] = { 0 }; + struct s2n_blob blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&blob, random_data, sizeof(random_data))); + + uint64_t previous_public_bytes_used = 0; + EXPECT_OK(s2n_get_public_random_bytes_used(&previous_public_bytes_used)); + uint64_t previous_private_bytes_used = 0; + EXPECT_OK(s2n_get_private_random_bytes_used(&previous_private_bytes_used)); + + EXPECT_OK(s2n_get_public_random_data(&blob)); + EXPECT_OK(s2n_get_private_random_data(&blob)); + + uint64_t public_bytes_used = 0; + EXPECT_OK(s2n_get_public_random_bytes_used(&public_bytes_used)); + uint64_t private_bytes_used = 0; + EXPECT_OK(s2n_get_private_random_bytes_used(&private_bytes_used)); + + if (s2n_is_in_fips_mode()) { + /* The libcrypto random implementation should be used when operating in FIPS mode, so + * the bytes used in the custom DRBG state should not have changed. + */ + EXPECT_EQUAL(public_bytes_used, previous_public_bytes_used); + EXPECT_EQUAL(private_bytes_used, previous_public_bytes_used); + } else { + EXPECT_TRUE(public_bytes_used > previous_public_bytes_used); + EXPECT_TRUE(private_bytes_used > previous_private_bytes_used); + } + + return S2N_RESULT_OK; +} + +/* A collection of tests executed for each test dimension */ +static int s2n_common_tests(struct random_test_case *test_case) +{ + uint8_t data1[RANDOM_GENERATE_DATA_SIZE]; + uint8_t data2[RANDOM_GENERATE_DATA_SIZE]; + struct s2n_blob blob1 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&blob1, data1, 0)); + struct s2n_blob blob2 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&blob2, data2, 0)); + int64_t bound = 0; + uint64_t output = 0; + + /* Get one byte of data, to make sure the pool is (almost) full */ + blob1.size = 1; + blob2.size = 1; + EXPECT_OK(s2n_get_public_random_data(&blob1)); + EXPECT_OK(s2n_get_private_random_data(&blob2)); + + /* Verify we generate unique data over threads */ + EXPECT_OK(s2n_thread_test(s2n_get_public_random_data, s2n_get_public_random_data)); + EXPECT_OK(s2n_thread_test(s2n_get_private_random_data, s2n_get_private_random_data)); + EXPECT_OK(s2n_thread_test(s2n_get_public_random_data, s2n_get_private_random_data)); + EXPECT_OK(s2n_thread_test(s2n_get_private_random_data, s2n_get_public_random_data)); + + /* Verify we generate unique data over forks */ + EXPECT_OK(s2n_fork_test(s2n_get_private_random_data, s2n_get_private_random_data)); + EXPECT_OK(s2n_fork_test(s2n_get_public_random_data, s2n_get_public_random_data)); + EXPECT_OK(s2n_fork_test(s2n_get_public_random_data, s2n_get_private_random_data)); + EXPECT_OK(s2n_fork_test(s2n_get_private_random_data, s2n_get_public_random_data)); + + /* Some fork detection mechanisms can also detect forks through clone(). + * s2n_is_X_supported() only determines whether the system runtime + * environment supports fork detection method X. The function is not aware + * of the test case which is running. So, we need the CLONE_* tags to + * determine whether the clone test should run or not since some test cases + * disables the fork detection methods that can detect forks through clone() + */ + if (test_case->test_case_must_pass_clone_test == CLONE_TEST_YES) { + EXPECT_EQUAL(s2n_is_madv_wipeonfork_supported() || s2n_is_map_inherit_zero_supported(), true); + EXPECT_OK(s2n_execute_clone_tests()); + } else if (test_case->test_case_must_pass_clone_test == CLONE_TEST_DETERMINE_AT_RUNTIME) { + if (s2n_is_madv_wipeonfork_supported() || s2n_is_map_inherit_zero_supported()) { + EXPECT_OK(s2n_execute_clone_tests()); + } + } + + /* Basic tests generating randomness */ + EXPECT_OK(s2n_basic_generate_tests()); + + /* Test that the correct random implementation is used */ + EXPECT_OK(s2n_random_implementation_test()); + + /* Verify that there are no trivially observable patterns in the output */ + EXPECT_OK(s2n_basic_pattern_tests(s2n_get_public_random_data)); + EXPECT_OK(s2n_basic_pattern_tests(s2n_get_private_random_data)); + + /* Special range function tests */ + EXPECT_OK(s2n_tests_get_range()); + + /* Try to cleanup in the current thread and gather random data again for + * each of the public functions. We did not call s2n_rand_cleanup(), so this + * should still work properly. + */ + EXPECT_OK(s2n_rand_cleanup_thread()); + blob1.size = RANDOM_GENERATE_DATA_SIZE; + EXPECT_OK(s2n_get_public_random_data(&blob1)); + EXPECT_OK(s2n_basic_generate_tests()); + + EXPECT_OK(s2n_rand_cleanup_thread()); + blob2.size = RANDOM_GENERATE_DATA_SIZE; + EXPECT_OK(s2n_get_private_random_data(&blob2)); + EXPECT_OK(s2n_basic_generate_tests()); + + bound = RANDOM_GENERATE_DATA_SIZE; + EXPECT_OK(s2n_rand_cleanup_thread()); + EXPECT_OK(s2n_public_random(bound, &output)); + EXPECT_TRUE(output < bound); + + /* Just a sanity check */ + EXPECT_BYTEARRAY_NOT_EQUAL(data1, data2, RANDOM_GENERATE_DATA_SIZE); + + /* Verify that fork detection also works if we fork before initializing + * the drbgs + */ + EXPECT_OK(s2n_rand_cleanup_thread()); + EXPECT_OK(s2n_fork_test(s2n_get_private_random_data, s2n_get_private_random_data)); + EXPECT_OK(s2n_rand_cleanup_thread()); + EXPECT_OK(s2n_fork_test(s2n_get_public_random_data, s2n_get_public_random_data)); + + /* Verify that threading before initializing doesn't cause any issues */ + EXPECT_OK(s2n_rand_cleanup_thread()); + EXPECT_OK(s2n_thread_test(s2n_get_public_random_data, s2n_get_public_random_data)); + EXPECT_OK(s2n_rand_cleanup_thread()); + EXPECT_OK(s2n_thread_test(s2n_get_private_random_data, s2n_get_private_random_data)); + + return S2N_SUCCESS; +} + +static int s2n_random_test_case_default_cb(struct random_test_case *test_case) +{ + EXPECT_SUCCESS(s2n_init()); + + /* Verify that randomness callbacks can't be set to NULL */ + EXPECT_FAILURE(s2n_rand_set_callbacks(NULL, s2n_cleanup_cb, s2n_entropy_cb, s2n_entropy_cb)); + EXPECT_FAILURE(s2n_rand_set_callbacks(s2n_init_cb, NULL, s2n_entropy_cb, s2n_entropy_cb)); + EXPECT_FAILURE(s2n_rand_set_callbacks(s2n_init_cb, s2n_cleanup_cb, NULL, s2n_entropy_cb)); + EXPECT_FAILURE(s2n_rand_set_callbacks(s2n_init_cb, s2n_cleanup_cb, s2n_entropy_cb, NULL)); + + EXPECT_EQUAL(s2n_common_tests(test_case), S2N_SUCCESS); + + EXPECT_SUCCESS(s2n_cleanup()); + + return EXIT_SUCCESS; +} + +/* Test case that turns off prediction resistance */ +static int s2n_random_test_case_without_pr_cb(struct random_test_case *test_case) +{ + EXPECT_SUCCESS(s2n_init()); + + POSIX_GUARD_RESULT(s2n_ignore_prediction_resistance_for_testing(true)); + EXPECT_EQUAL(s2n_common_tests(test_case), S2N_SUCCESS); + POSIX_GUARD_RESULT(s2n_ignore_prediction_resistance_for_testing(false)); + + EXPECT_SUCCESS(s2n_cleanup()); + + return EXIT_SUCCESS; +} + +/* Test case that turns off prediction resistance and all fork detection + * mechanisms except pthread_at_fork() + */ +static int s2n_random_test_case_without_pr_pthread_atfork_cb(struct random_test_case *test_case) +{ + if (s2n_is_pthread_atfork_supported() == false) { + TEST_DEBUG_PRINT("s2n_random_test.c test case not supported. Skipping.\nTest case: %s\n", test_case->test_case_label); + return S2N_SUCCESS; + } + + POSIX_GUARD_RESULT(s2n_ignore_wipeonfork_and_inherit_zero_for_testing()); + + EXPECT_SUCCESS(s2n_init()); + + POSIX_GUARD_RESULT(s2n_ignore_prediction_resistance_for_testing(true)); + EXPECT_EQUAL(s2n_common_tests(test_case), S2N_SUCCESS); + POSIX_GUARD_RESULT(s2n_ignore_prediction_resistance_for_testing(false)); + + EXPECT_SUCCESS(s2n_cleanup()); + + return EXIT_SUCCESS; +} + +static int s2n_random_test_case_without_pr_madv_wipeonfork_cb(struct random_test_case *test_case) +{ + if (s2n_is_madv_wipeonfork_supported() == false) { + TEST_DEBUG_PRINT("s2n_random_test.c test case not supported. Skipping.\nTest case: %s\n", test_case->test_case_label); + return S2N_SUCCESS; + } + + POSIX_GUARD_RESULT(s2n_ignore_pthread_atfork_for_testing()); + + EXPECT_SUCCESS(s2n_init()); + + POSIX_GUARD_RESULT(s2n_ignore_prediction_resistance_for_testing(true)); + EXPECT_EQUAL(s2n_common_tests(test_case), S2N_SUCCESS); + POSIX_GUARD_RESULT(s2n_ignore_prediction_resistance_for_testing(false)); + + EXPECT_SUCCESS(s2n_cleanup()); + + return S2N_SUCCESS; +} + +static int s2n_random_test_case_without_pr_map_inherit_zero_cb(struct random_test_case *test_case) +{ + if (s2n_is_map_inherit_zero_supported() == false) { + TEST_DEBUG_PRINT("s2n_random_test.c test case not supported. Skipping.\nTest case: %s\n", test_case->test_case_label); + return S2N_SUCCESS; + } + + POSIX_GUARD_RESULT(s2n_ignore_pthread_atfork_for_testing()); + + EXPECT_SUCCESS(s2n_init()); + + POSIX_GUARD_RESULT(s2n_ignore_prediction_resistance_for_testing(true)); + EXPECT_EQUAL(s2n_common_tests(test_case), S2N_SUCCESS); + POSIX_GUARD_RESULT(s2n_ignore_prediction_resistance_for_testing(false)); + + EXPECT_SUCCESS(s2n_cleanup()); + + return S2N_SUCCESS; +} + +static int s2n_random_test_case_failure_cb(struct random_test_case *test_case) +{ + EXPECT_SUCCESS(s2n_init()); + + /* This is a cheap way to ensure that failures in a fork bubble up to the + * parent as a failure. This should be caught in the parent when querying + * the return status code of the child. All s2n test macros will cause a + * process to exit with error status = 1. We call exit() directly to avoid + * messages being printed on stderr, in turn, appearing in logs. + */ + exit(1); + + EXPECT_SUCCESS(s2n_cleanup()); + + return EXIT_SUCCESS; +} + +static int s2n_random_noop_destructor_test_cb(struct random_test_case *test_case) +{ + /* Ensure that the destructor / cleanup does not require s2n_init to have been called. + * If applications load s2n-tls but do not actually use it, our cleanup should not fail. + * + * Other test cases may currently trigger this scenario if the feature they + * intend to test is not available so they exit before calling s2n_init. + */ + return EXIT_SUCCESS; +} + +static int s2n_random_rand_bytes_after_cleanup_cb(struct random_test_case *test_case) +{ + s2n_disable_atexit(); + EXPECT_SUCCESS(s2n_init()); + EXPECT_SUCCESS(s2n_cleanup()); + + unsigned char rndbytes[16]; + EXPECT_EQUAL(RAND_bytes(rndbytes, sizeof(rndbytes)), 1); + + return S2N_SUCCESS; +} + +struct random_test_case random_test_cases[] = { + { "Random API.", s2n_random_test_case_default_cb, CLONE_TEST_DETERMINE_AT_RUNTIME, EXIT_SUCCESS }, + { "Random API without prediction resistance.", s2n_random_test_case_without_pr_cb, CLONE_TEST_DETERMINE_AT_RUNTIME, EXIT_SUCCESS }, + { "Random API without prediction resistance and with only pthread_atfork fork detection mechanism.", s2n_random_test_case_without_pr_pthread_atfork_cb, CLONE_TEST_NO, EXIT_SUCCESS }, + { "Random API without prediction resistance and with only madv_wipeonfork fork detection mechanism.", s2n_random_test_case_without_pr_madv_wipeonfork_cb, CLONE_TEST_YES, EXIT_SUCCESS }, + { "Random API without prediction resistance and with only map_inheret_zero fork detection mechanism.", s2n_random_test_case_without_pr_map_inherit_zero_cb, CLONE_TEST_YES, EXIT_SUCCESS }, + { "Test destructor without s2n_init", s2n_random_noop_destructor_test_cb, CLONE_TEST_DETERMINE_AT_RUNTIME, EXIT_SUCCESS }, + /* The s2n FAIL_MSG() macro uses exit(1) not exit(EXIT_FAILURE). So, we need + * to use 1 below and in s2n_random_test_case_failure_cb(). + */ + { "Test failure.", s2n_random_test_case_failure_cb, CLONE_TEST_DETERMINE_AT_RUNTIME, 1 }, + { "Test libcrypto's RAND engine is reset correctly after manual s2n_cleanup()", s2n_random_rand_bytes_after_cleanup_cb, CLONE_TEST_DETERMINE_AT_RUNTIME, EXIT_SUCCESS }, +}; + +int main(int argc, char **argv) +{ + BEGIN_TEST_NO_INIT(); + + /* For each test case, creates a child process that runs the test case. + * + * Fork detection is lazily initialised on first invocation of + * s2n_get_fork_generation_number(). Hence, it is important that children + * are created before calling into the fork detection code. + */ + for (size_t i = 0; i < s2n_array_len(random_test_cases); i++) { + pid_t proc_id = 0; + + proc_id = fork(); + EXPECT_TRUE(proc_id >= 0); + + if (proc_id == 0) { + /* In child */ + EXPECT_EQUAL(random_test_cases[i].test_case_cb(&random_test_cases[i]), EXIT_SUCCESS); + + /* Exit code EXIT_SUCCESS means that tests in this process finished + * successfully. Any errors would have exited the process with an + * exit code != EXIT_SUCCESS. We verify this in the parent process. + * Also prevents child from creating more children. + */ + exit(EXIT_SUCCESS); + } else { + s2n_verify_child_exit_status(proc_id, random_test_cases[i].expected_return_status); + } + } + + /* We are very paranoid when it comes to randomness. So, run the basic test + * set without using the fork infrastructure above. + */ + EXPECT_EQUAL(random_test_cases[0].test_case_cb(&random_test_cases[0]), EXIT_SUCCESS); + + END_TEST_NO_INIT(); +} diff --git a/tests/unit/s2n_rc4_test.c b/tests/unit/s2n_rc4_test.c new file mode 100644 index 00000000000..31fab3d1c35 --- /dev/null +++ b/tests/unit/s2n_rc4_test.c @@ -0,0 +1,137 @@ +/* + * 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_cipher.h" +#include "crypto/s2n_fips.h" +#include "crypto/s2n_hmac.h" +#include "crypto/s2n_openssl.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_prf.h" +#include "tls/s2n_record.h" +#include "utils/s2n_random.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Test Openssl-3.0 does not support RC4 */ + if (S2N_OPENSSL_VERSION_AT_LEAST(3, 0, 0)) { + EXPECT_FALSE(s2n_rc4.is_available()); + } + + /* Test FIPS does not support RC4 */ + if (s2n_is_in_fips_mode()) { + EXPECT_FALSE(s2n_rc4.is_available()); + } + + struct s2n_connection *conn; + uint8_t mac_key[] = "sample mac key"; + uint8_t rc4_key[] = "123456789012345"; + struct s2n_blob key_iv = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&key_iv, rc4_key, sizeof(rc4_key))); + uint8_t random_data[S2N_DEFAULT_FRAGMENT_LENGTH + 1]; + struct s2n_blob r = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&r, random_data, sizeof(random_data))); + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + if (s2n_is_in_fips_mode()) { + /* Skip when FIPS mode is set as FIPS mode does not support RC4 */ + END_TEST(); + } + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_OK(s2n_get_public_random_data(&r)); + + /* Peer and we are in sync */ + conn->server = conn->secure; + conn->client = conn->secure; + + /* test the RC4 cipher with a SHA1 hash */ + conn->secure->cipher_suite->record_alg = &s2n_record_alg_rc4_sha; + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->init(&conn->secure->server_key)); + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->init(&conn->secure->client_key)); + if (conn->secure->cipher_suite->record_alg->cipher->is_available()) { + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->set_decryption_key(&conn->secure->client_key, &key_iv)); + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->set_encryption_key(&conn->secure->server_key, &key_iv)); + EXPECT_SUCCESS(s2n_hmac_init(&conn->secure->client_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); + EXPECT_SUCCESS(s2n_hmac_init(&conn->secure->server_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); + conn->actual_protocol_version = S2N_TLS11; + + for (size_t i = 0; i <= S2N_DEFAULT_FRAGMENT_LENGTH + 1; i++) { + struct s2n_blob in = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&in, random_data, i)); + int bytes_written; + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); + + s2n_result result = s2n_record_write(conn, TLS_APPLICATION_DATA, &in); + if (i <= S2N_DEFAULT_FRAGMENT_LENGTH) { + EXPECT_OK(result); + bytes_written = i; + } else { + EXPECT_ERROR_WITH_ERRNO(result, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); + bytes_written = S2N_DEFAULT_FRAGMENT_LENGTH; + } + + uint16_t predicted_length = bytes_written + 20; + EXPECT_EQUAL(conn->out.blob.data[0], TLS_APPLICATION_DATA); + EXPECT_EQUAL(conn->out.blob.data[1], 3); + EXPECT_EQUAL(conn->out.blob.data[2], 2); + EXPECT_EQUAL(conn->out.blob.data[3], (predicted_length >> 8) & 0xff); + EXPECT_EQUAL(conn->out.blob.data[4], predicted_length & 0xff); + + /* The data should be encrypted */ + if (bytes_written > 10) { + EXPECT_NOT_EQUAL(memcmp(conn->out.blob.data + 5, random_data, bytes_written), 0); + } + + /* Copy the encrypted out data to the in data */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, 5)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + + /* Check that the data looks right */ + EXPECT_EQUAL(bytes_written + 20, s2n_stuffer_data_available(&conn->in)); + + /* Let's decrypt it */ + uint8_t content_type; + uint16_t fragment_length; + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + EXPECT_SUCCESS(s2n_record_parse(conn)); + EXPECT_EQUAL(content_type, TLS_APPLICATION_DATA); + EXPECT_EQUAL(fragment_length, predicted_length); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + } + + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->destroy_key(&conn->secure->server_key)); + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->destroy_key(&conn->secure->client_key)); + } else { + EXPECT_FAILURE(conn->secure->cipher_suite->record_alg->cipher->set_decryption_key(&conn->secure->client_key, &key_iv)); + EXPECT_FAILURE(conn->secure->cipher_suite->record_alg->cipher->set_encryption_key(&conn->secure->server_key, &key_iv)); + } + EXPECT_SUCCESS(s2n_connection_free(conn)); + END_TEST(); +} diff --git a/tests/unit/s2n_record_size_test.c b/tests/unit/s2n_record_size_test.c new file mode 100644 index 00000000000..615a4d9dea3 --- /dev/null +++ b/tests/unit/s2n_record_size_test.c @@ -0,0 +1,522 @@ +/* + * 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_cipher.h" +#include "crypto/s2n_hmac.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_prf.h" +#include "tls/s2n_record.h" +#include "utils/s2n_random.h" + +#define ONE_BLOCK 1024 +#define ONE_HUNDRED_K 100000 +#define RECORD_SIZE_HIGH_BYTE_ORDER 3 +#define RECORD_SIZE_LOW_BYTE_ORDER 4 +#define BYTE_SHIFT 8 +#define RECORD_SIZE(data) ((data[RECORD_SIZE_HIGH_BYTE_ORDER] << BYTE_SHIFT) | data[RECORD_SIZE_LOW_BYTE_ORDER]) + +#define EXPECT_LESS_THAN_EQUAL(p1, p2) EXPECT_TRUE((p1) <= (p2)) + +static int destroy_server_keys(struct s2n_connection *server_conn) +{ + POSIX_GUARD(server_conn->initial->cipher_suite->record_alg->cipher->destroy_key(&server_conn->initial->server_key)); + POSIX_GUARD(server_conn->initial->cipher_suite->record_alg->cipher->destroy_key(&server_conn->initial->client_key)); + + return S2N_SUCCESS; +} + +static int setup_server_keys(struct s2n_connection *server_conn, struct s2n_blob *key) +{ + POSIX_GUARD(server_conn->initial->cipher_suite->record_alg->cipher->init(&server_conn->initial->server_key)); + POSIX_GUARD(server_conn->initial->cipher_suite->record_alg->cipher->init(&server_conn->initial->client_key)); + POSIX_GUARD(server_conn->initial->cipher_suite->record_alg->cipher->set_encryption_key(&server_conn->initial->server_key, key)); + POSIX_GUARD(server_conn->initial->cipher_suite->record_alg->cipher->set_decryption_key(&server_conn->initial->client_key, key)); + + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + uint8_t mac_key[] = "sample mac key"; + + uint8_t random_data[S2N_LARGE_RECORD_LENGTH + 1]; + struct s2n_blob r = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&r, random_data, sizeof(random_data))); + EXPECT_OK(s2n_get_public_random_data(&r)); + + uint8_t aes128_key[] = "123456789012345"; + struct s2n_blob aes128 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&aes128, aes128_key, sizeof(aes128_key))); + + /* Test record sizes with s2n_record_write */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + /* Client and server are in sync */ + conn->server = conn->secure; + conn->client = conn->secure; + + /* test the AES128 cipher with a SHA1 hash */ + conn->secure->cipher_suite->record_alg = &s2n_record_alg_aes128_sha; + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->init(&conn->secure->server_key)); + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->init(&conn->secure->client_key)); + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->set_encryption_key(&conn->secure->server_key, &aes128)); + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->set_decryption_key(&conn->secure->client_key, &aes128)); + EXPECT_SUCCESS(s2n_hmac_init(&conn->secure->client_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); + EXPECT_SUCCESS(s2n_hmac_init(&conn->secure->server_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); + conn->actual_protocol_version = S2N_TLS11; + + /* Test that different modes allows for different fragment/payload sizes. + * Record overheads (IV, HMAC, padding) do not count towards these size */ + const int small_payload = S2N_SMALL_FRAGMENT_LENGTH; + const int large_payload = S2N_LARGE_FRAGMENT_LENGTH; + const int medium_payload = S2N_DEFAULT_FRAGMENT_LENGTH; + struct s2n_blob fragment = r; + + /* Check the default: medium records */ + fragment.size = medium_payload; + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); + EXPECT_OK(s2n_record_write(conn, TLS_APPLICATION_DATA, &fragment)); + + /* Check explicitly small records */ + fragment.size = small_payload; + EXPECT_SUCCESS(s2n_connection_prefer_low_latency(conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); + EXPECT_OK(s2n_record_write(conn, TLS_APPLICATION_DATA, &fragment)); + + /* Check explicitly large records */ + fragment.size = large_payload; + EXPECT_SUCCESS(s2n_connection_prefer_throughput(conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); + EXPECT_OK(s2n_record_write(conn, TLS_APPLICATION_DATA, &fragment)); + + /* Clean up */ + conn->secure->cipher_suite->record_alg = &s2n_record_alg_null; /* restore mutated null cipher suite */ + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->destroy_key(&conn->secure->server_key)); + EXPECT_SUCCESS(conn->secure->cipher_suite->record_alg->cipher->destroy_key(&conn->secure->client_key)); + }; + + /* Test s2n_record_max_write_payload_size() have proper checks in place */ + { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + /* we deal with the default null cipher suite for now, as it makes reasoning + * about easier s2n_record_max_write_payload_size(), as it incur 0 overheads */ + uint16_t size; + server_conn->max_outgoing_fragment_length = ONE_BLOCK; + EXPECT_OK(s2n_record_max_write_payload_size(server_conn, &size)); + EXPECT_EQUAL(size, ONE_BLOCK); + + /* Trigger an overlarge payload by setting a maximum uint16_t value to max fragment length */ + server_conn->max_outgoing_fragment_length = UINT16_MAX; + /* Check that we are bound by S2N_TLS_MAXIMUM_FRAGMENT_LENGTH */ + EXPECT_OK(s2n_record_max_write_payload_size(server_conn, &size)); + EXPECT_EQUAL(size, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH); + + /* trigger a payload that is under the limits */ + server_conn->max_outgoing_fragment_length = 0; + EXPECT_ERROR_WITH_ERRNO(s2n_record_max_write_payload_size(server_conn, &size), S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL); + + /* Test boundary cases */ + + /* This is the theoretical maximum mfl allowed */ + server_conn->max_outgoing_fragment_length = S2N_TLS_MAXIMUM_FRAGMENT_LENGTH; + EXPECT_OK(s2n_record_max_write_payload_size(server_conn, &size)); + EXPECT_EQUAL(size, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH); + + /* MFL over limit is not allowed, but size is reduced to S2N_TLS_MAXIMUM_FRAGMENT_LENGTH*/ + server_conn->max_outgoing_fragment_length++; + EXPECT_OK(s2n_record_max_write_payload_size(server_conn, &size)); + EXPECT_EQUAL(size, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH); + + /* Test against different cipher suites */ + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->server->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + server_conn->max_outgoing_fragment_length = ONE_BLOCK; + EXPECT_OK(s2n_record_max_write_payload_size(server_conn, &size)); + EXPECT_EQUAL(size, ONE_BLOCK); /* Verify size matches exactly specified max fragment length */ + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test s2n_record_max_write_payload_size with custom send buffer size */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + /* Min buffer size */ + { + const uint32_t buffer_size = S2N_TLS_MAX_RECORD_LEN_FOR(S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE); + EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, buffer_size)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + uint16_t size = 0; + EXPECT_OK(s2n_record_max_write_payload_size(conn, &size)); + EXPECT_EQUAL(size, S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE); + }; + + /* Small buffer size */ + { + const uint32_t frag_len = 1000; + const uint32_t buffer_size = S2N_TLS_MAX_RECORD_LEN_FOR(frag_len); + EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, buffer_size)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + uint16_t size = 0; + EXPECT_OK(s2n_record_max_write_payload_size(conn, &size)); + EXPECT_EQUAL(size, frag_len); + }; + + /* Buffer exactly fits one record */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + const uint32_t frag_len = conn->max_outgoing_fragment_length; + const uint32_t buffer_size = S2N_TLS_MAX_RECORD_LEN_FOR(frag_len); + EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, buffer_size)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + uint16_t size = 0; + EXPECT_OK(s2n_record_max_write_payload_size(conn, &size)); + EXPECT_EQUAL(size, frag_len); + }; + + /* Buffer larger than one record */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + const uint32_t frag_len = conn->max_outgoing_fragment_length + 10; + const uint32_t buffer_size = S2N_TLS_MAX_RECORD_LEN_FOR(frag_len); + EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, buffer_size)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + uint16_t size = 0; + EXPECT_OK(s2n_record_max_write_payload_size(conn, &size)); + EXPECT_EQUAL(size, conn->max_outgoing_fragment_length); + }; + }; + + /* Test s2n_record_min_write_payload_size() */ + { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + uint16_t size = 0; + const int RECORD_SIZE_LESS_OVERHEADS = 1415; + + EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); + EXPECT_EQUAL(RECORD_SIZE_LESS_OVERHEADS, size); + + const int MIN_SIZE = RECORD_SIZE_LESS_OVERHEADS + S2N_TLS_RECORD_HEADER_LENGTH; + + /* CBC */ + { + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + server_conn->actual_protocol_version = S2N_TLS11; + server_conn->initial->cipher_suite->record_alg = &s2n_record_alg_3des_sha; + uint8_t des3_key[] = "12345678901234567890123"; + struct s2n_blob des3 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&des3, des3_key, sizeof(des3_key))); + server_conn->server = server_conn->secure; + EXPECT_SUCCESS(server_conn->secure->cipher_suite->record_alg->cipher->init(&server_conn->secure->server_key)); + EXPECT_SUCCESS(server_conn->secure->cipher_suite->record_alg->cipher->init(&server_conn->secure->client_key)); + EXPECT_SUCCESS(server_conn->secure->cipher_suite->record_alg->cipher->set_encryption_key(&server_conn->secure->server_key, &des3)); + EXPECT_SUCCESS(server_conn->secure->cipher_suite->record_alg->cipher->set_decryption_key(&server_conn->secure->client_key, &des3)); + EXPECT_SUCCESS(s2n_hmac_init(&server_conn->secure->server_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); + + EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); + r.size = size; + const int after_overheads = RECORD_SIZE_LESS_OVERHEADS - RECORD_SIZE_LESS_OVERHEADS % 8; /* rounded down to cbc block size (8) */ + const uint16_t PADDING_LENGTH_BYTE = 1; + const uint16_t RECORD_IV_SIZE = 8; + const uint16_t HMAC_DIGEST = 20; + EXPECT_EQUAL(size, after_overheads - HMAC_DIGEST - RECORD_IV_SIZE - PADDING_LENGTH_BYTE); + + EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); + const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); + EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); + EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); + }; + + /* AEAD */ + { + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + server_conn->initial->cipher_suite->record_alg = &s2n_record_alg_aes128_gcm; + EXPECT_SUCCESS(setup_server_keys(server_conn, &aes128)); + + EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); + r.size = size; + const uint16_t IV = 8; + const uint16_t TAG = 16; + EXPECT_EQUAL(size, RECORD_SIZE_LESS_OVERHEADS - IV - TAG); + + EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); + const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); + EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); + EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); + }; + + /* TLS1.3 AEAD */ + { + EXPECT_SUCCESS(destroy_server_keys(server_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->initial->cipher_suite->record_alg = &s2n_tls13_record_alg_aes128_gcm; + EXPECT_SUCCESS(setup_server_keys(server_conn, &aes128)); + + EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); + r.size = size; + const uint16_t IV = 0; + const uint16_t TAG = 16; + EXPECT_EQUAL(size, RECORD_SIZE_LESS_OVERHEADS - IV - TAG - S2N_TLS_CONTENT_TYPE_LENGTH); + + EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); + const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); + EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); + EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); + }; + + /* chacha20 */ + if (s2n_chacha20_poly1305.is_available()) { + EXPECT_SUCCESS(destroy_server_keys(server_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + + server_conn->initial->cipher_suite->record_alg = &s2n_record_alg_chacha20_poly1305; + uint8_t chacha20_poly1305_key_data[] = "1234567890123456789012345678901"; + struct s2n_blob chacha20_poly1305_key = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&chacha20_poly1305_key, chacha20_poly1305_key_data, sizeof(chacha20_poly1305_key_data))); + + EXPECT_SUCCESS(setup_server_keys(server_conn, &chacha20_poly1305_key)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); + EXPECT_EQUAL(size, RECORD_SIZE_LESS_OVERHEADS - S2N_TLS_CHACHA20_POLY1305_EXPLICIT_IV_LEN - S2N_TLS_GCM_TAG_LEN); + r.size = size; + + EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); + const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); + EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); + EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); + } + + /* TLS1.3 chacha20 */ + if (s2n_chacha20_poly1305.is_available()) { + EXPECT_SUCCESS(destroy_server_keys(server_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->initial->cipher_suite->record_alg = &s2n_tls13_record_alg_chacha20_poly1305; + uint8_t chacha20_poly1305_key_data[] = "1234567890123456789012345678901"; + struct s2n_blob chacha20_poly1305_key = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&chacha20_poly1305_key, chacha20_poly1305_key_data, sizeof(chacha20_poly1305_key_data))); + + EXPECT_SUCCESS(setup_server_keys(server_conn, &chacha20_poly1305_key)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); + EXPECT_EQUAL(size, RECORD_SIZE_LESS_OVERHEADS - S2N_TLS_CHACHA20_POLY1305_EXPLICIT_IV_LEN - S2N_TLS_GCM_TAG_LEN - S2N_TLS_CONTENT_TYPE_LENGTH); + r.size = size; + + EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); + const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); + EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); + EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); + } + + /* composite */ + if (s2n_aes128_sha.is_available() && s2n_aes128_sha256.is_available()) { + EXPECT_SUCCESS(destroy_server_keys(server_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + server_conn->initial->cipher_suite->record_alg = &s2n_record_alg_aes128_sha_composite; + server_conn->actual_protocol_version = S2N_TLS11; + uint8_t mac_key_sha[20] = "server key shaserve"; + EXPECT_SUCCESS(server_conn->initial->cipher_suite->record_alg->cipher->set_encryption_key(&server_conn->initial->server_key, &aes128)); + EXPECT_SUCCESS(server_conn->initial->cipher_suite->record_alg->cipher->set_decryption_key(&server_conn->initial->client_key, &aes128)); + EXPECT_SUCCESS(server_conn->initial->cipher_suite->record_alg->cipher->io.comp.set_mac_write_key(&server_conn->initial->server_key, mac_key_sha, sizeof(mac_key_sha))); + EXPECT_SUCCESS(server_conn->initial->cipher_suite->record_alg->cipher->io.comp.set_mac_write_key(&server_conn->initial->client_key, mac_key_sha, sizeof(mac_key_sha))); + + EXPECT_OK(s2n_record_min_write_payload_size(server_conn, &size)); + const uint16_t COMPOSITE_BLOCK_SIZE = 16; + const uint16_t COMPOSITE_DIGEST_LENGTH = 20; + const uint16_t COMPOSITE_PADDING_LENGTH = 1; + const uint16_t size_aligned_to_block = RECORD_SIZE_LESS_OVERHEADS - RECORD_SIZE_LESS_OVERHEADS % COMPOSITE_BLOCK_SIZE - COMPOSITE_DIGEST_LENGTH - COMPOSITE_PADDING_LENGTH; + const uint16_t explicit_iv_len = 16; + const uint16_t size_after_overheads = size_aligned_to_block - explicit_iv_len; + EXPECT_EQUAL(size, size_after_overheads); + r.size = size; + + EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &r)); + const uint16_t wire_size = s2n_stuffer_data_available(&server_conn->out); + EXPECT_LESS_THAN_EQUAL(wire_size, MIN_SIZE); + EXPECT_EQUAL(RECORD_SIZE(server_conn->out.blob.data), wire_size - S2N_TLS_RECORD_HEADER_LENGTH); + } + + r.size = sizeof(random_data); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test large fragment/record sending for TLS 1.3 */ + { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + struct s2n_cipher_suite *cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->server->cipher_suite = cipher_suite; + + struct s2n_session_key *session_key = &server_conn->server->server_key; + uint8_t *implicit_iv = server_conn->server->server_implicit_iv; + + /* init record algorithm */ + EXPECT_SUCCESS(cipher_suite->record_alg->cipher->init(session_key)); + S2N_BLOB_FROM_HEX(key, "0123456789abcdef0123456789abcdef"); + EXPECT_SUCCESS(cipher_suite->record_alg->cipher->set_encryption_key(session_key, &key)); + EXPECT_SUCCESS(cipher_suite->record_alg->cipher->set_decryption_key(session_key, &key)); + + S2N_BLOB_FROM_HEX(iv, "0123456789abcdef01234567"); + + /* copy iv bytes from input data */ + for (size_t i = 0; i < iv.size; i++) { + implicit_iv[i] = iv.data[i]; + } + + /* Configure to use s2n maximum fragment / record settings */ + EXPECT_SUCCESS(s2n_connection_prefer_throughput(server_conn)); + + /* Testing with a small blob */ + s2n_stack_blob(small_blob, ONE_BLOCK, ONE_BLOCK); + struct iovec small_io_vec = { 0 }; + small_io_vec.iov_base = small_blob.data; + small_io_vec.iov_len = small_blob.size; + + int bytes_taken; + + const uint16_t TLS13_RECORD_OVERHEAD = 22; + EXPECT_SUCCESS(bytes_taken = s2n_record_writev(server_conn, TLS_APPLICATION_DATA, &small_io_vec, 1, 0, small_blob.size)); + EXPECT_EQUAL(bytes_taken, ONE_BLOCK); /* we wrote the full blob size */ + EXPECT_EQUAL(s2n_stuffer_data_available(&server_conn->out), ONE_BLOCK + TLS13_RECORD_OVERHEAD); /* bytes on the wire */ + + /* Check we get a friendly error if we use s2n_record_write again */ + EXPECT_ERROR_WITH_ERRNO(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &small_blob), S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &small_blob)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + /* Testing a big 100k blob to be written */ + s2n_stack_blob(big_blob, ONE_HUNDRED_K, ONE_HUNDRED_K); + struct iovec big_io_vec = { 0 }; + big_io_vec.iov_base = big_blob.data; + big_io_vec.iov_len = big_blob.size; + + /* Test that s2n_record_writev() doesn't error on writing large payloads. + * Also asserts the bytes written on the wire. + */ + EXPECT_SUCCESS(bytes_taken = s2n_record_writev(server_conn, TLS_APPLICATION_DATA, &big_io_vec, 1, 0, big_blob.size)); + + /* We verify that s2n_record_writev() is able to send the maximum fragment length as specified by TLS RFCs */ + const uint16_t TLS_MAX_FRAG_LEN = 16384; + EXPECT_EQUAL(bytes_taken, TLS_MAX_FRAG_LEN); /* plaintext bytes taken */ + EXPECT_EQUAL(s2n_stuffer_data_available(&server_conn->out), TLS_MAX_FRAG_LEN + TLS13_RECORD_OVERHEAD); /* bytes sent on the wire */ + + /* These are invariant regardless of s2n implementation */ + EXPECT_TRUE(bytes_taken <= S2N_TLS_MAXIMUM_FRAGMENT_LENGTH); /* Plaintext max size - 2^14 = 16384 */ + EXPECT_TRUE(bytes_taken <= (S2N_TLS_MAXIMUM_FRAGMENT_LENGTH + 255)); /* Max record size for TLS 1.3 - 2^14 + 255 = 16639 */ + EXPECT_TRUE(s2n_stuffer_data_available(&server_conn->out) <= S2N_TLS_MAXIMUM_RECORD_LENGTH); + EXPECT_TRUE(s2n_stuffer_data_available(&server_conn->out) <= S2N_TLS13_MAXIMUM_RECORD_LENGTH); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + /* Now escape the sandbox and attempt to get record_write to use a larger plaintext bytes */ + /* However, the max fragment length should still be bounded based on the protocol specification */ + const uint16_t MAX_FORCED_OUTGOING_FRAGMENT_LENGTH = 16400; + + server_conn->max_outgoing_fragment_length = MAX_FORCED_OUTGOING_FRAGMENT_LENGTH; /* Trigger fragment length bounding */ + EXPECT_SUCCESS(bytes_taken = s2n_record_writev(server_conn, TLS_APPLICATION_DATA, &big_io_vec, 1, 0, big_blob.size)); + EXPECT_EQUAL(bytes_taken, TLS_MAX_FRAG_LEN); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + /* Force a generous 100k resize on the outgoing record stuffer */ + EXPECT_SUCCESS(s2n_stuffer_resize(&server_conn->out, ONE_HUNDRED_K)); + server_conn->max_outgoing_fragment_length = MAX_FORCED_OUTGOING_FRAGMENT_LENGTH; + EXPECT_SUCCESS(bytes_taken = s2n_record_writev(server_conn, TLS_APPLICATION_DATA, &big_io_vec, 1, 0, big_blob.size)); + EXPECT_EQUAL(bytes_taken, TLS_MAX_FRAG_LEN); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->out)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* s2n_record_max_write_size */ + { + uint16_t result = 0; + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + EXPECT_ERROR_WITH_ERRNO(s2n_record_max_write_size(NULL, 1, &result), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_record_max_write_size(conn, 1, NULL), S2N_ERR_NULL); + + conn->actual_protocol_version = 0; + conn->handshake.handshake_type = INITIAL; + EXPECT_OK(s2n_record_max_write_size(conn, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, &result)); + EXPECT_EQUAL(result, S2N_TLS_MAXIMUM_RECORD_LENGTH); + + conn->handshake.handshake_type = NEGOTIATED; + EXPECT_OK(s2n_record_max_write_size(conn, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, &result)); + EXPECT_EQUAL(result, S2N_TLS_MAXIMUM_RECORD_LENGTH); + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_OK(s2n_record_max_write_size(conn, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, &result)); + EXPECT_EQUAL(result, S2N_TLS12_MAXIMUM_RECORD_LENGTH); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_record_max_write_size(conn, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, &result)); + EXPECT_EQUAL(result, S2N_TLS13_MAXIMUM_RECORD_LENGTH); + + uint16_t diff = 10; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_record_max_write_size(conn, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH - diff, &result)); + EXPECT_EQUAL(result, S2N_TLS13_MAXIMUM_RECORD_LENGTH - diff); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_record_test.c b/tests/unit/s2n_record_test.c new file mode 100644 index 00000000000..bf7e9120fc6 --- /dev/null +++ b/tests/unit/s2n_record_test.c @@ -0,0 +1,437 @@ +/* + * 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 "tls/s2n_record.h" + +#include +#include + +#include "api/s2n.h" +#include "crypto/s2n_cipher.h" +#include "crypto/s2n_hmac.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_kex.h" +#include "tls/s2n_prf.h" +#include "utils/s2n_random.h" + +/* Mock block cipher that does nothing */ +int mock_block_endecrypt(struct s2n_session_key *key, struct s2n_blob *iv, struct s2n_blob *in, struct s2n_blob *out) +{ + return 0; +} + +struct s2n_cipher mock_block_cipher = { + .type = S2N_CBC, + .key_material_size = 0, + .io.cbc = { + .block_size = 16, + .record_iv_size = 16, + .encrypt = mock_block_endecrypt, + .decrypt = mock_block_endecrypt }, + .set_encryption_key = NULL, + .set_decryption_key = NULL, + .destroy_key = NULL, +}; + +struct s2n_record_algorithm mock_block_record_alg = { + .cipher = &mock_block_cipher, + .hmac_alg = S2N_HMAC_SHA1, +}; + +struct s2n_cipher_suite mock_block_cipher_suite = { + .available = 1, + .name = "TLS_MOCK_CBC", + .iana_value = { 0x12, 0x34 }, + .key_exchange_alg = &s2n_rsa, + .record_alg = &mock_block_record_alg, +}; + +struct s2n_record_algorithm mock_null_sha1_record_alg = { + .cipher = &s2n_null_cipher, + .hmac_alg = S2N_HMAC_SHA1, +}; + +int main(int argc, char **argv) +{ + struct s2n_connection *conn; + uint8_t mac_key[] = "sample mac key"; + struct s2n_blob fixed_iv = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&fixed_iv, mac_key, sizeof(mac_key))); + struct s2n_hmac_state check_mac; + uint8_t random_data[S2N_DEFAULT_FRAGMENT_LENGTH + 1]; + struct s2n_blob r = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&r, random_data, sizeof(random_data))); + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_SUCCESS(s2n_hmac_new(&check_mac)); + + EXPECT_SUCCESS(s2n_hmac_init(&check_mac, S2N_HMAC_SHA1, fixed_iv.data, fixed_iv.size)); + EXPECT_OK(s2n_get_public_random_data(&r)); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + /* Peer and we are in sync */ + conn->server = conn->initial; + conn->client = conn->initial; + + /* test the null cipher. */ + conn->initial->cipher_suite = &s2n_null_cipher_suite; + conn->actual_protocol_version = S2N_TLS11; + + for (size_t i = 0; i <= S2N_DEFAULT_FRAGMENT_LENGTH + 1; i++) { + struct s2n_blob in = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&in, random_data, i)); + int bytes_written; + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); + + s2n_result result = s2n_record_write(conn, TLS_ALERT, &in); + if (i <= S2N_DEFAULT_FRAGMENT_LENGTH) { + EXPECT_OK(result); + bytes_written = i; + } else { + EXPECT_ERROR_WITH_ERRNO(result, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); + bytes_written = S2N_DEFAULT_FRAGMENT_LENGTH; + } + + EXPECT_EQUAL(conn->out.blob.data[0], TLS_ALERT); + EXPECT_EQUAL(conn->out.blob.data[1], 3); + EXPECT_EQUAL(conn->out.blob.data[2], 2); + EXPECT_EQUAL(conn->out.blob.data[3], (bytes_written >> 8) & 0xff); + EXPECT_EQUAL(conn->out.blob.data[4], bytes_written & 0xff); + EXPECT_EQUAL(memcmp(conn->out.blob.data + 5, random_data, bytes_written), 0); + + EXPECT_SUCCESS(s2n_stuffer_resize_if_empty(&conn->in, S2N_LARGE_FRAGMENT_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, 5)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + + uint8_t content_type; + uint16_t fragment_length; + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + EXPECT_SUCCESS(s2n_record_parse(conn)); + EXPECT_EQUAL(content_type, TLS_ALERT); + EXPECT_EQUAL(fragment_length, bytes_written); + } + + /* test a fake streaming cipher with a MAC */ + conn->initial->cipher_suite->record_alg = &mock_null_sha1_record_alg; + EXPECT_SUCCESS(s2n_hmac_init(&conn->initial->client_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); + EXPECT_SUCCESS(s2n_hmac_init(&conn->initial->server_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); + conn->initial->cipher_suite = &s2n_null_cipher_suite; + conn->actual_protocol_version = S2N_TLS11; + + for (size_t i = 0; i <= S2N_DEFAULT_FRAGMENT_LENGTH + 1; i++) { + struct s2n_blob in = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&in, random_data, i)); + int bytes_written; + + EXPECT_SUCCESS(s2n_hmac_reset(&check_mac)); + EXPECT_SUCCESS(s2n_hmac_update(&check_mac, conn->initial->server_sequence_number, 8)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); + + s2n_result result = s2n_record_write(conn, TLS_ALERT, &in); + if (i <= S2N_DEFAULT_FRAGMENT_LENGTH) { + EXPECT_OK(result); + bytes_written = i; + } else { + EXPECT_ERROR_WITH_ERRNO(result, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); + bytes_written = S2N_DEFAULT_FRAGMENT_LENGTH; + } + + uint16_t predicted_length = bytes_written + 20; + EXPECT_EQUAL(conn->out.blob.data[0], TLS_ALERT); + EXPECT_EQUAL(conn->out.blob.data[1], 3); + EXPECT_EQUAL(conn->out.blob.data[2], 2); + EXPECT_EQUAL(conn->out.blob.data[3], (predicted_length >> 8) & 0xff); + EXPECT_EQUAL(conn->out.blob.data[4], predicted_length & 0xff); + EXPECT_EQUAL(memcmp(conn->out.blob.data + 5, random_data, bytes_written), 0); + + uint8_t top = bytes_written >> 8; + uint8_t bot = bytes_written & 0xff; + EXPECT_SUCCESS(s2n_hmac_update(&check_mac, conn->out.blob.data, 3)); + EXPECT_SUCCESS(s2n_hmac_update(&check_mac, &top, 1)); + EXPECT_SUCCESS(s2n_hmac_update(&check_mac, &bot, 1)); + EXPECT_SUCCESS(s2n_hmac_update(&check_mac, random_data, bytes_written)); + + uint8_t check_digest[20]; + EXPECT_SUCCESS(s2n_hmac_digest(&check_mac, check_digest, 20)); + EXPECT_SUCCESS(s2n_hmac_digest_verify(conn->out.blob.data + 5 + bytes_written, check_digest, 20)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, 5)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + + uint8_t original_seq_num[8]; + EXPECT_MEMCPY_SUCCESS(original_seq_num, conn->server->client_sequence_number, 8); + + uint8_t content_type; + uint16_t fragment_length; + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + EXPECT_SUCCESS(s2n_record_parse(conn)); + EXPECT_EQUAL(content_type, TLS_ALERT); + EXPECT_EQUAL(fragment_length, predicted_length); + + /* Simulate a replay attack and verify that replaying the same record + * fails due to the sequence number check */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_reread(&conn->out)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + EXPECT_FAILURE(s2n_record_parse(conn)); + + /* Restore the original sequence number */ + EXPECT_MEMCPY_SUCCESS(conn->server->client_sequence_number, original_seq_num, 8); + + /* Deliberately corrupt a byte of the output and check that the record + * won't parse + */ + uint64_t byte_to_corrupt; + EXPECT_OK(s2n_public_random(fragment_length, &byte_to_corrupt)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_reread(&conn->out)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, 5)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + + conn->in.blob.data[byte_to_corrupt] += 1; + EXPECT_FAILURE_WITH_ERRNO(s2n_record_parse(conn), S2N_ERR_BAD_MESSAGE); + } + + /* Test a mock block cipher with a mac - in TLS1.0 mode */ + EXPECT_SUCCESS(s2n_hmac_init(&conn->initial->client_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); + EXPECT_SUCCESS(s2n_hmac_init(&conn->initial->server_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); + conn->actual_protocol_version = S2N_TLS10; + conn->initial->cipher_suite = &mock_block_cipher_suite; + + for (size_t i = 0; i <= S2N_DEFAULT_FRAGMENT_LENGTH + 1; i++) { + struct s2n_blob in = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&in, random_data, i)); + int bytes_written; + + EXPECT_SUCCESS(s2n_hmac_reset(&check_mac)); + EXPECT_SUCCESS(s2n_hmac_update(&check_mac, conn->initial->client_sequence_number, 8)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); + + s2n_result result = s2n_record_write(conn, TLS_APPLICATION_DATA, &in); + if (i <= S2N_DEFAULT_FRAGMENT_LENGTH) { + EXPECT_OK(result); + bytes_written = i; + } else { + EXPECT_ERROR_WITH_ERRNO(result, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); + bytes_written = S2N_DEFAULT_FRAGMENT_LENGTH; + } + + uint16_t predicted_length = bytes_written + 1 + 20; + if (predicted_length % 16) { + predicted_length += (16 - (predicted_length % 16)); + } + EXPECT_EQUAL(conn->out.blob.data[0], TLS_APPLICATION_DATA); + EXPECT_EQUAL(conn->out.blob.data[1], 3); + EXPECT_EQUAL(conn->out.blob.data[2], 1); + EXPECT_EQUAL(conn->out.blob.data[3], (predicted_length >> 8) & 0xff); + EXPECT_EQUAL(conn->out.blob.data[4], predicted_length & 0xff); + EXPECT_EQUAL(memcmp(conn->out.blob.data + 5, random_data, bytes_written), 0); + + /* The last byte of out should indicate how much padding there was */ + uint8_t p = conn->out.blob.data[conn->out.write_cursor - 1]; + const uint32_t remaining = 5 + bytes_written + 20 + p + 1; + EXPECT_EQUAL(remaining, s2n_stuffer_data_available(&conn->out)); + + /* Check that the last 'p' bytes are all set to 'p' */ + for (size_t j = 0; j <= (size_t) p; j++) { + EXPECT_EQUAL(conn->out.blob.data[5 + bytes_written + 20 + j], p); + } + + uint8_t top = bytes_written >> 8; + uint8_t bot = bytes_written & 0xff; + EXPECT_SUCCESS(s2n_hmac_update(&check_mac, conn->out.blob.data, 3)); + EXPECT_SUCCESS(s2n_hmac_update(&check_mac, &top, 1)); + EXPECT_SUCCESS(s2n_hmac_update(&check_mac, &bot, 1)); + EXPECT_SUCCESS(s2n_hmac_update(&check_mac, random_data, bytes_written)); + + uint8_t check_digest[20]; + EXPECT_SUCCESS(s2n_hmac_digest(&check_mac, check_digest, 20)); + EXPECT_SUCCESS(s2n_hmac_digest_verify(conn->out.blob.data + 5 + bytes_written, check_digest, 20)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, 5)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + + uint8_t content_type; + uint16_t fragment_length; + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + EXPECT_SUCCESS(s2n_record_parse(conn)); + EXPECT_EQUAL(content_type, TLS_APPLICATION_DATA); + EXPECT_EQUAL(fragment_length, predicted_length); + } + + /* Test a mock block cipher with a mac - in TLS1.1+ mode */ + EXPECT_SUCCESS(s2n_hmac_init(&conn->initial->client_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); + EXPECT_SUCCESS(s2n_hmac_init(&conn->initial->server_record_mac, S2N_HMAC_SHA1, mac_key, sizeof(mac_key))); + conn->actual_protocol_version = S2N_TLS11; + conn->initial->cipher_suite = &mock_block_cipher_suite; + + for (int i = 0; i <= S2N_DEFAULT_FRAGMENT_LENGTH + 1; i++) { + struct s2n_blob in = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&in, random_data, i)); + int bytes_written; + + EXPECT_SUCCESS(s2n_hmac_reset(&check_mac)); + EXPECT_SUCCESS(s2n_hmac_update(&check_mac, conn->initial->client_sequence_number, 8)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); + + s2n_result result = s2n_record_write(conn, TLS_APPLICATION_DATA, &in); + if (i <= S2N_DEFAULT_FRAGMENT_LENGTH) { + EXPECT_OK(result); + bytes_written = i; + } else { + EXPECT_ERROR_WITH_ERRNO(result, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE); + bytes_written = S2N_DEFAULT_FRAGMENT_LENGTH; + } + + uint16_t predicted_length = bytes_written + 1 + 20 + 16; + if (predicted_length % 16) { + predicted_length += (16 - (predicted_length % 16)); + } + EXPECT_EQUAL(conn->out.blob.data[0], TLS_APPLICATION_DATA); + EXPECT_EQUAL(conn->out.blob.data[1], 3); + EXPECT_EQUAL(conn->out.blob.data[2], 2); + EXPECT_EQUAL(conn->out.blob.data[3], (predicted_length >> 8) & 0xff); + EXPECT_EQUAL(conn->out.blob.data[4], predicted_length & 0xff); + EXPECT_EQUAL(memcmp(conn->out.blob.data + 16 + 5, random_data, bytes_written), 0); + + /* The last byte of out should indicate how much padding there was */ + uint8_t p = conn->out.blob.data[conn->out.write_cursor - 1]; + EXPECT_EQUAL(5 + bytes_written + 20 + 16 + p + 1, s2n_stuffer_data_available(&conn->out)); + + /* Check that the last 'p' bytes are all set to 'p' */ + for (size_t j = 0; j <= (size_t) p; j++) { + EXPECT_EQUAL(conn->out.blob.data[5 + bytes_written + 16 + 20 + j], p); + } + + uint8_t top = bytes_written >> 8; + uint8_t bot = bytes_written & 0xff; + EXPECT_SUCCESS(s2n_hmac_update(&check_mac, conn->out.blob.data, 3)); + EXPECT_SUCCESS(s2n_hmac_update(&check_mac, &top, 1)); + EXPECT_SUCCESS(s2n_hmac_update(&check_mac, &bot, 1)); + EXPECT_SUCCESS(s2n_hmac_update(&check_mac, random_data, bytes_written)); + + uint8_t check_digest[20]; + EXPECT_SUCCESS(s2n_hmac_digest(&check_mac, check_digest, 20)); + EXPECT_SUCCESS(s2n_hmac_digest_verify(conn->out.blob.data + 16 + 5 + bytes_written, check_digest, 20)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, 5)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + + uint8_t content_type; + uint16_t fragment_length; + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + EXPECT_SUCCESS(s2n_record_parse(conn)); + EXPECT_EQUAL(content_type, TLS_APPLICATION_DATA); + EXPECT_EQUAL(fragment_length, predicted_length); + } + + /* Test TLS record limit */ + struct s2n_blob empty_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&empty_blob, NULL, 0)); + conn->initial->cipher_suite = &s2n_null_cipher_suite; + + /* Fast forward the sequence number */ + uint8_t max_num_records[] = { 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF }; + EXPECT_MEMCPY_SUCCESS(conn->initial->server_sequence_number, max_num_records, sizeof(max_num_records)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); + /* Sequence number should wrap around */ + EXPECT_ERROR_WITH_ERRNO(s2n_record_write(conn, TLS_ALERT, &empty_blob), S2N_ERR_RECORD_LIMIT); + + /* Test TLS 1.3 Record should reflect as TLS 1.2 version on the wire */ + { + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_record_write(conn, TLS_ALERT, &empty_blob)); + + /* Make sure that TLS 1.3 records appear as TLS 1.2 version */ + EXPECT_EQUAL(conn->out.blob.data[1], 3); + EXPECT_EQUAL(conn->out.blob.data[2], 3); + + /* Copy written bytes for reading */ + EXPECT_SUCCESS(s2n_stuffer_resize_if_empty(&conn->in, S2N_LARGE_FRAGMENT_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->header_in, 5)); + EXPECT_SUCCESS(s2n_stuffer_copy(&conn->out, &conn->in, s2n_stuffer_data_available(&conn->out))); + + /* Trigger condition to check for protocol version */ + conn->actual_protocol_version_established = 1; + uint8_t content_type; + uint16_t fragment_length; + EXPECT_SUCCESS(s2n_record_header_parse(conn, &content_type, &fragment_length)); + + /* If record version on wire is TLS 1.3, check s2n_record_header_parse fails */ + EXPECT_SUCCESS(s2n_stuffer_reread(&conn->header_in)); + conn->header_in.blob.data[1] = 3; + conn->header_in.blob.data[2] = 4; + EXPECT_FAILURE_WITH_ERRNO(s2n_record_header_parse(conn, &content_type, &fragment_length), S2N_ERR_BAD_MESSAGE); + }; + + /* Test: ApplicationData MUST be encrypted */ + { + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + conn->actual_protocol_version = S2N_TLS13; + + /* Write fails with no secrets / cipher set */ + EXPECT_ERROR_WITH_ERRNO(s2n_record_write(conn, TLS_APPLICATION_DATA, &empty_blob), S2N_ERR_ENCRYPT); + + /* Read fails with no secrets / cipher set */ + uint8_t header_bytes[] = { TLS_APPLICATION_DATA, 0x03, 0x04, 0x00, 0x00 }; + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->header_in, header_bytes, sizeof(header_bytes))); + EXPECT_FAILURE_WITH_ERRNO(s2n_record_parse(conn), S2N_ERR_DECRYPT); + }; + + /* Test s2n_sslv2_record_header_parse fails when fragment_length < 3 */ + { + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + + uint8_t record_type = 0; + uint8_t protocol_version = 0; + uint16_t fragment_length = 0; + + /* First two bytes are the fragment length */ + uint8_t header_bytes[] = { 0x00, 0x00, 0x00, 0x00, 0x00 }; + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->header_in, header_bytes, sizeof(header_bytes))); + + EXPECT_FAILURE_WITH_ERRNO(s2n_sslv2_record_header_parse(conn, &record_type, &protocol_version, &fragment_length), S2N_ERR_SAFETY); + + /* Check the rest of the stuffer has not been read yet */ + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->header_in), 3); + }; + + EXPECT_SUCCESS(s2n_hmac_free(&check_mac)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + + END_TEST(); +} diff --git a/tests/unit/s2n_record_write_test.c b/tests/unit/s2n_record_write_test.c new file mode 100644 index 00000000000..7d78f21c0dd --- /dev/null +++ b/tests/unit/s2n_record_write_test.c @@ -0,0 +1,84 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_record.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_safety.h" + +int main(int argc, char *argv[]) +{ + BEGIN_TEST(); + + /* Test: Records sent before the ServerHello include a sane protocol version */ + { + const uint8_t expected_version = S2N_TLS10; + + /* Test: ClientHellos include a sane protocol version */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + + DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_SUCCESS(s2n_connection_set_send_io_stuffer(&out, client_conn)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + + uint8_t content_type = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&out, &content_type)); + EXPECT_EQUAL(content_type, TLS_HANDSHAKE); + + uint8_t version_high = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&out, &version_high)); + EXPECT_EQUAL(version_high, expected_version / 10); + + uint8_t version_low = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&out, &version_low)); + EXPECT_EQUAL(version_low, expected_version % 10); + } + + /* Test: Alerts include a sane protocol version */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_SUCCESS(s2n_connection_set_send_io_stuffer(&out, server_conn)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_SUCCESS(s2n_shutdown_send(server_conn, &blocked)); + + uint8_t content_type = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&out, &content_type)); + EXPECT_EQUAL(content_type, TLS_ALERT); + + uint8_t version_high = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&out, &version_high)); + EXPECT_EQUAL(version_high, expected_version / 10); + + uint8_t version_low = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&out, &version_low)); + EXPECT_EQUAL(version_low, expected_version % 10); + } + } + + END_TEST(); +} diff --git a/tests/unit/s2n_recv_test.c b/tests/unit/s2n_recv_test.c new file mode 100644 index 00000000000..aa3fbe4c9b1 --- /dev/null +++ b/tests/unit/s2n_recv_test.c @@ -0,0 +1,670 @@ +/* + * 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 "api/s2n.h" +#include "api/unstable/renegotiate.h" +#include "s2n_test.h" +#include "testlib/s2n_ktls_test_utils.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_random.h" + +bool s2n_custom_recv_fn_called = false; + +int s2n_expect_concurrent_error_recv_fn(void *io_context, uint8_t *buf, uint32_t len) +{ + struct s2n_connection *conn = (struct s2n_connection *) io_context; + s2n_custom_recv_fn_called = true; + + s2n_blocked_status blocked = 0; + ssize_t result = s2n_recv(conn, buf, len, &blocked); + EXPECT_FAILURE_WITH_ERRNO(result, S2N_ERR_REENTRANCY); + return result; +} + +static ssize_t s2n_test_ktls_recvmsg_cb(void *io_context, struct msghdr *msg) +{ + POSIX_ENSURE_REF(io_context); + return *(ssize_t *) io_context; +} + +static int s2n_test_reneg_req_cb(struct s2n_connection *conn, void *context, + s2n_renegotiate_response *response) +{ + POSIX_ENSURE_REF(context); + size_t *count = (size_t *) context; + (*count)++; + *response = S2N_RENEGOTIATE_IGNORE; + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key * chain_and_key, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(config)); + + /* s2n_peek */ + { + /* We do full handshakes and send with a real connection here instead of + * just calling s2n_connection_set_secrets because s2n_peek depends on details + * of how data is encrypted, and we don't want to make any incorrect assumptions. + */ + + /* Safety check */ + EXPECT_EQUAL(s2n_peek(NULL), 0); + + const uint8_t test_data[100] = "hello world"; + const size_t test_data_size = sizeof(test_data); + + /* s2n_peek reports available plaintext bytes */ + { + s2n_blocked_status blocked = 0; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Write some data */ + EXPECT_EQUAL(s2n_send(client_conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + + /* Initially, no data reported as available */ + EXPECT_EQUAL(s2n_peek(server_conn), 0); + + /* Read some, but not all, of the data written */ + uint8_t output[sizeof(test_data)] = { 0 }; + const size_t expected_peek_size = 10; + const size_t recv_size = test_data_size - expected_peek_size; + EXPECT_EQUAL(s2n_recv(server_conn, output, recv_size, &blocked), recv_size); + + /* After a partial read, some data reported as available */ + EXPECT_EQUAL(s2n_peek(server_conn), expected_peek_size); + + /* Read the rest of the data */ + EXPECT_EQUAL(s2n_recv(server_conn, output, expected_peek_size, &blocked), expected_peek_size); + + /* After the complete read, no data reported as available */ + EXPECT_EQUAL(s2n_peek(server_conn), 0); + }; + + /* s2n_peek doesn't report bytes belonging to partially read, still encrypted records */ + { + s2n_blocked_status blocked = 0; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* Use stuffers for IO so that we can trigger a block on a read */ + DEFER_CLEANUP(struct s2n_stuffer server_in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer server_out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_out, &server_in, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_in, &server_out, server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Write some data */ + EXPECT_EQUAL(s2n_send(client_conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + + /* Drop some of the data */ + EXPECT_SUCCESS(s2n_stuffer_wipe_n(&server_in, 10)); + + /* Try to read the data, but block */ + uint8_t output[sizeof(test_data)] = { 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, output, sizeof(test_data), &blocked), + S2N_ERR_IO_BLOCKED); + + /* conn->in contains data, but s2n_peek reports no data available */ + EXPECT_TRUE(s2n_stuffer_data_available(&server_conn->in)); + EXPECT_EQUAL(s2n_peek(server_conn), 0); + }; + + /* s2n_peek doesn't report bytes belonging to post-handshake messages */ + if (s2n_is_tls13_fully_supported()) { + s2n_blocked_status blocked = 0; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* Use stuffers for IO so that we can trigger a block on a read */ + DEFER_CLEANUP(struct s2n_stuffer server_in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer server_out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_out, &server_in, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_in, &server_out, server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Send a KeyUpdate message */ + s2n_atomic_flag_set(&client_conn->key_update_pending); + EXPECT_SUCCESS(s2n_key_update_send(client_conn, &blocked)); + EXPECT_FALSE(s2n_atomic_flag_test(&client_conn->key_update_pending)); + + /* Drop some of the data */ + EXPECT_SUCCESS(s2n_stuffer_wipe_n(&server_in, 10)); + + /* Try to read the KeyUpdate message, but block */ + uint8_t output[1] = { 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, output, sizeof(output), &blocked), + S2N_ERR_IO_BLOCKED); + + /* conn->in contains data, but s2n_peek reports no data available */ + EXPECT_TRUE(s2n_stuffer_data_available(&server_conn->in)); + EXPECT_EQUAL(s2n_peek(server_conn), 0); + }; + }; + + /* s2n_recv cannot be called concurrently */ + { + /* Setup connection */ + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + /* Setup bad recv callback */ + EXPECT_SUCCESS(s2n_connection_set_recv_cb(conn, s2n_expect_concurrent_error_recv_fn)); + EXPECT_SUCCESS(s2n_connection_set_recv_ctx(conn, (void *) conn)); + EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); + + uint8_t test_data[100] = { 0 }; + s2n_blocked_status blocked = 0; + s2n_custom_recv_fn_called = false; + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(conn, test_data, sizeof(test_data), &blocked), + S2N_ERR_IO); + EXPECT_TRUE(s2n_custom_recv_fn_called); + + /* Cleanup */ + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* s2n_config_set_recv_multi_record */ + { + const uint8_t test_data_size = 100; + DEFER_CLEANUP(struct s2n_blob test_data = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&test_data, test_data_size)); + + const size_t recv_size = test_data_size * 2; + DEFER_CLEANUP(struct s2n_blob output = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&output, recv_size)); + + { + s2n_blocked_status blocked = 0; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Write some data, in three records */ + for (size_t i = 0; i < 3; i++) { + EXPECT_EQUAL(s2n_send(client_conn, test_data.data, test_data.size, &blocked), test_data.size); + } + + /* Disable multi-record recv, set legacy behavior */ + EXPECT_SUCCESS(s2n_config_set_recv_multi_record(config, false)); + + EXPECT_EQUAL(s2n_recv(server_conn, output.data, recv_size, &blocked), test_data_size); + + /* Now enable multi record recv */ + EXPECT_SUCCESS(s2n_config_set_recv_multi_record(config, true)); + + /* So we should be able to read the remaining two records in a single call */ + EXPECT_EQUAL(s2n_recv(server_conn, output.data, recv_size, &blocked), recv_size); + } + } + + /* recv blocked status + * + * This test preserves the `blocked` parameter contract with various states of the connection + */ + { + const uint8_t test_data_size = 100; + const size_t record_count = 3; + DEFER_CLEANUP(struct s2n_blob test_data = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&test_data, test_data_size)); + + const size_t total_data_size = test_data_size * record_count; + DEFER_CLEANUP(struct s2n_blob output = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&output, total_data_size)); + + s2n_blocked_status blocked = 0; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + for (size_t multi_record = 0; multi_record <= 1; multi_record++) { + EXPECT_SUCCESS(s2n_config_set_recv_multi_record(config, multi_record)); + size_t max_recv_size = test_data_size; + + /* In multi-record, we can read all of the records in one go */ + if (multi_record) { + max_recv_size *= record_count; + } + + for (size_t read_size = 1; read_size <= total_data_size; read_size++) { + /* Write some data across multiple records */ + for (size_t send_count = 0; send_count < record_count; send_count++) { + EXPECT_EQUAL(s2n_send(client_conn, test_data.data, test_data.size, &blocked), test_data.size); + } + + /* Call `s2n_recv` multiple times with an empty buffer to make sure that's handled correctly */ + for (size_t empty_count = 0; empty_count < 10; empty_count++) { + EXPECT_EQUAL(s2n_recv(server_conn, output.data, 0, &blocked), 0); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + } + + size_t recv_bytes = 0; + while (recv_bytes < total_data_size) { + size_t expected_recv_size = MIN(MIN(read_size, total_data_size - recv_bytes), max_recv_size); + + /* Perform the actual recv call */ + ssize_t actual_recv_size = s2n_recv(server_conn, output.data, read_size, &blocked); + + if (multi_record) { + /* In multi-record mode we should always read the size we expect */ + EXPECT_EQUAL(actual_recv_size, expected_recv_size); + } else { + /* In single-record mode, we could potentially get a smaller read than a full record due to + * random record boundaries so we can only assert it's within the range we expect. */ + EXPECT_NOT_EQUAL(actual_recv_size, 0); + EXPECT_TRUE(actual_recv_size <= expected_recv_size); + } + + /* Keep track of the total amount of bytes read */ + recv_bytes += actual_recv_size; + + /* Due to the history of this API, some applications depend on the blocked status to know if + * the connection's `in` stuffer was completely cleared. This behavior needs to be preserved. + * + * Moving forward, applications should instead use `s2n_peek`, which accomplishes the same thing + * without conflating being blocked on reading from the OS socket vs blocked on the application's + * buffer size. + */ + if (s2n_peek(server_conn) == 0) { + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + } else { + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + } + } + + /* The final read should return blocked since we don't have any more data from the socket */ + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, output.data, read_size, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + } + } + + EXPECT_FAILURE_WITH_ERRNO(s2n_shutdown(client_conn, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + /* Call `s2n_recv` multiple times at the end of the stream after receiving a shutdown */ + for (size_t eos_count = 0; eos_count < 10; eos_count++) { + EXPECT_EQUAL(s2n_recv(server_conn, output.data, output.size, &blocked), 0); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + } + + EXPECT_SUCCESS(s2n_shutdown(server_conn, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_SUCCESS(s2n_shutdown(client_conn, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + }; + + /* Test with ktls */ + { + uint8_t test_data[100] = { 0 }; + struct s2n_blob test_data_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&test_data_blob, test_data, sizeof(test_data))); + EXPECT_OK(s2n_get_public_random_data(&test_data_blob)); + + const struct iovec test_iovec = { + .iov_base = test_data, + .iov_len = sizeof(test_data), + }; + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + /* Test: receive all requested application data */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; + + size_t written = 0; + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, + &test_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, sizeof(test_data)); + + uint8_t output[sizeof(test_data)] = { 0 }; + int read = s2n_recv(conn, output, sizeof(output), &blocked); + EXPECT_EQUAL(read, written); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_BYTEARRAY_EQUAL(output, test_data, read); + }; + + /* Test: receive partial application data */ + { + const size_t partial_size = sizeof(test_data) / 2; + struct iovec partial_iovec = test_iovec; + partial_iovec.iov_len = partial_size; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; + + size_t written = 0; + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, + &partial_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, partial_size); + + uint8_t output[sizeof(test_data)] = { 0 }; + int read = s2n_recv(conn, output, sizeof(output), &blocked); + EXPECT_EQUAL(read, written); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_BYTEARRAY_EQUAL(output, test_data, read); + }; + + /* Test: drain buffered application data */ + { + const size_t partial_size = sizeof(test_data) / 2; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; + + size_t written = 0; + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, + &test_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, sizeof(test_data)); + + /* The first read doesn't read all the available data */ + uint8_t output[sizeof(test_data)] = { 0 }; + int read = s2n_recv(conn, output, partial_size, &blocked); + EXPECT_EQUAL(read, partial_size); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + EXPECT_BYTEARRAY_EQUAL(output, test_data, partial_size); + EXPECT_EQUAL(ctx->recvmsg_invoked_count, 1); + + /* The second read drains the remaining data */ + const size_t remaining = sizeof(test_data) - partial_size; + read = s2n_recv(conn, output + read, remaining, &blocked); + EXPECT_EQUAL(read, remaining); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_BYTEARRAY_EQUAL(output, test_data, sizeof(test_data)); + EXPECT_EQUAL(ctx->recvmsg_invoked_count, 1); + }; + + /* Test: receive blocks */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + + uint8_t output[sizeof(test_data)] = { 0 }; + int read = s2n_recv(conn, output, sizeof(output), &blocked); + EXPECT_FAILURE_WITH_ERRNO(read, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + }; + + /* Test: receive indicates end-of-data */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + + ssize_t ret_val = 0; + EXPECT_OK(s2n_ktls_set_recvmsg_cb(conn, s2n_test_ktls_recvmsg_cb, &ret_val)); + + uint8_t output[10] = { 0 }; + int read = s2n_recv(conn, output, sizeof(output), &blocked); + EXPECT_FAILURE_WITH_ERRNO(read, S2N_ERR_CLOSED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + /* Error fatal but not blinded */ + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + EXPECT_EQUAL(s2n_connection_get_delay(conn), 0); + }; + + /* Test: receive alert */ + { + /* Use a specific alert -- if we just use random data, we might + * stumble into a close_notify or user_canceled. + */ + uint8_t alert_data[] = { + S2N_TLS_ALERT_LEVEL_FATAL, + S2N_TLS_ALERT_DECRYPT_ERROR, + }; + const struct iovec alert_iovec = { + .iov_base = alert_data, + .iov_len = sizeof(alert_data), + }; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; + + size_t written = 0; + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_ALERT, &alert_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, sizeof(alert_data)); + + uint8_t output[10] = { 0 }; + int read = s2n_recv(conn, output, sizeof(output), &blocked); + EXPECT_FAILURE_WITH_ERRNO(read, S2N_ERR_ALERT); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + /* Error fatal but not blinded */ + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + EXPECT_EQUAL(s2n_connection_get_delay(conn), 0); + }; + + /* Test: receive handshake message */ + { + DEFER_CLEANUP(struct s2n_config *reneg_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(reneg_config); + + size_t reneg_request_count = 0; + EXPECT_SUCCESS(s2n_config_set_renegotiate_request_cb(reneg_config, + s2n_test_reneg_req_cb, &reneg_request_count)); + + uint8_t hello_request[TLS_HANDSHAKE_HEADER_LENGTH] = { TLS_HELLO_REQUEST }; + const struct iovec hello_request_iovec = { + .iov_base = hello_request, + .iov_len = sizeof(hello_request), + }; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, reneg_config)); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + conn->secure_renegotiation = true; + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; + + size_t written = 0; + + /* Send the handshake message */ + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_HANDSHAKE, + &hello_request_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, sizeof(hello_request)); + + /* Also send some application data */ + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, + &test_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, sizeof(test_data)); + + /* Verify that we received the application data */ + uint8_t output[sizeof(test_data)] = { 0 }; + int read = s2n_recv(conn, output, sizeof(output), &blocked); + EXPECT_EQUAL(read, sizeof(test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_BYTEARRAY_EQUAL(output, test_data, read); + + /* Verify that we received and processed the handshake message */ + EXPECT_EQUAL(reneg_request_count, 1); + }; + + /* Test: Multirecord mode */ + { + DEFER_CLEANUP(struct s2n_config *multi_config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(multi_config); + EXPECT_SUCCESS(s2n_config_set_recv_multi_record(multi_config, true)); + + /* Test: receive all requested application data */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, multi_config)); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; + + /* Write a lot of very small records */ + struct iovec offset_iovec = { 0 }; + for (size_t offset = 0; offset < sizeof(test_data); offset++) { + offset_iovec.iov_base = test_data + offset; + offset_iovec.iov_len = 1; + + size_t written = 0; + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, + &offset_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, 1); + } + + /* Receive all the data from the many small records */ + uint8_t output[sizeof(test_data)] = { 0 }; + int read = s2n_recv(conn, output, sizeof(output), &blocked); + EXPECT_EQUAL(read, sizeof(test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_BYTEARRAY_EQUAL(output, test_data, sizeof(test_data)); + }; + + /* Test: receive partial application data */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, multi_config)); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_RECV); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(conn, conn, &pair)); + struct s2n_test_ktls_io_stuffer *ctx = &pair.client_in; + + /* Write a lot of very small records, but don't write the full + * expected test data size. */ + const size_t partial_size = sizeof(test_data) / 2; + struct iovec offset_iovec = { 0 }; + for (size_t offset = 0; offset < partial_size; offset++) { + offset_iovec.iov_base = test_data + offset; + offset_iovec.iov_len = 1; + + size_t written = 0; + EXPECT_OK(s2n_ktls_sendmsg(ctx, TLS_APPLICATION_DATA, + &offset_iovec, 1, &blocked, &written)); + EXPECT_EQUAL(written, 1); + } + + /* Receive the partial data */ + uint8_t output[sizeof(test_data)] = { 0 }; + int read = s2n_recv(conn, output, sizeof(output), &blocked); + EXPECT_EQUAL(read, partial_size); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_BYTEARRAY_EQUAL(output, test_data, partial_size); + }; + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_release_non_empty_buffers_test.c b/tests/unit/s2n_release_non_empty_buffers_test.c new file mode 100644 index 00000000000..c2a20dcc74f --- /dev/null +++ b/tests/unit/s2n_release_non_empty_buffers_test.c @@ -0,0 +1,203 @@ +/* + * 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 +#include +#include +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "utils/s2n_random.h" + +#define MAX_BUF_SIZE 10000 + +static const uint8_t buf_to_send[1023] = { 27 }; + +int mock_client(struct s2n_test_io_pair *io_pair) +{ + struct s2n_connection *conn; + struct s2n_config *client_config; + s2n_blocked_status blocked; + int result = 0; + + conn = s2n_connection_new(S2N_CLIENT); + client_config = s2n_config_new(); + s2n_config_disable_x509_verification(client_config); + s2n_connection_set_config(conn, client_config); + + /* Unlike the server, the client just passes ownership of I/O to s2n */ + s2n_connection_set_io_pair(conn, io_pair); + + result = s2n_negotiate(conn, &blocked); + if (result < 0) { + _exit(1); + } + + if (s2n_send(conn, buf_to_send, sizeof(buf_to_send), &blocked) != sizeof(buf_to_send)) { + _exit(2); + } + + s2n_shutdown(conn, &blocked); + s2n_connection_free(conn); + s2n_config_free(client_config); + s2n_cleanup(); + + exit(0); +} + +/** + * This test ensures that we don't allow releasing connection buffers if they contain part + * of the unprocessed record, avoiding connection corruption. + */ +int main(int argc, char **argv) +{ + s2n_blocked_status blocked; + int status; + pid_t pid; + char *cert_chain_pem; + char *private_key_pem; + uint8_t buf[sizeof(buf_to_send)]; + uint32_t n = 0; + ssize_t ret = 0; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Run the client */ + mock_client(&io_pair); + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + DEFER_CLEANUP(struct s2n_stuffer in, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer out, s2n_stuffer_free); + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Make pipes non-blocking */ + EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.server)); + EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.server)); + + /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, conn)); + + /* Negotiate the handshake. */ + do { + ret = s2n_negotiate(conn, &blocked); + EXPECT_TRUE(ret == 0 || (blocked && (errno == EAGAIN || errno == EWOULDBLOCK))); + + /* check to see if we need to copy more over from the pipes to the buffers + * to continue the handshake */ + s2n_stuffer_recv_from_fd(&in, io_pair.server, MAX_BUF_SIZE, NULL); + s2n_stuffer_send_to_fd(&out, io_pair.server, s2n_stuffer_data_available(&out), NULL); + } while (blocked); + + /* Receive only 100 bytes of the record and try to call s2n_recv */ + while (n < 100) { + ret = s2n_stuffer_recv_from_fd(&in, io_pair.server, 100 - n, &n); + + if (errno == EAGAIN || errno == EWOULDBLOCK) { + continue; + } else { + POSIX_GUARD(ret); + } + } + + /* s2n_recv should fail as we received only part of the record */ + EXPECT_FAILURE(s2n_recv(conn, buf, sizeof(buf), &blocked)); + EXPECT_TRUE(blocked == S2N_BLOCKED_ON_READ); + + /* Now try to release the buffers and expect failure as buffers are not empty */ + EXPECT_FAILURE(s2n_connection_release_buffers(conn)); + + /* Read the rest of the buffer and expect s2n_recv to succeed */ + do { + ret = s2n_stuffer_recv_from_fd(&in, io_pair.server, MAX_BUF_SIZE, NULL); + + if (errno == EAGAIN || errno == EWOULDBLOCK) { + continue; + } else { + POSIX_GUARD(ret); + } + + ret = s2n_recv(conn, buf, sizeof(buf), &blocked); + } while (ret < 0 && s2n_error_get_type(s2n_errno) == S2N_ERR_T_BLOCKED + && blocked == S2N_BLOCKED_ON_READ); + + /* Expect that we read the data client sent us */ + EXPECT_TRUE(ret == sizeof(buf_to_send)); + EXPECT_TRUE(memcmp(buf, buf_to_send, ret) == 0); + + /* Since full record was processed, we should be able to release buffers */ + EXPECT_SUCCESS(s2n_connection_release_buffers(conn)); + + /* Shutdown after negotiating */ + uint8_t server_shutdown = 0; + do { + ret = s2n_shutdown(conn, &blocked); + EXPECT_TRUE(ret == 0 || (blocked && (errno == EAGAIN || errno == EWOULDBLOCK))); + if (ret == 0) { + server_shutdown = 1; + } + + s2n_stuffer_recv_from_fd(&in, io_pair.server, MAX_BUF_SIZE, NULL); + s2n_stuffer_send_to_fd(&out, io_pair.server, s2n_stuffer_data_available(&out), NULL); + } while (!server_shutdown); + + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + + /* Clean up */ + free(cert_chain_pem); + free(private_key_pem); + + s2n_cleanup(); + + END_TEST(); + + return 0; +} diff --git a/tests/unit/s2n_renegotiate_io_test.c b/tests/unit/s2n_renegotiate_io_test.c new file mode 100644 index 00000000000..ab11fd0ae39 --- /dev/null +++ b/tests/unit/s2n_renegotiate_io_test.c @@ -0,0 +1,501 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_renegotiate.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_safety.h" +#include "utils/s2n_socket.h" + +/* We use bitflags to test every combination + * of where application data could appear in renegotiation. + * + * So app_data_case==6 would mean "110", so we would be testing + * a renegotiation handshake that received application data + * before and after the server hello. + */ +enum S2N_TEST_APP_DATA_CASES { + S2N_TEST_APP_DATA_BEFORE_RENEG = 1, + S2N_TEST_APP_DATA_BEFORE_SERVER_HELLO = 2, + S2N_TEST_APP_DATA_AFTER_SERVER_HELLO = 4, + S2N_TEST_MAX_TEST_CASES = 8, +}; +#define S2N_TEST_APP_DATA_LEN 10 + +static S2N_RESULT s2n_renegotiate_test_server_and_client(struct s2n_connection *server_conn, struct s2n_connection *client_conn) +{ + ssize_t app_data_read = 0; + uint8_t recv_buffer[1] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + while (s2n_renegotiate(client_conn, recv_buffer, sizeof(recv_buffer), &app_data_read, &blocked) != S2N_SUCCESS) { + RESULT_ENSURE_EQ(s2n_errno, S2N_ERR_IO_BLOCKED); + RESULT_ENSURE_EQ(blocked, S2N_BLOCKED_ON_READ); + RESULT_ENSURE_EQ(app_data_read, 0); + s2n_negotiate(server_conn, &blocked); + } + return S2N_RESULT_OK; +} + +int main(int argc, char *argv[]) +{ + BEGIN_TEST(); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + + uint8_t app_data[] = "test application data"; + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + /* Test basic renegotiation */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* First handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); + + /* Second handshake */ + EXPECT_OK(s2n_renegotiate_test_server_and_client(server_conn, client_conn)); + }; + + /* Test that s2n_renegotiate can handle ApplicationData */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + ssize_t app_data_read = 0; + uint8_t recv_buffer[sizeof(app_data)] = { 0 }; + + /* First handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); + + /* Server sends ApplicationData */ + EXPECT_EQUAL(s2n_send(server_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + + /* Client receives ApplicationData */ + EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate(client_conn, recv_buffer, sizeof(recv_buffer), &app_data_read, &blocked), + S2N_ERR_APP_DATA_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_DATA); + EXPECT_EQUAL(app_data_read, sizeof(app_data)); + EXPECT_BYTEARRAY_EQUAL(recv_buffer, app_data, sizeof(app_data)); + + /* Client also made progress on the handshake */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + + /* Finish renegotiation */ + EXPECT_OK(s2n_renegotiate_test_server_and_client(server_conn, client_conn)); + }; + + /* Test that s2n_renegotiate can handle an ApplicationData fragment larger than the receive buffer */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + ssize_t app_data_read = 0; + uint8_t recv_buffer[sizeof(app_data)] = { 0 }; + + /* First handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); + + /* Server sends ApplicationData */ + EXPECT_EQUAL(s2n_send(server_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + + /* Client receives first part of ApplicationData */ + const size_t first_read_len = 2; + EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate(client_conn, recv_buffer, first_read_len, &app_data_read, &blocked), + S2N_ERR_APP_DATA_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_DATA); + EXPECT_EQUAL(app_data_read, first_read_len); + EXPECT_BYTEARRAY_EQUAL(recv_buffer, app_data, first_read_len); + + /* Client also made progress on the handshake */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + + /* Client receives second part of ApplicationData */ + EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate(client_conn, recv_buffer + first_read_len, + sizeof(app_data) - first_read_len, &app_data_read, &blocked), + S2N_ERR_APP_DATA_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_DATA); + EXPECT_EQUAL(app_data_read, sizeof(app_data) - first_read_len); + EXPECT_BYTEARRAY_EQUAL(recv_buffer, app_data, sizeof(app_data)); + + /* Client waits for more data */ + for (size_t i = 0; i < 10; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate(client_conn, recv_buffer, sizeof(recv_buffer), + &app_data_read, &blocked), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + EXPECT_EQUAL(app_data_read, 0); + } + + /* Finish renegotiation */ + EXPECT_OK(s2n_renegotiate_test_server_and_client(server_conn, client_conn)); + }; + + /* Test that s2n_renegotiate can handle multiple ApplicationData records */ + { + const size_t app_data_record_count = 10; + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + ssize_t app_data_read = 0; + uint8_t recv_buffer[sizeof(app_data)] = { 0 }; + + /* First handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); + + /* Server sends ApplicationData */ + for (size_t i = 0; i < app_data_record_count; i++) { + EXPECT_EQUAL(s2n_send(server_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + } + + /* Client receives ApplicationData */ + for (size_t i = 0; i < app_data_record_count; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate(client_conn, recv_buffer, sizeof(recv_buffer), + &app_data_read, &blocked), + S2N_ERR_APP_DATA_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_DATA); + EXPECT_EQUAL(app_data_read, sizeof(app_data)); + EXPECT_BYTEARRAY_EQUAL(recv_buffer, app_data, sizeof(app_data)); + memset(recv_buffer, 0, sizeof(recv_buffer)); + } + + /* Client also made progress on the handshake */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + + /* Finish renegotiation */ + EXPECT_OK(s2n_renegotiate_test_server_and_client(server_conn, client_conn)); + }; + + /* Test that s2n_renegotiate rejects ApplicationData after receiving the ServerHello */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + ssize_t app_data_read = 0; + uint8_t recv_buffer[sizeof(app_data)] = { 0 }; + + /* First handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); + + /* Client writes ClientHello */ + EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate(client_conn, recv_buffer, sizeof(recv_buffer), &app_data_read, &blocked), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + /* Server writes ServerHello */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_NOT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_HELLO); + EXPECT_TRUE(IS_NEGOTIATED(server_conn)); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + /* Server sends ApplicationData */ + EXPECT_EQUAL(s2n_send(server_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + + /* Client rejects ApplicationData */ + EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate(client_conn, recv_buffer, sizeof(recv_buffer), &app_data_read, &blocked), + S2N_ERR_BAD_MESSAGE); + EXPECT_NOT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_HELLO); + EXPECT_TRUE(IS_NEGOTIATED(server_conn)); + }; + + /* Test that s2n_renegotiate rejects incorrect handshake messages */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + ssize_t app_data_read = 0; + uint8_t recv_buffer[sizeof(app_data)] = { 0 }; + + /* First handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); + + /* Client writes ClientHello */ + EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate(client_conn, recv_buffer, sizeof(recv_buffer), &app_data_read, &blocked), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + /* Server reads ClientHello */ + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, SERVER_HELLO)); + + /* Server writes wrong message. + * We use "SERVER_HELLO_DONE" because it's an empty message and requires no setup. + */ + server_conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; + server_conn->handshake.message_number = 3; + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_HELLO_DONE); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &blocked), S2N_ERR_IO_BLOCKED); + + /* Client rejects unexpected message */ + EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate(client_conn, recv_buffer, sizeof(recv_buffer), &app_data_read, &blocked), + S2N_ERR_BAD_MESSAGE); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + }; + + /* Test that s2n_renegotiate handles handshake IO blocked on send */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + uint8_t recv_buffer[1] = { 0 }; + + /* First handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); + + /* Block send IO with empty buffer */ + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_connection_set_send_io_stuffer(&output, client_conn)); + + /* Verify send is blocked on write */ + ssize_t app_data_read = 0; + for (size_t i = 0; i < 10; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate(client_conn, recv_buffer, sizeof(recv_buffer), &app_data_read, &blocked), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(app_data_read, 0); + } + + /* Unblock send IO by allocating buffer */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + + /* Verify send blocks on read */ + EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate(client_conn, recv_buffer, sizeof(recv_buffer), &app_data_read, &blocked), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + EXPECT_EQUAL(app_data_read, 0); + }; + + /* Test timing of application data during the renegotiation handshake. + * + * We want to ensure that s2n_renegotiate can handle ApplicationData at any time. + */ + { + uint8_t messages[][S2N_TEST_APP_DATA_LEN] = { + "one", "two", "three" + }; + EXPECT_EQUAL(1 << s2n_array_len(messages), S2N_TEST_MAX_TEST_CASES); + + /* Sanity checks + * We want to ensure all interesting cases are hit at least once. + */ + bool reneg_ch_had_app_data = false; + bool reneg_ch_had_no_app_data = false; + bool reneg_extra_app_data = false; + bool reneg_sh_had_app_data = false; + bool reneg_sh_had_no_app_data = false; + + for (size_t app_data_case = 0; app_data_case < S2N_TEST_MAX_TEST_CASES; app_data_case++) { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + ssize_t app_data_read = 0; + uint8_t recv_buffer[S2N_TEST_APP_DATA_LEN * 2] = { 0 }; + size_t send_i = 0, recv_i = 0; + + /* First handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Before Renegotiation */ + { + if (app_data_case & S2N_TEST_APP_DATA_BEFORE_RENEG) { + EXPECT_EQUAL(s2n_send(server_conn, messages[send_i], S2N_TEST_APP_DATA_LEN, &blocked), S2N_TEST_APP_DATA_LEN); + send_i++; + } + + EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + }; + + /* Client: ClientHello sent */ + { + int r = s2n_renegotiate(client_conn, recv_buffer, sizeof(recv_buffer), &app_data_read, &blocked); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + /* Expect to have received any outstanding application data */ + if (send_i > 0 && recv_i < send_i) { + EXPECT_FAILURE_WITH_ERRNO(r, S2N_ERR_APP_DATA_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_DATA); + EXPECT_EQUAL(app_data_read, S2N_TEST_APP_DATA_LEN); + EXPECT_BYTEARRAY_EQUAL(recv_buffer, messages[recv_i], S2N_TEST_APP_DATA_LEN); + recv_i++; + reneg_ch_had_app_data = true; + } else { + EXPECT_FAILURE_WITH_ERRNO(r, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + EXPECT_EQUAL(app_data_read, 0); + reneg_ch_had_no_app_data = true; + } + }; + + /* Server: ClientHello recv, ServerHello sent */ + { + if (app_data_case & S2N_TEST_APP_DATA_BEFORE_SERVER_HELLO) { + EXPECT_EQUAL(s2n_send(server_conn, messages[send_i], S2N_TEST_APP_DATA_LEN, &blocked), S2N_TEST_APP_DATA_LEN); + send_i++; + } + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + EXPECT_NOT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_HELLO); + EXPECT_TRUE(IS_NEGOTIATED(server_conn)); + + /* All application data sent after the ServerHello is invalid. + * We specifically don't increment sent_i, because we won't use this data. + */ + if (app_data_case & S2N_TEST_APP_DATA_AFTER_SERVER_HELLO) { + EXPECT_EQUAL(s2n_send(server_conn, messages[send_i], S2N_TEST_APP_DATA_LEN, &blocked), S2N_TEST_APP_DATA_LEN); + } + }; + + /* Client: ApplicationData recv */ + while (send_i > 0 && recv_i < send_i) { + EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate(client_conn, recv_buffer, sizeof(recv_buffer), &app_data_read, &blocked), + S2N_ERR_APP_DATA_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + EXPECT_EQUAL(app_data_read, S2N_TEST_APP_DATA_LEN); + EXPECT_BYTEARRAY_EQUAL(recv_buffer, messages[recv_i], S2N_TEST_APP_DATA_LEN); + recv_i++; + reneg_extra_app_data = true; + } + + /* Client: ServerHello recv */ + { + int r = s2n_renegotiate(client_conn, recv_buffer, sizeof(recv_buffer), &app_data_read, &blocked); + + /* No application data is allowed after the ServerHello */ + if (app_data_case & S2N_TEST_APP_DATA_AFTER_SERVER_HELLO) { + EXPECT_FAILURE_WITH_ERRNO(r, S2N_ERR_BAD_MESSAGE); + reneg_sh_had_app_data = true; + continue; + } + + EXPECT_FAILURE_WITH_ERRNO(r, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + EXPECT_NOT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + EXPECT_TRUE(IS_NEGOTIATED(client_conn)); + reneg_sh_had_no_app_data = true; + }; + + /* Handshake completes */ + EXPECT_OK(s2n_renegotiate_test_server_and_client(server_conn, client_conn)); + } + + EXPECT_TRUE(reneg_ch_had_app_data); + EXPECT_TRUE(reneg_ch_had_no_app_data); + EXPECT_TRUE(reneg_extra_app_data); + EXPECT_TRUE(reneg_sh_had_app_data); + EXPECT_TRUE(reneg_sh_had_no_app_data); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_renegotiate_test.c b/tests/unit/s2n_renegotiate_test.c new file mode 100644 index 00000000000..d599b1e949f --- /dev/null +++ b/tests/unit/s2n_renegotiate_test.c @@ -0,0 +1,585 @@ +/* + * 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 "tls/s2n_renegotiate.h" + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_record.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_safety.h" +#include "utils/s2n_socket.h" + +struct s2n_reneg_test_case { + uint8_t protocol_version; + struct s2n_cipher_suite *cipher_suite; + uint8_t max_frag_code; +}; + +const struct s2n_reneg_test_case dhe_test_cases[] = { + { + .protocol_version = S2N_SSLv3, + .cipher_suite = &s2n_dhe_rsa_with_3des_ede_cbc_sha, + .max_frag_code = 0, + }, + { + .protocol_version = S2N_TLS10, + .cipher_suite = &s2n_dhe_rsa_with_aes_128_cbc_sha, + .max_frag_code = S2N_TLS_MAX_FRAG_LEN_512, + }, + { + .protocol_version = S2N_TLS11, + .cipher_suite = &s2n_dhe_rsa_with_aes_256_cbc_sha, + .max_frag_code = S2N_TLS_MAX_FRAG_LEN_1024, + }, + { + .protocol_version = S2N_TLS12, + .cipher_suite = &s2n_dhe_rsa_with_aes_128_cbc_sha256, + .max_frag_code = 0, + }, + { + .protocol_version = S2N_TLS12, + .cipher_suite = &s2n_dhe_rsa_with_aes_256_gcm_sha384, + .max_frag_code = S2N_TLS_MAX_FRAG_LEN_2048, + }, + { + .protocol_version = S2N_TLS12, + .cipher_suite = &s2n_dhe_rsa_with_chacha20_poly1305_sha256, + .max_frag_code = S2N_TLS_MAX_FRAG_LEN_4096, + }, +}; + +int main(int argc, char *argv[]) +{ + BEGIN_TEST(); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + char dh_params[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dh_params, sizeof(dh_params))); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + + uint8_t app_data[] = "smaller hello world"; + uint8_t large_app_data[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = "hello world and a lot of zeroes"; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + /* Test s2n_renegotiate_wipe */ + { + /* Default IO unaffected by wipe */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + /* "io_pair" just uses file descriptors and the default io callbacks */ + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + EXPECT_EQUAL(client_conn->send, s2n_socket_write); + EXPECT_TRUE(client_conn->managed_send_io); + EXPECT_EQUAL(client_conn->recv, s2n_socket_read); + EXPECT_TRUE(client_conn->managed_recv_io); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + + uint8_t recv_buffer[sizeof(app_data)] = { 0 }; + EXPECT_EQUAL(s2n_send(server_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_EQUAL(s2n_recv(client_conn, recv_buffer, sizeof(recv_buffer), &blocked), sizeof(app_data)); + EXPECT_BYTEARRAY_EQUAL(recv_buffer, app_data, sizeof(app_data)); + }; + + /* Custom IO callbacks unaffected by wipe */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + /* "io_stuffers" use custom IO callbacks written for tests */ + DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&out, &in, server_conn)); + EXPECT_NOT_EQUAL(client_conn->send, s2n_socket_write); + EXPECT_FALSE(client_conn->managed_send_io); + EXPECT_NOT_EQUAL(client_conn->recv, s2n_socket_read); + EXPECT_FALSE(client_conn->managed_recv_io); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + + uint8_t recv_buffer[sizeof(app_data)] = { 0 }; + EXPECT_EQUAL(s2n_send(server_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_EQUAL(s2n_recv(client_conn, recv_buffer, sizeof(recv_buffer), &blocked), sizeof(app_data)); + EXPECT_BYTEARRAY_EQUAL(recv_buffer, app_data, sizeof(app_data)); + }; + + /* Fragment size unaffected by wipe */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&out, &in, server_conn)); + + EXPECT_SUCCESS(s2n_connection_prefer_low_latency(client_conn)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_EQUAL(s2n_send(client_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + size_t original_out_size = s2n_stuffer_data_available(&out); + EXPECT_SUCCESS(s2n_stuffer_wipe(&out)); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + + EXPECT_EQUAL(s2n_send(client_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + size_t wiped_out_size = s2n_stuffer_data_available(&out); + EXPECT_EQUAL(original_out_size, wiped_out_size); + }; + + /* Forced very small fragment size unaffected by wipe */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&out, &in, server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + const size_t small_frag_len = S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE; + client_conn->max_outgoing_fragment_length = small_frag_len; + + EXPECT_EQUAL(s2n_send(client_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + size_t original_out_size = s2n_stuffer_data_available(&out); + EXPECT_SUCCESS(s2n_stuffer_wipe(&out)); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + EXPECT_EQUAL(client_conn->max_outgoing_fragment_length, small_frag_len); + + EXPECT_EQUAL(s2n_send(client_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + size_t wiped_out_size = s2n_stuffer_data_available(&out); + EXPECT_EQUAL(original_out_size, wiped_out_size); + }; + + /* Handshake succeeds after wipe */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + }; + + /* Handshake with added client auth succeeds after wipe */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_NONE)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_NONE)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_FALSE(IS_CLIENT_AUTH_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_CLIENT_AUTH_HANDSHAKE(server_conn)); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); + + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(IS_CLIENT_AUTH_HANDSHAKE(client_conn)); + EXPECT_FALSE(IS_CLIENT_AUTH_NO_CERT(client_conn)); + EXPECT_TRUE(IS_CLIENT_AUTH_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_CLIENT_AUTH_NO_CERT(server_conn)); + }; + + /* Handshake with different fragment length succeeds after wipe */ + { + DEFER_CLEANUP(struct s2n_config *small_frag_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(small_frag_config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(small_frag_config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(small_frag_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(small_frag_config, "default")); + EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(small_frag_config)); + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(small_frag_config, S2N_TLS_MAX_FRAG_LEN_512)); + + DEFER_CLEANUP(struct s2n_config *larger_frag_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(larger_frag_config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(larger_frag_config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(larger_frag_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(larger_frag_config, "default")); + EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(larger_frag_config)); + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(larger_frag_config, S2N_TLS_MAX_FRAG_LEN_4096)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, larger_frag_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, larger_frag_config)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, small_frag_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, small_frag_config)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, larger_frag_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, larger_frag_config)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + }; + + /* renegotiation_info is non-empty after wipe */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the renegotiation_info was empty / missing */ + ssize_t renegotiation_info_len = s2n_client_hello_get_extension_length(&server_conn->client_hello, + S2N_EXTENSION_RENEGOTIATION_INFO); + EXPECT_EQUAL(renegotiation_info_len, 0); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + EXPECT_TRUE(client_conn->handshake.finished_len > 0); + EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the renegotiation_info was not empty / missing */ + renegotiation_info_len = s2n_client_hello_get_extension_length(&server_conn->client_hello, + S2N_EXTENSION_RENEGOTIATION_INFO); + EXPECT_TRUE(renegotiation_info_len > sizeof(uint8_t)); + }; + + /* Wipe of insecure connection not allowed */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + client_conn->actual_protocol_version = S2N_TLS12; + + EXPECT_FALSE(client_conn->secure_renegotiation); + EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate_wipe(client_conn), S2N_ERR_NO_RENEGOTIATION); + client_conn->secure_renegotiation = true; + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + }; + + /* Wipe of TLS1.3 connection not allowed */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + client_conn->secure_renegotiation = true; + + EXPECT_TRUE(client_conn->actual_protocol_version > S2N_TLS12); + EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate_wipe(client_conn), S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + client_conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + }; + + /* Wipe mid-write not allowed */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&out, &in, server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Initiate a partial send */ + uint16_t partial_send_len = client_conn->max_outgoing_fragment_length / 2; + DEFER_CLEANUP(struct s2n_stuffer small_out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_alloc(&small_out, partial_send_len)); + EXPECT_SUCCESS(s2n_connection_set_send_io_stuffer(&small_out, client_conn)); + EXPECT_FAILURE_WITH_ERRNO(s2n_send(client_conn, large_app_data, sizeof(large_app_data), &blocked), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + + EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate_wipe(client_conn), S2N_ERR_INVALID_STATE); + + /* Finish the send */ + EXPECT_SUCCESS(s2n_connection_set_send_io_stuffer(&out, client_conn)); + EXPECT_EQUAL(s2n_send(client_conn, large_app_data, sizeof(large_app_data), &blocked), sizeof(large_app_data)); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + }; + + /* Wipe mid-read not allowed */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_stuffer in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&out, &in, server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Initiate a partial recv */ + uint16_t partial_recv_len = sizeof(app_data) / 2; + uint8_t recv_buffer[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH] = { 0 }; + EXPECT_EQUAL(s2n_send(server_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_EQUAL(s2n_recv(client_conn, recv_buffer, partial_recv_len, &blocked), partial_recv_len); + EXPECT_BYTEARRAY_EQUAL(app_data, recv_buffer, partial_recv_len); + EXPECT_TRUE(s2n_peek(client_conn) > 0); + + EXPECT_FAILURE_WITH_ERRNO(s2n_renegotiate_wipe(client_conn), S2N_ERR_INVALID_STATE); + + /* Finish the recv */ + size_t remaining_recv_len = sizeof(app_data) - partial_recv_len; + EXPECT_EQUAL(s2n_recv(client_conn, recv_buffer, remaining_recv_len, &blocked), remaining_recv_len); + EXPECT_BYTEARRAY_EQUAL(app_data + partial_recv_len, recv_buffer, remaining_recv_len); + EXPECT_EQUAL(s2n_peek(client_conn), 0); + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + }; + }; + + /* Test the basic renegotiation mechanism with a variety of connection parameters. + * A client should always be able to receive and negotiate after wiping a connection for renegotiation. + */ + { + /* Setup a security policy that only contains one cipher */ + struct s2n_cipher_preferences one_cipher_preference = { .count = 1, .suites = NULL }; + struct s2n_security_policy one_cipher_policy = security_policy_test_all; + one_cipher_policy.cipher_preferences = &one_cipher_preference; + + /* This config can only be used for servers, because currently only servers can have multiple certs */ + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, ecdsa_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(server_config, dh_params)); + EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(server_config)); + server_config->security_policy = &one_cipher_policy; + + /* Setting the max fragment length will require modifying the client config */ + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + + /* The oldest version s2n-tls supports is SSLv3. + * However, SSLv3 requires MD5 for its PRF. + */ + uint8_t oldest_tested_version = S2N_SSLv3; + if (!s2n_hash_is_available(S2N_HASH_MD5)) { + oldest_tested_version = S2N_TLS10; + } + + struct s2n_reneg_test_case test_cases[2000] = { 0 }; + size_t test_cases_count = 0; + + /* FFDHE is very, VERY slow. + * To avoid this test taking multiple minutes, + * we choose a limited number of dhe test cases. + */ + for (size_t i = 0; i < s2n_array_len(dhe_test_cases); i++) { + if (!dhe_test_cases[i].cipher_suite->available) { + continue; + } + if (dhe_test_cases[i].protocol_version < oldest_tested_version) { + continue; + } + test_cases[test_cases_count] = dhe_test_cases[i]; + test_cases_count++; + EXPECT_TRUE(test_cases_count < s2n_array_len(test_cases)); + } + EXPECT_TRUE(test_cases_count > 0); + + const struct s2n_cipher_preferences *ciphers = security_policy_test_all.cipher_preferences; + for (uint8_t version = oldest_tested_version; version < S2N_TLS13; version++) { + for (size_t cipher_i = 0; cipher_i < ciphers->count; cipher_i++) { + struct s2n_cipher_suite *cipher = ciphers->suites[cipher_i]; + + if (!cipher->available) { + continue; + } + + if (version < cipher->minimum_required_tls_version) { + continue; + } + + if (cipher->key_exchange_alg == &s2n_dhe) { + /* See dhe_test_cases */ + continue; + } + + for (size_t max_frag_i = 0; max_frag_i < s2n_array_len(mfl_code_to_length); max_frag_i++) { + test_cases[test_cases_count] = (struct s2n_reneg_test_case){ + .protocol_version = version, + .cipher_suite = ciphers->suites[cipher_i], + .max_frag_code = max_frag_i, + }; + test_cases_count++; + EXPECT_TRUE(test_cases_count < s2n_array_len(test_cases)); + } + } + } + + for (size_t i = 0; i < test_cases_count; i++) { + uint8_t recv_buffer[sizeof(app_data)] = { 0 }; + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + /* Setup test case */ + server_conn->server_protocol_version = test_cases[i].protocol_version; + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(client_config, test_cases[i].max_frag_code)); + one_cipher_preference.suites = &test_cases[i].cipher_suite; + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify test case setup */ + EXPECT_EQUAL(client_conn->actual_protocol_version, test_cases[i].protocol_version); + EXPECT_EQUAL(client_conn->max_outgoing_fragment_length, mfl_code_to_length[test_cases[i].max_frag_code]); + if (test_cases[i].protocol_version > S2N_SSLv3) { + EXPECT_EQUAL(client_conn->secure->cipher_suite, test_cases[i].cipher_suite); + } else { + EXPECT_EQUAL(client_conn->secure->cipher_suite, test_cases[i].cipher_suite->sslv3_cipher_suite); + } + + EXPECT_SUCCESS(s2n_renegotiate_wipe(client_conn)); + + /* Test that the client can still receive application data */ + EXPECT_EQUAL(s2n_send(server_conn, app_data, sizeof(app_data), &blocked), sizeof(app_data)); + EXPECT_EQUAL(s2n_recv(client_conn, recv_buffer, sizeof(recv_buffer), &blocked), sizeof(app_data)); + EXPECT_BYTEARRAY_EQUAL(recv_buffer, app_data, sizeof(app_data)); + + /* Test that a second handshake can occur. */ + EXPECT_SUCCESS(s2n_renegotiate_wipe(server_conn)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + } + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_resume_test.c b/tests/unit/s2n_resume_test.c new file mode 100644 index 00000000000..eec6c211d75 --- /dev/null +++ b/tests/unit/s2n_resume_test.c @@ -0,0 +1,1843 @@ +/* + * 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 "tls/s2n_resume.h" + +#include "s2n_test.h" +#include "tests/testlib/s2n_testlib.h" +#include "tls/s2n_tls.h" +/* To test static function */ +#include "tls/s2n_resume.c" +#include "utils/s2n_safety.h" + +#define TICKET_ISSUE_TIME_BYTES 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07 +#define TICKET_AGE_ADD_BYTES 0x01, 0x01, 0x01, 0x01 +#define TICKET_AGE_ADD 16843009 +#define SECRET_LEN 0x02 +#define SECRET 0x03, 0x04 +#define KEYING_MATERIAL_EXPIRATION_BYTES 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08 +#define EMPTY_EARLY_DATA_SIZE 0x00, 0x00, 0x00, 0x00 +#define CLIENT_TICKET 0x10, 0x10 + +#define NONEMPTY_EARLY_DATA_SIZE 0x12 +#define APP_PROTOCOL_LEN 0x02 +#define APP_PROTOCOL 0x05, 0x06 +#define EARLY_DATA_CONTEXT_LEN 0x03 +#define EARLY_DATA_CONTEXT 0x07, 0x08, 0x09 + +#define SIZE_OF_MAX_EARLY_DATA_SIZE sizeof(uint32_t) +#define SIZE_OF_KEYING_EXPIRATION sizeof(uint64_t) + +#define S2N_TLS12_STATE_SIZE_IN_BYTES_WITHOUT_EMS S2N_TLS12_STATE_SIZE_IN_BYTES - 1 + +#define SECONDS_TO_NANOS(seconds) ((seconds) * (uint64_t) ONE_SEC_IN_NANOS) + +const uint64_t ticket_issue_time = 283686952306183; +const uint64_t keying_material_expiration = 283686952306184; + +static int s2n_test_session_ticket_callback(struct s2n_connection *conn, void *ctx, struct s2n_session_ticket *ticket) +{ + return S2N_SUCCESS; +} + +static int mock_time(void *data, uint64_t *nanoseconds) +{ + *nanoseconds = ticket_issue_time; + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Two random secrets of different sizes */ + S2N_BLOB_FROM_HEX(test_master_secret, + "ee85dd54781bd4d8a100589a9fe6ac9a3797b811e977f549cd" + "531be2441d7c63e2b9729d145c11d84af35957727565a4"); + + S2N_BLOB_FROM_HEX(test_session_secret, + "18df06843d13a08bf2a449844c5f8a" + "478001bc4d4c627984d5a41da8d0402919"); + + uint8_t tls12_ticket[S2N_TLS12_STATE_SIZE_IN_BYTES_WITHOUT_EMS] = { + S2N_SERIALIZED_FORMAT_TLS12_V1, + S2N_TLS12, + TLS_RSA_WITH_AES_128_GCM_SHA256, + TICKET_ISSUE_TIME_BYTES, + }; + + uint8_t tls12_ticket_with_ems[S2N_TLS12_STATE_SIZE_IN_BYTES] = { + S2N_SERIALIZED_FORMAT_TLS12_V3, + S2N_TLS12, + TLS_RSA_WITH_AES_128_GCM_SHA256, + TICKET_ISSUE_TIME_BYTES, + }; + + uint8_t tls13_ticket[] = { + S2N_SERIALIZED_FORMAT_TLS13_V1, + S2N_TLS13, + TLS_AES_128_GCM_SHA256, + TICKET_ISSUE_TIME_BYTES, + TICKET_AGE_ADD_BYTES, + SECRET_LEN, + SECRET, + EMPTY_EARLY_DATA_SIZE, + }; + + uint8_t tls13_server_ticket[] = { + S2N_SERIALIZED_FORMAT_TLS13_V1, + S2N_TLS13, + TLS_AES_128_GCM_SHA256, + TICKET_ISSUE_TIME_BYTES, + TICKET_AGE_ADD_BYTES, + SECRET_LEN, + SECRET, + KEYING_MATERIAL_EXPIRATION_BYTES, + EMPTY_EARLY_DATA_SIZE, + }; + + uint8_t tls13_ticket_with_early_data[] = { + S2N_SERIALIZED_FORMAT_TLS13_V1, + S2N_TLS13, + TLS_AES_128_GCM_SHA256, + TICKET_ISSUE_TIME_BYTES, + TICKET_AGE_ADD_BYTES, + SECRET_LEN, + SECRET, + 0x00, + 0x00, + 0x00, + NONEMPTY_EARLY_DATA_SIZE, + APP_PROTOCOL_LEN, + APP_PROTOCOL, + 0x00, + EARLY_DATA_CONTEXT_LEN, + EARLY_DATA_CONTEXT, + }; + + uint8_t faulty_format_ticket[] = { + 0xFF, + }; + + /* s2n_connection_get_session_state_size */ + { + /* Safety */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + size_t size = 0; + EXPECT_ERROR_WITH_ERRNO(s2n_connection_get_session_state_size(NULL, &size), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_connection_get_session_state_size(conn, NULL), S2N_ERR_NULL); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS1.2: session state is fixed */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS12; + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + + /* Result matches constant */ + size_t actual_size = 0; + EXPECT_OK(s2n_connection_get_session_state_size(conn, &actual_size)); + EXPECT_EQUAL(actual_size, S2N_TLS12_STATE_SIZE_IN_BYTES); + + /* Result matches actual size of data */ + DEFER_CLEANUP(struct s2n_stuffer actual_data = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&actual_data, actual_size)); + EXPECT_SUCCESS(s2n_tls12_serialize_resumption_state(conn, &actual_data)); + const uint32_t expected_size = s2n_stuffer_data_available(&actual_data); + if (expected_size != actual_size) { + fprintf(stderr, "\nS2N_TLS12_STATE_SIZE_IN_BYTES (%i) should be set to %u\n\n", + S2N_TLS12_STATE_SIZE_IN_BYTES, expected_size); + } + EXPECT_EQUAL(actual_size, s2n_stuffer_data_available(&actual_data)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS1.3 with all variables empty except non-zero session secret */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + /* Set non-zero length secret */ + uint8_t secret_size = 0; + EXPECT_SUCCESS(s2n_hmac_digest_size(conn->secure->cipher_suite->prf_alg, &secret_size)); + EXPECT_SUCCESS(s2n_realloc(&conn->tls13_ticket_fields.session_secret, secret_size)); + + /* Result matches constant */ + size_t actual_size = 0; + EXPECT_OK(s2n_connection_get_session_state_size(conn, &actual_size)); + EXPECT_EQUAL(actual_size, S2N_TLS13_FIXED_STATE_SIZE + secret_size); + + /* Result matches actual size of data */ + DEFER_CLEANUP(struct s2n_stuffer actual_data = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&actual_data, actual_size)); + EXPECT_OK(s2n_tls13_serialize_resumption_state(conn, &actual_data)); + const uint32_t expected_size = s2n_stuffer_data_available(&actual_data); + if (actual_size != expected_size) { + fprintf(stderr, "\nS2N_TLS13_FIXED_STATE_SIZE (%i) should be set to %u\n\n", + S2N_TLS13_FIXED_STATE_SIZE, expected_size); + } + EXPECT_EQUAL(actual_size, expected_size); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS1.3 with all variables empty except non-zero session secret */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS13; + /* use a different hash digest to get more coverage/certainty */ + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + + /* Set non-zero length secret */ + uint8_t secret_size = 0; + EXPECT_SUCCESS(s2n_hmac_digest_size(conn->secure->cipher_suite->prf_alg, &secret_size)); + EXPECT_SUCCESS(s2n_realloc(&conn->tls13_ticket_fields.session_secret, secret_size)); + + /* Result matches constant */ + size_t actual_size = 0; + EXPECT_OK(s2n_connection_get_session_state_size(conn, &actual_size)); + EXPECT_EQUAL(actual_size, S2N_TLS13_FIXED_STATE_SIZE + secret_size); + + /* Result matches actual size of data */ + DEFER_CLEANUP(struct s2n_stuffer actual_data = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&actual_data, actual_size)); + EXPECT_OK(s2n_tls13_serialize_resumption_state(conn, &actual_data)); + EXPECT_EQUAL(actual_size, s2n_stuffer_data_available(&actual_data)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Minimal TLS1.3 with early data: all variable fields empty except non-zero session secret */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(conn, 1)); + + /* Set non-zero length secret */ + uint8_t secret_size = 0; + EXPECT_SUCCESS(s2n_hmac_digest_size(conn->secure->cipher_suite->prf_alg, &secret_size)); + EXPECT_SUCCESS(s2n_alloc(&conn->tls13_ticket_fields.session_secret, secret_size)); + + /* Result matches constants */ + size_t actual_size = 0; + EXPECT_OK(s2n_connection_get_session_state_size(conn, &actual_size)); + EXPECT_EQUAL(actual_size, S2N_TLS13_FIXED_STATE_SIZE + S2N_TLS13_FIXED_EARLY_DATA_STATE_SIZE + secret_size); + + /* Result matches actual size of data */ + DEFER_CLEANUP(struct s2n_stuffer actual_data = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&actual_data, actual_size)); + EXPECT_OK(s2n_tls13_serialize_resumption_state(conn, &actual_data)); + const uint32_t expected_size = s2n_stuffer_data_available(&actual_data); + if (actual_size != expected_size) { + fprintf(stderr, "\nS2N_TLS13_FIXED_EARLY_DATA_STATE_SIZE (%i) should be set to %u\n\n", + S2N_TLS13_FIXED_EARLY_DATA_STATE_SIZE, expected_size - S2N_TLS13_FIXED_STATE_SIZE); + } + EXPECT_EQUAL(actual_size, expected_size); + }; + + /* TLS1.3 with early data: all variable fields set */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + + /* Set non-zero length secret */ + uint8_t secret_size = 0; + EXPECT_SUCCESS(s2n_hmac_digest_size(conn->secure->cipher_suite->prf_alg, &secret_size)); + EXPECT_SUCCESS(s2n_alloc(&conn->tls13_ticket_fields.session_secret, secret_size)); + + /* Set early data fields */ + const uint8_t data[] = "test data"; + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(conn, 1)); + EXPECT_MEMCPY_SUCCESS(conn->application_protocol, data, sizeof(data)); + EXPECT_SUCCESS(s2n_connection_set_server_early_data_context(conn, data, sizeof(data))); + + /* Result matches constants */ + size_t actual_size = 0; + EXPECT_OK(s2n_connection_get_session_state_size(conn, &actual_size)); + EXPECT_NOT_EQUAL(actual_size, 0); + + /* Result matches actual size of data */ + DEFER_CLEANUP(struct s2n_stuffer actual_data = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&actual_data, actual_size)); + EXPECT_OK(s2n_tls13_serialize_resumption_state(conn, &actual_data)); + EXPECT_EQUAL(actual_size, s2n_stuffer_data_available(&actual_data)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* s2n_connection_get_session_length */ + { + /* Safety */ + EXPECT_EQUAL(s2n_connection_get_session_length(NULL), 0); + + /* Session Ticket */ + { + const uint16_t client_ticket_size = 10; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, true)); + + /* TLS 1.2 */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_realloc(&conn->client_ticket, client_ticket_size)); + conn->actual_protocol_version = S2N_TLS12; + int session_length = s2n_connection_get_session_length(conn); + EXPECT_NOT_EQUAL(session_length, 0); + + /* Result matches size expected by s2n_connection_get_session */ + DEFER_CLEANUP(struct s2n_blob session_data = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&session_data, session_length)); + EXPECT_SUCCESS(s2n_connection_get_session(conn, session_data.data, session_data.size)); + } + + /* TLS 1.3 */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_realloc(&conn->client_ticket, client_ticket_size)); + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + + /* Set non-zero length secret */ + uint8_t secret_size = 0; + EXPECT_SUCCESS(s2n_hmac_digest_size(conn->secure->cipher_suite->prf_alg, &secret_size)); + EXPECT_SUCCESS(s2n_alloc(&conn->tls13_ticket_fields.session_secret, secret_size)); + int session_length = s2n_connection_get_session_length(conn); + EXPECT_NOT_EQUAL(session_length, 0); + + /* Result matches size expected by s2n_connection_get_session */ + DEFER_CLEANUP(struct s2n_blob session_data = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&session_data, session_length)); + EXPECT_SUCCESS(s2n_connection_get_session(conn, session_data.data, session_data.size)); + } + }; + + /* Session ID */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, true)); + EXPECT_SUCCESS(s2n_config_set_session_cache_onoff(config, true)); + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->session_id_len = 5; + + /* TLS1.3: Always zero. Stateful tickets are not yet supported. */ + { + conn->actual_protocol_version = S2N_TLS13; + uint8_t data = 0; + EXPECT_EQUAL(s2n_connection_get_session_length(conn), 0); + EXPECT_SUCCESS(s2n_connection_get_session(conn, &data, 1)); + EXPECT_EQUAL(data, 0); + }; + + /* TLS1.2 */ + { + conn->actual_protocol_version = S2N_TLS12; + + int session_length = s2n_connection_get_session_length(conn); + EXPECT_NOT_EQUAL(session_length, 0); + + /* Result matches size expected by s2n_connection_get_session */ + DEFER_CLEANUP(struct s2n_blob session_id_data = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&session_id_data, session_length)); + EXPECT_SUCCESS(s2n_connection_get_session(conn, session_id_data.data, session_id_data.size)); + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + }; + + /* s2n_connection_get_session_id_length */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_session_id_length(NULL), S2N_ERR_NULL); + + conn->session_id_len = 5; + conn->actual_protocol_version = S2N_TLS12; + EXPECT_EQUAL(s2n_connection_get_session_id_length(conn), 5); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_EQUAL(s2n_connection_get_session_id_length(conn), 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* s2n_tls12_serialize_resumption_state */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS12; + + struct s2n_blob blob = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&blob, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN)); + EXPECT_SUCCESS(s2n_stuffer_init(&stuffer, &blob)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, test_master_secret.data, S2N_TLS_SECRET_LEN)); + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + + uint8_t ems_state[] = { false, true }; + for (size_t i = 0; i < sizeof(ems_state); i++) { + /* Test the two different EMS states */ + conn->ems_negotiated = ems_state[i]; + + uint8_t s_data[S2N_TLS12_STATE_SIZE_IN_BYTES] = { 0 }; + struct s2n_blob state_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&state_blob, s_data, sizeof(s_data))); + struct s2n_stuffer output = { 0 }; + + EXPECT_SUCCESS(s2n_stuffer_init(&output, &state_blob)); + EXPECT_SUCCESS(s2n_tls12_serialize_resumption_state(conn, &output)); + + uint8_t serial_id = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&output, &serial_id)); + EXPECT_EQUAL(serial_id, S2N_SERIALIZED_FORMAT_TLS12_V3); + + uint8_t version = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&output, &version)); + EXPECT_EQUAL(version, S2N_TLS12); + + uint8_t iana_value[2] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&output, iana_value, S2N_TLS_CIPHER_SUITE_LEN)); + EXPECT_BYTEARRAY_EQUAL(conn->secure->cipher_suite->iana_value, &iana_value, S2N_TLS_CIPHER_SUITE_LEN); + + /* Current time */ + EXPECT_SUCCESS(s2n_stuffer_skip_read(&output, sizeof(uint64_t))); + + uint8_t master_secret[S2N_TLS_SECRET_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&output, master_secret, S2N_TLS_SECRET_LEN)); + EXPECT_BYTEARRAY_EQUAL(test_master_secret.data, master_secret, S2N_TLS_SECRET_LEN); + + uint8_t ems_info = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&output, &ems_info)); + EXPECT_EQUAL(ems_info, ems_state[i]); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* s2n_tls13_serialize_resumption_state */ + { + /* Safety checks */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + struct s2n_stuffer output = { 0 }; + + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_serialize_resumption_state(NULL, &output), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_serialize_resumption_state(conn, NULL), S2N_ERR_NULL); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test TLS1.3 client serialization */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_wall_clock(config, mock_time, NULL)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + + conn->tls13_ticket_fields = (struct s2n_ticket_fields){ .ticket_age_add = 1 }; + EXPECT_SUCCESS(s2n_dup(&test_session_secret, &conn->tls13_ticket_fields.session_secret)); + + EXPECT_OK(s2n_tls13_serialize_resumption_state(conn, &output)); + + uint8_t serial_id = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&output, &serial_id)); + EXPECT_EQUAL(serial_id, S2N_SERIALIZED_FORMAT_TLS13_V1); + + uint8_t version = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&output, &version)); + EXPECT_EQUAL(version, S2N_TLS13); + + uint8_t iana_value[2] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&output, iana_value, S2N_TLS_CIPHER_SUITE_LEN)); + EXPECT_BYTEARRAY_EQUAL(conn->secure->cipher_suite->iana_value, &iana_value, S2N_TLS_CIPHER_SUITE_LEN); + + uint64_t actual_ticket_issue_time = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint64(&output, &actual_ticket_issue_time)); + EXPECT_EQUAL(actual_ticket_issue_time, ticket_issue_time); + + uint32_t ticket_age_add = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint32(&output, &ticket_age_add)); + EXPECT_EQUAL(ticket_age_add, conn->tls13_ticket_fields.ticket_age_add); + + uint8_t secret_len = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&output, &secret_len)); + EXPECT_EQUAL(secret_len, conn->tls13_ticket_fields.session_secret.size); + + uint8_t session_secret[S2N_TLS_SECRET_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&output, session_secret, secret_len)); + EXPECT_BYTEARRAY_EQUAL(test_session_secret.data, session_secret, secret_len); + + uint32_t max_early_data_size = 1; + EXPECT_SUCCESS(s2n_stuffer_read_uint32(&output, &max_early_data_size)); + EXPECT_EQUAL(max_early_data_size, 0); + + EXPECT_EQUAL(s2n_stuffer_data_available(&output), 0); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test TLS1.3 server serialization: keying material expiration time */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_wall_clock(config, mock_time, NULL)); + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_connection_set_server_keying_material_lifetime(conn, 1)); + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + + conn->tls13_ticket_fields = (struct s2n_ticket_fields){ .ticket_age_add = 1 }; + EXPECT_SUCCESS(s2n_dup(&test_session_secret, &conn->tls13_ticket_fields.session_secret)); + + /* New expiration time */ + { + uint64_t expected_expiration_time = ticket_issue_time + ONE_SEC_IN_NANOS; + + EXPECT_OK(s2n_tls13_serialize_resumption_state(conn, &output)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&output, s2n_stuffer_data_available(&output))); + EXPECT_SUCCESS(s2n_stuffer_rewind_read(&output, SIZE_OF_MAX_EARLY_DATA_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_rewind_read(&output, SIZE_OF_KEYING_EXPIRATION)); + + uint64_t actual_keying_material_expiration = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint64(&output, &actual_keying_material_expiration)); + EXPECT_EQUAL(actual_keying_material_expiration, expected_expiration_time); + + EXPECT_EQUAL(s2n_stuffer_data_available(&output), SIZE_OF_MAX_EARLY_DATA_SIZE); + EXPECT_SUCCESS(s2n_stuffer_wipe(&output)); + }; + + DEFER_CLEANUP(struct s2n_psk *chosen_psk = s2n_test_psk_new(conn), s2n_psk_free); + EXPECT_NOT_NULL(chosen_psk); + chosen_psk->type = S2N_PSK_TYPE_RESUMPTION; + conn->psk_params.chosen_psk = chosen_psk; + + /* Existing expiration time */ + { + uint64_t expected_expiration_time = ticket_issue_time + 1; + chosen_psk->keying_material_expiration = expected_expiration_time; + + EXPECT_OK(s2n_tls13_serialize_resumption_state(conn, &output)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&output, s2n_stuffer_data_available(&output))); + EXPECT_SUCCESS(s2n_stuffer_rewind_read(&output, SIZE_OF_MAX_EARLY_DATA_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_rewind_read(&output, SIZE_OF_KEYING_EXPIRATION)); + + uint64_t actual_keying_material_expiration = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint64(&output, &actual_keying_material_expiration)); + EXPECT_EQUAL(actual_keying_material_expiration, expected_expiration_time); + + EXPECT_EQUAL(s2n_stuffer_data_available(&output), SIZE_OF_MAX_EARLY_DATA_SIZE); + EXPECT_SUCCESS(s2n_stuffer_wipe(&output)); + }; + + /* Existing expiration time not supported by server settings */ + { + uint64_t expected_expiration_time = ticket_issue_time + ONE_SEC_IN_NANOS; + chosen_psk->keying_material_expiration = UINT64_MAX; + + EXPECT_OK(s2n_tls13_serialize_resumption_state(conn, &output)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&output, s2n_stuffer_data_available(&output))); + EXPECT_SUCCESS(s2n_stuffer_rewind_read(&output, SIZE_OF_MAX_EARLY_DATA_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_rewind_read(&output, SIZE_OF_KEYING_EXPIRATION)); + + uint64_t actual_keying_material_expiration = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint64(&output, &actual_keying_material_expiration)); + EXPECT_EQUAL(actual_keying_material_expiration, expected_expiration_time); + + EXPECT_EQUAL(s2n_stuffer_data_available(&output), SIZE_OF_MAX_EARLY_DATA_SIZE); + EXPECT_SUCCESS(s2n_stuffer_wipe(&output)); + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test TLS1.3 serialization with early data */ + { + const uint32_t test_max_early_data_size = UINT8_MAX; + const uint8_t test_early_data_context[] = "context"; + const uint8_t test_app_protocol[] = "protocol"; + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_server_early_data_context(conn, test_early_data_context, sizeof(test_early_data_context))); + EXPECT_MEMCPY_SUCCESS(conn->application_protocol, test_app_protocol, sizeof(test_app_protocol)); + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + conn->actual_protocol_version = S2N_TLS13; + + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + + conn->tls13_ticket_fields = (struct s2n_ticket_fields){ .ticket_age_add = 1 }; + EXPECT_SUCCESS(s2n_dup(&test_session_secret, &conn->tls13_ticket_fields.session_secret)); + + /* Write ticket without early data. Save size for comparison. */ + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(conn, 0)); + EXPECT_OK(s2n_tls13_serialize_resumption_state(conn, &output)); + size_t basic_state_size = s2n_stuffer_data_available(&output); + EXPECT_SUCCESS(s2n_stuffer_wipe(&output)); + + /* Write ticket with early data. Skip the non-early-data information. */ + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(conn, test_max_early_data_size)); + EXPECT_OK(s2n_tls13_serialize_resumption_state(conn, &output)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&output, basic_state_size)); + EXPECT_SUCCESS(s2n_stuffer_rewind_read(&output, SIZE_OF_MAX_EARLY_DATA_SIZE)); + + uint32_t max_early_data_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint32(&output, &max_early_data_size)); + EXPECT_EQUAL(max_early_data_size, test_max_early_data_size); + + uint8_t app_protocol_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&output, &app_protocol_size)); + EXPECT_NOT_EQUAL(app_protocol_size, 0); + EXPECT_EQUAL(app_protocol_size, strlen((const char *) test_app_protocol)); + + uint8_t app_protocol[sizeof(test_app_protocol)] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&output, app_protocol, app_protocol_size)); + EXPECT_BYTEARRAY_EQUAL(app_protocol, test_app_protocol, sizeof(test_app_protocol)); + + uint16_t early_data_context_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&output, &early_data_context_size)); + EXPECT_NOT_EQUAL(early_data_context_size, 0); + EXPECT_EQUAL(early_data_context_size, sizeof(test_early_data_context)); + + uint8_t early_data_context[sizeof(test_early_data_context)] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&output, early_data_context, early_data_context_size)); + EXPECT_BYTEARRAY_EQUAL(early_data_context, test_early_data_context, sizeof(test_early_data_context)); + + EXPECT_EQUAL(s2n_stuffer_data_available(&output), 0); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Erroneous secret size */ + { + struct { + uint32_t secret_size; + bool success; + } test_cases[] = { + { + /* too small */ + .secret_size = 0, + .success = false, + }, + { + .secret_size = 1, + .success = true, + }, + { + .secret_size = UINT8_MAX, + .success = true, + }, + { + /* too large */ + .secret_size = UINT8_MAX + 1, + .success = false, + } + }; + + for (int i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS13; + + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + + conn->tls13_ticket_fields = (struct s2n_ticket_fields){ .ticket_age_add = 1 }; + EXPECT_SUCCESS(s2n_dup(&test_session_secret, &conn->tls13_ticket_fields.session_secret)); + + EXPECT_SUCCESS(s2n_realloc(&conn->tls13_ticket_fields.session_secret, test_cases[i].secret_size)); + if (test_cases[i].success) { + EXPECT_OK(s2n_tls13_serialize_resumption_state(conn, &output)); + } else { + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_serialize_resumption_state(conn, &output), S2N_ERR_SAFETY); + } + } + } + }; + + /* s2n_deserialize_resumption_state */ + { + /* Deserialize ticket with incorrect format errors */ + { + struct s2n_blob ticket_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&ticket_blob, faulty_format_ticket, sizeof(faulty_format_ticket))); + struct s2n_stuffer ticket_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&ticket_stuffer, &ticket_blob)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&ticket_stuffer, sizeof(faulty_format_ticket))); + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + EXPECT_ERROR_WITH_ERRNO(s2n_deserialize_resumption_state(conn, NULL, &ticket_stuffer), + S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Deserialized ticket without EMS data errors */ + { + struct s2n_blob ticket_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&ticket_blob, tls12_ticket, sizeof(tls12_ticket))); + struct s2n_stuffer ticket_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&ticket_stuffer, &ticket_blob)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&ticket_stuffer, sizeof(tls12_ticket) - S2N_TLS_SECRET_LEN)); + /* The secret needs to be written to the ticket separately as it has a fixed length */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&ticket_stuffer, test_master_secret.data, S2N_TLS_SECRET_LEN)); + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + /* Security policy must allow cipher suite hard coded into ticket */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + + EXPECT_ERROR_WITH_ERRNO(s2n_deserialize_resumption_state(conn, NULL, &ticket_stuffer), S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Client processes hardcoded TLS1.2 ticket with EMS data correctly */ + { + struct s2n_blob ticket_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&ticket_blob, tls12_ticket_with_ems, sizeof(tls12_ticket_with_ems))); + struct s2n_stuffer ticket_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&ticket_stuffer, &ticket_blob)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&ticket_stuffer, sizeof(tls12_ticket_with_ems) - S2N_TLS_SECRET_LEN - 1)); + /* The secret needs to be written to the ticket separately as it has a fixed length */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&ticket_stuffer, test_master_secret.data, S2N_TLS_SECRET_LEN)); + + /* Write EMS data */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&ticket_stuffer, 1)); + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + /* Security policy must allow cipher suite hard coded into ticket */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + + EXPECT_OK(s2n_deserialize_resumption_state(conn, NULL, &ticket_stuffer)); + + EXPECT_TRUE(conn->ems_negotiated); + EXPECT_EQUAL(conn->resume_protocol_version, S2N_TLS12); + EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_rsa_with_aes_128_gcm_sha256); + + EXPECT_BYTEARRAY_EQUAL(test_master_secret.data, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Client processes TLS1.2 ticket with EMS data correctly */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + conn->actual_protocol_version = S2N_TLS12; + /* Security policy must allow chosen cipher suite */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + + struct s2n_blob blob = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&blob, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN)); + EXPECT_SUCCESS(s2n_stuffer_init(&stuffer, &blob)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, test_master_secret.data, S2N_TLS_SECRET_LEN)); + conn->secure->cipher_suite = &s2n_rsa_with_aes_128_gcm_sha256; + conn->ems_negotiated = true; + + uint8_t s_data[S2N_TLS12_STATE_SIZE_IN_BYTES] = { 0 }; + struct s2n_blob state_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&state_blob, s_data, sizeof(s_data))); + struct s2n_stuffer output = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&output, &state_blob)); + + EXPECT_SUCCESS(s2n_tls12_serialize_resumption_state(conn, &output)); + EXPECT_OK(s2n_deserialize_resumption_state(conn, NULL, &output)); + + EXPECT_TRUE(conn->ems_negotiated); + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(conn->secure->cipher_suite, &s2n_rsa_with_aes_128_gcm_sha256); + + EXPECT_BYTEARRAY_EQUAL(test_master_secret.data, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Server processes TLS1.2 ticket with EMS data correctly */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS12; + + struct s2n_blob blob = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&blob, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN)); + EXPECT_SUCCESS(s2n_stuffer_init(&stuffer, &blob)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&stuffer, test_master_secret.data, S2N_TLS_SECRET_LEN)); + conn->secure->cipher_suite = &s2n_rsa_with_aes_128_gcm_sha256; + conn->ems_negotiated = true; + + uint8_t s_data[S2N_TLS12_STATE_SIZE_IN_BYTES] = { 0 }; + struct s2n_blob state_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&state_blob, s_data, sizeof(s_data))); + struct s2n_stuffer output = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&output, &state_blob)); + + EXPECT_SUCCESS(s2n_tls12_serialize_resumption_state(conn, &output)); + + /* EMS state in current session matches EMS state in previous session */ + conn->ems_negotiated = true; + EXPECT_OK(s2n_deserialize_resumption_state(conn, NULL, &output)); + EXPECT_TRUE(conn->ems_negotiated); + + /** + *= https://tools.ietf.org/rfc/rfc7627#section-5.3 + *= type=test + *# 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. + **/ + conn->ems_negotiated = false; + EXPECT_SUCCESS(s2n_stuffer_reread(&output)); + EXPECT_ERROR_WITH_ERRNO(s2n_deserialize_resumption_state(conn, NULL, &output), + S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); + EXPECT_TRUE(conn->ems_negotiated); + + /** + *= https://tools.ietf.org/rfc/rfc7627#section-5.3 + *= type=test + *# If the original session did not use the "extended_master_secret" + *# extension but the new ClientHello contains the extension, then the + *# server MUST NOT perform the abbreviated handshake. Instead, it + *# SHOULD continue with a full handshake (as described in + *# Section 5.2) to negotiate a new session. + **/ + EXPECT_SUCCESS(s2n_stuffer_wipe_n(&output, 1)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&output, 0)); + conn->ems_negotiated = true; + EXPECT_SUCCESS(s2n_stuffer_reread(&output)); + EXPECT_ERROR_WITH_ERRNO(s2n_deserialize_resumption_state(conn, NULL, &output), + S2N_ERR_INVALID_SERIALIZED_SESSION_STATE); + EXPECT_FALSE(conn->ems_negotiated); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Deserialized ticket sets correct PSK values for session resumption in TLS1.3 */ + { + struct s2n_blob ticket_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&ticket_blob, tls13_ticket, sizeof(tls13_ticket))); + struct s2n_stuffer ticket_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&ticket_stuffer, &ticket_blob)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&ticket_stuffer, sizeof(tls13_ticket))); + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_wall_clock(config, mock_time, NULL)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Initialize client ticket */ + const uint8_t client_ticket[] = { CLIENT_TICKET }; + EXPECT_SUCCESS(s2n_realloc(&conn->client_ticket, sizeof(client_ticket))); + EXPECT_MEMCPY_SUCCESS(conn->client_ticket.data, client_ticket, sizeof(client_ticket)); + + EXPECT_OK(s2n_deserialize_resumption_state(conn, &conn->client_ticket, &ticket_stuffer)); + + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, 0, (void **) &psk)); + EXPECT_NOT_NULL(psk); + + EXPECT_EQUAL(psk->type, S2N_PSK_TYPE_RESUMPTION); + EXPECT_BYTEARRAY_EQUAL(psk->identity.data, client_ticket, sizeof(client_ticket)); + + EXPECT_EQUAL(psk->secret.size, SECRET_LEN); + uint8_t secret[] = { SECRET }; + EXPECT_BYTEARRAY_EQUAL(psk->secret.data, secret, sizeof(secret)); + + EXPECT_EQUAL(psk->hmac_alg, S2N_HMAC_SHA256); + + EXPECT_EQUAL(psk->ticket_age_add, TICKET_AGE_ADD); + EXPECT_EQUAL(psk->ticket_issue_time, ticket_issue_time); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Deserialized TLS1.3 server ticket sets correct keying material value */ + { + struct s2n_blob ticket_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&ticket_blob, tls13_server_ticket, sizeof(tls13_server_ticket))); + struct s2n_stuffer ticket_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&ticket_stuffer, &ticket_blob)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&ticket_stuffer, ticket_blob.size)); + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_wall_clock(config, mock_time, NULL)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Initialize client ticket */ + const uint8_t client_ticket[] = { CLIENT_TICKET }; + EXPECT_SUCCESS(s2n_realloc(&conn->client_ticket, sizeof(client_ticket))); + EXPECT_MEMCPY_SUCCESS(conn->client_ticket.data, client_ticket, sizeof(client_ticket)); + + EXPECT_OK(s2n_deserialize_resumption_state(conn, &conn->client_ticket, &ticket_stuffer)); + + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, 0, (void **) &psk)); + EXPECT_NOT_NULL(psk); + + EXPECT_EQUAL(psk->ticket_issue_time, ticket_issue_time); + EXPECT_EQUAL(psk->keying_material_expiration, keying_material_expiration); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Deserializing TLS1.3 server ticket fails for expired keying material */ + { + struct s2n_blob ticket_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&ticket_blob, tls13_server_ticket, sizeof(tls13_server_ticket))); + struct s2n_stuffer ticket_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&ticket_stuffer, &ticket_blob)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&ticket_stuffer, ticket_blob.size)); + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + uint64_t current_time = keying_material_expiration; + EXPECT_OK(s2n_config_mock_wall_clock(config, ¤t_time)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Initialize client ticket */ + const uint8_t client_ticket[] = { CLIENT_TICKET }; + EXPECT_SUCCESS(s2n_realloc(&conn->client_ticket, sizeof(client_ticket))); + EXPECT_MEMCPY_SUCCESS(conn->client_ticket.data, client_ticket, sizeof(client_ticket)); + + EXPECT_ERROR_WITH_ERRNO(s2n_deserialize_resumption_state(conn, &conn->client_ticket, &ticket_stuffer), + S2N_ERR_KEYING_MATERIAL_EXPIRED); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Deserialized ticket sets correct PSK values for early data */ + { + const uint8_t expected_app_protocol[] = { APP_PROTOCOL }; + const uint8_t expected_context[] = { EARLY_DATA_CONTEXT }; + + struct s2n_blob ticket_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&ticket_blob, tls13_ticket_with_early_data, sizeof(tls13_ticket_with_early_data))); + struct s2n_stuffer ticket_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&ticket_stuffer, &ticket_blob)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&ticket_stuffer, ticket_blob.size)); + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_wall_clock(config, mock_time, NULL)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Initialize client ticket */ + const uint8_t client_ticket[] = { CLIENT_TICKET }; + EXPECT_SUCCESS(s2n_realloc(&conn->client_ticket, sizeof(client_ticket))); + EXPECT_MEMCPY_SUCCESS(conn->client_ticket.data, client_ticket, sizeof(client_ticket)); + + EXPECT_OK(s2n_deserialize_resumption_state(conn, &conn->client_ticket, &ticket_stuffer)); + + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, 0, (void **) &psk)); + EXPECT_NOT_NULL(psk); + + EXPECT_EQUAL(psk->early_data_config.protocol_version, S2N_TLS13); + EXPECT_EQUAL(psk->early_data_config.max_early_data_size, NONEMPTY_EARLY_DATA_SIZE); + EXPECT_EQUAL(psk->early_data_config.application_protocol.size, APP_PROTOCOL_LEN); + EXPECT_BYTEARRAY_EQUAL(psk->early_data_config.application_protocol.data, expected_app_protocol, APP_PROTOCOL_LEN); + EXPECT_EQUAL(psk->early_data_config.context.size, EARLY_DATA_CONTEXT_LEN); + EXPECT_BYTEARRAY_EQUAL(psk->early_data_config.context.data, expected_context, EARLY_DATA_CONTEXT_LEN); + EXPECT_EQUAL(psk->early_data_config.cipher_suite, &s2n_tls13_aes_128_gcm_sha256); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Deserializing state ignores extra data. + * This will make it possible to easily add new fields in the future, without needing + * to worry about versioning. */ + { + uint8_t extra_data[] = "more ticket data, maybe from the future"; + + DEFER_CLEANUP(struct s2n_stuffer ticket_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&ticket_stuffer, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&ticket_stuffer, tls13_ticket_with_early_data, + sizeof(tls13_ticket_with_early_data))); + /* Add some unexpected data */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&ticket_stuffer, extra_data, sizeof(extra_data))); + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_wall_clock(config, mock_time, NULL)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Initialize client ticket */ + const uint8_t client_ticket[] = { CLIENT_TICKET }; + EXPECT_SUCCESS(s2n_realloc(&conn->client_ticket, sizeof(client_ticket))); + EXPECT_MEMCPY_SUCCESS(conn->client_ticket.data, client_ticket, sizeof(client_ticket)); + + EXPECT_OK(s2n_deserialize_resumption_state(conn, &conn->client_ticket, &ticket_stuffer)); + EXPECT_EQUAL(conn->psk_params.psk_list.len, 1); + EXPECT_EQUAL(s2n_stuffer_data_available(&ticket_stuffer), sizeof(extra_data)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Any existing psks are removed when creating a new resumption psk */ + { + struct s2n_blob ticket_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&ticket_blob, tls13_ticket, sizeof(tls13_ticket))); + struct s2n_stuffer ticket_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&ticket_stuffer, &ticket_blob)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&ticket_stuffer, sizeof(tls13_ticket))); + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_wall_clock(config, mock_time, NULL)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Initialize client ticket */ + uint8_t client_ticket[] = { CLIENT_TICKET }; + EXPECT_SUCCESS(s2n_realloc(&conn->client_ticket, sizeof(client_ticket))); + EXPECT_MEMCPY_SUCCESS(conn->client_ticket.data, client_ticket, sizeof(client_ticket)); + + /* Add existing resumption psk */ + const uint8_t resumption_data[] = "resumption data"; + DEFER_CLEANUP(struct s2n_psk resumption_psk = { 0 }, s2n_psk_wipe); + EXPECT_OK(s2n_psk_init(&resumption_psk, S2N_PSK_TYPE_RESUMPTION)); + EXPECT_SUCCESS(s2n_psk_set_identity(&resumption_psk, resumption_data, sizeof(resumption_data))); + EXPECT_SUCCESS(s2n_psk_set_secret(&resumption_psk, resumption_data, sizeof(resumption_data))); + EXPECT_SUCCESS(s2n_connection_append_psk(conn, &resumption_psk)); + + EXPECT_OK(s2n_deserialize_resumption_state(conn, &conn->client_ticket, &ticket_stuffer)); + + EXPECT_EQUAL(conn->psk_params.psk_list.len, 1); + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, 0, (void **) &psk)); + EXPECT_NOT_NULL(psk); + + EXPECT_EQUAL(psk->type, S2N_PSK_TYPE_RESUMPTION); + EXPECT_BYTEARRAY_EQUAL(psk->identity.data, client_ticket, sizeof(client_ticket)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Fails if external PSKs already set */ + { + struct s2n_blob ticket_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&ticket_blob, tls13_ticket, sizeof(tls13_ticket))); + struct s2n_stuffer ticket_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&ticket_stuffer, &ticket_blob)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&ticket_stuffer, sizeof(tls13_ticket))); + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_wall_clock(config, mock_time, NULL)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Initialize client ticket */ + uint8_t client_ticket[] = { CLIENT_TICKET }; + EXPECT_SUCCESS(s2n_realloc(&conn->client_ticket, sizeof(client_ticket))); + EXPECT_MEMCPY_SUCCESS(conn->client_ticket.data, client_ticket, sizeof(client_ticket)); + + /* Add existing external psk */ + const uint8_t external_data[] = "external data"; + DEFER_CLEANUP(struct s2n_psk *external_psk = s2n_external_psk_new(), s2n_psk_free); + EXPECT_SUCCESS(s2n_psk_set_identity(external_psk, external_data, sizeof(external_data))); + EXPECT_SUCCESS(s2n_psk_set_secret(external_psk, external_data, sizeof(external_data))); + EXPECT_SUCCESS(s2n_connection_append_psk(conn, external_psk)); + + EXPECT_ERROR_WITH_ERRNO(s2n_deserialize_resumption_state(conn, &conn->client_ticket, &ticket_stuffer), S2N_ERR_PSK_MODE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Functional test: Both TLS1.3 client and server can deserialize what they serialize */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_wall_clock(config, mock_time, NULL)); + + for (s2n_mode mode = S2N_SERVER; mode <= S2N_CLIENT; mode++) { + struct s2n_connection *conn = s2n_connection_new(mode); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + conn->tls13_ticket_fields = (struct s2n_ticket_fields){ .ticket_age_add = TICKET_AGE_ADD }; + EXPECT_SUCCESS(s2n_dup(&test_session_secret, &conn->tls13_ticket_fields.session_secret)); + + /* Initialize client ticket */ + uint8_t client_ticket[] = { CLIENT_TICKET }; + EXPECT_SUCCESS(s2n_realloc(&conn->client_ticket, sizeof(client_ticket))); + EXPECT_MEMCPY_SUCCESS(conn->client_ticket.data, client_ticket, sizeof(client_ticket)); + + EXPECT_OK(s2n_serialize_resumption_state(conn, &stuffer)); + EXPECT_OK(s2n_deserialize_resumption_state(conn, &conn->client_ticket, &stuffer)); + + /* Check PSK values are correct */ + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, 0, (void **) &psk)); + EXPECT_NOT_NULL(psk); + + EXPECT_EQUAL(psk->type, S2N_PSK_TYPE_RESUMPTION); + EXPECT_BYTEARRAY_EQUAL(psk->identity.data, client_ticket, sizeof(client_ticket)); + + EXPECT_EQUAL(psk->secret.size, test_session_secret.size); + EXPECT_BYTEARRAY_EQUAL(psk->secret.data, test_session_secret.data, test_session_secret.size); + + EXPECT_EQUAL(psk->hmac_alg, conn->secure->cipher_suite->prf_alg); + + EXPECT_EQUAL(psk->ticket_age_add, TICKET_AGE_ADD); + EXPECT_EQUAL(psk->ticket_issue_time, ticket_issue_time); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Functional test: The TLS1.3 client can deserialize what it serializes with early data */ + { + const uint32_t test_max_early_data_size = 100; + const uint8_t test_early_data_context[] = "test context"; + const uint8_t test_app_protocol[] = "test protocol"; + const uint8_t test_app_protocol_len = strlen((const char *) test_app_protocol); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_wall_clock(config, mock_time, NULL)); + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(conn, test_max_early_data_size)); + EXPECT_SUCCESS(s2n_connection_set_server_early_data_context(conn, test_early_data_context, sizeof(test_early_data_context))); + EXPECT_MEMCPY_SUCCESS(conn->application_protocol, test_app_protocol, sizeof(test_app_protocol)); + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + /* Initialize client ticket */ + conn->tls13_ticket_fields = (struct s2n_ticket_fields){ .ticket_age_add = TICKET_AGE_ADD }; + EXPECT_SUCCESS(s2n_dup(&test_session_secret, &conn->tls13_ticket_fields.session_secret)); + uint8_t client_ticket[] = { CLIENT_TICKET }; + EXPECT_SUCCESS(s2n_realloc(&conn->client_ticket, sizeof(client_ticket))); + EXPECT_MEMCPY_SUCCESS(conn->client_ticket.data, client_ticket, sizeof(client_ticket)); + + EXPECT_OK(s2n_serialize_resumption_state(conn, &stuffer)); + EXPECT_OK(s2n_deserialize_resumption_state(conn, &conn->client_ticket, &stuffer)); + + /* Check PSK values are correct */ + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, 0, (void **) &psk)); + EXPECT_NOT_NULL(psk); + + EXPECT_EQUAL(psk->early_data_config.protocol_version, S2N_TLS13); + EXPECT_EQUAL(psk->early_data_config.max_early_data_size, test_max_early_data_size); + EXPECT_EQUAL(psk->early_data_config.cipher_suite, &s2n_tls13_aes_256_gcm_sha384); + EXPECT_EQUAL(psk->early_data_config.application_protocol.size, test_app_protocol_len); + EXPECT_BYTEARRAY_EQUAL(psk->early_data_config.application_protocol.data, test_app_protocol, test_app_protocol_len); + EXPECT_EQUAL(psk->early_data_config.context.size, sizeof(test_early_data_context)); + EXPECT_BYTEARRAY_EQUAL(psk->early_data_config.context.data, test_early_data_context, sizeof(test_early_data_context)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Functional test: Both TLS1.2 client and server can deserialize what they serialize */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_wall_clock(config, mock_time, NULL)); + + for (s2n_mode mode = S2N_SERVER; mode <= S2N_CLIENT; mode++) { + struct s2n_connection *conn = s2n_connection_new(mode); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS12; + conn->secure->cipher_suite = &s2n_rsa_with_aes_128_gcm_sha256; + /* Security policy must allow chosen cipher suite */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + + uint8_t s_data[S2N_TLS12_STATE_SIZE_IN_BYTES] = { 0 }; + struct s2n_blob state_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&state_blob, s_data, sizeof(s_data))); + struct s2n_stuffer stuffer = { 0 }; + + EXPECT_SUCCESS(s2n_stuffer_init(&stuffer, &state_blob)); + + EXPECT_OK(s2n_serialize_resumption_state(conn, &stuffer)); + EXPECT_OK(s2n_deserialize_resumption_state(conn, NULL, &stuffer)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + }; + + /* s2n_validate_ticket_age */ + { + /* Ticket issue time is in the future */ + { + uint64_t current_time = SECONDS_TO_NANOS(0); + uint64_t issue_time = 10; + EXPECT_ERROR_WITH_ERRNO(s2n_validate_ticket_age(current_time, issue_time), S2N_ERR_INVALID_SESSION_TICKET); + }; + + /** Ticket age is longer than a week + *= https://tools.ietf.org/rfc/rfc8446#section-4.6.1 + *= type=test + *# Clients MUST NOT cache + *# tickets for longer than 7 days, regardless of the ticket_lifetime, + *# and MAY delete tickets earlier based on local policy. + */ + { + uint64_t current_time = SECONDS_TO_NANOS(ONE_WEEK_IN_SEC + 1); + uint64_t issue_time = 0; + EXPECT_ERROR_WITH_ERRNO(s2n_validate_ticket_age(current_time, issue_time), S2N_ERR_INVALID_SESSION_TICKET); + }; + + /* Ticket age is a exactly a week */ + { + uint64_t current_time = SECONDS_TO_NANOS(ONE_WEEK_IN_SEC); + uint64_t issue_time = 0; + EXPECT_OK(s2n_validate_ticket_age(current_time, issue_time)); + }; + }; + + /* s2n_encrypt_session_ticket */ + { + /* Session ticket keys. Taken from test vectors in https://tools.ietf.org/html/rfc5869 */ + uint8_t ticket_key_name[16] = "2016.07.26.15\0"; + S2N_BLOB_FROM_HEX(ticket_key, + "077709362c2e32df0ddc3f0dc47bba6390b6c73bb50f9c3122ec844ad7c2b3e5"); + + /* Check encrypted data can be decrypted correctly for TLS12 */ + { + struct s2n_connection *conn; + struct s2n_config *config; + uint64_t current_time; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(config = s2n_config_new()); + + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(config->wall_clock(config->sys_clock_ctx, ¤t_time)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), + ticket_key.data, ticket_key.size, current_time / ONE_SEC_IN_NANOS)); + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->actual_protocol_version = S2N_TLS12; + conn->handshake.handshake_type = NEGOTIATED; + + struct s2n_blob secret = { 0 }; + struct s2n_stuffer secret_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&secret, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN)); + EXPECT_SUCCESS(s2n_stuffer_init(&secret_stuffer, &secret)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&secret_stuffer, test_master_secret.data, S2N_TLS_SECRET_LEN)); + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + + EXPECT_SUCCESS(s2n_encrypt_session_ticket(conn, &conn->client_ticket_to_decrypt)); + EXPECT_NOT_EQUAL(s2n_stuffer_data_available(&conn->client_ticket_to_decrypt), 0); + + /* Wiping the master secret to prove that the decryption function actually writes the master secret */ + memset(conn->secrets.version.tls12.master_secret, 0, test_master_secret.size); + + EXPECT_SUCCESS(s2n_decrypt_session_ticket(conn, &conn->client_ticket_to_decrypt)); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->client_ticket_to_decrypt), 0); + + /* Check decryption was successful by comparing master key */ + EXPECT_BYTEARRAY_EQUAL(conn->secrets.version.tls12.master_secret, test_master_secret.data, test_master_secret.size); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Check session ticket can be decrypted with a small secret in TLS13 session resumption. */ + { + struct s2n_connection *conn; + struct s2n_config *config; + uint64_t current_time; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(config = s2n_config_new()); + + /* Setting up session resumption encryption key */ + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(config->wall_clock(config->sys_clock_ctx, ¤t_time)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), + ticket_key.data, ticket_key.size, current_time / ONE_SEC_IN_NANOS)); + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + conn->tls13_ticket_fields = (struct s2n_ticket_fields){ .ticket_age_add = 1 }; + EXPECT_SUCCESS(s2n_dup(&test_session_secret, &conn->tls13_ticket_fields.session_secret)); + + /* This secret is smaller than the maximum secret length */ + EXPECT_TRUE(conn->tls13_ticket_fields.session_secret.size < S2N_TLS_SECRET_LEN); + + EXPECT_SUCCESS(s2n_encrypt_session_ticket(conn, &output)); + EXPECT_SUCCESS(s2n_decrypt_session_ticket(conn, &output)); + + EXPECT_EQUAL(s2n_stuffer_data_available(&output), 0); + + /* Check decryption was successful */ + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, 0, (void **) &psk)); + EXPECT_NOT_NULL(psk); + EXPECT_EQUAL(psk->hmac_alg, s2n_tls13_aes_128_gcm_sha256.prf_alg); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Check session ticket can be decrypted with the maximum size secret in TLS13 session resumption. */ + { + struct s2n_connection *conn; + struct s2n_config *config; + uint64_t current_time; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(config = s2n_config_new()); + + /* Setting up session resumption encryption key */ + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(config->wall_clock(config->sys_clock_ctx, ¤t_time)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), + ticket_key.data, ticket_key.size, current_time / ONE_SEC_IN_NANOS)); + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + conn->tls13_ticket_fields = (struct s2n_ticket_fields){ .ticket_age_add = 1 }; + EXPECT_SUCCESS(s2n_dup(&test_master_secret, &conn->tls13_ticket_fields.session_secret)); + + /* This secret is equal to the maximum secret length */ + EXPECT_EQUAL(conn->tls13_ticket_fields.session_secret.size, S2N_TLS_SECRET_LEN); + + EXPECT_SUCCESS(s2n_encrypt_session_ticket(conn, &output)); + EXPECT_SUCCESS(s2n_decrypt_session_ticket(conn, &output)); + + EXPECT_EQUAL(s2n_stuffer_data_available(&output), 0); + + /* Check decryption was successful */ + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, 0, (void **) &psk)); + EXPECT_NOT_NULL(psk); + EXPECT_EQUAL(psk->hmac_alg, s2n_tls13_aes_128_gcm_sha256.prf_alg); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Check session ticket is correct when using early data with TLS1.3. */ + { + const uint8_t test_early_data_context[] = "context"; + const char test_app_proto[] = "https"; + + /* Setting up session resumption encryption key */ + uint64_t current_time = 0; + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(config->wall_clock(config->sys_clock_ctx, ¤t_time)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), + ticket_key.data, ticket_key.size, current_time / ONE_SEC_IN_NANOS)); + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(conn, 10)); + EXPECT_SUCCESS(s2n_connection_set_server_early_data_context(conn, test_early_data_context, sizeof(test_early_data_context))); + EXPECT_MEMCPY_SUCCESS(conn->application_protocol, test_app_proto, sizeof(test_app_proto)); + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + conn->tls13_ticket_fields = (struct s2n_ticket_fields){ .ticket_age_add = 1 }; + EXPECT_SUCCESS(s2n_dup(&test_master_secret, &conn->tls13_ticket_fields.session_secret)); + + EXPECT_SUCCESS(s2n_encrypt_session_ticket(conn, &output)); + EXPECT_SUCCESS(s2n_decrypt_session_ticket(conn, &output)); + + EXPECT_EQUAL(s2n_stuffer_data_available(&output), 0); + + /* Check decryption was successful */ + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, 0, (void **) &psk)); + EXPECT_NOT_NULL(psk); + EXPECT_EQUAL(psk->early_data_config.cipher_suite, &s2n_tls13_aes_128_gcm_sha256); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + }; + + /* s2n_config_set_initial_ticket_count */ + { + struct s2n_connection *conn; + struct s2n_config *config; + uint8_t num_tickets = 1; + + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_EQUAL(conn->tickets_to_send, 0); + EXPECT_FALSE(config->use_tickets); + + EXPECT_SUCCESS(s2n_config_set_initial_ticket_count(config, 0)); + EXPECT_TRUE(config->use_tickets); + + EXPECT_SUCCESS(s2n_config_set_initial_ticket_count(config, num_tickets)); + EXPECT_TRUE(config->use_tickets); + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_EQUAL(conn->tickets_to_send, num_tickets); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* s2n_connection_add_new_tickets_to_send */ + { + /* New number of session tickets can be set */ + { + struct s2n_connection *conn; + uint8_t original_num_tickets = 1; + uint8_t new_num_tickets = 10; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + conn->tickets_to_send = original_num_tickets; + + EXPECT_SUCCESS(s2n_connection_add_new_tickets_to_send(conn, new_num_tickets)); + + EXPECT_EQUAL(conn->tickets_to_send, original_num_tickets + new_num_tickets); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Overflow error is caught */ + { + struct s2n_connection *conn; + uint8_t new_num_tickets = 1; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + conn->tickets_to_send = UINT16_MAX; + + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_add_new_tickets_to_send(conn, new_num_tickets), S2N_ERR_INTEGER_OVERFLOW); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Fails if keying material expired */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + conn->tickets_to_send = UINT16_MAX; + + DEFER_CLEANUP(struct s2n_psk *chosen_psk = s2n_test_psk_new(conn), s2n_psk_free); + EXPECT_NOT_NULL(chosen_psk); + chosen_psk->type = S2N_PSK_TYPE_RESUMPTION; + chosen_psk->keying_material_expiration = 0; + conn->psk_params.chosen_psk = chosen_psk; + + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_add_new_tickets_to_send(conn, 1), S2N_ERR_KEYING_MATERIAL_EXPIRED); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* s2n_config_set_session_ticket_cb */ + { + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + void *ctx = NULL; + + /* Safety check */ + { + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_session_ticket_cb(NULL, s2n_test_session_ticket_callback, ctx), S2N_ERR_NULL); + }; + + EXPECT_NULL(config->session_ticket_cb); + EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_callback, ctx)); + EXPECT_EQUAL(config->session_ticket_cb, s2n_test_session_ticket_callback); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* s2n_session_ticket_get_data_len */ + { + /* Safety checks */ + { + struct s2n_session_ticket session_ticket = { 0 }; + size_t data_len = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_session_ticket_get_data_len(NULL, &data_len), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_session_ticket_get_data_len(&session_ticket, NULL), S2N_ERR_NULL); + }; + + /* Empty ticket */ + { + struct s2n_session_ticket session_ticket = { 0 }; + size_t data_len = 0; + EXPECT_SUCCESS(s2n_session_ticket_get_data_len(&session_ticket, &data_len)); + EXPECT_EQUAL(data_len, 0); + }; + + /* Valid ticket */ + { + uint8_t ticket_data[] = "session ticket data"; + struct s2n_blob ticket_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&ticket_blob, ticket_data, sizeof(ticket_data))); + struct s2n_session_ticket session_ticket = { .ticket_data = ticket_blob }; + + size_t data_len = 0; + EXPECT_SUCCESS(s2n_session_ticket_get_data_len(&session_ticket, &data_len)); + EXPECT_EQUAL(data_len, sizeof(ticket_data)); + }; + }; + + /* s2n_session_ticket_get_data */ + { + /* Safety checks */ + { + struct s2n_session_ticket session_ticket = { 0 }; + size_t max_data_len = 0; + uint8_t *data = NULL; + EXPECT_FAILURE_WITH_ERRNO(s2n_session_ticket_get_data(NULL, max_data_len, data), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_session_ticket_get_data(&session_ticket, max_data_len, NULL), S2N_ERR_NULL); + }; + + /* Valid ticket */ + { + uint8_t ticket_data[] = "session ticket data"; + struct s2n_blob ticket_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&ticket_blob, ticket_data, sizeof(ticket_data))); + struct s2n_session_ticket session_ticket = { .ticket_data = ticket_blob }; + + uint8_t data[sizeof(ticket_data)]; + size_t max_data_len = sizeof(data); + EXPECT_SUCCESS(s2n_session_ticket_get_data(&session_ticket, max_data_len, data)); + EXPECT_BYTEARRAY_EQUAL(data, ticket_data, sizeof(ticket_data)); + }; + + /* Ticket data is larger than customer buffer */ + { + uint8_t ticket_data[] = "session ticket data"; + struct s2n_blob ticket_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&ticket_blob, ticket_data, sizeof(ticket_data))); + struct s2n_session_ticket session_ticket = { .ticket_data = ticket_blob }; + + uint8_t data[sizeof(ticket_data) - 1]; + size_t max_data_len = sizeof(data); + EXPECT_FAILURE_WITH_ERRNO(s2n_session_ticket_get_data(&session_ticket, max_data_len, data), + S2N_ERR_SERIALIZED_SESSION_STATE_TOO_LONG); + }; + }; + + /* s2n_session_ticket_get_lifetime */ + { + /* Safety checks */ + { + struct s2n_session_ticket session_ticket = { 0 }; + uint32_t lifetime = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_session_ticket_get_lifetime(NULL, &lifetime), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_session_ticket_get_lifetime(&session_ticket, NULL), S2N_ERR_NULL); + }; + + /* Valid lifetime */ + { + uint32_t lifetime = 100; + struct s2n_session_ticket session_ticket = { .session_lifetime = lifetime }; + + uint32_t ticket_lifetime = 0; + EXPECT_SUCCESS(s2n_session_ticket_get_lifetime(&session_ticket, &ticket_lifetime)); + EXPECT_EQUAL(lifetime, ticket_lifetime); + }; + }; + + /* s2n_connection_set_server_keying_material_lifetime */ + { + struct s2n_connection conn = { 0 }; + + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_set_server_keying_material_lifetime(NULL, 0), S2N_ERR_NULL); + + EXPECT_SUCCESS(s2n_connection_set_server_keying_material_lifetime(&conn, 1)); + EXPECT_EQUAL(conn.server_keying_material_lifetime, 1); + + EXPECT_SUCCESS(s2n_connection_set_server_keying_material_lifetime(&conn, 0)); + EXPECT_EQUAL(conn.server_keying_material_lifetime, 0); + + EXPECT_SUCCESS(s2n_connection_set_server_keying_material_lifetime(&conn, UINT32_MAX)); + EXPECT_EQUAL(conn.server_keying_material_lifetime, UINT32_MAX); + }; + + /* s2n_allowed_to_cache_connection */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_REQUIRED)); + + /* Turn session caching on */ + config->use_session_cache = 1; + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Cannot cache connection if client auth is required */ + EXPECT_FALSE(s2n_allowed_to_cache_connection(conn)); + + EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_NONE)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Allowed to cache connection if client auth is not required */ + EXPECT_TRUE(s2n_allowed_to_cache_connection(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test s2n_connection_set_session */ + { + uint8_t server_state[] = "encrypted state"; + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_wall_clock(config, mock_time, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + + /* Invalid TLS1.2 tickets should not modify the connection. + * + * This basically tests that deserialization errors aren't fatal / unrecoverable. + */ + { + DEFER_CLEANUP(struct s2n_stuffer ticket_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&ticket_stuffer, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&ticket_stuffer, S2N_STATE_WITH_SESSION_TICKET)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&ticket_stuffer, sizeof(server_state))); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&ticket_stuffer, server_state, sizeof(server_state))); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&ticket_stuffer, + tls12_ticket_with_ems, sizeof(tls12_ticket_with_ems))); + + size_t ticket_size = s2n_stuffer_data_available(&ticket_stuffer); + uint8_t *ticket_bytes = s2n_stuffer_raw_read(&ticket_stuffer, ticket_size); + EXPECT_NOT_NULL(ticket_bytes); + + /* Test that deserialize modifies the connection in limited ways. + * + * No mechanism exists to do more than a shallow comparison of two connections. + * To prove that a shallow comparison is sufficient, we need to prove + * that s2n_deserialize_resumption_state does not modify the memory + * associated with pointers on the connection. To prove that, we can + * test that s2n_deserialize_resumption_state can successfully operate + * on an s2n_connection with a limited number of its pointers initialized. + */ + { + struct s2n_connection empty_conn = { 0 }; + struct s2n_crypto_parameters crypto_params = { .cipher_suite = &s2n_null_cipher_suite }; + empty_conn.secure = &crypto_params; + empty_conn.mode = S2N_CLIENT; + /* We can safely assume that a connection doesn't modify its config */ + empty_conn.config = config; + EXPECT_SUCCESS(s2n_connection_set_session(&empty_conn, ticket_bytes, ticket_size)); + EXPECT_SUCCESS(s2n_free(&empty_conn.client_ticket)); + }; + + /* Test that deserialize does not modify the connection on parsing failure, + * given the constraints proven above. + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Trigger the deserialization failure as late as possible. + * The last byte is optional, so drop the last two bytes. + */ + size_t bad_ticket_size = ticket_size - 2; + + /* Test the connection is not modified by a failed deserialize */ + uint8_t saved_conn[sizeof(struct s2n_connection)] = { 0 }; + EXPECT_MEMCPY_SUCCESS(saved_conn, conn, sizeof(struct s2n_connection)); + uint8_t saved_secure[sizeof(struct s2n_crypto_parameters)] = { 0 }; + EXPECT_MEMCPY_SUCCESS(saved_secure, conn->secure, sizeof(struct s2n_crypto_parameters)); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_set_session(conn, ticket_bytes, bad_ticket_size), + S2N_ERR_STUFFER_OUT_OF_DATA); + EXPECT_BYTEARRAY_EQUAL(saved_conn, conn, sizeof(struct s2n_connection)); + EXPECT_BYTEARRAY_EQUAL(saved_secure, conn->secure, sizeof(struct s2n_crypto_parameters)); + + /* No valid ticket */ + EXPECT_EQUAL(s2n_connection_get_session_length(conn), 0); + }; + + /* Test that deserialize does not modify the connection on a cipher selection failure, + * given the constraints proven above. + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Trigger the deserialization failure when checking the validity + * of the chosen cipher, not when parsing the ticket. + */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "null")); + + /* Test the connection is not modified by a failed deserialize */ + uint8_t saved_conn[sizeof(struct s2n_connection)] = { 0 }; + EXPECT_MEMCPY_SUCCESS(saved_conn, conn, sizeof(struct s2n_connection)); + uint8_t saved_secure[sizeof(struct s2n_crypto_parameters)] = { 0 }; + EXPECT_MEMCPY_SUCCESS(saved_secure, conn->secure, sizeof(struct s2n_crypto_parameters)); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_set_session(conn, ticket_bytes, ticket_size), + S2N_ERR_CIPHER_NOT_SUPPORTED); + EXPECT_BYTEARRAY_EQUAL(saved_conn, conn, sizeof(struct s2n_connection)); + EXPECT_BYTEARRAY_EQUAL(saved_secure, conn->secure, sizeof(struct s2n_crypto_parameters)); + + /* No valid ticket */ + EXPECT_EQUAL(s2n_connection_get_session_length(conn), 0); + }; + }; + + /* Invalid TLS1.3 tickets should not modify the connection. + * + * This basically tests that deserialization errors aren't fatal / unrecoverable. + */ + { + DEFER_CLEANUP(struct s2n_stuffer ticket_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&ticket_stuffer, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&ticket_stuffer, S2N_STATE_WITH_SESSION_TICKET)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&ticket_stuffer, sizeof(server_state))); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&ticket_stuffer, server_state, sizeof(server_state))); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&ticket_stuffer, tls13_ticket, sizeof(tls13_ticket))); + + size_t ticket_size = s2n_stuffer_data_available(&ticket_stuffer); + uint8_t *ticket_bytes = s2n_stuffer_raw_read(&ticket_stuffer, ticket_size); + EXPECT_NOT_NULL(ticket_bytes); + + /* Test that the connection is only shallowly modified by a successful deserialize. + * + * No mechanism exists to do more than a shallow comparison of two connections. + * To prove that a shallow comparison is sufficient, we need to prove + * that s2n_deserialize_resumption_state does not modify the memory + * associated with pointers on the connection. To prove that, we can + * test that s2n_deserialize_resumption_state can successfully operate + * on an s2n_connection with none of its pointers initialized. + */ + { + struct s2n_connection empty_conn = { 0 }; + empty_conn.mode = S2N_CLIENT; + /* We can safely assume that a connection doesn't modify its config */ + empty_conn.config = config; + EXPECT_SUCCESS(s2n_connection_set_session(&empty_conn, ticket_bytes, ticket_size)); + EXPECT_OK(s2n_psk_parameters_wipe(&empty_conn.psk_params)); + }; + + /* Test that deserialize does not modify the connection on failure, + * given the constraints proven above. + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Trigger the deserialization failure as late as possible in parsing. + * Drop the last byte we expect. + */ + size_t bad_ticket_size = ticket_size - 1; + + /* Test the connection is not modified by a failed deserialize */ + uint8_t saved[sizeof(struct s2n_connection)] = { 0 }; + EXPECT_MEMCPY_SUCCESS(saved, conn, sizeof(struct s2n_connection)); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_set_session(conn, ticket_bytes, bad_ticket_size), + S2N_ERR_STUFFER_OUT_OF_DATA); + EXPECT_BYTEARRAY_EQUAL(saved, conn, sizeof(struct s2n_connection)); + + /* No valid ticket */ + EXPECT_EQUAL(s2n_connection_get_session_length(conn), 0); + }; + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_rfc5952_test.c b/tests/unit/s2n_rfc5952_test.c new file mode 100644 index 00000000000..47c8f333a2c --- /dev/null +++ b/tests/unit/s2n_rfc5952_test.c @@ -0,0 +1,91 @@ +/* + * 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 "s2n_test.h" + +#if defined(__FreeBSD__) || defined(__OpenBSD__) + #include + #include + #include +#endif + +#include "utils/s2n_rfc5952.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + uint8_t ipv4[4]; + uint8_t ipv6[16]; + + uint8_t ipv4_buf[sizeof("255.255.255.255")]; + uint8_t ipv6_buf[sizeof("1111:2222:3333:4444:5555:6666:7777:8888")]; + + struct s2n_blob ipv4_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&ipv4_blob, ipv4_buf, sizeof(ipv4_buf))); + struct s2n_blob ipv6_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&ipv6_blob, ipv6_buf, sizeof(ipv6_buf))); + + EXPECT_SUCCESS(inet_pton(AF_INET, "111.222.111.111", ipv4)); + EXPECT_OK(s2n_inet_ntop(AF_INET, ipv4, &ipv4_blob)); + EXPECT_EQUAL(strcmp("111.222.111.111", (char *) ipv4_buf), 0); + + EXPECT_SUCCESS(inet_pton(AF_INET, "0.0.0.0", ipv4)); + EXPECT_OK(s2n_inet_ntop(AF_INET, ipv4, &ipv4_blob)); + EXPECT_EQUAL(strcmp("0.0.0.0", (char *) ipv4_buf), 0); + + EXPECT_SUCCESS(inet_pton(AF_INET, "100.104.123.1", ipv4)); + EXPECT_OK(s2n_inet_ntop(AF_INET, ipv4, &ipv4_blob)); + EXPECT_EQUAL(strcmp("100.104.123.1", (char *) ipv4_buf), 0); + + EXPECT_SUCCESS(inet_pton(AF_INET, "255.255.255.255", ipv4)); + EXPECT_OK(s2n_inet_ntop(AF_INET, ipv4, &ipv4_blob)); + EXPECT_EQUAL(strcmp("255.255.255.255", (char *) ipv4_buf), 0); + + EXPECT_SUCCESS(inet_pton(AF_INET6, "2001:db8:0:0:0:0:2:1", ipv6)); + EXPECT_OK(s2n_inet_ntop(AF_INET6, ipv6, &ipv6_blob)); + EXPECT_EQUAL(strcmp("2001:db8::2:1", (char *) ipv6_buf), 0); + + EXPECT_SUCCESS(inet_pton(AF_INET6, "2001:db8::1", ipv6)); + EXPECT_OK(s2n_inet_ntop(AF_INET6, ipv6, &ipv6_blob)); + EXPECT_EQUAL(strcmp("2001:db8::1", (char *) ipv6_buf), 0); + + EXPECT_SUCCESS(inet_pton(AF_INET6, "2001:db8:0:1:1:1:1:1", ipv6)); + EXPECT_OK(s2n_inet_ntop(AF_INET6, ipv6, &ipv6_blob)); + EXPECT_EQUAL(strcmp("2001:db8:0:1:1:1:1:1", (char *) ipv6_buf), 0); + + EXPECT_SUCCESS(inet_pton(AF_INET6, "2001:db8::1:0:0:1", ipv6)); + EXPECT_OK(s2n_inet_ntop(AF_INET6, ipv6, &ipv6_blob)); + EXPECT_EQUAL(strcmp("2001:db8::1:0:0:1", (char *) ipv6_buf), 0); + + EXPECT_SUCCESS(inet_pton(AF_INET6, "0:0:0:0:0:0:0:1", ipv6)); + EXPECT_OK(s2n_inet_ntop(AF_INET6, ipv6, &ipv6_blob)); + EXPECT_EQUAL(strcmp("::1", (char *) ipv6_buf), 0); + + EXPECT_SUCCESS(inet_pton(AF_INET6, "0:0:0:0:0:0:0:0", ipv6)); + EXPECT_OK(s2n_inet_ntop(AF_INET6, ipv6, &ipv6_blob)); + EXPECT_EQUAL(strcmp("::", (char *) ipv6_buf), 0); + +/* Prevents build failure on Mac */ +#ifndef AF_BLUETOOTH + #define AF_BLUETOOTH 31 +#endif + + EXPECT_ERROR_WITH_ERRNO(s2n_inet_ntop(AF_BLUETOOTH, ipv6, &ipv6_blob), S2N_ERR_INVALID_ARGUMENT); + END_TEST(); +} diff --git a/tests/unit/s2n_rsa_pss_rsae_test.c b/tests/unit/s2n_rsa_pss_rsae_test.c new file mode 100644 index 00000000000..2b6d373077b --- /dev/null +++ b/tests/unit/s2n_rsa_pss_rsae_test.c @@ -0,0 +1,321 @@ +/* + * 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 "crypto/s2n_hash.h" +#include "crypto/s2n_rsa.h" +#include "crypto/s2n_rsa_pss.h" +#include "error/s2n_errno.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" + +#define HASH_ALG S2N_HASH_SHA256 +#define HASH_LENGTH 256 +#define RANDOM_BLOB_SIZE RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE + +#define hash_state_new(name, input) \ + DEFER_CLEANUP(struct s2n_hash_state name = { 0 }, s2n_hash_free); \ + EXPECT_SUCCESS(s2n_hash_new(&name)); \ + EXPECT_SUCCESS(s2n_hash_init(&name, HASH_ALG)); \ + EXPECT_SUCCESS(s2n_hash_update(&name, (input).data, (input).size)); + +#define hash_state_for_alg_new(name, hash_alg, input) \ + DEFER_CLEANUP(struct s2n_hash_state name = { 0 }, s2n_hash_free); \ + EXPECT_SUCCESS(s2n_hash_new(&name)); \ + EXPECT_SUCCESS(s2n_hash_init(&name, hash_alg)); \ + EXPECT_SUCCESS(s2n_hash_update(&name, (input).data, (input).size)); + +struct s2n_rsa_pss_test_case { + s2n_hash_algorithm hash_alg; + const char *key_param_n; + const char *key_param_e; + const char *key_param_d; + const char *message; + const char *signature; + bool should_pass; +}; + +/* Small selection of NIST test vectors, taken from: + * https://csrc.nist.gov/Projects/cryptographic-algorithm-validation-program/Digital-Signatures + * + * Note that TLS1.3 requires that the saltlen match the digest length. + * Not all NIST vectors satisfy that requirement. + * Specifically, none of the FIPS 186-2 vectors satisfy that requirement: they all use a 20 byte salt. + * So the tests here are a selection of the FIPS 186-3 vectors. + */ +const struct s2n_rsa_pss_test_case test_cases[] = { + { + .hash_alg = S2N_HASH_SHA224, + .key_param_n = "acfdfb1dcb1459b489cd4a8c9fab64a7da4f044bef1c506f0872e9476f3357abda509d9fb1db6a4f5306c40c058826253171cfedbc160776a48ec35b655bb9963286b6aece1c77dff987a0aae9720ba035dda67f317101bd3cd4e6caac867a8c38b87067938e96e72df1875f94e43e4c06f7a86a1dbe07836ee69763eee29bc13ca906d7740c29e651872a9ec7c6237f7c8290bb0800a030b323d09e7c903751d21a224266f9d6c94c17a4c0cd8175ea67b9d9020f2b3f31a96206084cddb2acef70b11ae25a46c4f6817c4813466d7cac76b27927145bd499ff87f22a946b688e980a00a3d54c72ab9c2c88a55a3ea4c6784068673532737cbe4799e98bd711", + .key_param_e = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b29bc5", + .key_param_d = "29d83f1a032c6a1702d25eb47ee39a09969b83b3ecd764c9ce9cbe08e4dba20d711931820a6d90d26cd666d4eb842f66c12f189d13f67df06eb57edd4e37290a4adbf65a0fe5684e95d8287d9df3b32d3ca674bac95d13adb11dc40121f720df8dc2b5551fd98e167159e2eecdbcf7273a53ddafd8ee08ec9f857c1cfae59c282d3b13cc7dfa8fa85f68517460186f5b72faebe431d262af40d04ad00fd86e2b2cad64d86dcb5f738fc627eb4fcc3fd5fdd5742bf48959701a841016ae6f552a0e40c9279c544baf7c9c467e4958c7018c9f2a4becd9e03f40b4e854acb6534b1b3b5fd27fc085318fd7174a48660ef647574e0f2b99783fb17cece00096d601", + .message = "7ced8a54c8e35ef5c87d03ee6357b658e2e528eda55ad30f14c88d0cd9895ea04ddf8fbb2fd703859c73cb9f3b07f4acb9e4a311753465f87c25c09bb74a0ebf633e8b7ec28aac4a10c8b22fb9098058c975a9d5a431ce9cf78627cdee3f5f3aa852a526e8c3004d0dc6e22544240164fcdf62c29a19b6006e32ea29e631fa18", + .signature = "8c074bae48454875aefa2b7ea090d11d8860d7cbee5ee4c3ed02cb45aadb0b4516872b0e4521789d503b4e70092ca2a0c7a88efb7d74c63ce8dffcf06995af7c9567a8df05a01b243c5f3edcfa3922d06967bec9d0faad2c84486dd38602a416ef253e4a28f74ca290e4d743accccd204d8b136dd197e7a2f25a2707f339c6ba444c19bc047dff0584c479ab07c2ae68f219c3c430f19cae3f711c0efab8d09f85ab66ae948c357db67078a359b9c746d2d66b31486c83765ab097b540b5e6f626c9111a295855dff5c2acea102f6a29b9569909dad0d4c79a941a3e71b3137dc68ae2296b6f8175bfab205432e409be9075d12580b5c14924dd53c3d44745d7", + .should_pass = true, + }, + { + .hash_alg = S2N_HASH_SHA224, + .key_param_n = "acfdfb1dcb1459b489cd4a8c9fab64a7da4f044bef1c506f0872e9476f3357abda509d9fb1db6a4f5306c40c058826253171cfedbc160776a48ec35b655bb9963286b6aece1c77dff987a0aae9720ba035dda67f317101bd3cd4e6caac867a8c38b87067938e96e72df1875f94e43e4c06f7a86a1dbe07836ee69763eee29bc13ca906d7740c29e651872a9ec7c6237f7c8290bb0800a030b323d09e7c903751d21a224266f9d6c94c17a4c0cd8175ea67b9d9020f2b3f31a96206084cddb2acef70b11ae25a46c4f6817c4813466d7cac76b27927145bd499ff87f22a946b688e980a00a3d54c72ab9c2c88a55a3ea4c6784068673532737cbe4799e98bd711", + .key_param_e = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b29bc5", + .key_param_d = "29d83f1a032c6a1702d25eb47ee39a09969b83b3ecd764c9ce9cbe08e4dba20d711931820a6d90d26cd666d4eb842f66c12f189d13f67df06eb57edd4e37290a4adbf65a0fe5684e95d8287d9df3b32d3ca674bac95d13adb11dc40121f720df8dc2b5551fd98e167159e2eecdbcf7273a53ddafd8ee08ec9f857c1cfae59c282d3b13cc7dfa8fa85f68517460186f5b72faebe431d262af40d04ad00fd86e2b2cad64d86dcb5f738fc627eb4fcc3fd5fdd5742bf48959701a841016ae6f552a0e40c9279c544baf7c9c467e4958c7018c9f2a4becd9e03f40b4e854acb6534b1b3b5fd27fc085318fd7174a48660ef647574e0f2b99783fb17cece00096d601", + .message = "23708e055e891826f8011b1f48a1c7ad24b5d008f9c91ec31ab1c5f358cc3793d389b927102913a04bc78c800e96153e026bc5ec067b85177e650defed730fa7c71cb11f80ce41c1e9eb24a9bc121008759f7cca6475547601fbf0567f6447d9a4346035d7ff0a507b74cde17b9b20d2265bdcea3e3ff1a84b7a5872352849ef", + .signature = "a8cbf34ffefba916abf4961a6a1032c01bc4aeb8278ba42a18f4f67fdfeab49ebb05cc289aba475a9667649c6ecf6a8ca17ad81f17d5109807c2b260ac440e134e55ee429213cb8e8a2314e7049019fe95ba36e3ec1ed1e77108e4fd84abee28423996e3fbba3d38b285a055d6390d3d8f5b21415e6874ab37423efea5726d47bbfed125494a2164eab12394f89843f8d8a5bd3bf05b31b598e4caa0ad2a8cad4826d238e4ab8d005379f100f97398d896684b08753c89863c224d65a475443d51a91ed668cac691ea7030aa737c3eeeb9db33cff17ce9a8e399bb2b3cdcf2444567398db196fc86ba76114dbd13096cabd692b083fea1c7a94e6564c2e8c093", + .should_pass = false, /* (1 - Message changed) */ + }, + { + .hash_alg = S2N_HASH_SHA224, + .key_param_n = "acfdfb1dcb1459b489cd4a8c9fab64a7da4f044bef1c506f0872e9476f3357abda509d9fb1db6a4f5306c40c058826253171cfedbc160776a48ec35b655bb9963286b6aece1c77dff987a0aae9720ba035dda67f317101bd3cd4e6caac867a8c38b87067938e96e72df1875f94e43e4c06f7a86a1dbe07836ee69763eee29bc13ca906d7740c29e651872a9ec7c6237f7c8290bb0800a030b323d09e7c903751d21a224266f9d6c94c17a4c0cd8175ea67b9d9020f2b3f31a96206084cddb2acef70b11ae25a46c4f6817c4813466d7cac76b27927145bd499ff87f22a946b688e980a00a3d54c72ab9c2c88a55a3ea4c6784068673532737cbe4799e98bd711", + .key_param_e = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000eaf989", + .key_param_d = "29d83f1a032c6a1702d25eb47ee39a09969b83b3ecd764c9ce9cbe08e4dba20d711931820a6d90d26cd666d4eb842f66c12f189d13f67df06eb57edd4e37290a4adbf65a0fe5684e95d8287d9df3b32d3ca674bac95d13adb11dc40121f720df8dc2b5551fd98e167159e2eecdbcf7273a53ddafd8ee08ec9f857c1cfae59c282d3b13cc7dfa8fa85f68517460186f5b72faebe431d262af40d04ad00fd86e2b2cad64d86dcb5f738fc627eb4fcc3fd5fdd5742bf48959701a841016ae6f552a0e40c9279c544baf7c9c467e4958c7018c9f2a4becd9e03f40b4e854acb6534b1b3b5fd27fc085318fd7174a48660ef647574e0f2b99783fb17cece00096d601", + .message = "cd69ac8d1c46f54d46cc9aff98e078521357a36177b91392a0459abe15351993df43f437976bc32ec3f34b7e63a9ac11c66cdb5aa2ed533c64b70827c56d9d849e53d653fd10501278b370e1c1f399e57bbba2ea4ce6874c34475e171dec8d6db3a33b2875be04c10c14f171dc48a795da4ede579d2a158bb7fb84d745395317", + .signature = "5725c1ac60b2d610977fae6c52a94155d687d575aa2f5cee817dbf77e1ed692266c0ff14a2105b343dcbb075c43ee6a839a64bd1d151d3801e62f9d76c8d94787c29d1e88ca2ea7f23d7ffe1aac3d5072f556a8087516df5dff9fcde5b0e849d08f1b42707d2cda2eac462bc8810737e03d47dc8240db73c3ccf7106886f8ae9c4c36473493cb5242e2d70bd5c36be35ce1e5aa5286d6439b247fb64ba06af222b133f05d39e6a44a1ad6ad567b9e0b5655486af0b9cfef1d714f311dfcd983bd90674f964f64243be02feb3a202cd5f945505826fda112bba3e5b837bdcb9e974953ee7b1ad7a6e21a0a64268d52eaba083c6719632c21e6615fe4395ce3f53", + .should_pass = false, /* (2 - Public Key e changed ) */ + }, + { + .hash_alg = S2N_HASH_SHA224, + .key_param_n = "acfdfb1dcb1459b489cd4a8c9fab64a7da4f044bef1c506f0872e9476f3357abda509d9fb1db6a4f5306c40c058826253171cfedbc160776a48ec35b655bb9963286b6aece1c77dff987a0aae9720ba035dda67f317101bd3cd4e6caac867a8c38b87067938e96e72df1875f94e43e4c06f7a86a1dbe07836ee69763eee29bc13ca906d7740c29e651872a9ec7c6237f7c8290bb0800a030b323d09e7c903751d21a224266f9d6c94c17a4c0cd8175ea67b9d9020f2b3f31a96206084cddb2acef70b11ae25a46c4f6817c4813466d7cac76b27927145bd499ff87f22a946b688e980a00a3d54c72ab9c2c88a55a3ea4c6784068673532737cbe4799e98bd711", + .key_param_e = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b29bc5", + .key_param_d = "29d83f1a032c6a1702d25eb47ee39a09969b83b3ecd764c9ce9cbe08e4dba20d711931820a6d90d26cd666d4eb842f66c12f189d13f67df06eb57edd4e37290a4adbf65a0fe5684e95d8287d9df3b32d3ca674bac95d13adb11dc40121f720df8dc2b5551fd98e167159e2eecdbcf7273a53ddafd8ee08ec9f857c1cfae59c282d3b13cc7dfa8fa85f68517460186f5b72faebe431d262af40d04ad00fd86e2b2cad64d86dcb5f738fc627eb4fcc3fd5fdd5742bf48959701a841016ae6f552a0e40c9279c544baf7c9c467e4958c7018c9f2a4becd9e03f40b4e854acb6534b1b3b5fd27fc085318fd7174a48660ef647574e0f2b99783fb17cece00096d601", + .message = "0af6365d6065dfc44795d87a8119fb44cb8223e0fb26e1eb4b207102f598ec280045be67592f5bba25ba2e2b56e0d2397cbe857cde52da8cca83ae1e29615c7056af35e8319f2af86fdccc4434cd7707e319c9b2356659d78867a6467a154e76b73c81260f3ab443cc039a0d42695076a79bd8ca25ebc8952ed443c2103b2900", + .signature = "3b32dab565d2f0a5ac267ba8fd4c381f5b0c40f20c19f0dc7b236232a8bdb55c1c33f13f4f74266d7628767763582bb0fb526263e2a4521e09a3207ee5fd7cfdcdac85ccffe3e1048f590dadc19035bf2f3b5b954e8c783f6bd3e99d98930aea8eced5e3119331be1d606410d16d0e5ef069179cd6b513b94b36c1cfe6ad64ad285e818b58c4c89bf9330c4bcec96d08ba21a35cd4851da1f405d928df3fcfeb85108171bb4ae5b0310193bd8b5b861983b7a2483ba6bccfbf2d6e15bac1e2f2f8363c6154546744e301fd2661ebb3a29030d269c8954ace992b075c63ac12a992eed4956cb2a48495d82c47fa68e66740d38a80caa69190b19730ed7d72835b", + .should_pass = false, /* (3 - Signature changed ) */ + }, + { + .hash_alg = S2N_HASH_SHA224, + .key_param_n = "acfdfb1dcb1459b489cd4a8c9fab64a7da4f044bef1c506f0872e9476f3357abda509d9fb1db6a4f5306c40c058826253171cfedbc160776a48ec35b655bb9963286b6aece1c77dff987a0aae9720ba035dda67f317101bd3cd4e6caac867a8c38b87067938e96e72df1875f94e43e4c06f7a86a1dbe07836ee69763eee29bc13ca906d7740c29e651872a9ec7c6237f7c8290bb0800a030b323d09e7c903751d21a224266f9d6c94c17a4c0cd8175ea67b9d9020f2b3f31a96206084cddb2acef70b11ae25a46c4f6817c4813466d7cac76b27927145bd499ff87f22a946b688e980a00a3d54c72ab9c2c88a55a3ea4c6784068673532737cbe4799e98bd711", + .key_param_e = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b29bc5", + .key_param_d = "29d83f1a032c6a1702d25eb47ee39a09969b83b3ecd764c9ce9cbe08e4dba20d711931820a6d90d26cd666d4eb842f66c12f189d13f67df06eb57edd4e37290a4adbf65a0fe5684e95d8287d9df3b32d3ca674bac95d13adb11dc40121f720df8dc2b5551fd98e167159e2eecdbcf7273a53ddafd8ee08ec9f857c1cfae59c282d3b13cc7dfa8fa85f68517460186f5b72faebe431d262af40d04ad00fd86e2b2cad64d86dcb5f738fc627eb4fcc3fd5fdd5742bf48959701a841016ae6f552a0e40c9279c544baf7c9c467e4958c7018c9f2a4becd9e03f40b4e854acb6534b1b3b5fd27fc085318fd7174a48660ef647574e0f2b99783fb17cece00096d601", + .message = "c9a121fa15bffefb864fe3cfc2b1bf775886be3ff5151c40daee3c288dccf43ecfc02ba0cf8ca7cf9d4d206ee15e9947cd78f08f501eb36b8d3835b38bfee1f52e17cfbd66029513a6b66046988543e80f46ce1a3db3e30a2610c5b9540e7202ccee33d842e971cf89d0cadd4df0646204388167229e54238cbe14c450c44e6c", + .signature = "7d081b0e38d73256476358c013908f3497810ac4bd5d76252f3ef440c5742ea552ef5348eccf6ef13afdf616288dbbf05a5b25e66a8745d9ddf66639ca89366e28062529e80b655b0268e922c8d77eb8ceee830fa14c15238dc1442dfdaffbf92436c794c24f6104374bf131616bcf4cca12608cee203e7eb0eba04556a0a0ac3dea9fa39dac8082e39f9a739955e036d0636008f5c3c4f823a5e6e5229fde6b94f986520de9b9a77ca34ccc0236762c77e33e105a9346553dc0e4469d3929ec38e8813a551af26b0f7d9dbb12d40b7d9fe195948e1ea1245362b01faeb4157605a9ab5158a7bc4df644f12121b03ee22e9f23fa5f40067b333b865bf3e41244", + .should_pass = false, /* (4 - Format of the EM is incorrect - hash moved to left ) */ + }, + { + .hash_alg = S2N_HASH_SHA224, + .key_param_n = "acfdfb1dcb1459b489cd4a8c9fab64a7da4f044bef1c506f0872e9476f3357abda509d9fb1db6a4f5306c40c058826253171cfedbc160776a48ec35b655bb9963286b6aece1c77dff987a0aae9720ba035dda67f317101bd3cd4e6caac867a8c38b87067938e96e72df1875f94e43e4c06f7a86a1dbe07836ee69763eee29bc13ca906d7740c29e651872a9ec7c6237f7c8290bb0800a030b323d09e7c903751d21a224266f9d6c94c17a4c0cd8175ea67b9d9020f2b3f31a96206084cddb2acef70b11ae25a46c4f6817c4813466d7cac76b27927145bd499ff87f22a946b688e980a00a3d54c72ab9c2c88a55a3ea4c6784068673532737cbe4799e98bd711", + .key_param_e = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000b29bc5", + .key_param_d = "29d83f1a032c6a1702d25eb47ee39a09969b83b3ecd764c9ce9cbe08e4dba20d711931820a6d90d26cd666d4eb842f66c12f189d13f67df06eb57edd4e37290a4adbf65a0fe5684e95d8287d9df3b32d3ca674bac95d13adb11dc40121f720df8dc2b5551fd98e167159e2eecdbcf7273a53ddafd8ee08ec9f857c1cfae59c282d3b13cc7dfa8fa85f68517460186f5b72faebe431d262af40d04ad00fd86e2b2cad64d86dcb5f738fc627eb4fcc3fd5fdd5742bf48959701a841016ae6f552a0e40c9279c544baf7c9c467e4958c7018c9f2a4becd9e03f40b4e854acb6534b1b3b5fd27fc085318fd7174a48660ef647574e0f2b99783fb17cece00096d601", + .message = "4cde2a6d9ecfdefa3c631c95aa9933634ae12668db8b53a03f80524b8130208816cb7d2563b492b68033d7e43c6a3408618a67f93946a521508884d77c6318e91b4a5c779c7fd40841cd71d7227ab56e767817760edba9ce2290f8da504b341ee2c1910b5018ec18059bb21566b3febc1112018a6232a7cd3cfe77fd06cfbc4f", + .signature = "4151dbee3d483d20b57b51d80bd2d382fc85575fa031582a88a135b2615c0a079bb08c2cb7ba6020c35706aadfb6bc35d69ce4df0b2e5fca1ea62a807e9765501571d6d123ae114afc19b26ba4b5db1b0522859d2269117059651053b9d72cc253c90285652a7e3094dce3eb5c9ef5fbf35eb268e3b8fe693c2bfc4bbb8682d709a12f3038fd04629bc48ae13ee91112741dab717c58856cc4215eedaeedd57edb2926085ebc05d15038f0deba90b00e18e710e64d31ff563a1ac88ecf9cadca2845babe9dd26771f0a629976fe13ba14f7c9ca37b98bb3a9d96ca9d72273da78ff3b17f6d7aa2645c42e3251e9161c958bd6f10d20f08fe2437aa79d73f9081", + .should_pass = false, /* (5 - Format of the EM is incorrect - 00 on end of pad removed ) */ + }, + { + .hash_alg = S2N_HASH_SHA256, + .key_param_n = "a47d04e7cacdba4ea26eca8a4c6e14563c2ce03b623b768c0d49868a57121301dbf783d82f4c055e73960e70550187d0af62ac3496f0a3d9103c2eb7919a72752fa7ce8c688d81e3aee99468887a15288afbb7acb845b7c522b5c64e678fcd3d22feb84b44272700be527d2b2025a3f83c2383bf6a39cf5b4e48b3cf2f56eef0dfff18555e31037b915248694876f3047814415164f2c660881e694b58c28038a032ad25634aad7b39171dee368e3d59bfb7299e4601d4587e68caaf8db457b75af42fc0cf1ae7caced286d77fac6cedb03ad94f1433d2c94d08e60bc1fdef0543cd2951e765b38230fdd18de5d2ca627ddc032fe05bbd2ff21e2db1c2f94d8b", + .key_param_e = "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000010e43f", + .key_param_d = "11a0dd285f66471a8da30bcb8c24a1d5c8db942fc9920797ca442460a800b75bbc738beb8ee0e874b053e64707df4cfc7837c40e5be68b8a8e1d0145169ca6271d81887e19a1cd95b2fd0de0dba347fe637bcc6cdc24eebe03c24d4cf3a5c6154d78f141fe34169924d0f89533658eacfdeae99ce1a88027c18ff92653a835aa3891bfffcd388ffc2388ce2b10568543750502ccbc69c0088f1d690e97a5f5bdd1888cd2faa43c04ae24539522dde2d9c202f655fc55754440b53a1532aab47851f60b7a067e240b738e1b1daae6ca0d59eeae27686cd88857e9adadc2d4b82b07a61a358456aaf807669693ffb13c9964a63654cadc81ee59df511ca3a4bd67", + .message = "e002377affb04f0fe4598de9d92d31d6c786040d5776976556a2cfc55e54a1dcb3cb1b126bd6a4bed2a184990ccea773fcc79d246553e6c64f686d21ad4152673cafec22aeb40f6a084e8a5b4991f4c64cf8a927effd0fd775e71e8329e41fdd4457b3911173187b4f09a817d79ea2397fc12dfe3d9c9a0290c8ead31b6690a6", + .signature = "4f9b425c2058460e4ab2f5c96384da2327fd29150f01955a76b4efe956af06dc08779a374ee4607eab61a93adc5608f4ec36e47f2a0f754e8ff839a8a19b1db1e884ea4cf348cd455069eb87afd53645b44e28a0a56808f5031da5ba9112768dfbfca44ebe63a0c0572b731d66122fb71609be1480faa4e4f75e43955159d70f081e2a32fbb19a48b9f162cf6b2fb445d2d6994bc58910a26b5943477803cdaaa1bd74b0da0a5d053d8b1dc593091db5388383c26079f344e2aea600d0e324164b450f7b9b465111b7265f3b1b063089ae7e2623fc0fda8052cf4bf3379102fbf71d7c98e8258664ceed637d20f95ff0111881e650ce61f251d9c3a629ef222d", + .should_pass = true, + }, + { + .hash_alg = S2N_HASH_SHA256, + .key_param_n = "ce4924ff470fb99d17f66595561a74ded22092d1dc27122ae15ca8cac4bfae11daa9e37a941430dd1b81aaf472f320835ee2fe744c83f1320882a8a02316ceb375f5c4909232bb2c6520b249c88be4f47b8b86fdd93678c69e64f50089e907a5504fdd43f0cad24aaa9e317ef2ecade3b5c1fd31f3c327d70a0e2d4867e6fe3f26272e8b6a3cce17843e359b82eb7a4cad8c42460179cb6c07fa252efaec428fd5cae5208b298b255109026e21272424ec0c52e1e5f72c5ab06f5d2a05e77c193b647ec948bb844e0c2ef1307f53cb800d4f55523d86038bb9e21099a861b6b9bcc969e5dddbdf7171b37d616381b78c3b22ef66510b2765d9617556b175599879d8558100ad90b830e87ad460a22108baa5ed0f2ba9dfc05167f8ab61fc9f8ae01603f9dd5e66ce1e642b604bca9294b57fb7c0d83f054bacf4454c298a272c44bc718f54605b91e0bfafd772aebaf3828846c93018f98e315708d50be8401eb9a8778dcbd0d6db9370860411b004cd37fbb8b5df87edee7aae949fff34607b", + .key_param_e = "00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000073b193", + .key_param_d = "258f084036b7ffda1d0aa0373a50011dd976b7fd0ee4b889654b044ab241fb754675466909429b1acba9d9c1abf2e9bb494cea81c4ba10dcd1036f36ea81dc24ce983e3ae7da7cf810ddc05c96f9cc3a9046fdf58c9902172c7e53a1bced1b7884f728133be9b4a911023e3159d5f252f407a8080c88f122cf4a9e53f103aecb412cd44d9d53c145757b14eb85a5b0d7f8be88c56bb00e7357d43d6a828953f93124d1b39c0cc137dff2972a402ebfe29eb614c6578e102c61a6001833323d4b79bee101e76a9c59a358471b6225688584fbdd790a1e38a60a5f8bf647f7374680aa1d6cc0372fd12ef233bf6bf726fa4af45e1ead9b58df08f62aa76fe9fd9bb1a975bb1c4ddb9b005453f957dfe4148d2644c1c490877431b67e975c5e02b2dc408de09e531c05c0517311a5cfeb4165b5f44060bb3433fff6ee8f0ad3f559b8458f20cbdca84649f0c8a3b6989f676bc0fe4691032d2a08978f9053abf21c1d081f8ec32735dd1ff0407c3302bf55d167197dbe92c678294d5f1f832da5bb", + .message = "0897d40e7c0f2dfc07b0c7fddaf5fd8fcc6af9c1fdc17bebb923d59c9fc43bd402ba39738f0f85f23015f75131f9d650a29b55e2fc9d5ddf07bb8df9fa5a80f1e4634e0b4c5155bf148939b1a4ea29e344a66429c850fcde7336dad616f0039378391abcfafe25ca7bb594057af07faf7a322f7fab01e051c63cc51b39af4d23", + .signature = "8ebed002d4f54de5898a5f2e69d770ed5a5ce1d45ad6dd9ce5f1179d1c46daa4d0394e21a99d803358d9abfd23bb53166394f997b909e675662066324ca1f2b731deba170525c4ee8fa752d2d7f201b10219489f5784e399d916302fd4b7adf88490df876501c46742a93cfb3aaab9602e65d7e60d7c4ceadb7eb67e421d180323a6d38f38b9f999213ebfccc7e04f060fbdb7c210206522b494e199e98c6c24e457f8696644fdcaebc1b9031c818322c29d135e1172fa0fdf7be1007dabcaab4966332e7ea1456b6ce879cd910c9110104fc7d3dcab076f2bd182bb8327a863254570cdf2ab38e0cda31779deaad616e3437ed659d74e5a4e045a70133890b81bc4f24ab6da67a2ee0ce15baba337d091cb5a1c44da690f81145b0252a6549bbb20cd5cc47afec755eb37fed55a9a33d36557424503d805a0a120b76941f4150d89342d7a7fa3a2b08c515e6f68429cf7afd1a3fce0f428351a6f9eda3ab24a7ef591994c21fbf1001f99239e88340f9b359ec72e8a212a1920e6cf993ff848", + .should_pass = false, /* saltlen is 0 instead of 32 */ + }, + { + .hash_alg = S2N_HASH_SHA384, + .key_param_n = "cb59aae30883db678ea7b2a5e7009799066f060757525166030714a25e808482e752f04f78611b3509d6005b411530c9ada4d8fbddc85f9db3d209eccc6cf0cae9aeb902e96688d2547974b7eb3323eeaa2257cf8d5c7c97f5176e2cfd29e19d0487380e3e64338c73bd592d52e9dbcc9606ed5835758c1a112c6a004256b5c4338322695df2ba573be0e79a7b9f9afd497dd38eed39f05f88d7d399d1e984d048067596ad1847ce51221babf51873bad2d81fa91cf3d9fd307e3ebd41fc49a8b3252ed7a71fd2357330bef2f1f89f2c80f740567e2ae8d168e56f007e8fefa33d2eb92b7d830a4f978ffe842ef0697db50602b19642afc50ac1f837e476c0fd", + .key_param_e = "0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000024f1bf", + .key_param_d = "50cc6f7dd99782ce5943d719eccf85ff22ae29e61473dbb80f3b210b16377c51c1d3d65cbaa558db54c58d6683e1aabe03ab4f3802f1cb67eae16787cf5ccf618024c7d8b46a69c73289c7ccf96ff23863f7de4cdb89cfc6b66072184fd61f5fb9b84750d7d63dfdea78e130214a0ba949c0f0a3ddf3ef1395910991590405ff4fad11ad22b159297479cc14f96739e5e63c28610a0ab413823db00f3b3067ec2d4ba17475d439d3b00951146621887ceb845707d5daedd29744ad6e5105159a31f7309e2e39e847be3be0a3a302bef77b8d3979570deaa2713c1221b06d7bff6941e1b59dc1cb88158664be80a9fd7c17b08cfa49f381ad5d186ff5f91fa8c5", + .message = "f991a40a6c3cda01f1a2fed01ca0cf425588a071205eb997a147fa205f3ec10448090e53f56be512309cf445b3f6764d33f157749d5199c7a09ef6246bd5c793b85d24d9093c4d4b318b48e11727cc8bb7aa5ec8699aba7466e074e1887bdf2a51752ec42f16d956fe5943cbcf9c99a5e89bfd940c9fe447fcf3bc823d98d371", + .signature = "6b42514e88d93079d158336897dc34b450e424d61f6ddcf86fe8c9a368ae8a22c4ee4084c978b5169379da10ae6a6ae8cd80028e198cd0a8db532cd78a409f53baf7d231b545835b0dc06d594d76868d986889419a959268fd321bbc8bad5e800e452fe1a1a2a5a851d542494473deb425171a2f37ffc4cf0600a8d561b20f777407bbef1b596f92e518c0929e8bd52b015c2718d14443a56056f65015515673deef32ae5399ae71f97873ec1508f8c41d6a66a13017685134e5425c4b580a7f6986c26fb272f0ed215d6698dcec9e7c5258173b295b3611869254a538945de952dedf291837df0d7a205e1b76b01140df4edce3afe7245d46ee3b292bb117b1", + .should_pass = true, + }, + { + .hash_alg = S2N_HASH_SHA384, + .key_param_n = "8b71c2bcb324a3fc23d292fb4f18cab5140d521013361a07071bc788859cbba33fc226b2cef9c1b3663d307acd3e4d8eb7acff63d048495a2d61fbeb617a42c4f424a347673173902cd1cb11780003e715662d195996fbff55f6b9feb54a18197e6848aa8baa15fa020cc54e72ec976d766ed63ee4e00071a11e29d7baf30e3f", + .key_param_e = "000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000035c661", + .key_param_d = "3e3e9386d77f5a669da5424826c12e3717df7badde17a69ca195f59251cf5fe7b33c3ed8466b1ca22cfeb016de414e205d0e082a84a2bb75e63daae4d2024e8bb72eb3adf586a9f639c94122c6984c6d246895aa4888c25d26d1490469ed98f963fc45fe7971d4d4bfe996cae28b5ce343c787c6c602f830bc09c55dcff53401", + .message = "aab88ff728c8f829841a14e56194bbf278d69f88317a81b4749aa5fdbc9383486e09bff96a2c5b5bdf392c4263438aef43334c33170ef4d89a76263cb9745f3fea74e35fbf91f722bb1351b56436cdd2992e61e6266753749611a9b449dce281c600e37251813446c1b16c858cf6ea6424cdc6e9860f07510f7417af925574d5", + .signature = "657296e902331b8030a72920c6c16b22ea65fe18e7e10b7cdbb8a44ef0f4c66f3e9c22f8f35e4184b420ad3f1bdbc1d6a65e6230abca8a9bee10887833dae15a84bf09a4542389c685fd33e7385c6001b49aa108f2272a46a832bbadc067ff06b09b2f5f40c81cc2acac03311a3945f7a9f2ea81213ba9ba626d6a7ed49f17dd", + .should_pass = false, /* saltlen is 20 instead of 48 */ + }, +}; + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Load the RSA cert */ + struct s2n_cert_chain_and_key *rsa_cert_chain; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&rsa_cert_chain, + S2N_RSA_2048_PKCS1_CERT_CHAIN, S2N_RSA_2048_PKCS1_KEY)); + + s2n_stack_blob(result, HASH_LENGTH, HASH_LENGTH); + + /* Generate a random blob of data */ + s2n_stack_blob(random_msg, RANDOM_BLOB_SIZE, RANDOM_BLOB_SIZE); + EXPECT_OK(s2n_get_private_random_data(&random_msg)); + + /* If RSA_PSS not supported, cannot sign/verify with PSS */ + { + struct s2n_pkey rsa_public_key; + s2n_pkey_type rsa_pkey_type; + EXPECT_SUCCESS(s2n_asn1der_to_public_key_and_type(&rsa_public_key, &rsa_pkey_type, &rsa_cert_chain->cert_chain->head->raw)); + EXPECT_EQUAL(rsa_pkey_type, S2N_PKEY_TYPE_RSA); + + hash_state_new(sign_hash, random_msg); + hash_state_new(verify_hash, random_msg); + + EXPECT_EQUAL(s2n_is_rsa_pss_signing_supported(), RSA_PSS_SIGNING_SUPPORTED); + + if (!s2n_is_rsa_pss_signing_supported()) { + EXPECT_FAILURE_WITH_ERRNO(rsa_public_key.sign(rsa_cert_chain->private_key, S2N_SIGNATURE_RSA_PSS_RSAE, &sign_hash, &result), + S2N_ERR_RSA_PSS_NOT_SUPPORTED); + EXPECT_FAILURE_WITH_ERRNO(rsa_public_key.verify(&rsa_public_key, S2N_SIGNATURE_RSA_PSS_RSAE, &verify_hash, &result), + S2N_ERR_RSA_PSS_NOT_SUPPORTED); + } else { + EXPECT_SUCCESS(rsa_public_key.sign(rsa_cert_chain->private_key, S2N_SIGNATURE_RSA_PSS_RSAE, &sign_hash, &result)); + EXPECT_SUCCESS(rsa_public_key.verify(&rsa_public_key, S2N_SIGNATURE_RSA_PSS_RSAE, &verify_hash, &result)); + } + + EXPECT_SUCCESS(s2n_pkey_free(&rsa_public_key)); + }; + +#if RSA_PSS_CERTS_SUPPORTED + + struct s2n_cert_chain_and_key *rsa_pss_cert_chain; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&rsa_pss_cert_chain, + S2N_RSA_PSS_2048_SHA256_LEAF_CERT, S2N_RSA_PSS_2048_SHA256_LEAF_KEY)); + + /* Self-Talk tests */ + { + struct s2n_pkey rsa_public_key; + s2n_pkey_type rsa_pkey_type; + EXPECT_SUCCESS(s2n_asn1der_to_public_key_and_type(&rsa_public_key, &rsa_pkey_type, &rsa_cert_chain->cert_chain->head->raw)); + EXPECT_EQUAL(rsa_pkey_type, S2N_PKEY_TYPE_RSA); + + /* Test: RSA cert can sign/verify with PSS */ + { + hash_state_new(sign_hash, random_msg); + hash_state_new(verify_hash, random_msg); + + EXPECT_SUCCESS(rsa_public_key.sign(rsa_cert_chain->private_key, S2N_SIGNATURE_RSA_PSS_RSAE, &sign_hash, &result)); + EXPECT_SUCCESS(rsa_public_key.verify(&rsa_public_key, S2N_SIGNATURE_RSA_PSS_RSAE, &verify_hash, &result)); + }; + + /* Test: RSA cert can't verify with PSS what it signed with PKCS1v1.5 */ + { + hash_state_new(sign_hash, random_msg); + hash_state_new(verify_hash, random_msg); + + EXPECT_SUCCESS(rsa_public_key.sign(rsa_cert_chain->private_key, S2N_SIGNATURE_RSA_PSS_RSAE, &sign_hash, &result)); + EXPECT_FAILURE_WITH_ERRNO(rsa_public_key.verify(&rsa_public_key, S2N_SIGNATURE_RSA, &verify_hash, &result), + S2N_ERR_VERIFY_SIGNATURE); + }; + + /* Test: RSA cert can't verify with PKCS1v1.5 what it signed with PSS */ + { + hash_state_new(sign_hash, random_msg); + hash_state_new(verify_hash, random_msg); + + EXPECT_SUCCESS(rsa_public_key.sign(rsa_cert_chain->private_key, S2N_SIGNATURE_RSA, &sign_hash, &result)); + EXPECT_FAILURE_WITH_ERRNO(rsa_public_key.verify(&rsa_public_key, S2N_SIGNATURE_RSA_PSS_RSAE, &verify_hash, &result), + S2N_ERR_VERIFY_SIGNATURE); + }; + + /* Test: If they share the same RSA key, + * an RSA cert and an RSA_PSS cert are equivalent for PSS signatures. */ + { + struct s2n_pkey rsa_pss_public_key; + s2n_pkey_type rsa_pss_pkey_type; + EXPECT_SUCCESS(s2n_asn1der_to_public_key_and_type(&rsa_pss_public_key, &rsa_pss_pkey_type, &rsa_pss_cert_chain->cert_chain->head->raw)); + EXPECT_EQUAL(rsa_pss_pkey_type, S2N_PKEY_TYPE_RSA_PSS); + + /* Set the keys equal. */ + RSA *rsa_key_copy = EVP_PKEY_get1_RSA(rsa_public_key.pkey); + POSIX_GUARD_OSSL(EVP_PKEY_set1_RSA(rsa_pss_public_key.pkey, rsa_key_copy), S2N_ERR_KEY_INIT); + RSA_free(rsa_key_copy); + + /* RSA signed with PSS, RSA_PSS verified with PSS */ + { + hash_state_new(sign_hash, random_msg); + hash_state_new(verify_hash, random_msg); + + EXPECT_SUCCESS(rsa_public_key.sign(rsa_cert_chain->private_key, S2N_SIGNATURE_RSA_PSS_RSAE, &sign_hash, &result)); + EXPECT_SUCCESS(rsa_pss_public_key.verify(&rsa_public_key, S2N_SIGNATURE_RSA_PSS_PSS, &verify_hash, &result)); + }; + + /* RSA_PSS signed with PSS, RSA verified with PSS */ + { + hash_state_new(sign_hash, random_msg); + hash_state_new(verify_hash, random_msg); + + EXPECT_SUCCESS(rsa_pss_public_key.sign(rsa_cert_chain->private_key, S2N_SIGNATURE_RSA_PSS_PSS, &sign_hash, &result)); + EXPECT_SUCCESS(rsa_public_key.verify(&rsa_public_key, S2N_SIGNATURE_RSA_PSS_RSAE, &verify_hash, &result)); + }; + + EXPECT_SUCCESS(s2n_pkey_free(&rsa_pss_public_key)); + }; + + EXPECT_SUCCESS(s2n_pkey_free(&rsa_public_key)); + }; + + /* Test: NIST test vectors */ + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + struct s2n_rsa_pss_test_case test_case = test_cases[i]; + + struct s2n_pkey rsa_public_key = { 0 }; + s2n_pkey_type rsa_pkey_type = 0; + EXPECT_SUCCESS(s2n_asn1der_to_public_key_and_type(&rsa_public_key, &rsa_pkey_type, + &rsa_cert_chain->cert_chain->head->raw)); + EXPECT_EQUAL(rsa_pkey_type, S2N_PKEY_TYPE_RSA); + + /* Modify the rsa_public_key for each test_case. */ + RSA *rsa_key_copy = EVP_PKEY_get1_RSA(rsa_public_key.pkey); + BIGNUM *n = BN_new(), *e = BN_new(), *d = BN_new(); + EXPECT_SUCCESS(BN_hex2bn(&n, test_case.key_param_n)); + EXPECT_SUCCESS(BN_hex2bn(&e, test_case.key_param_e)); + EXPECT_SUCCESS(BN_hex2bn(&d, test_case.key_param_d)); + EXPECT_SUCCESS(RSA_set0_key(rsa_key_copy, n, e, d)); + POSIX_GUARD_OSSL(EVP_PKEY_set1_RSA(rsa_public_key.pkey, rsa_key_copy), S2N_ERR_KEY_INIT); + RSA_free(rsa_key_copy); + + struct s2n_stuffer message_stuffer = { 0 }, signature_stuffer = { 0 }; + s2n_stuffer_alloc_ro_from_hex_string(&message_stuffer, test_case.message); + s2n_stuffer_alloc_ro_from_hex_string(&signature_stuffer, test_case.signature); + hash_state_for_alg_new(verify_hash, test_case.hash_alg, message_stuffer.blob); + + int ret_val = rsa_public_key.verify(&rsa_public_key, S2N_SIGNATURE_RSA_PSS_RSAE, + &verify_hash, &signature_stuffer.blob); + if (test_case.should_pass) { + EXPECT_SUCCESS(ret_val); + } else { + EXPECT_FAILURE_WITH_ERRNO(ret_val, S2N_ERR_VERIFY_SIGNATURE); + } + + EXPECT_SUCCESS(s2n_stuffer_free(&message_stuffer)); + EXPECT_SUCCESS(s2n_stuffer_free(&signature_stuffer)); + EXPECT_SUCCESS(s2n_pkey_free(&rsa_public_key)); + } + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(rsa_pss_cert_chain)); +#endif + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(rsa_cert_chain)); + END_TEST(); +} diff --git a/tests/unit/s2n_rsa_pss_test.c b/tests/unit/s2n_rsa_pss_test.c new file mode 100644 index 00000000000..15a58f9c04d --- /dev/null +++ b/tests/unit/s2n_rsa_pss_test.c @@ -0,0 +1,350 @@ +/* + * 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 "crypto/s2n_rsa_pss.h" + +#include "crypto/s2n_certificate.h" +#include "crypto/s2n_dhe.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "utils/s2n_random.h" + +int s2n_flip_random_bit(struct s2n_blob *blob) +{ + /* Flip a random bit in the blob */ + uint64_t byte_flip_pos; + POSIX_GUARD_RESULT(s2n_public_random(blob->size, &byte_flip_pos)); + uint64_t bit_flip_pos; + POSIX_GUARD_RESULT(s2n_public_random(8, &bit_flip_pos)); + + uint8_t mask = 0x01 << (uint8_t) bit_flip_pos; + blob->data[byte_flip_pos] ^= mask; + + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Don't use RSA-PSS certs if unsupported */ +#if !RSA_PSS_CERTS_SUPPORTED + EXPECT_FALSE(s2n_is_rsa_pss_certs_supported()); + END_TEST(); +#endif + EXPECT_TRUE(s2n_is_rsa_pss_certs_supported()); + + /* Check that s2n_is_rsa_pss_certs_supported() is a superset of s2n_is_rsa_pss_signing_supported() */ + EXPECT_TRUE(s2n_is_rsa_pss_signing_supported()); + + /* Positive Test: Ensure we can sign and verify a randomly generated signature. + * Pseudocode: assert(SUCCESS == verify(Key1_public, message, sign(Key1_private, message))) + */ + { + struct s2n_config *server_config; + char *cert_chain_pem; + char *private_key_pem; + struct s2n_cert_chain_and_key *chain_and_key; + struct s2n_pkey public_key = { 0 }; + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + + /* Load the Private Key */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + + /* Load the Public Key */ + EXPECT_SUCCESS(s2n_asn1der_to_public_key_and_type(&public_key, &pkey_type, &chain_and_key->cert_chain->head->raw)); + EXPECT_EQUAL(pkey_type, S2N_PKEY_TYPE_RSA_PSS); + + /* Sign and Verify a Random Value to ensure that Public and Private Key Matches */ + EXPECT_SUCCESS(s2n_pkey_match(&public_key, chain_and_key->private_key)); + + /* Release Resources */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_pkey_free(&public_key)); + free(cert_chain_pem); + free(private_key_pem); + + /* Verify repeated key frees. + * (Later calls should be a no-op) */ + EXPECT_SUCCESS(s2n_pkey_free(&public_key)); + }; + + /* Negative Test: Loading mismatching RSA PSS Public/Private Keys will fail. + * Pseudocode: assert(FAILURE == load_pem_pair(Key1_public, Key2_private)) + */ + { + struct s2n_config *server_config; + char *leaf_cert_chain_pem; + char *root_private_key_pem; + struct s2n_cert_chain_and_key *misconfigured_chain_and_key; + struct s2n_pkey public_key = { 0 }; + + EXPECT_NOT_NULL(leaf_cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(root_private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, leaf_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + + /* Incorrectly reading the CA's Private Key from disk, not the Leaf's Private Key */ + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_KEY, root_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(misconfigured_chain_and_key = s2n_cert_chain_and_key_new()); + + /* Attempting to Load RSA_PSS Certificate with wrong RSA_PSS Key should fail */ + EXPECT_FAILURE(s2n_cert_chain_and_key_load_pem(misconfigured_chain_and_key, leaf_cert_chain_pem, root_private_key_pem)); + + /* Release Resources */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(misconfigured_chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_pkey_free(&public_key)); + free(leaf_cert_chain_pem); + free(root_private_key_pem); + }; + + /* Negative Test: Ensure flipping a bit in the signature is rejected + * Pseudocode: assert(FAILURE == verify(Key1_public, message, bitflip(sign(Key1_private, message))) + */ + { + struct s2n_config *server_config; + char *cert_chain_pem; + char *private_key_pem; + struct s2n_cert_chain_and_key *chain_and_key; + struct s2n_pkey public_key = { 0 }; + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_cert_chain(chain_and_key, cert_chain_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_private_key(chain_and_key, private_key_pem)); + + /* Parse the leaf cert for the public key and certificate type */ + EXPECT_SUCCESS(s2n_asn1der_to_public_key_and_type(&public_key, &pkey_type, &chain_and_key->cert_chain->head->raw)); + EXPECT_NOT_EQUAL(pkey_type, S2N_PKEY_TYPE_UNKNOWN); + EXPECT_SUCCESS(s2n_cert_set_cert_type(chain_and_key->cert_chain->head, pkey_type)); + + struct s2n_pkey *private_key = chain_and_key->private_key; + { + EXPECT_NOT_NULL(public_key.pkey); + EXPECT_NOT_NULL(private_key); + EXPECT_NOT_NULL(private_key->pkey); + + /* Generate a random blob to sign and verify */ + s2n_stack_blob(random_msg, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE); + EXPECT_OK(s2n_get_private_random_data(&random_msg)); + + /* Sign/Verify API's only accept Hashes, so hash our Random Data */ + DEFER_CLEANUP(struct s2n_hash_state sign_hash = { 0 }, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&sign_hash)); + EXPECT_SUCCESS(s2n_hash_init(&sign_hash, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&sign_hash, random_msg.data, random_msg.size)); + + DEFER_CLEANUP(struct s2n_hash_state verify_hash = { 0 }, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&verify_hash)); + EXPECT_SUCCESS(s2n_hash_init(&verify_hash, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&verify_hash, random_msg.data, random_msg.size)); + + /* Sign and Verify the Hash of the Random Blob */ + s2n_stack_blob(signature_data, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE); + EXPECT_SUCCESS(s2n_pkey_sign(private_key, S2N_SIGNATURE_RSA_PSS_PSS, &sign_hash, &signature_data)); + + /* Flip a random bit in the signature */ + EXPECT_SUCCESS(s2n_flip_random_bit(&signature_data)); + EXPECT_FAILURE(s2n_pkey_verify(&public_key, S2N_SIGNATURE_RSA_PSS_PSS, &verify_hash, &signature_data)); + }; + + /* Release Resources */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_pkey_free(&public_key)); + free(cert_chain_pem); + free(private_key_pem); + }; + + /* Negative Test: Ensure Verification with wrong key fails + * Pseudocode: assert(FAILURE == verify(Key2_public, message, sign(Key1_private, message))) + */ + { + struct s2n_config *server_config; + char *root_cert_chain_pem; + char *root_private_key_pem; + char *leaf_cert_chain_pem; + char *leaf_private_key_pem; + struct s2n_cert_chain_and_key *root_chain_and_key; + struct s2n_cert_chain_and_key *leaf_chain_and_key; + struct s2n_pkey root_public_key = { 0 }; + struct s2n_pkey leaf_public_key = { 0 }; + s2n_pkey_type root_pkey_type = S2N_PKEY_TYPE_UNKNOWN; + s2n_pkey_type leaf_pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + EXPECT_NOT_NULL(root_cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(root_private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(leaf_cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(leaf_private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_CERT, root_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_KEY, root_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, leaf_cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_KEY, leaf_private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(root_chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_NOT_NULL(leaf_chain_and_key = s2n_cert_chain_and_key_new()); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_cert_chain(root_chain_and_key, root_cert_chain_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_private_key(root_chain_and_key, root_private_key_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_cert_chain(leaf_chain_and_key, leaf_cert_chain_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_private_key(leaf_chain_and_key, leaf_private_key_pem)); + + /* Parse the cert for the public key and certificate type */ + EXPECT_SUCCESS(s2n_asn1der_to_public_key_and_type(&root_public_key, &root_pkey_type, &root_chain_and_key->cert_chain->head->raw)); + EXPECT_SUCCESS(s2n_asn1der_to_public_key_and_type(&leaf_public_key, &leaf_pkey_type, &leaf_chain_and_key->cert_chain->head->raw)); + EXPECT_NOT_EQUAL(root_pkey_type, S2N_PKEY_TYPE_UNKNOWN); + EXPECT_NOT_EQUAL(leaf_pkey_type, S2N_PKEY_TYPE_UNKNOWN); + + EXPECT_SUCCESS(s2n_cert_set_cert_type(root_chain_and_key->cert_chain->head, root_pkey_type)); + EXPECT_SUCCESS(s2n_cert_set_cert_type(leaf_chain_and_key->cert_chain->head, leaf_pkey_type)); + + struct s2n_pkey *root_private_key = root_chain_and_key->private_key; + struct s2n_pkey *leaf_private_key = leaf_chain_and_key->private_key; + { + EXPECT_NOT_NULL(root_public_key.pkey); + EXPECT_NOT_NULL(leaf_public_key.pkey); + + EXPECT_NOT_NULL(root_private_key); + EXPECT_NOT_NULL(root_private_key->pkey); + EXPECT_NOT_NULL(leaf_private_key); + EXPECT_NOT_NULL(leaf_private_key->pkey); + + /* Generate a random blob to sign and verify */ + s2n_stack_blob(random_msg, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE); + EXPECT_OK(s2n_get_private_random_data(&random_msg)); + + /* Sign/Verify API's only accept Hashes, so hash our Random Data */ + DEFER_CLEANUP(struct s2n_hash_state sign_hash = { 0 }, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&sign_hash)); + EXPECT_SUCCESS(s2n_hash_init(&sign_hash, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&sign_hash, random_msg.data, random_msg.size)); + + DEFER_CLEANUP(struct s2n_hash_state verify_hash = { 0 }, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&verify_hash)); + EXPECT_SUCCESS(s2n_hash_init(&verify_hash, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&verify_hash, random_msg.data, random_msg.size)); + + /* Sign and Verify the Hash of the Random Blob */ + s2n_stack_blob(signature_data, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE); + + /* Sign with Root's Key, but verify with Leaf's Key. This should fail. */ + EXPECT_SUCCESS(s2n_pkey_sign(root_private_key, S2N_SIGNATURE_RSA_PSS_PSS, &sign_hash, &signature_data)); + EXPECT_FAILURE(s2n_pkey_verify(&leaf_public_key, S2N_SIGNATURE_RSA_PSS_PSS, &verify_hash, &signature_data)); + }; + + /* Release Resources */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(root_chain_and_key)); + EXPECT_SUCCESS(s2n_pkey_free(&root_public_key)); + free(root_cert_chain_pem); + free(root_private_key_pem); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(leaf_chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_pkey_free(&leaf_public_key)); + free(leaf_cert_chain_pem); + free(leaf_private_key_pem); + }; + + /* Negative Test: Ensure flipping a bit in message given to verification fails + * Pseudocode: assert(FAILURE == verify(Key1_public, bitflip(message), sign(Key1_private, message))) + */ + { + struct s2n_config *server_config; + char *cert_chain_pem; + char *private_key_pem; + struct s2n_cert_chain_and_key *chain_and_key; + struct s2n_pkey public_key = { 0 }; + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_CA_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_cert_chain(chain_and_key, cert_chain_pem)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_private_key(chain_and_key, private_key_pem)); + + /* Parse the leaf cert for the public key and certificate type */ + EXPECT_SUCCESS(s2n_asn1der_to_public_key_and_type(&public_key, &pkey_type, &chain_and_key->cert_chain->head->raw)); + EXPECT_NOT_EQUAL(pkey_type, S2N_PKEY_TYPE_UNKNOWN); + EXPECT_SUCCESS(s2n_cert_set_cert_type(chain_and_key->cert_chain->head, pkey_type)); + + struct s2n_pkey *private_key = chain_and_key->private_key; + { + EXPECT_NOT_NULL(public_key.pkey); + EXPECT_NOT_NULL(private_key); + EXPECT_NOT_NULL(private_key->pkey); + + /* Generate a random blob to sign and verify */ + s2n_stack_blob(random_msg, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE, RSA_PSS_SIGN_VERIFY_RANDOM_BLOB_SIZE); + EXPECT_OK(s2n_get_private_random_data(&random_msg)); + + /* Sign/Verify API's only accept Hashes, so hash our Random Data */ + DEFER_CLEANUP(struct s2n_hash_state sign_hash = { 0 }, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&sign_hash)); + EXPECT_SUCCESS(s2n_hash_init(&sign_hash, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&sign_hash, random_msg.data, random_msg.size)); + + /* Flip a random bit in the message before verification */ + EXPECT_SUCCESS(s2n_flip_random_bit(&random_msg)); + + DEFER_CLEANUP(struct s2n_hash_state verify_hash = { 0 }, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&verify_hash)); + EXPECT_SUCCESS(s2n_hash_init(&verify_hash, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&verify_hash, random_msg.data, random_msg.size)); + + /* Sign and Verify the Hash of the Random Blob */ + s2n_stack_blob(signature_data, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE, RSA_PSS_SIGN_VERIFY_SIGNATURE_SIZE); + EXPECT_SUCCESS(s2n_pkey_sign(private_key, S2N_SIGNATURE_RSA_PSS_PSS, &sign_hash, &signature_data)); + EXPECT_FAILURE(s2n_pkey_verify(&public_key, S2N_SIGNATURE_RSA_PSS_PSS, &verify_hash, &signature_data)); + }; + + /* Release Resources */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_pkey_free(&public_key)); + free(cert_chain_pem); + free(private_key_pem); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_safety_blinding_test.c b/tests/unit/s2n_safety_blinding_test.c new file mode 100644 index 00000000000..aab11f28888 --- /dev/null +++ b/tests/unit/s2n_safety_blinding_test.c @@ -0,0 +1,183 @@ +/* + * 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 "s2n_test.h" +#include "tls/s2n_connection.h" +#include "utils/s2n_safety.h" + +#define TEST_ERRNO S2N_ERR_T_INTERNAL_END + +#define SETUP_TEST(conn) \ + EXPECT_SUCCESS(s2n_connection_wipe(conn)); \ + EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); + +#define EXPECT_BLINDING(conn) \ + EXPECT_NOT_EQUAL(s2n_connection_get_delay(conn), 0); \ + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + +#define EXPECT_NO_BLINDING(conn) \ + EXPECT_EQUAL(s2n_connection_get_delay(conn), 0); \ + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_FULL_DUPLEX)); + +S2N_RESULT s2n_result_func(bool success) +{ + RESULT_ENSURE(success, TEST_ERRNO); + return S2N_RESULT_OK; +} + +int s2n_posix_func(bool success) +{ + POSIX_ENSURE(success, TEST_ERRNO); + return S2N_SUCCESS; +} + +int ptr_value = 0; +int *s2n_ptr_func(bool success) +{ + PTR_ENSURE(success, TEST_ERRNO); + return &ptr_value; +} + +S2N_RESULT s2n_result_test(struct s2n_connection *conn) +{ + WITH_ERROR_BLINDING(conn, RESULT_GUARD(s2n_result_func(true))); + EXPECT_NO_BLINDING(conn); + + WITH_ERROR_BLINDING(conn, RESULT_ENSURE(true, S2N_ERR_UNIMPLEMENTED)); + EXPECT_NO_BLINDING(conn); + + WITH_ERROR_BLINDING(conn, RESULT_GUARD_POSIX(s2n_posix_func(true))); + EXPECT_NO_BLINDING(conn); + + WITH_ERROR_BLINDING(conn, RESULT_GUARD_PTR(s2n_ptr_func(true))); + EXPECT_NO_BLINDING(conn); + + WITH_ERROR_BLINDING(conn, RESULT_GUARD(s2n_result_func(false))); + return S2N_RESULT_OK; +} + +int s2n_posix_test(struct s2n_connection *conn) +{ + WITH_ERROR_BLINDING(conn, POSIX_GUARD_RESULT(s2n_result_func(true))); + EXPECT_NO_BLINDING(conn); + + WITH_ERROR_BLINDING(conn, POSIX_ENSURE(true, S2N_ERR_UNIMPLEMENTED)); + EXPECT_NO_BLINDING(conn); + + WITH_ERROR_BLINDING(conn, POSIX_GUARD(s2n_posix_func(true))); + EXPECT_NO_BLINDING(conn); + + WITH_ERROR_BLINDING(conn, POSIX_GUARD_PTR(s2n_ptr_func(true))); + EXPECT_NO_BLINDING(conn); + + WITH_ERROR_BLINDING(conn, POSIX_GUARD(s2n_posix_func(false))); + return S2N_SUCCESS; +} + +int *s2n_ptr_test(struct s2n_connection *conn) +{ + WITH_ERROR_BLINDING(conn, PTR_GUARD_RESULT(s2n_result_func(true))); + EXPECT_NO_BLINDING(conn); + + WITH_ERROR_BLINDING(conn, PTR_ENSURE(true, S2N_ERR_UNIMPLEMENTED)); + EXPECT_NO_BLINDING(conn); + + WITH_ERROR_BLINDING(conn, PTR_GUARD_POSIX(s2n_posix_func(true))); + EXPECT_NO_BLINDING(conn); + + WITH_ERROR_BLINDING(conn, PTR_GUARD(s2n_ptr_func(true))); + EXPECT_NO_BLINDING(conn); + + WITH_ERROR_BLINDING(conn, PTR_GUARD(s2n_ptr_func(false))); + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Test: s2n_connection_apply_error_blinding */ + { + /* Safety check */ + struct s2n_connection *conn = NULL; + EXPECT_ERROR_WITH_ERRNO(s2n_connection_apply_error_blinding(NULL), S2N_ERR_NULL); + EXPECT_OK(s2n_connection_apply_error_blinding(&conn)); + + conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + + /* No-op for no error */ + { + SETUP_TEST(conn); + s2n_errno = S2N_ERR_OK; + EXPECT_OK(s2n_connection_apply_error_blinding(&conn)); + EXPECT_NO_BLINDING(conn); + }; + + /* No-op for retriable errors */ + { + SETUP_TEST(conn); + s2n_errno = S2N_ERR_IO_BLOCKED; + EXPECT_OK(s2n_connection_apply_error_blinding(&conn)); + EXPECT_NO_BLINDING(conn); + }; + + /* Closes connection but does not blind for non-blinding errors */ + { + SETUP_TEST(conn); + s2n_errno = S2N_ERR_CIPHER_NOT_SUPPORTED; + EXPECT_OK(s2n_connection_apply_error_blinding(&conn)); + EXPECT_EQUAL(s2n_connection_get_delay(conn), 0); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + }; + + /* Blinds for an average error */ + { + SETUP_TEST(conn); + s2n_errno = S2N_ERR_UNIMPLEMENTED; + EXPECT_OK(s2n_connection_apply_error_blinding(&conn)); + EXPECT_BLINDING(conn); + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: WITH_ERROR_BLINDING macro + * The WITH_ERROR_BLINDING macro relies on the current method exiting early. + * We can't trigger that behavior in main, so we call separate test methods. + * Each test method verifies that some success cases don't lead to blinding, then + * triggers blinding. Back in main, we verify that the blinding occurred. */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + + SETUP_TEST(conn); + EXPECT_ERROR_WITH_ERRNO(s2n_result_test(conn), TEST_ERRNO); + EXPECT_BLINDING(conn); + + SETUP_TEST(conn); + EXPECT_FAILURE_WITH_ERRNO(s2n_posix_test(conn), TEST_ERRNO); + EXPECT_BLINDING(conn); + + SETUP_TEST(conn); + EXPECT_NULL(s2n_ptr_test(conn)); + EXPECT_NOT_EQUAL(s2n_connection_get_delay(conn), 0); + EXPECT_BLINDING(conn); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_safety_macros_test.c b/tests/unit/s2n_safety_macros_test.c new file mode 100644 index 00000000000..6af86da91b8 --- /dev/null +++ b/tests/unit/s2n_safety_macros_test.c @@ -0,0 +1,1043 @@ +/* + * 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. + */ + +/* clang-format off */ + +#include "s2n_test.h" + +#include "utils/s2n_safety.h" + +/** + * DO NOT DIRECTLY MODIFY THIS FILE: + * + * The code in this file is generated from scripts/s2n_safety_macros.py and any modifications + * should be in there. + */ + +/* harnesses */ + +static s2n_result RESULT_BAIL_harness() +{ + RESULT_BAIL(S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_harness(bool is_ok) +{ + RESULT_ENSURE(is_ok, S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_DEBUG_ENSURE_harness(bool is_ok) +{ + RESULT_DEBUG_ENSURE(is_ok, S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_OK_harness(bool is_ok) +{ + RESULT_ENSURE_OK(RESULT_ENSURE_harness(is_ok), S2N_ERR_IO); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_GTE_harness_uint32(uint32_t a, uint32_t b) +{ + RESULT_ENSURE_GTE(a, b); + /* test the inverse */ + RESULT_ENSURE_LTE(b, a); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_GTE_harness_int32(int32_t a, int32_t b) +{ + RESULT_ENSURE_GTE(a, b); + /* test the inverse */ + RESULT_ENSURE_LTE(b, a); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_LTE_harness_uint32(uint32_t a, uint32_t b) +{ + RESULT_ENSURE_LTE(a, b); + /* test the inverse */ + RESULT_ENSURE_GTE(b, a); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_LTE_harness_int32(int32_t a, int32_t b) +{ + RESULT_ENSURE_LTE(a, b); + /* test the inverse */ + RESULT_ENSURE_GTE(b, a); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_GT_harness_uint32(uint32_t a, uint32_t b) +{ + RESULT_ENSURE_GT(a, b); + /* test the inverse */ + RESULT_ENSURE_LT(b, a); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_GT_harness_int32(int32_t a, int32_t b) +{ + RESULT_ENSURE_GT(a, b); + /* test the inverse */ + RESULT_ENSURE_LT(b, a); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_LT_harness_uint32(uint32_t a, uint32_t b) +{ + RESULT_ENSURE_LT(a, b); + /* test the inverse */ + RESULT_ENSURE_GT(b, a); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_LT_harness_int32(int32_t a, int32_t b) +{ + RESULT_ENSURE_LT(a, b); + /* test the inverse */ + RESULT_ENSURE_GT(b, a); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_EQ_harness_uint32(uint32_t a, uint32_t b) +{ + RESULT_ENSURE_EQ(a, b); + RESULT_ENSURE_EQ(b, a); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_EQ_harness_int32(int32_t a, int32_t b) +{ + RESULT_ENSURE_EQ(a, b); + RESULT_ENSURE_EQ(b, a); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_NE_harness_uint32(uint32_t a, uint32_t b) +{ + RESULT_ENSURE_NE(a, b); + RESULT_ENSURE_NE(b, a); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_NE_harness_int32(int32_t a, int32_t b) +{ + RESULT_ENSURE_NE(a, b); + RESULT_ENSURE_NE(b, a); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_INCLUSIVE_RANGE_harness_uint32(uint32_t a, uint32_t b, uint32_t c) +{ + RESULT_ENSURE_INCLUSIVE_RANGE(a, b, c); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_INCLUSIVE_RANGE_harness_int32(int32_t a, int32_t b, int32_t c) +{ + RESULT_ENSURE_INCLUSIVE_RANGE(a, b, c); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_EXCLUSIVE_RANGE_harness_uint32(uint32_t a, uint32_t b, uint32_t c) +{ + RESULT_ENSURE_EXCLUSIVE_RANGE(a, b, c); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_EXCLUSIVE_RANGE_harness_int32(int32_t a, int32_t b, int32_t c) +{ + RESULT_ENSURE_EXCLUSIVE_RANGE(a, b, c); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_REF_harness(const char* str) +{ + RESULT_ENSURE_REF(str); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_ENSURE_MUT_harness(uint32_t* v) +{ + RESULT_ENSURE_MUT(v); + return S2N_RESULT_OK; +} + +static S2N_RESULT RESULT_PRECONDITION_harness_check(bool is_ok) +{ + RESULT_ENSURE(is_ok, S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_PRECONDITION_harness(s2n_result result) +{ + RESULT_PRECONDITION(result); + return S2N_RESULT_OK; +} + +static S2N_RESULT RESULT_POSTCONDITION_harness_check(bool is_ok) +{ + RESULT_ENSURE(is_ok, S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_POSTCONDITION_harness(s2n_result result) +{ + RESULT_POSTCONDITION(result); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_CHECKED_MEMCPY_harness(uint32_t* dest, uint32_t* source, size_t len) +{ + RESULT_CHECKED_MEMCPY(dest, source, len); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_CHECKED_MEMSET_harness(uint32_t* dest, uint8_t value, size_t len) +{ + RESULT_CHECKED_MEMSET(dest, value, len); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_GUARD_harness(s2n_result result) +{ + RESULT_GUARD(result); + return S2N_RESULT_OK; +} + +static s2n_result RESULT_GUARD_OSSL_harness(int result, int error) +{ + RESULT_GUARD_OSSL(result, error); + return S2N_RESULT_OK; +} + +static int POSIX_BAIL_harness() +{ + POSIX_BAIL(S2N_ERR_SAFETY); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_harness(bool is_ok) +{ + POSIX_ENSURE(is_ok, S2N_ERR_SAFETY); + return S2N_SUCCESS; +} + +static int POSIX_DEBUG_ENSURE_harness(bool is_ok) +{ + POSIX_DEBUG_ENSURE(is_ok, S2N_ERR_SAFETY); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_OK_harness(bool is_ok) +{ + POSIX_ENSURE_OK(POSIX_ENSURE_harness(is_ok), S2N_ERR_IO); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_GTE_harness_uint32(uint32_t a, uint32_t b) +{ + POSIX_ENSURE_GTE(a, b); + /* test the inverse */ + POSIX_ENSURE_LTE(b, a); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_GTE_harness_int32(int32_t a, int32_t b) +{ + POSIX_ENSURE_GTE(a, b); + /* test the inverse */ + POSIX_ENSURE_LTE(b, a); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_LTE_harness_uint32(uint32_t a, uint32_t b) +{ + POSIX_ENSURE_LTE(a, b); + /* test the inverse */ + POSIX_ENSURE_GTE(b, a); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_LTE_harness_int32(int32_t a, int32_t b) +{ + POSIX_ENSURE_LTE(a, b); + /* test the inverse */ + POSIX_ENSURE_GTE(b, a); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_GT_harness_uint32(uint32_t a, uint32_t b) +{ + POSIX_ENSURE_GT(a, b); + /* test the inverse */ + POSIX_ENSURE_LT(b, a); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_GT_harness_int32(int32_t a, int32_t b) +{ + POSIX_ENSURE_GT(a, b); + /* test the inverse */ + POSIX_ENSURE_LT(b, a); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_LT_harness_uint32(uint32_t a, uint32_t b) +{ + POSIX_ENSURE_LT(a, b); + /* test the inverse */ + POSIX_ENSURE_GT(b, a); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_LT_harness_int32(int32_t a, int32_t b) +{ + POSIX_ENSURE_LT(a, b); + /* test the inverse */ + POSIX_ENSURE_GT(b, a); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_EQ_harness_uint32(uint32_t a, uint32_t b) +{ + POSIX_ENSURE_EQ(a, b); + POSIX_ENSURE_EQ(b, a); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_EQ_harness_int32(int32_t a, int32_t b) +{ + POSIX_ENSURE_EQ(a, b); + POSIX_ENSURE_EQ(b, a); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_NE_harness_uint32(uint32_t a, uint32_t b) +{ + POSIX_ENSURE_NE(a, b); + POSIX_ENSURE_NE(b, a); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_NE_harness_int32(int32_t a, int32_t b) +{ + POSIX_ENSURE_NE(a, b); + POSIX_ENSURE_NE(b, a); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_INCLUSIVE_RANGE_harness_uint32(uint32_t a, uint32_t b, uint32_t c) +{ + POSIX_ENSURE_INCLUSIVE_RANGE(a, b, c); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_INCLUSIVE_RANGE_harness_int32(int32_t a, int32_t b, int32_t c) +{ + POSIX_ENSURE_INCLUSIVE_RANGE(a, b, c); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_EXCLUSIVE_RANGE_harness_uint32(uint32_t a, uint32_t b, uint32_t c) +{ + POSIX_ENSURE_EXCLUSIVE_RANGE(a, b, c); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_EXCLUSIVE_RANGE_harness_int32(int32_t a, int32_t b, int32_t c) +{ + POSIX_ENSURE_EXCLUSIVE_RANGE(a, b, c); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_REF_harness(const char* str) +{ + POSIX_ENSURE_REF(str); + return S2N_SUCCESS; +} + +static int POSIX_ENSURE_MUT_harness(uint32_t* v) +{ + POSIX_ENSURE_MUT(v); + return S2N_SUCCESS; +} + +static S2N_RESULT POSIX_PRECONDITION_harness_check(bool is_ok) +{ + RESULT_ENSURE(is_ok, S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +static int POSIX_PRECONDITION_harness(s2n_result result) +{ + POSIX_PRECONDITION(result); + return S2N_SUCCESS; +} + +static S2N_RESULT POSIX_POSTCONDITION_harness_check(bool is_ok) +{ + RESULT_ENSURE(is_ok, S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +static int POSIX_POSTCONDITION_harness(s2n_result result) +{ + POSIX_POSTCONDITION(result); + return S2N_SUCCESS; +} + +static int POSIX_CHECKED_MEMCPY_harness(uint32_t* dest, uint32_t* source, size_t len) +{ + POSIX_CHECKED_MEMCPY(dest, source, len); + return S2N_SUCCESS; +} + +static int POSIX_CHECKED_MEMSET_harness(uint32_t* dest, uint8_t value, size_t len) +{ + POSIX_CHECKED_MEMSET(dest, value, len); + return S2N_SUCCESS; +} + +static int POSIX_GUARD_harness(int result) +{ + POSIX_GUARD(result); + return S2N_SUCCESS; +} + +static int POSIX_GUARD_OSSL_harness(int result, int error) +{ + POSIX_GUARD_OSSL(result, error); + return S2N_SUCCESS; +} + +static const char* PTR_BAIL_harness() +{ + PTR_BAIL(S2N_ERR_SAFETY); + return "ok"; +} + +static const char* PTR_ENSURE_harness(bool is_ok) +{ + PTR_ENSURE(is_ok, S2N_ERR_SAFETY); + return "ok"; +} + +static const char* PTR_DEBUG_ENSURE_harness(bool is_ok) +{ + PTR_DEBUG_ENSURE(is_ok, S2N_ERR_SAFETY); + return "ok"; +} + +static const char* PTR_ENSURE_OK_harness(bool is_ok) +{ + PTR_ENSURE_OK(PTR_ENSURE_harness(is_ok), S2N_ERR_IO); + return "ok"; +} + +static const char* PTR_ENSURE_GTE_harness_uint32(uint32_t a, uint32_t b) +{ + PTR_ENSURE_GTE(a, b); + /* test the inverse */ + PTR_ENSURE_LTE(b, a); + return "ok"; +} + +static const char* PTR_ENSURE_GTE_harness_int32(int32_t a, int32_t b) +{ + PTR_ENSURE_GTE(a, b); + /* test the inverse */ + PTR_ENSURE_LTE(b, a); + return "ok"; +} + +static const char* PTR_ENSURE_LTE_harness_uint32(uint32_t a, uint32_t b) +{ + PTR_ENSURE_LTE(a, b); + /* test the inverse */ + PTR_ENSURE_GTE(b, a); + return "ok"; +} + +static const char* PTR_ENSURE_LTE_harness_int32(int32_t a, int32_t b) +{ + PTR_ENSURE_LTE(a, b); + /* test the inverse */ + PTR_ENSURE_GTE(b, a); + return "ok"; +} + +static const char* PTR_ENSURE_GT_harness_uint32(uint32_t a, uint32_t b) +{ + PTR_ENSURE_GT(a, b); + /* test the inverse */ + PTR_ENSURE_LT(b, a); + return "ok"; +} + +static const char* PTR_ENSURE_GT_harness_int32(int32_t a, int32_t b) +{ + PTR_ENSURE_GT(a, b); + /* test the inverse */ + PTR_ENSURE_LT(b, a); + return "ok"; +} + +static const char* PTR_ENSURE_LT_harness_uint32(uint32_t a, uint32_t b) +{ + PTR_ENSURE_LT(a, b); + /* test the inverse */ + PTR_ENSURE_GT(b, a); + return "ok"; +} + +static const char* PTR_ENSURE_LT_harness_int32(int32_t a, int32_t b) +{ + PTR_ENSURE_LT(a, b); + /* test the inverse */ + PTR_ENSURE_GT(b, a); + return "ok"; +} + +static const char* PTR_ENSURE_EQ_harness_uint32(uint32_t a, uint32_t b) +{ + PTR_ENSURE_EQ(a, b); + PTR_ENSURE_EQ(b, a); + return "ok"; +} + +static const char* PTR_ENSURE_EQ_harness_int32(int32_t a, int32_t b) +{ + PTR_ENSURE_EQ(a, b); + PTR_ENSURE_EQ(b, a); + return "ok"; +} + +static const char* PTR_ENSURE_NE_harness_uint32(uint32_t a, uint32_t b) +{ + PTR_ENSURE_NE(a, b); + PTR_ENSURE_NE(b, a); + return "ok"; +} + +static const char* PTR_ENSURE_NE_harness_int32(int32_t a, int32_t b) +{ + PTR_ENSURE_NE(a, b); + PTR_ENSURE_NE(b, a); + return "ok"; +} + +static const char* PTR_ENSURE_INCLUSIVE_RANGE_harness_uint32(uint32_t a, uint32_t b, uint32_t c) +{ + PTR_ENSURE_INCLUSIVE_RANGE(a, b, c); + return "ok"; +} + +static const char* PTR_ENSURE_INCLUSIVE_RANGE_harness_int32(int32_t a, int32_t b, int32_t c) +{ + PTR_ENSURE_INCLUSIVE_RANGE(a, b, c); + return "ok"; +} + +static const char* PTR_ENSURE_EXCLUSIVE_RANGE_harness_uint32(uint32_t a, uint32_t b, uint32_t c) +{ + PTR_ENSURE_EXCLUSIVE_RANGE(a, b, c); + return "ok"; +} + +static const char* PTR_ENSURE_EXCLUSIVE_RANGE_harness_int32(int32_t a, int32_t b, int32_t c) +{ + PTR_ENSURE_EXCLUSIVE_RANGE(a, b, c); + return "ok"; +} + +static const char* PTR_ENSURE_REF_harness(const char* str) +{ + PTR_ENSURE_REF(str); + return "ok"; +} + +static const char* PTR_ENSURE_MUT_harness(uint32_t* v) +{ + PTR_ENSURE_MUT(v); + return "ok"; +} + +static S2N_RESULT PTR_PRECONDITION_harness_check(bool is_ok) +{ + RESULT_ENSURE(is_ok, S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +static const char* PTR_PRECONDITION_harness(s2n_result result) +{ + PTR_PRECONDITION(result); + return "ok"; +} + +static S2N_RESULT PTR_POSTCONDITION_harness_check(bool is_ok) +{ + RESULT_ENSURE(is_ok, S2N_ERR_SAFETY); + return S2N_RESULT_OK; +} + +static const char* PTR_POSTCONDITION_harness(s2n_result result) +{ + PTR_POSTCONDITION(result); + return "ok"; +} + +static const char* PTR_CHECKED_MEMCPY_harness(uint32_t* dest, uint32_t* source, size_t len) +{ + PTR_CHECKED_MEMCPY(dest, source, len); + return "ok"; +} + +static const char* PTR_CHECKED_MEMSET_harness(uint32_t* dest, uint8_t value, size_t len) +{ + PTR_CHECKED_MEMSET(dest, value, len); + return "ok"; +} + +static const char* PTR_GUARD_harness(const char* result) +{ + PTR_GUARD(result); + return "ok"; +} + +static const char* PTR_GUARD_OSSL_harness(int result, int error) +{ + PTR_GUARD_OSSL(result, error); + return "ok"; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* RESULT_BAIL(error) */ + EXPECT_ERROR_WITH_ERRNO(RESULT_BAIL_harness(), S2N_ERR_SAFETY); + + /* RESULT_ENSURE(condition, error) */ + EXPECT_OK(RESULT_ENSURE_harness(true)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_harness(false), S2N_ERR_SAFETY); + + /* RESULT_DEBUG_ENSURE(condition, error) */ + EXPECT_OK(RESULT_DEBUG_ENSURE_harness(true)); + #ifdef NDEBUG + EXPECT_OK(RESULT_DEBUG_ENSURE_harness(false)); + #else + EXPECT_ERROR_WITH_ERRNO(RESULT_DEBUG_ENSURE_harness(false), S2N_ERR_SAFETY); + #endif + + /* RESULT_ENSURE_OK(result, error) */ + EXPECT_OK(RESULT_ENSURE_OK_harness(true)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_OK_harness(false), S2N_ERR_IO); + + /* RESULT_ENSURE_GTE(a, b) */ + EXPECT_OK(RESULT_ENSURE_GTE_harness_uint32(0, 0)); + EXPECT_OK(RESULT_ENSURE_GTE_harness_uint32(1, 0)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_GTE_harness_uint32(0, 1), S2N_ERR_SAFETY); + EXPECT_OK(RESULT_ENSURE_GTE_harness_int32(-1, -2)); + EXPECT_OK(RESULT_ENSURE_GTE_harness_int32(-1, -1)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_GTE_harness_int32(-2, -1), S2N_ERR_SAFETY); + + /* RESULT_ENSURE_LTE(a, b) */ + EXPECT_OK(RESULT_ENSURE_LTE_harness_uint32(0, 0)); + EXPECT_OK(RESULT_ENSURE_LTE_harness_uint32(0, 1)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_LTE_harness_uint32(1, 0), S2N_ERR_SAFETY); + EXPECT_OK(RESULT_ENSURE_LTE_harness_int32(-2, -1)); + EXPECT_OK(RESULT_ENSURE_LTE_harness_int32(-1, -1)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_LTE_harness_int32(-1, -2), S2N_ERR_SAFETY); + + /* RESULT_ENSURE_GT(a, b) */ + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_GT_harness_uint32(0, 0), S2N_ERR_SAFETY); + EXPECT_OK(RESULT_ENSURE_GT_harness_uint32(1, 0)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_GT_harness_uint32(0, 1), S2N_ERR_SAFETY); + EXPECT_OK(RESULT_ENSURE_GT_harness_int32(-1, -2)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_GT_harness_int32(-1, -1), S2N_ERR_SAFETY); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_GT_harness_int32(-2, -1), S2N_ERR_SAFETY); + + /* RESULT_ENSURE_LT(a, b) */ + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_LT_harness_uint32(0, 0), S2N_ERR_SAFETY); + EXPECT_OK(RESULT_ENSURE_LT_harness_uint32(0, 1)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_LT_harness_uint32(1, 0), S2N_ERR_SAFETY); + EXPECT_OK(RESULT_ENSURE_LT_harness_int32(-2, -1)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_LT_harness_int32(-1, -1), S2N_ERR_SAFETY); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_LT_harness_int32(-1, -2), S2N_ERR_SAFETY); + + /* RESULT_ENSURE_EQ(a, b) */ + EXPECT_OK(RESULT_ENSURE_EQ_harness_uint32(0, 0)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_EQ_harness_uint32(1, 0), S2N_ERR_SAFETY); + EXPECT_OK(RESULT_ENSURE_EQ_harness_int32(-1, -1)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_EQ_harness_int32(-2, -1), S2N_ERR_SAFETY); + + /* RESULT_ENSURE_NE(a, b) */ + EXPECT_OK(RESULT_ENSURE_NE_harness_uint32(1, 0)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_NE_harness_uint32(0, 0), S2N_ERR_SAFETY); + EXPECT_OK(RESULT_ENSURE_NE_harness_int32(-2, -1)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_NE_harness_int32(-1, -1), S2N_ERR_SAFETY); + + /* RESULT_ENSURE_INCLUSIVE_RANGE(min, n, max) */ + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 0, 2), S2N_ERR_SAFETY); + EXPECT_OK(RESULT_ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 1, 2)); + EXPECT_OK(RESULT_ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 2, 2)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 3, 2), S2N_ERR_SAFETY); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_INCLUSIVE_RANGE_harness_int32(-2, -3, -1), S2N_ERR_SAFETY); + EXPECT_OK(RESULT_ENSURE_INCLUSIVE_RANGE_harness_int32(-2, -2, -1)); + EXPECT_OK(RESULT_ENSURE_INCLUSIVE_RANGE_harness_int32(-2, -1, -1)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_INCLUSIVE_RANGE_harness_int32(-2, 0, -1), S2N_ERR_SAFETY); + + /* RESULT_ENSURE_EXCLUSIVE_RANGE(min, n, max) */ + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 0, 3), S2N_ERR_SAFETY); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 1, 3), S2N_ERR_SAFETY); + EXPECT_OK(RESULT_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 2, 3)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 3, 3), S2N_ERR_SAFETY); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 4, 3), S2N_ERR_SAFETY); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -4, -1), S2N_ERR_SAFETY); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -3, -1), S2N_ERR_SAFETY); + EXPECT_OK(RESULT_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -2, -1)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -1, -1), S2N_ERR_SAFETY); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, 0, -1), S2N_ERR_SAFETY); + + /* RESULT_ENSURE_REF(x) */ + EXPECT_OK(RESULT_ENSURE_REF_harness("")); + EXPECT_OK(RESULT_ENSURE_REF_harness("ok")); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_REF_harness(NULL), S2N_ERR_NULL); + + /* RESULT_ENSURE_MUT(x) */ + uint32_t RESULT_ensure_mut_test = 0; + EXPECT_OK(RESULT_ENSURE_MUT_harness(&RESULT_ensure_mut_test)); + RESULT_ensure_mut_test = 1; + EXPECT_OK(RESULT_ENSURE_MUT_harness(&RESULT_ensure_mut_test)); + EXPECT_ERROR_WITH_ERRNO(RESULT_ENSURE_MUT_harness(NULL), S2N_ERR_NULL); + + /* RESULT_PRECONDITION(result) */ + EXPECT_OK(RESULT_PRECONDITION_harness(RESULT_PRECONDITION_harness_check(true))); + EXPECT_ERROR_WITH_ERRNO(RESULT_PRECONDITION_harness(RESULT_PRECONDITION_harness_check(false)), S2N_ERR_SAFETY); + + /* RESULT_POSTCONDITION(result) */ + EXPECT_OK(RESULT_POSTCONDITION_harness(RESULT_POSTCONDITION_harness_check(true))); + #ifdef NDEBUG + EXPECT_OK(RESULT_POSTCONDITION_harness(RESULT_POSTCONDITION_harness_check(false))); + #else + EXPECT_ERROR_WITH_ERRNO(RESULT_POSTCONDITION_harness(RESULT_POSTCONDITION_harness_check(false)), S2N_ERR_SAFETY); + #endif + + /* RESULT_CHECKED_MEMCPY(destination, source, len) */ + uint32_t RESULT__checked_memcpy_dest = 1; + uint32_t RESULT__checked_memcpy_source = 2; + EXPECT_OK(RESULT_CHECKED_MEMCPY_harness(&RESULT__checked_memcpy_dest, &RESULT__checked_memcpy_source, 0)); + EXPECT_EQUAL(RESULT__checked_memcpy_dest, 1); + EXPECT_ERROR_WITH_ERRNO(RESULT_CHECKED_MEMCPY_harness(NULL, &RESULT__checked_memcpy_source, 4), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(RESULT_CHECKED_MEMCPY_harness(&RESULT__checked_memcpy_dest, NULL, 4), S2N_ERR_NULL); + EXPECT_OK(RESULT_CHECKED_MEMCPY_harness(&RESULT__checked_memcpy_dest, &RESULT__checked_memcpy_source, 4)); + EXPECT_EQUAL(RESULT__checked_memcpy_dest, RESULT__checked_memcpy_source); + + /* RESULT_CHECKED_MEMSET(destination, value, len) */ + uint32_t RESULT__checked_memset_dest = 1; + EXPECT_OK(RESULT_CHECKED_MEMSET_harness(&RESULT__checked_memset_dest, 0x42, 0)); + EXPECT_EQUAL(RESULT__checked_memset_dest, 1); + EXPECT_ERROR_WITH_ERRNO(RESULT_CHECKED_MEMSET_harness(NULL, 0x42, 1), S2N_ERR_NULL); + EXPECT_OK(RESULT_CHECKED_MEMSET_harness(&RESULT__checked_memset_dest, 0x42, 4)); + EXPECT_EQUAL(RESULT__checked_memset_dest, 0x42424242); + + /* RESULT_GUARD(result) */ + EXPECT_OK(RESULT_GUARD_harness(RESULT_ENSURE_harness(true))); + EXPECT_ERROR_WITH_ERRNO(RESULT_GUARD_harness(RESULT_ENSURE_harness(false)), S2N_ERR_SAFETY); + + /* RESULT_GUARD_OSSL(result, error) */ + EXPECT_OK(RESULT_GUARD_OSSL_harness(1, S2N_ERR_SAFETY)); + EXPECT_ERROR_WITH_ERRNO(RESULT_GUARD_OSSL_harness(0, S2N_ERR_SAFETY), S2N_ERR_SAFETY); + + /* POSIX_BAIL(error) */ + EXPECT_FAILURE_WITH_ERRNO(POSIX_BAIL_harness(), S2N_ERR_SAFETY); + + /* POSIX_ENSURE(condition, error) */ + EXPECT_SUCCESS(POSIX_ENSURE_harness(true)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_harness(false), S2N_ERR_SAFETY); + + /* POSIX_DEBUG_ENSURE(condition, error) */ + EXPECT_SUCCESS(POSIX_DEBUG_ENSURE_harness(true)); + #ifdef NDEBUG + EXPECT_SUCCESS(POSIX_DEBUG_ENSURE_harness(false)); + #else + EXPECT_FAILURE_WITH_ERRNO(POSIX_DEBUG_ENSURE_harness(false), S2N_ERR_SAFETY); + #endif + + /* POSIX_ENSURE_OK(result, error) */ + EXPECT_SUCCESS(POSIX_ENSURE_OK_harness(true)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_OK_harness(false), S2N_ERR_IO); + + /* POSIX_ENSURE_GTE(a, b) */ + EXPECT_SUCCESS(POSIX_ENSURE_GTE_harness_uint32(0, 0)); + EXPECT_SUCCESS(POSIX_ENSURE_GTE_harness_uint32(1, 0)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_GTE_harness_uint32(0, 1), S2N_ERR_SAFETY); + EXPECT_SUCCESS(POSIX_ENSURE_GTE_harness_int32(-1, -2)); + EXPECT_SUCCESS(POSIX_ENSURE_GTE_harness_int32(-1, -1)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_GTE_harness_int32(-2, -1), S2N_ERR_SAFETY); + + /* POSIX_ENSURE_LTE(a, b) */ + EXPECT_SUCCESS(POSIX_ENSURE_LTE_harness_uint32(0, 0)); + EXPECT_SUCCESS(POSIX_ENSURE_LTE_harness_uint32(0, 1)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_LTE_harness_uint32(1, 0), S2N_ERR_SAFETY); + EXPECT_SUCCESS(POSIX_ENSURE_LTE_harness_int32(-2, -1)); + EXPECT_SUCCESS(POSIX_ENSURE_LTE_harness_int32(-1, -1)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_LTE_harness_int32(-1, -2), S2N_ERR_SAFETY); + + /* POSIX_ENSURE_GT(a, b) */ + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_GT_harness_uint32(0, 0), S2N_ERR_SAFETY); + EXPECT_SUCCESS(POSIX_ENSURE_GT_harness_uint32(1, 0)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_GT_harness_uint32(0, 1), S2N_ERR_SAFETY); + EXPECT_SUCCESS(POSIX_ENSURE_GT_harness_int32(-1, -2)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_GT_harness_int32(-1, -1), S2N_ERR_SAFETY); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_GT_harness_int32(-2, -1), S2N_ERR_SAFETY); + + /* POSIX_ENSURE_LT(a, b) */ + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_LT_harness_uint32(0, 0), S2N_ERR_SAFETY); + EXPECT_SUCCESS(POSIX_ENSURE_LT_harness_uint32(0, 1)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_LT_harness_uint32(1, 0), S2N_ERR_SAFETY); + EXPECT_SUCCESS(POSIX_ENSURE_LT_harness_int32(-2, -1)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_LT_harness_int32(-1, -1), S2N_ERR_SAFETY); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_LT_harness_int32(-1, -2), S2N_ERR_SAFETY); + + /* POSIX_ENSURE_EQ(a, b) */ + EXPECT_SUCCESS(POSIX_ENSURE_EQ_harness_uint32(0, 0)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_EQ_harness_uint32(1, 0), S2N_ERR_SAFETY); + EXPECT_SUCCESS(POSIX_ENSURE_EQ_harness_int32(-1, -1)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_EQ_harness_int32(-2, -1), S2N_ERR_SAFETY); + + /* POSIX_ENSURE_NE(a, b) */ + EXPECT_SUCCESS(POSIX_ENSURE_NE_harness_uint32(1, 0)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_NE_harness_uint32(0, 0), S2N_ERR_SAFETY); + EXPECT_SUCCESS(POSIX_ENSURE_NE_harness_int32(-2, -1)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_NE_harness_int32(-1, -1), S2N_ERR_SAFETY); + + /* POSIX_ENSURE_INCLUSIVE_RANGE(min, n, max) */ + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 0, 2), S2N_ERR_SAFETY); + EXPECT_SUCCESS(POSIX_ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 1, 2)); + EXPECT_SUCCESS(POSIX_ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 2, 2)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 3, 2), S2N_ERR_SAFETY); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_INCLUSIVE_RANGE_harness_int32(-2, -3, -1), S2N_ERR_SAFETY); + EXPECT_SUCCESS(POSIX_ENSURE_INCLUSIVE_RANGE_harness_int32(-2, -2, -1)); + EXPECT_SUCCESS(POSIX_ENSURE_INCLUSIVE_RANGE_harness_int32(-2, -1, -1)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_INCLUSIVE_RANGE_harness_int32(-2, 0, -1), S2N_ERR_SAFETY); + + /* POSIX_ENSURE_EXCLUSIVE_RANGE(min, n, max) */ + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 0, 3), S2N_ERR_SAFETY); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 1, 3), S2N_ERR_SAFETY); + EXPECT_SUCCESS(POSIX_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 2, 3)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 3, 3), S2N_ERR_SAFETY); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 4, 3), S2N_ERR_SAFETY); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -4, -1), S2N_ERR_SAFETY); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -3, -1), S2N_ERR_SAFETY); + EXPECT_SUCCESS(POSIX_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -2, -1)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -1, -1), S2N_ERR_SAFETY); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, 0, -1), S2N_ERR_SAFETY); + + /* POSIX_ENSURE_REF(x) */ + EXPECT_SUCCESS(POSIX_ENSURE_REF_harness("")); + EXPECT_SUCCESS(POSIX_ENSURE_REF_harness("ok")); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_REF_harness(NULL), S2N_ERR_NULL); + + /* POSIX_ENSURE_MUT(x) */ + uint32_t POSIX_ensure_mut_test = 0; + EXPECT_SUCCESS(POSIX_ENSURE_MUT_harness(&POSIX_ensure_mut_test)); + POSIX_ensure_mut_test = 1; + EXPECT_SUCCESS(POSIX_ENSURE_MUT_harness(&POSIX_ensure_mut_test)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_ENSURE_MUT_harness(NULL), S2N_ERR_NULL); + + /* POSIX_PRECONDITION(result) */ + EXPECT_SUCCESS(POSIX_PRECONDITION_harness(POSIX_PRECONDITION_harness_check(true))); + EXPECT_FAILURE_WITH_ERRNO(POSIX_PRECONDITION_harness(POSIX_PRECONDITION_harness_check(false)), S2N_ERR_SAFETY); + + /* POSIX_POSTCONDITION(result) */ + EXPECT_SUCCESS(POSIX_POSTCONDITION_harness(POSIX_POSTCONDITION_harness_check(true))); + #ifdef NDEBUG + EXPECT_SUCCESS(POSIX_POSTCONDITION_harness(POSIX_POSTCONDITION_harness_check(false))); + #else + EXPECT_FAILURE_WITH_ERRNO(POSIX_POSTCONDITION_harness(POSIX_POSTCONDITION_harness_check(false)), S2N_ERR_SAFETY); + #endif + + /* POSIX_CHECKED_MEMCPY(destination, source, len) */ + uint32_t POSIX__checked_memcpy_dest = 1; + uint32_t POSIX__checked_memcpy_source = 2; + EXPECT_SUCCESS(POSIX_CHECKED_MEMCPY_harness(&POSIX__checked_memcpy_dest, &POSIX__checked_memcpy_source, 0)); + EXPECT_EQUAL(POSIX__checked_memcpy_dest, 1); + EXPECT_FAILURE_WITH_ERRNO(POSIX_CHECKED_MEMCPY_harness(NULL, &POSIX__checked_memcpy_source, 4), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(POSIX_CHECKED_MEMCPY_harness(&POSIX__checked_memcpy_dest, NULL, 4), S2N_ERR_NULL); + EXPECT_SUCCESS(POSIX_CHECKED_MEMCPY_harness(&POSIX__checked_memcpy_dest, &POSIX__checked_memcpy_source, 4)); + EXPECT_EQUAL(POSIX__checked_memcpy_dest, POSIX__checked_memcpy_source); + + /* POSIX_CHECKED_MEMSET(destination, value, len) */ + uint32_t POSIX__checked_memset_dest = 1; + EXPECT_SUCCESS(POSIX_CHECKED_MEMSET_harness(&POSIX__checked_memset_dest, 0x42, 0)); + EXPECT_EQUAL(POSIX__checked_memset_dest, 1); + EXPECT_FAILURE_WITH_ERRNO(POSIX_CHECKED_MEMSET_harness(NULL, 0x42, 1), S2N_ERR_NULL); + EXPECT_SUCCESS(POSIX_CHECKED_MEMSET_harness(&POSIX__checked_memset_dest, 0x42, 4)); + EXPECT_EQUAL(POSIX__checked_memset_dest, 0x42424242); + + /* POSIX_GUARD(result) */ + EXPECT_SUCCESS(POSIX_GUARD_harness(POSIX_ENSURE_harness(true))); + EXPECT_FAILURE_WITH_ERRNO(POSIX_GUARD_harness(POSIX_ENSURE_harness(false)), S2N_ERR_SAFETY); + + /* POSIX_GUARD_OSSL(result, error) */ + EXPECT_SUCCESS(POSIX_GUARD_OSSL_harness(1, S2N_ERR_SAFETY)); + EXPECT_FAILURE_WITH_ERRNO(POSIX_GUARD_OSSL_harness(0, S2N_ERR_SAFETY), S2N_ERR_SAFETY); + + /* PTR_BAIL(error) */ + EXPECT_NULL_WITH_ERRNO(PTR_BAIL_harness(), S2N_ERR_SAFETY); + + /* PTR_ENSURE(condition, error) */ + EXPECT_NOT_NULL(PTR_ENSURE_harness(true)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_harness(false), S2N_ERR_SAFETY); + + /* PTR_DEBUG_ENSURE(condition, error) */ + EXPECT_NOT_NULL(PTR_DEBUG_ENSURE_harness(true)); + #ifdef NDEBUG + EXPECT_NOT_NULL(PTR_DEBUG_ENSURE_harness(false)); + #else + EXPECT_NULL_WITH_ERRNO(PTR_DEBUG_ENSURE_harness(false), S2N_ERR_SAFETY); + #endif + + /* PTR_ENSURE_OK(result, error) */ + EXPECT_NOT_NULL(PTR_ENSURE_OK_harness(true)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_OK_harness(false), S2N_ERR_IO); + + /* PTR_ENSURE_GTE(a, b) */ + EXPECT_NOT_NULL(PTR_ENSURE_GTE_harness_uint32(0, 0)); + EXPECT_NOT_NULL(PTR_ENSURE_GTE_harness_uint32(1, 0)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_GTE_harness_uint32(0, 1), S2N_ERR_SAFETY); + EXPECT_NOT_NULL(PTR_ENSURE_GTE_harness_int32(-1, -2)); + EXPECT_NOT_NULL(PTR_ENSURE_GTE_harness_int32(-1, -1)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_GTE_harness_int32(-2, -1), S2N_ERR_SAFETY); + + /* PTR_ENSURE_LTE(a, b) */ + EXPECT_NOT_NULL(PTR_ENSURE_LTE_harness_uint32(0, 0)); + EXPECT_NOT_NULL(PTR_ENSURE_LTE_harness_uint32(0, 1)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_LTE_harness_uint32(1, 0), S2N_ERR_SAFETY); + EXPECT_NOT_NULL(PTR_ENSURE_LTE_harness_int32(-2, -1)); + EXPECT_NOT_NULL(PTR_ENSURE_LTE_harness_int32(-1, -1)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_LTE_harness_int32(-1, -2), S2N_ERR_SAFETY); + + /* PTR_ENSURE_GT(a, b) */ + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_GT_harness_uint32(0, 0), S2N_ERR_SAFETY); + EXPECT_NOT_NULL(PTR_ENSURE_GT_harness_uint32(1, 0)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_GT_harness_uint32(0, 1), S2N_ERR_SAFETY); + EXPECT_NOT_NULL(PTR_ENSURE_GT_harness_int32(-1, -2)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_GT_harness_int32(-1, -1), S2N_ERR_SAFETY); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_GT_harness_int32(-2, -1), S2N_ERR_SAFETY); + + /* PTR_ENSURE_LT(a, b) */ + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_LT_harness_uint32(0, 0), S2N_ERR_SAFETY); + EXPECT_NOT_NULL(PTR_ENSURE_LT_harness_uint32(0, 1)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_LT_harness_uint32(1, 0), S2N_ERR_SAFETY); + EXPECT_NOT_NULL(PTR_ENSURE_LT_harness_int32(-2, -1)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_LT_harness_int32(-1, -1), S2N_ERR_SAFETY); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_LT_harness_int32(-1, -2), S2N_ERR_SAFETY); + + /* PTR_ENSURE_EQ(a, b) */ + EXPECT_NOT_NULL(PTR_ENSURE_EQ_harness_uint32(0, 0)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_EQ_harness_uint32(1, 0), S2N_ERR_SAFETY); + EXPECT_NOT_NULL(PTR_ENSURE_EQ_harness_int32(-1, -1)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_EQ_harness_int32(-2, -1), S2N_ERR_SAFETY); + + /* PTR_ENSURE_NE(a, b) */ + EXPECT_NOT_NULL(PTR_ENSURE_NE_harness_uint32(1, 0)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_NE_harness_uint32(0, 0), S2N_ERR_SAFETY); + EXPECT_NOT_NULL(PTR_ENSURE_NE_harness_int32(-2, -1)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_NE_harness_int32(-1, -1), S2N_ERR_SAFETY); + + /* PTR_ENSURE_INCLUSIVE_RANGE(min, n, max) */ + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 0, 2), S2N_ERR_SAFETY); + EXPECT_NOT_NULL(PTR_ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 1, 2)); + EXPECT_NOT_NULL(PTR_ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 2, 2)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_INCLUSIVE_RANGE_harness_uint32(1, 3, 2), S2N_ERR_SAFETY); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_INCLUSIVE_RANGE_harness_int32(-2, -3, -1), S2N_ERR_SAFETY); + EXPECT_NOT_NULL(PTR_ENSURE_INCLUSIVE_RANGE_harness_int32(-2, -2, -1)); + EXPECT_NOT_NULL(PTR_ENSURE_INCLUSIVE_RANGE_harness_int32(-2, -1, -1)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_INCLUSIVE_RANGE_harness_int32(-2, 0, -1), S2N_ERR_SAFETY); + + /* PTR_ENSURE_EXCLUSIVE_RANGE(min, n, max) */ + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 0, 3), S2N_ERR_SAFETY); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 1, 3), S2N_ERR_SAFETY); + EXPECT_NOT_NULL(PTR_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 2, 3)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 3, 3), S2N_ERR_SAFETY); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_EXCLUSIVE_RANGE_harness_uint32(1, 4, 3), S2N_ERR_SAFETY); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -4, -1), S2N_ERR_SAFETY); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -3, -1), S2N_ERR_SAFETY); + EXPECT_NOT_NULL(PTR_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -2, -1)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, -1, -1), S2N_ERR_SAFETY); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_EXCLUSIVE_RANGE_harness_int32(-3, 0, -1), S2N_ERR_SAFETY); + + /* PTR_ENSURE_REF(x) */ + EXPECT_NOT_NULL(PTR_ENSURE_REF_harness("")); + EXPECT_NOT_NULL(PTR_ENSURE_REF_harness("ok")); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_REF_harness(NULL), S2N_ERR_NULL); + + /* PTR_ENSURE_MUT(x) */ + uint32_t PTR_ensure_mut_test = 0; + EXPECT_NOT_NULL(PTR_ENSURE_MUT_harness(&PTR_ensure_mut_test)); + PTR_ensure_mut_test = 1; + EXPECT_NOT_NULL(PTR_ENSURE_MUT_harness(&PTR_ensure_mut_test)); + EXPECT_NULL_WITH_ERRNO(PTR_ENSURE_MUT_harness(NULL), S2N_ERR_NULL); + + /* PTR_PRECONDITION(result) */ + EXPECT_NOT_NULL(PTR_PRECONDITION_harness(PTR_PRECONDITION_harness_check(true))); + EXPECT_NULL_WITH_ERRNO(PTR_PRECONDITION_harness(PTR_PRECONDITION_harness_check(false)), S2N_ERR_SAFETY); + + /* PTR_POSTCONDITION(result) */ + EXPECT_NOT_NULL(PTR_POSTCONDITION_harness(PTR_POSTCONDITION_harness_check(true))); + #ifdef NDEBUG + EXPECT_NOT_NULL(PTR_POSTCONDITION_harness(PTR_POSTCONDITION_harness_check(false))); + #else + EXPECT_NULL_WITH_ERRNO(PTR_POSTCONDITION_harness(PTR_POSTCONDITION_harness_check(false)), S2N_ERR_SAFETY); + #endif + + /* PTR_CHECKED_MEMCPY(destination, source, len) */ + uint32_t PTR__checked_memcpy_dest = 1; + uint32_t PTR__checked_memcpy_source = 2; + EXPECT_NOT_NULL(PTR_CHECKED_MEMCPY_harness(&PTR__checked_memcpy_dest, &PTR__checked_memcpy_source, 0)); + EXPECT_EQUAL(PTR__checked_memcpy_dest, 1); + EXPECT_NULL_WITH_ERRNO(PTR_CHECKED_MEMCPY_harness(NULL, &PTR__checked_memcpy_source, 4), S2N_ERR_NULL); + EXPECT_NULL_WITH_ERRNO(PTR_CHECKED_MEMCPY_harness(&PTR__checked_memcpy_dest, NULL, 4), S2N_ERR_NULL); + EXPECT_NOT_NULL(PTR_CHECKED_MEMCPY_harness(&PTR__checked_memcpy_dest, &PTR__checked_memcpy_source, 4)); + EXPECT_EQUAL(PTR__checked_memcpy_dest, PTR__checked_memcpy_source); + + /* PTR_CHECKED_MEMSET(destination, value, len) */ + uint32_t PTR__checked_memset_dest = 1; + EXPECT_NOT_NULL(PTR_CHECKED_MEMSET_harness(&PTR__checked_memset_dest, 0x42, 0)); + EXPECT_EQUAL(PTR__checked_memset_dest, 1); + EXPECT_NULL_WITH_ERRNO(PTR_CHECKED_MEMSET_harness(NULL, 0x42, 1), S2N_ERR_NULL); + EXPECT_NOT_NULL(PTR_CHECKED_MEMSET_harness(&PTR__checked_memset_dest, 0x42, 4)); + EXPECT_EQUAL(PTR__checked_memset_dest, 0x42424242); + + /* PTR_GUARD(result) */ + EXPECT_NOT_NULL(PTR_GUARD_harness(PTR_ENSURE_harness(true))); + EXPECT_NULL_WITH_ERRNO(PTR_GUARD_harness(PTR_ENSURE_harness(false)), S2N_ERR_SAFETY); + + /* PTR_GUARD_OSSL(result, error) */ + EXPECT_NOT_NULL(PTR_GUARD_OSSL_harness(1, S2N_ERR_SAFETY)); + EXPECT_NULL_WITH_ERRNO(PTR_GUARD_OSSL_harness(0, S2N_ERR_SAFETY), S2N_ERR_SAFETY); + + + END_TEST(); + return S2N_SUCCESS; +} diff --git a/tests/unit/s2n_safety_test.c b/tests/unit/s2n_safety_test.c new file mode 100644 index 00000000000..41122318a4b --- /dev/null +++ b/tests/unit/s2n_safety_test.c @@ -0,0 +1,392 @@ +/* + * 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 "utils/s2n_safety.h" + +#include "s2n_test.h" + +#define CHECK_OVF_0(fn, type, a, b) \ + do { \ + type result_val; \ + EXPECT_FAILURE(fn((a), (b), &result_val)); \ + } while (0) + +#define CHECK_OVF(fn, type, a, b) \ + do { \ + CHECK_OVF_0(fn, type, a, b); \ + CHECK_OVF_0(fn, type, b, a); \ + } while (0) + +#define CHECK_NO_OVF_0(fn, type, a, b, r) \ + do { \ + type result_val; \ + EXPECT_SUCCESS(fn((a), (b), &result_val)); \ + EXPECT_EQUAL(result_val, (r)); \ + } while (0) + +#define CHECK_NO_OVF(fn, type, a, b, r) \ + do { \ + CHECK_NO_OVF_0(fn, type, a, b, r); \ + CHECK_NO_OVF_0(fn, type, b, a, r); \ + } while (0) + +static int failure_gte() +{ + POSIX_ENSURE_GTE(0, 1); + + return 0; +} + +static int success_gte() +{ + POSIX_ENSURE_GTE(0, 0); + POSIX_ENSURE_GTE(1, 0); + + return 0; +} + +static int failure_gt() +{ + POSIX_ENSURE_GT(0, 0); + POSIX_ENSURE_GT(0, 1); + + return 0; +} + +static int success_gt() +{ + POSIX_ENSURE_GT(1, 0); + + return 0; +} + +static int failure_lte() +{ + POSIX_ENSURE_LTE(1, 0); + + return 0; +} + +static int success_lte() +{ + POSIX_ENSURE_LTE(1, 1); + POSIX_ENSURE_LTE(0, 1); + + return 0; +} + +static int failure_lt() +{ + POSIX_ENSURE_LT(1, 0); + POSIX_ENSURE_LT(1, 1); + + return 0; +} + +static int success_lt() +{ + POSIX_ENSURE_LT(0, 1); + + return 0; +} + +static int success_notnull() +{ + POSIX_ENSURE_REF(&""); + + return 0; +} + +static int failure_notnull() +{ + POSIX_ENSURE_REF(NULL); + + return 0; +} + +static int success_memcpy() +{ + char dst[1024]; + char src[1024] = { 0 }; + + POSIX_CHECKED_MEMCPY(dst, src, 1024); + + return 0; +} + +static int failure_memcpy() +{ + char src[1024]; + char *ptr = NULL; + + POSIX_CHECKED_MEMCPY(ptr, src, 1024); + + return 0; +} + +static int success_inclusive_range() +{ + POSIX_ENSURE_INCLUSIVE_RANGE(0, 0, 2); + POSIX_ENSURE_INCLUSIVE_RANGE(0, 1, 2); + POSIX_ENSURE_INCLUSIVE_RANGE(0, 2, 2); + + return 0; +} + +static int failure_inclusive_range_too_high() +{ + POSIX_ENSURE_INCLUSIVE_RANGE(0, 3, 2); + + return 0; +} + +static int failure_inclusive_range_too_low() +{ + POSIX_ENSURE_INCLUSIVE_RANGE(0, -1, 2); + + return 0; +} + +static int success_exclusive_range() +{ + POSIX_ENSURE_EXCLUSIVE_RANGE(0, 1, 2); + + return 0; +} + +static int failure_exclusive_range_too_high() +{ + POSIX_ENSURE_EXCLUSIVE_RANGE(0, 3, 2); + + return 0; +} + +static int failure_exclusive_range_too_low() +{ + POSIX_ENSURE_EXCLUSIVE_RANGE(0, -1, 2); + + return 0; +} + +static int failure_exclusive_range_eq_high() +{ + POSIX_ENSURE_EXCLUSIVE_RANGE(0, 2, 2); + + return 0; +} + +static int failure_exclusive_range_eq_low() +{ + POSIX_ENSURE_EXCLUSIVE_RANGE(0, 0, 2); + + return 0; +} + +static int success_ct_pkcs1() +{ + uint8_t pkcs1_data[] = { 0x00, 0x02, 0x80, 0x08, 0x0c, 0x00, 0xab, 0xcd, 0xef, 0x00 }; + uint8_t outbuf[] = { 0x11, 0x22, 0x33, 0x44 }; + uint8_t expected[] = { 0xab, 0xcd, 0xef, 0x00 }; + + s2n_constant_time_pkcs1_unpad_or_dont(outbuf, pkcs1_data, sizeof(pkcs1_data), sizeof(outbuf)); + + return memcmp(outbuf, expected, sizeof(expected)) ? -1 : 0; +} + +static int success_ct_pkcs1_negative() +{ + uint8_t pkcs1_data_too_long[] = { 0x00, 0x02, 0x80, 0x0f, 0x00, 0x10, 0xab, 0xcd, 0xef, 0x00 }; + uint8_t outbuf[] = { 0x11, 0x22, 0x33, 0x44 }; + uint8_t expected[] = { 0x11, 0x22, 0x33, 0x44 }; + + s2n_constant_time_pkcs1_unpad_or_dont(outbuf, pkcs1_data_too_long, sizeof(pkcs1_data_too_long), sizeof(outbuf)); + if (memcmp(outbuf, expected, sizeof(expected))) { + return -1; + } + + uint8_t pkcs1_data_too_short[] = { 0x00, 0x02, 0x80, 0x01, 0x02, 0x07, 0x00, 0xcd, 0xef, 0x00 }; + + s2n_constant_time_pkcs1_unpad_or_dont(outbuf, pkcs1_data_too_short, sizeof(pkcs1_data_too_short), sizeof(outbuf)); + if (memcmp(outbuf, expected, sizeof(expected))) { + return -1; + } + + uint8_t pkcs1_data_zeroes_in_pad[] = { 0x00, 0x02, 0x80, 0x00, 0x0c, 0x00, 0xab, 0xcd, 0xef, 0x00 }; + s2n_constant_time_pkcs1_unpad_or_dont(outbuf, pkcs1_data_zeroes_in_pad, sizeof(pkcs1_data_zeroes_in_pad), sizeof(outbuf)); + if (memcmp(outbuf, expected, sizeof(expected))) { + return -1; + } + + uint8_t pkcs1_data_zeroes_in_pad2[] = { 0x00, 0x02, 0x80, 0x11, 0x00, 0x00, 0xab, 0xcd, 0xef, 0x00 }; + s2n_constant_time_pkcs1_unpad_or_dont(outbuf, pkcs1_data_zeroes_in_pad2, sizeof(pkcs1_data_zeroes_in_pad2), sizeof(outbuf)); + if (memcmp(outbuf, expected, sizeof(expected))) { + return -1; + } + + uint8_t pkcs1_data_bad_prefix1[] = { 0x01, 0x02, 0x80, 0x08, 0x0c, 0x00, 0xab, 0xcd, 0xef, 0x00 }; + s2n_constant_time_pkcs1_unpad_or_dont(outbuf, pkcs1_data_bad_prefix1, sizeof(pkcs1_data_bad_prefix1), sizeof(outbuf)); + if (memcmp(outbuf, expected, sizeof(expected))) { + return -1; + } + + uint8_t pkcs1_data_bad_prefix2[] = { 0x00, 0x12, 0x80, 0x08, 0x0c, 0x00, 0xab, 0xcd, 0xef, 0x00 }; + s2n_constant_time_pkcs1_unpad_or_dont(outbuf, pkcs1_data_bad_prefix2, sizeof(pkcs1_data_bad_prefix2), sizeof(outbuf)); + if (memcmp(outbuf, expected, sizeof(expected))) { + return -1; + } + + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_FAILURE(failure_gte()); + EXPECT_FAILURE(failure_lte()); + EXPECT_FAILURE(failure_gt()); + EXPECT_FAILURE(failure_lt()); + EXPECT_FAILURE(failure_notnull()); + EXPECT_FAILURE(failure_memcpy()); + EXPECT_FAILURE(failure_inclusive_range_too_high()); + EXPECT_FAILURE(failure_inclusive_range_too_low()); + EXPECT_FAILURE(failure_exclusive_range_too_high()); + EXPECT_FAILURE(failure_exclusive_range_too_low()); + EXPECT_FAILURE(failure_exclusive_range_eq_high()); + EXPECT_FAILURE(failure_exclusive_range_eq_low()); + + EXPECT_SUCCESS(success_gte()); + EXPECT_SUCCESS(success_lte()); + EXPECT_SUCCESS(success_gt()); + EXPECT_SUCCESS(success_lt()); + EXPECT_SUCCESS(success_notnull()); + EXPECT_SUCCESS(success_memcpy()); + EXPECT_SUCCESS(success_inclusive_range()); + EXPECT_SUCCESS(success_exclusive_range()); + EXPECT_SUCCESS(success_ct_pkcs1()); + EXPECT_SUCCESS(success_ct_pkcs1_negative()); + + uint8_t a[4] = { 1, 2, 3, 4 }; + uint8_t b[4] = { 1, 2, 3, 4 }; + uint8_t c[4] = { 5, 6, 7, 8 }; + uint8_t d[4] = { 5, 6, 7, 8 }; + uint8_t e[4] = { 1, 2, 3, 4 }; + uint8_t f[4] = { 1, 2, 3, 5 }; + + EXPECT_TRUE(s2n_constant_time_equals(a, b, sizeof(a))); + EXPECT_FALSE(s2n_constant_time_equals(a, c, sizeof(a))); + EXPECT_FALSE(s2n_constant_time_equals(a, NULL, sizeof(a))); + EXPECT_FALSE(s2n_constant_time_equals(NULL, b, sizeof(b))); + EXPECT_TRUE(s2n_constant_time_equals(NULL, NULL, 0)); + EXPECT_FALSE(s2n_constant_time_equals(NULL, NULL, sizeof(a))); + EXPECT_TRUE(s2n_constant_time_equals(a, c, 0)); + /* ensure the function checks all of the bytes */ + EXPECT_TRUE(s2n_constant_time_equals(a, f, 3)); + EXPECT_FALSE(s2n_constant_time_equals(a, f, 4)); + + EXPECT_SUCCESS(s2n_constant_time_copy_or_dont(a, c, sizeof(a), 0)); + EXPECT_EQUAL(s2n_constant_time_equals(a, c, sizeof(a)), 1); + + for (int i = 1; i < 256; i++) { + EXPECT_SUCCESS(s2n_constant_time_copy_or_dont(b, d, sizeof(a), i)); + EXPECT_EQUAL(s2n_constant_time_equals(b, d, sizeof(a)), 0); + EXPECT_EQUAL(s2n_constant_time_equals(b, e, sizeof(a)), 1); + } + + uint8_t x[1]; + uint8_t y[1]; + + for (int i = 0; i < 256; i++) { + for (int j = 0; j < 256; j++) { + x[0] = i; + y[0] = j; + + int expected = 0; + + if (i == j) { + expected = 1; + } + + EXPECT_EQUAL(s2n_constant_time_equals(x, y, sizeof(x)), expected); + } + } + + CHECK_NO_OVF(s2n_mul_overflow, uint32_t, 0, 0, 0); + CHECK_NO_OVF(s2n_mul_overflow, uint32_t, 0, 1, 0); + CHECK_NO_OVF(s2n_mul_overflow, uint32_t, 0, ~0u, 0); + CHECK_NO_OVF(s2n_mul_overflow, uint32_t, 4, 5, 20); + CHECK_NO_OVF(s2n_mul_overflow, uint32_t, 1234, 4321, 5332114); + + CHECK_NO_OVF(s2n_mul_overflow, uint32_t, 0xFFFFFFFF, 1, 0xFFFFFFFF); + CHECK_NO_OVF(s2n_mul_overflow, uint32_t, 0xFFFF, 1, 0xFFFF); + CHECK_NO_OVF(s2n_mul_overflow, uint32_t, 0xFFFF, 0xFFFF, 0xfffe0001u); + CHECK_NO_OVF(s2n_mul_overflow, uint32_t, 0x10000, 0xFFFF, 0xFFFF0000u); + CHECK_NO_OVF(s2n_mul_overflow, uint32_t, 0x10001, 0xFFFF, 0xFFFFFFFFu); + CHECK_NO_OVF(s2n_mul_overflow, uint32_t, 0x10001, 0xFFFE, 0xFFFEFFFEu); + CHECK_NO_OVF(s2n_mul_overflow, uint32_t, 0x10002, 0xFFFE, 0xFFFFFFFCu); + CHECK_OVF(s2n_mul_overflow, uint32_t, 0x10003, 0xFFFE); + CHECK_NO_OVF(s2n_mul_overflow, uint32_t, 0xFFFE, 0xFFFE, 0xFFFC0004u); + CHECK_OVF(s2n_mul_overflow, uint32_t, 0x1FFFF, 0x1FFFF); + CHECK_OVF(s2n_mul_overflow, uint32_t, ~0u, ~0u); + + uint32_t result = 1; + EXPECT_SUCCESS(s2n_align_to(0, 10, &result)); + EXPECT_EQUAL(result, 0); + + EXPECT_FAILURE(s2n_align_to(1, 0, &result)); + + EXPECT_SUCCESS(s2n_align_to(10, 16, &result)); + EXPECT_EQUAL(result, 16); + + EXPECT_SUCCESS(s2n_align_to(20, 16, &result)); + EXPECT_EQUAL(result, 32); + + EXPECT_FAILURE(s2n_align_to(UINT32_MAX, 4, &result)); + + EXPECT_SUCCESS(s2n_align_to(10, 4096, &result)); + EXPECT_EQUAL(result, 4096); + + EXPECT_SUCCESS(s2n_align_to(4097, 4096, &result)); + EXPECT_EQUAL(result, 8192); + + EXPECT_SUCCESS(s2n_align_to(4096, 4096, &result)); + EXPECT_EQUAL(result, 4096); + + EXPECT_FAILURE(s2n_align_to(UINT32_MAX - 4000, 4096, &result)); + EXPECT_FAILURE(s2n_align_to(UINT32_MAX, 4096, &result)); + const uint32_t HALF_MAX = UINT32_MAX / 2; + const uint32_t ACTUAL_MAX = UINT32_MAX; + + CHECK_NO_OVF(s2n_add_overflow, uint32_t, 0, 0, 0); + CHECK_NO_OVF(s2n_add_overflow, uint32_t, 0, 1, 1); + CHECK_NO_OVF(s2n_add_overflow, uint32_t, 4, 5, 9); + CHECK_NO_OVF(s2n_add_overflow, uint32_t, 1234, 4321, 5555); + CHECK_NO_OVF(s2n_add_overflow, uint32_t, 0, ACTUAL_MAX, ACTUAL_MAX); + CHECK_NO_OVF(s2n_add_overflow, uint32_t, HALF_MAX, HALF_MAX, ACTUAL_MAX - 1); + CHECK_NO_OVF(s2n_add_overflow, uint32_t, HALF_MAX + 1, HALF_MAX, ACTUAL_MAX); + CHECK_NO_OVF(s2n_add_overflow, uint32_t, 100, ACTUAL_MAX - 102, ACTUAL_MAX - 2); + CHECK_NO_OVF(s2n_add_overflow, uint32_t, 100, ACTUAL_MAX - 100, ACTUAL_MAX); + CHECK_OVF(s2n_add_overflow, uint32_t, 1, ACTUAL_MAX); + CHECK_OVF(s2n_add_overflow, uint32_t, 100, ACTUAL_MAX); + CHECK_OVF(s2n_add_overflow, uint32_t, HALF_MAX, ACTUAL_MAX); + CHECK_OVF(s2n_add_overflow, uint32_t, ACTUAL_MAX, ACTUAL_MAX); + CHECK_OVF(s2n_add_overflow, uint32_t, HALF_MAX + 1, HALF_MAX + 1); + CHECK_OVF(s2n_add_overflow, uint32_t, 100, ACTUAL_MAX - 99); + CHECK_OVF(s2n_add_overflow, uint32_t, 100, ACTUAL_MAX - 1); + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_security_policies_test.c b/tests/unit/s2n_security_policies_test.c new file mode 100644 index 00000000000..bb325e34d9c --- /dev/null +++ b/tests/unit/s2n_security_policies_test.c @@ -0,0 +1,1085 @@ +/* + * 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 "tls/s2n_security_policies.h" + +#include "crypto/s2n_rsa_pss.h" +#include "crypto/s2n_rsa_signing.h" +#include "pq-crypto/s2n_pq.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_kem.h" +#include "tls/s2n_signature_algorithms.h" + +static S2N_RESULT s2n_test_security_policies_compatible(const struct s2n_security_policy *policy, + const char *default_policy, struct s2n_cert_chain_and_key *cert_chain) +{ + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), + s2n_config_ptr_free); + RESULT_GUARD_POSIX(s2n_config_add_cert_chain_and_key_to_store(server_config, cert_chain)); + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), + s2n_config_ptr_free); + RESULT_GUARD_POSIX(s2n_config_set_unsafe_for_testing(client_config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + RESULT_GUARD_POSIX(s2n_connection_set_config(server, server_config)); + RESULT_GUARD_POSIX(s2n_connection_set_cipher_preferences(server, default_policy)); + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + RESULT_GUARD_POSIX(s2n_connection_set_config(client, client_config)); + client->security_policy_override = policy; + + DEFER_CLEANUP(struct s2n_test_io_pair test_io_pair = { 0 }, + s2n_io_pair_close); + RESULT_GUARD_POSIX(s2n_io_pair_init_non_blocking(&test_io_pair)); + RESULT_GUARD_POSIX(s2n_connections_set_io_pair(client, server, &test_io_pair)); + RESULT_GUARD_POSIX(s2n_negotiate_test_server_and_client(server, client)); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_TRUE(s2n_array_len(ALL_SUPPORTED_KEM_GROUPS) == S2N_SUPPORTED_KEM_GROUPS_COUNT); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *rsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&rsa_chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *rsa_pss_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + if (s2n_is_rsa_pss_certs_supported()) { + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&rsa_pss_chain_and_key, + S2N_RSA_PSS_2048_SHA256_LEAF_CERT, S2N_RSA_PSS_2048_SHA256_LEAF_KEY)); + } + + /* Perform basic checks on all Security Policies. */ + for (size_t policy_index = 0; security_policy_selection[policy_index].version != NULL; policy_index++) { + const struct s2n_security_policy *security_policy = security_policy_selection[policy_index].security_policy; + + /* TLS 1.3 + PQ checks */ + if (security_policy->kem_preferences->tls13_kem_group_count > 0) { + /* Ensure that no TLS 1.3 KEM group preference lists go over max supported limit */ + EXPECT_TRUE(security_policy->kem_preferences->tls13_kem_group_count <= S2N_SUPPORTED_KEM_GROUPS_COUNT); + + /* Ensure all TLS 1.3 KEM groups in all policies are in the global list of all supported KEM groups */ + for (size_t i = 0; i < security_policy->kem_preferences->tls13_kem_group_count; i++) { + const struct s2n_kem_group *kem_group = security_policy->kem_preferences->tls13_kem_groups[i]; + + bool kem_group_is_supported = false; + for (size_t j = 0; j < S2N_SUPPORTED_KEM_GROUPS_COUNT; j++) { + if (kem_group->iana_id == ALL_SUPPORTED_KEM_GROUPS[j]->iana_id) { + kem_group_is_supported = true; + break; + } + } + EXPECT_TRUE(kem_group_is_supported); + } + } + + /* TLS 1.3 Cipher suites have TLS 1.3 Signature Algorithms Test */ + bool has_tls_13_cipher = false; + for (size_t i = 0; i < security_policy->cipher_preferences->count; i++) { + if (security_policy->cipher_preferences->suites[i]->minimum_required_tls_version == S2N_TLS13) { + has_tls_13_cipher = true; + break; + } + } + + if (has_tls_13_cipher) { + /* Validate that s2n_tls13_default_sig_scheme() is successful on all TLS 1.3 Security Policies for all + * TLS 1.3 Ciphers */ + { + struct s2n_cipher_suite tls_13_ciphers[] = { + s2n_tls13_aes_128_gcm_sha256, + s2n_tls13_aes_256_gcm_sha384, + s2n_tls13_chacha20_poly1305_sha256 + }; + + for (size_t i = 0; i < s2n_array_len(tls_13_ciphers); i++) { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + + if (security_policy_selection[policy_index].security_policy->minimum_protocol_version > s2n_get_highest_fully_supported_tls_version()) { + /* We purposefully do not allow users to configure Security Policies with a minimum allowed TLS + * versions that are greater than what libcrypto supports. */ + EXPECT_FAILURE(s2n_config_set_cipher_preferences(config, security_policy_selection[policy_index].version)); + EXPECT_SUCCESS(s2n_config_free(config)); + continue; + } + + struct s2n_cert_chain_and_key *default_cert; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&default_cert, S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, default_cert)); + EXPECT_TRUE(config->is_rsa_cert_configured); + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(client_conn); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, security_policy_selection[policy_index].version)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_NOT_NULL(config->default_certs_by_type.certs[S2N_PKEY_TYPE_RSA]); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + client_conn->actual_protocol_version = S2N_TLS13; + server_conn->actual_protocol_version = S2N_TLS13; + client_conn->secure->cipher_suite = &tls_13_ciphers[i]; + server_conn->secure->cipher_suite = &tls_13_ciphers[i]; + + const struct s2n_signature_scheme *chosen_scheme = NULL; + + if (s2n_is_rsa_pss_signing_supported()) { + /* If RSA PSS signing is supported, then we should always be able to select a default Signature + * Scheme for RSA Certs for TLS 1.3 */ + EXPECT_SUCCESS(s2n_tls13_default_sig_scheme(client_conn, &chosen_scheme)); + EXPECT_SUCCESS(s2n_tls13_default_sig_scheme(server_conn, &chosen_scheme)); + } else { + /* We can't pick a default TLS 1.3 signature scheme when configured with an RSA Cert when we + * do not support RSA PSS signing since RSA PSS signing is required for TLS 1.3 */ + EXPECT_FAILURE(s2n_tls13_default_sig_scheme(client_conn, &chosen_scheme)); + EXPECT_FAILURE(s2n_tls13_default_sig_scheme(server_conn, &chosen_scheme)); + } + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(default_cert)); + } + } + + /* Same as above test, but with ECDSA Certificates */ + { + struct s2n_cipher_suite tls_13_ciphers[] = { + s2n_tls13_aes_128_gcm_sha256, + s2n_tls13_aes_256_gcm_sha384, + s2n_tls13_chacha20_poly1305_sha256 + }; + + for (size_t i = 0; i < s2n_array_len(tls_13_ciphers); i++) { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + + if (security_policy_selection[policy_index].security_policy->minimum_protocol_version > s2n_get_highest_fully_supported_tls_version()) { + /* We purposefully do not allow users to configure Security Policies with a minimum allowed TLS + * Version of TLS 1.3, if TLS 1.3 algorithms aren't fully supported by the libcrypto we're using */ + EXPECT_FAILURE(s2n_config_set_cipher_preferences(config, security_policy_selection[policy_index].version)); + EXPECT_SUCCESS(s2n_config_free(config)); + continue; + } + + struct s2n_cert_chain_and_key *default_cert; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&default_cert, S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, default_cert)); + EXPECT_FALSE(config->is_rsa_cert_configured); + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(client_conn); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, security_policy_selection[policy_index].version)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, NULL)); + EXPECT_NOT_NULL(config->default_certs_by_type.certs[S2N_PKEY_TYPE_ECDSA]); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + client_conn->actual_protocol_version = S2N_TLS13; + server_conn->actual_protocol_version = S2N_TLS13; + client_conn->secure->cipher_suite = &tls_13_ciphers[i]; + server_conn->secure->cipher_suite = &tls_13_ciphers[i]; + + const struct s2n_signature_scheme *chosen_scheme = NULL; + + /* If an ECDSA Certificate is configured, then we should always be able to pick a default Signature + * Scheme (even if RSA PSS is not supported by the libcrypto) */ + EXPECT_SUCCESS(s2n_tls13_default_sig_scheme(client_conn, &chosen_scheme)); + EXPECT_SUCCESS(s2n_tls13_default_sig_scheme(server_conn, &chosen_scheme)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(default_cert)); + } + } + + bool has_tls_13_sig_alg = false; + bool has_rsa_pss = false; + + for (size_t i = 0; i < security_policy->signature_preferences->count; i++) { + int min = security_policy->signature_preferences->signature_schemes[i]->minimum_protocol_version; + int max = security_policy->signature_preferences->signature_schemes[i]->maximum_protocol_version; + s2n_signature_algorithm sig_alg = security_policy->signature_preferences->signature_schemes[i]->sig_alg; + + if (min == S2N_TLS13 || max >= S2N_TLS13) { + has_tls_13_sig_alg = true; + } + + if (sig_alg == S2N_SIGNATURE_RSA_PSS_PSS || sig_alg == S2N_SIGNATURE_RSA_PSS_RSAE) { + has_rsa_pss = true; + } + } + + EXPECT_TRUE(has_tls_13_sig_alg); + EXPECT_TRUE(has_rsa_pss); + } + } + + const struct s2n_security_policy *security_policy = NULL; + + /* Test common known good cipher suites for expected configuration */ + { + EXPECT_SUCCESS(s2n_find_security_policy_from_version("default", &security_policy)); + EXPECT_TRUE(s2n_ecc_is_extension_required(security_policy)); + EXPECT_FALSE(s2n_pq_kem_is_extension_required(security_policy)); + EXPECT_NULL(security_policy->kem_preferences->kems); + EXPECT_EQUAL(0, security_policy->kem_preferences->kem_count); + EXPECT_NULL(security_policy->kem_preferences->tls13_kem_groups); + EXPECT_EQUAL(0, security_policy->kem_preferences->tls13_kem_group_count); + EXPECT_FALSE(s2n_security_policy_supports_tls13(security_policy)); + + security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("default_tls13", &security_policy)); + EXPECT_TRUE(s2n_ecc_is_extension_required(security_policy)); + EXPECT_FALSE(s2n_pq_kem_is_extension_required(security_policy)); + EXPECT_TRUE(s2n_security_policy_supports_tls13(security_policy)); + EXPECT_EQUAL(0, security_policy->kem_preferences->kems); + EXPECT_NULL(security_policy->kem_preferences->tls13_kem_groups); + EXPECT_EQUAL(0, security_policy->kem_preferences->tls13_kem_group_count); + EXPECT_NULL(security_policy->kem_preferences->kems); + + /* The "all" security policy contains both TLS 1.2 KEM extension and TLS 1.3 KEM SupportedGroup entries */ + security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("test_all", &security_policy)); + EXPECT_TRUE(s2n_ecc_is_extension_required(security_policy)); + EXPECT_TRUE(s2n_pq_kem_is_extension_required(security_policy)); + EXPECT_EQUAL(1, security_policy->kem_preferences->kem_count); + EXPECT_NOT_NULL(security_policy->kem_preferences->kems); + EXPECT_EQUAL(&s2n_kyber_512_r3, security_policy->kem_preferences->kems[0]); + EXPECT_EQUAL(security_policy->kem_preferences->tls13_kem_groups, pq_kem_groups_r3_2023_06); +#if defined(S2N_LIBCRYPTO_SUPPORTS_KYBER) && EVP_APIS_SUPPORTED + EXPECT_EQUAL(6, security_policy->kem_preferences->tls13_kem_group_count); +#elif defined(S2N_LIBCRYPTO_SUPPORTS_KYBER) && !EVP_APIS_SUPPORTED + EXPECT_EQUAL(4, security_policy->kem_preferences->tls13_kem_group_count); +#elif !defined(S2N_LIBCRYPTO_SUPPORTS_KYBER) && EVP_APIS_SUPPORTED + EXPECT_EQUAL(2, security_policy->kem_preferences->tls13_kem_group_count); +#else + EXPECT_EQUAL(1, security_policy->kem_preferences->tls13_kem_group_count); +#endif + security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("KMS-TLS-1-0-2018-10", &security_policy)); + EXPECT_TRUE(s2n_ecc_is_extension_required(security_policy)); + EXPECT_FALSE(s2n_pq_kem_is_extension_required(security_policy)); + EXPECT_EQUAL(0, security_policy->kem_preferences->kem_count); + EXPECT_NULL(security_policy->kem_preferences->kems); + EXPECT_NULL(security_policy->kem_preferences->tls13_kem_groups); + EXPECT_EQUAL(0, security_policy->kem_preferences->tls13_kem_group_count); + + security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("KMS-PQ-TLS-1-0-2019-06", &security_policy)); + EXPECT_TRUE(s2n_ecc_is_extension_required(security_policy)); + EXPECT_FALSE(s2n_pq_kem_is_extension_required(security_policy)); + EXPECT_EQUAL(0, security_policy->kem_preferences->kem_count); + EXPECT_NULL(security_policy->kem_preferences->kems); + EXPECT_NULL(security_policy->kem_preferences->kems); + EXPECT_NULL(security_policy->kem_preferences->tls13_kem_groups); + EXPECT_EQUAL(0, security_policy->kem_preferences->tls13_kem_group_count); + + security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("PQ-SIKE-TEST-TLS-1-0-2019-11", &security_policy)); + EXPECT_TRUE(s2n_ecc_is_extension_required(security_policy)); + EXPECT_FALSE(s2n_pq_kem_is_extension_required(security_policy)); + EXPECT_EQUAL(0, security_policy->kem_preferences->kem_count); + EXPECT_NULL(security_policy->kem_preferences->kems); + EXPECT_NULL(security_policy->kem_preferences->tls13_kem_groups); + EXPECT_EQUAL(0, security_policy->kem_preferences->tls13_kem_group_count); + + security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("PQ-SIKE-TEST-TLS-1-0-2020-02", &security_policy)); + EXPECT_TRUE(s2n_ecc_is_extension_required(security_policy)); + EXPECT_FALSE(s2n_pq_kem_is_extension_required(security_policy)); + EXPECT_EQUAL(0, security_policy->kem_preferences->kem_count); + EXPECT_NULL(security_policy->kem_preferences->kems); + EXPECT_NULL(security_policy->kem_preferences->tls13_kem_groups); + EXPECT_EQUAL(0, security_policy->kem_preferences->tls13_kem_group_count); + + security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("KMS-PQ-TLS-1-0-2020-02", &security_policy)); + EXPECT_TRUE(s2n_ecc_is_extension_required(security_policy)); + EXPECT_FALSE(s2n_pq_kem_is_extension_required(security_policy)); + EXPECT_EQUAL(0, security_policy->kem_preferences->kem_count); + EXPECT_NULL(security_policy->kem_preferences->kems); + EXPECT_NULL(security_policy->kem_preferences->tls13_kem_groups); + EXPECT_EQUAL(0, security_policy->kem_preferences->tls13_kem_group_count); + + security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("KMS-PQ-TLS-1-0-2020-07", &security_policy)); + EXPECT_TRUE(s2n_ecc_is_extension_required(security_policy)); + EXPECT_TRUE(s2n_pq_kem_is_extension_required(security_policy)); + EXPECT_EQUAL(1, security_policy->kem_preferences->kem_count); + EXPECT_NOT_NULL(security_policy->kem_preferences->kems); + EXPECT_EQUAL(security_policy->kem_preferences->kems, pq_kems_r3_2021_05); + EXPECT_NOT_NULL(security_policy->kem_preferences->tls13_kem_groups); + EXPECT_EQUAL(security_policy->kem_preferences->tls13_kem_groups, pq_kem_groups_r3_2021_05); +#if EVP_APIS_SUPPORTED + EXPECT_EQUAL(2, security_policy->kem_preferences->tls13_kem_group_count); +#else + EXPECT_EQUAL(1, security_policy->kem_preferences->tls13_kem_group_count); +#endif + + security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("PQ-TLS-1-0-2020-12", &security_policy)); + EXPECT_TRUE(s2n_ecc_is_extension_required(security_policy)); + EXPECT_TRUE(s2n_pq_kem_is_extension_required(security_policy)); + EXPECT_EQUAL(1, security_policy->kem_preferences->kem_count); + EXPECT_NOT_NULL(security_policy->kem_preferences->kems); + EXPECT_EQUAL(security_policy->kem_preferences->kems, pq_kems_r3_2021_05); + EXPECT_NOT_NULL(security_policy->kem_preferences->tls13_kem_groups); + EXPECT_EQUAL(security_policy->kem_preferences->tls13_kem_groups, pq_kem_groups_r3_2021_05); +#if EVP_APIS_SUPPORTED + EXPECT_EQUAL(2, security_policy->kem_preferences->tls13_kem_group_count); +#else + EXPECT_EQUAL(1, security_policy->kem_preferences->tls13_kem_group_count); +#endif + + security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("PQ-TLS-1-1-2021-05-17", &security_policy)); + EXPECT_TRUE(s2n_ecc_is_extension_required(security_policy)); + EXPECT_TRUE(s2n_pq_kem_is_extension_required(security_policy)); + EXPECT_EQUAL(1, security_policy->kem_preferences->kem_count); + EXPECT_NOT_NULL(security_policy->kem_preferences->kems); + EXPECT_EQUAL(security_policy->kem_preferences->kems, pq_kems_r3_2021_05); + EXPECT_NOT_NULL(security_policy->kem_preferences->tls13_kem_groups); + EXPECT_EQUAL(security_policy->kem_preferences->tls13_kem_groups, pq_kem_groups_r3_2021_05); + EXPECT_EQUAL(security_policy->signature_preferences, &s2n_signature_preferences_20140601); + + security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("PQ-TLS-1-0-2021-05-18", &security_policy)); + EXPECT_TRUE(s2n_ecc_is_extension_required(security_policy)); + EXPECT_TRUE(s2n_pq_kem_is_extension_required(security_policy)); + EXPECT_EQUAL(1, security_policy->kem_preferences->kem_count); + EXPECT_NOT_NULL(security_policy->kem_preferences->kems); + EXPECT_EQUAL(security_policy->kem_preferences->kems, pq_kems_r3_2021_05); + EXPECT_NOT_NULL(security_policy->kem_preferences->tls13_kem_groups); + EXPECT_EQUAL(security_policy->kem_preferences->tls13_kem_groups, pq_kem_groups_r3_2021_05); + EXPECT_EQUAL(security_policy->signature_preferences, &s2n_signature_preferences_20140601); + + security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("PQ-TLS-1-0-2021-05-19", &security_policy)); + EXPECT_TRUE(s2n_ecc_is_extension_required(security_policy)); + EXPECT_TRUE(s2n_pq_kem_is_extension_required(security_policy)); + EXPECT_EQUAL(1, security_policy->kem_preferences->kem_count); + EXPECT_NOT_NULL(security_policy->kem_preferences->kems); + EXPECT_EQUAL(security_policy->kem_preferences->kems, pq_kems_r3_2021_05); + EXPECT_NOT_NULL(security_policy->kem_preferences->tls13_kem_groups); + EXPECT_EQUAL(security_policy->kem_preferences->tls13_kem_groups, pq_kem_groups_r3_2021_05); + EXPECT_EQUAL(security_policy->signature_preferences, &s2n_signature_preferences_20140601); + + security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("PQ-TLS-1-0-2021-05-20", &security_policy)); + EXPECT_TRUE(s2n_ecc_is_extension_required(security_policy)); + EXPECT_TRUE(s2n_pq_kem_is_extension_required(security_policy)); + EXPECT_EQUAL(1, security_policy->kem_preferences->kem_count); + EXPECT_NOT_NULL(security_policy->kem_preferences->kems); + EXPECT_EQUAL(security_policy->kem_preferences->kems, pq_kems_r3_2021_05); + EXPECT_NOT_NULL(security_policy->kem_preferences->tls13_kem_groups); + EXPECT_EQUAL(security_policy->kem_preferences->tls13_kem_groups, pq_kem_groups_r3_2021_05); + EXPECT_EQUAL(security_policy->signature_preferences, &s2n_signature_preferences_20140601); + + security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("PQ-TLS-1-1-2021-05-21", &security_policy)); + EXPECT_TRUE(s2n_ecc_is_extension_required(security_policy)); + EXPECT_TRUE(s2n_pq_kem_is_extension_required(security_policy)); + EXPECT_EQUAL(1, security_policy->kem_preferences->kem_count); + EXPECT_NOT_NULL(security_policy->kem_preferences->kems); + EXPECT_EQUAL(security_policy->kem_preferences->kems, pq_kems_r3_2021_05); + EXPECT_NOT_NULL(security_policy->kem_preferences->tls13_kem_groups); + EXPECT_EQUAL(security_policy->kem_preferences->tls13_kem_groups, pq_kem_groups_r3_2021_05); + EXPECT_EQUAL(security_policy->signature_preferences, &s2n_signature_preferences_20200207); + + security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("PQ-TLS-1-0-2021-05-22", &security_policy)); + EXPECT_TRUE(s2n_ecc_is_extension_required(security_policy)); + EXPECT_TRUE(s2n_pq_kem_is_extension_required(security_policy)); + EXPECT_EQUAL(1, security_policy->kem_preferences->kem_count); + EXPECT_NOT_NULL(security_policy->kem_preferences->kems); + EXPECT_EQUAL(security_policy->kem_preferences->kems, pq_kems_r3_2021_05); + EXPECT_NOT_NULL(security_policy->kem_preferences->tls13_kem_groups); + EXPECT_EQUAL(security_policy->kem_preferences->tls13_kem_groups, pq_kem_groups_r3_2021_05); + EXPECT_EQUAL(security_policy->signature_preferences, &s2n_signature_preferences_20200207); + + security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("PQ-TLS-1-0-2021-05-23", &security_policy)); + EXPECT_TRUE(s2n_ecc_is_extension_required(security_policy)); + EXPECT_TRUE(s2n_pq_kem_is_extension_required(security_policy)); + EXPECT_EQUAL(1, security_policy->kem_preferences->kem_count); + EXPECT_NOT_NULL(security_policy->kem_preferences->kems); + EXPECT_EQUAL(security_policy->kem_preferences->kems, pq_kems_r3_2021_05); + EXPECT_NOT_NULL(security_policy->kem_preferences->tls13_kem_groups); + EXPECT_EQUAL(security_policy->kem_preferences->tls13_kem_groups, pq_kem_groups_r3_2021_05); + EXPECT_EQUAL(security_policy->signature_preferences, &s2n_signature_preferences_20200207); + + security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("PQ-TLS-1-0-2021-05-24", &security_policy)); + EXPECT_TRUE(s2n_ecc_is_extension_required(security_policy)); + EXPECT_TRUE(s2n_pq_kem_is_extension_required(security_policy)); + EXPECT_EQUAL(1, security_policy->kem_preferences->kem_count); + EXPECT_NOT_NULL(security_policy->kem_preferences->kems); + EXPECT_EQUAL(security_policy->kem_preferences->kems, pq_kems_r3_2021_05); + EXPECT_NOT_NULL(security_policy->kem_preferences->tls13_kem_groups); + EXPECT_EQUAL(security_policy->kem_preferences->tls13_kem_groups, pq_kem_groups_r3_2021_05); + EXPECT_EQUAL(security_policy->signature_preferences, &s2n_signature_preferences_20200207); + + security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("PQ-TLS-1-0-2021-05-25", &security_policy)); + EXPECT_TRUE(s2n_ecc_is_extension_required(security_policy)); + EXPECT_TRUE(s2n_pq_kem_is_extension_required(security_policy)); + EXPECT_EQUAL(1, security_policy->kem_preferences->kem_count); + EXPECT_NOT_NULL(security_policy->kem_preferences->kems); + EXPECT_EQUAL(security_policy->kem_preferences->kems, pq_kems_r3_2021_05); + EXPECT_NOT_NULL(security_policy->kem_preferences->tls13_kem_groups); + EXPECT_EQUAL(security_policy->kem_preferences->tls13_kem_groups, pq_kem_groups_r3_2021_05); + EXPECT_EQUAL(security_policy->signature_preferences, &s2n_signature_preferences_20140601); + + security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("PQ-TLS-1-0-2021-05-26", &security_policy)); + EXPECT_TRUE(s2n_ecc_is_extension_required(security_policy)); + EXPECT_TRUE(s2n_pq_kem_is_extension_required(security_policy)); + EXPECT_EQUAL(1, security_policy->kem_preferences->kem_count); + EXPECT_NOT_NULL(security_policy->kem_preferences->kems); + EXPECT_EQUAL(security_policy->kem_preferences->kems, pq_kems_r3_2021_05); + EXPECT_NOT_NULL(security_policy->kem_preferences->tls13_kem_groups); + EXPECT_EQUAL(security_policy->kem_preferences->tls13_kem_groups, pq_kem_groups_r3_2021_05); + EXPECT_EQUAL(security_policy->signature_preferences, &s2n_signature_preferences_20200207); + + security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("PQ-TLS-1-3-2023-06-01", &security_policy)); + EXPECT_TRUE(s2n_ecc_is_extension_required(security_policy)); + EXPECT_FALSE(s2n_pq_kem_is_extension_required(security_policy)); + EXPECT_EQUAL(security_policy->kem_preferences, &kem_preferences_pq_tls_1_3_2023_06); + EXPECT_EQUAL(0, security_policy->kem_preferences->kem_count); + EXPECT_NULL(security_policy->kem_preferences->kems); + EXPECT_NOT_NULL(security_policy->kem_preferences->tls13_kem_groups); + EXPECT_EQUAL(security_policy->kem_preferences->tls13_kem_groups, pq_kem_groups_r3_2023_06); +#if defined(S2N_LIBCRYPTO_SUPPORTS_KYBER) && EVP_APIS_SUPPORTED + EXPECT_EQUAL(6, security_policy->kem_preferences->tls13_kem_group_count); +#elif defined(S2N_LIBCRYPTO_SUPPORTS_KYBER) && !EVP_APIS_SUPPORTED + EXPECT_EQUAL(4, security_policy->kem_preferences->tls13_kem_group_count); +#elif !defined(S2N_LIBCRYPTO_SUPPORTS_KYBER) && EVP_APIS_SUPPORTED + EXPECT_EQUAL(2, security_policy->kem_preferences->tls13_kem_group_count); +#else + EXPECT_EQUAL(1, security_policy->kem_preferences->tls13_kem_group_count); +#endif + + security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("20141001", &security_policy)); + EXPECT_FALSE(s2n_ecc_is_extension_required(security_policy)); + EXPECT_FALSE(s2n_pq_kem_is_extension_required(security_policy)); + EXPECT_EQUAL(0, security_policy->kem_preferences->kem_count); + EXPECT_NULL(security_policy->kem_preferences->kems); + EXPECT_NULL(security_policy->kem_preferences->tls13_kem_groups); + EXPECT_EQUAL(0, security_policy->kem_preferences->tls13_kem_group_count); + + security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("20201021", &security_policy)); + EXPECT_TRUE(s2n_ecc_is_extension_required(security_policy)); + EXPECT_FALSE(s2n_pq_kem_is_extension_required(security_policy)); + EXPECT_EQUAL(0, security_policy->kem_preferences->kem_count); + EXPECT_NULL(security_policy->kem_preferences->kems); + EXPECT_NULL(security_policy->kem_preferences->tls13_kem_groups); + EXPECT_EQUAL(0, security_policy->kem_preferences->tls13_kem_group_count); + } + + { + char tls12_only_security_policy_strings[][255] = { + "default", + "default_fips", + "ELBSecurityPolicy-TLS-1-0-2015-04", + "ELBSecurityPolicy-TLS-1-0-2015-05", + "ELBSecurityPolicy-2016-08", + "ELBSecurityPolicy-TLS-1-1-2017-01", + "ELBSecurityPolicy-TLS-1-2-2017-01", + "ELBSecurityPolicy-TLS-1-2-Ext-2018-06", + "ELBSecurityPolicy-FS-2018-06", + "ELBSecurityPolicy-FS-1-2-2019-08", + "ELBSecurityPolicy-FS-1-1-2019-08", + "ELBSecurityPolicy-FS-1-2-Res-2019-08", + "CloudFront-Upstream", + "CloudFront-Upstream-TLS-1-0", + "CloudFront-Upstream-TLS-1-1", + "CloudFront-Upstream-TLS-1-2", + /* CloudFront legacy viewer facing policies (max TLS 1.2) */ + "CloudFront-SSL-v-3-Legacy", + "CloudFront-TLS-1-0-2014-Legacy", + "CloudFront-TLS-1-0-2016-Legacy", + "CloudFront-TLS-1-1-2016-Legacy", + "CloudFront-TLS-1-2-2018-Legacy", + "CloudFront-TLS-1-2-2019-Legacy", + "KMS-TLS-1-0-2018-10", + "KMS-PQ-TLS-1-0-2019-06", + "KMS-PQ-TLS-1-0-2020-02", + "KMS-PQ-TLS-1-0-2020-07", + "PQ-SIKE-TEST-TLS-1-0-2019-11", + "PQ-SIKE-TEST-TLS-1-0-2020-02", + "KMS-FIPS-TLS-1-2-2018-10", + "20140601", + "20141001", + "20150202", + "20150214", + "20150306", + "20160411", + "20160804", + "20160824", + "20170210", + "20170328", + "20190214", + "20170405", + "20170718", + "20190120", + "20190121", + "20190122", + "20201021", + "test_all_fips", + "test_all_ecdsa", + "test_ecdsa_priority", + "test_all_tls12", + }; + + for (size_t i = 0; i < s2n_array_len(tls12_only_security_policy_strings); i++) { + security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version(tls12_only_security_policy_strings[i], &security_policy)); + EXPECT_FALSE(s2n_security_policy_supports_tls13(security_policy)); + } + + char tls13_security_policy_strings[][255] = { + "default_tls13", + "test_all", + "test_all_tls13", + "20190801", + "20190802", + "KMS-TLS-1-2-2023-06", + /* CloudFront viewer facing */ + "CloudFront-SSL-v-3", + "CloudFront-TLS-1-0-2014", + "CloudFront-TLS-1-0-2016", + "CloudFront-TLS-1-1-2016", + "CloudFront-TLS-1-2-2017", + "CloudFront-TLS-1-2-2018", + "CloudFront-TLS-1-2-2019", + "CloudFront-TLS-1-2-2021", + "CloudFront-TLS-1-2-2021-ChaCha20-Boosted", + /* AWS Common Runtime SDK */ + "AWS-CRT-SDK-SSLv3.0", + "AWS-CRT-SDK-TLSv1.0", + "AWS-CRT-SDK-TLSv1.1", + "AWS-CRT-SDK-TLSv1.2", + "AWS-CRT-SDK-TLSv1.3", + "AWS-CRT-SDK-SSLv3.0-2023", + "AWS-CRT-SDK-TLSv1.0-2023", + "AWS-CRT-SDK-TLSv1.1-2023", + "AWS-CRT-SDK-TLSv1.2-2023", + "AWS-CRT-SDK-TLSv1.3-2023", + /* PQ TLS */ + "PQ-TLS-1-2-2023-04-07", + "PQ-TLS-1-2-2023-04-08", + "PQ-TLS-1-2-2023-04-09", + "PQ-TLS-1-2-2023-04-10", + "PQ-TLS-1-3-2023-06-01", + "PQ-TLS-1-2-2023-10-07", + "PQ-TLS-1-2-2023-10-08", + "PQ-TLS-1-2-2023-10-09", + "PQ-TLS-1-2-2023-10-10", + }; + for (size_t i = 0; i < s2n_array_len(tls13_security_policy_strings); i++) { + security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version(tls13_security_policy_strings[i], &security_policy)); + EXPECT_TRUE(s2n_security_policy_supports_tls13(security_policy)); + } + } + + /* Test that null fails */ + { + security_policy = NULL; + EXPECT_FALSE(s2n_ecc_is_extension_required(security_policy)); + EXPECT_FALSE(s2n_pq_kem_is_extension_required(security_policy)); + EXPECT_FALSE(s2n_security_policy_supports_tls13(security_policy)); + } + + /* Test that security policies have valid chacha20 boosting configurations when chacha20 is available */ + if (s2n_chacha20_poly1305.is_available()) { + for (size_t i = 0; security_policy_selection[i].version != NULL; i++) { + const struct s2n_security_policy *sec_policy = security_policy_selection[i].security_policy; + EXPECT_NOT_NULL(sec_policy); + const struct s2n_cipher_preferences *cipher_preference = sec_policy->cipher_preferences; + EXPECT_NOT_NULL(cipher_preference); + + /* No need to check cipher preferences with chacha20 boosting disabled */ + if (!cipher_preference->allow_chacha20_boosting) { + continue; + } + + bool cipher_preferences_has_chacha20_cipher_suite = false; + + /* Iterate over cipher preferences and try to find a chacha20 ciphersuite */ + for (size_t j = 0; j < cipher_preference->count; j++) { + struct s2n_cipher_suite *cipher = cipher_preference->suites[j]; + EXPECT_NOT_NULL(cipher); + + if (s2n_cipher_suite_uses_chacha20_alg(cipher)) { + cipher_preferences_has_chacha20_cipher_suite = true; + break; + } + } + + /* If chacha20 boosting support is enabled, then the cipher preference must have at least one chacha20 cipher suite */ + EXPECT_TRUE(cipher_preferences_has_chacha20_cipher_suite); + } + } + + /* Test a security policy not on the official list */ + { + struct s2n_cipher_suite *fake_suites[] = { + &s2n_ecdhe_kyber_rsa_with_aes_256_gcm_sha384, + &s2n_tls13_chacha20_poly1305_sha256, + }; + + const struct s2n_cipher_preferences fake_cipher_preference = { + .count = s2n_array_len(fake_suites), + .suites = fake_suites, + }; + + const struct s2n_kem_preferences fake_kem_preference = { + .kem_count = 1, + .kems = NULL, + }; + + const struct s2n_security_policy fake_security_policy = { + .minimum_protocol_version = S2N_TLS10, + .cipher_preferences = &fake_cipher_preference, + .kem_preferences = &fake_kem_preference, + }; + + security_policy = &fake_security_policy; + EXPECT_TRUE(s2n_ecc_is_extension_required(security_policy)); + EXPECT_TRUE(s2n_pq_kem_is_extension_required(security_policy)); + EXPECT_TRUE(s2n_security_policy_supports_tls13(security_policy)); + } + { + struct s2n_config *config = s2n_config_new(); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + EXPECT_EQUAL(config->security_policy, &security_policy_20170210); + EXPECT_EQUAL(config->security_policy->cipher_preferences, &cipher_preferences_20170210); + EXPECT_EQUAL(config->security_policy->kem_preferences, &kem_preferences_null); + EXPECT_EQUAL(config->security_policy->signature_preferences, &s2n_signature_preferences_20140601); + EXPECT_EQUAL(config->security_policy->ecc_preferences, &s2n_ecc_preferences_20140601); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20170210")); + EXPECT_EQUAL(config->security_policy, &security_policy_20170210); + EXPECT_EQUAL(config->security_policy->cipher_preferences, &cipher_preferences_20170210); + EXPECT_EQUAL(config->security_policy->kem_preferences, &kem_preferences_null); + EXPECT_EQUAL(config->security_policy->signature_preferences, &s2n_signature_preferences_20140601); + EXPECT_EQUAL(config->security_policy->ecc_preferences, &s2n_ecc_preferences_20140601); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_EQUAL(config->security_policy, &security_policy_default_tls13); + EXPECT_EQUAL(config->security_policy->cipher_preferences, &cipher_preferences_20210831); + EXPECT_EQUAL(config->security_policy->kem_preferences, &kem_preferences_null); + EXPECT_EQUAL(config->security_policy->signature_preferences, &s2n_signature_preferences_20200207); + EXPECT_EQUAL(config->security_policy->certificate_signature_preferences, &s2n_certificate_signature_preferences_20201110); + EXPECT_EQUAL(config->security_policy->ecc_preferences, &s2n_ecc_preferences_20200310); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20190801")); + EXPECT_EQUAL(config->security_policy, &security_policy_20190801); + EXPECT_EQUAL(config->security_policy->cipher_preferences, &cipher_preferences_20190801); + EXPECT_EQUAL(config->security_policy->kem_preferences, &kem_preferences_null); + EXPECT_EQUAL(config->security_policy->signature_preferences, &s2n_signature_preferences_20200207); + EXPECT_EQUAL(config->security_policy->ecc_preferences, &s2n_ecc_preferences_20200310); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "null")); + EXPECT_EQUAL(config->security_policy, &security_policy_null); + EXPECT_EQUAL(config->security_policy->cipher_preferences, &cipher_preferences_null); + EXPECT_EQUAL(config->security_policy->kem_preferences, &kem_preferences_null); + EXPECT_EQUAL(config->security_policy->signature_preferences, &s2n_signature_preferences_null); + EXPECT_EQUAL(config->security_policy->ecc_preferences, &s2n_ecc_preferences_null); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all")); + EXPECT_EQUAL(config->security_policy, &security_policy_test_all); + EXPECT_EQUAL(config->security_policy->cipher_preferences, &cipher_preferences_test_all); + EXPECT_EQUAL(config->security_policy->kem_preferences, &kem_preferences_all); + EXPECT_EQUAL(config->security_policy->signature_preferences, &s2n_signature_preferences_20201021); + EXPECT_EQUAL(config->security_policy->ecc_preferences, &s2n_ecc_preferences_test_all); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all_tls12")); + EXPECT_EQUAL(config->security_policy, &security_policy_test_all_tls12); + EXPECT_EQUAL(config->security_policy->cipher_preferences, &cipher_preferences_test_all_tls12); + EXPECT_EQUAL(config->security_policy->kem_preferences, &kem_preferences_pq_tls_1_0_2021_05); + EXPECT_EQUAL(config->security_policy->signature_preferences, &s2n_signature_preferences_20201021); + EXPECT_EQUAL(config->security_policy->ecc_preferences, &s2n_ecc_preferences_20201021); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "KMS-PQ-TLS-1-0-2020-07")); + EXPECT_EQUAL(config->security_policy, &security_policy_kms_pq_tls_1_0_2020_07); + EXPECT_EQUAL(config->security_policy->cipher_preferences, &cipher_preferences_kms_pq_tls_1_0_2020_07); + EXPECT_EQUAL(config->security_policy->kem_preferences, &kem_preferences_pq_tls_1_0_2021_05); + EXPECT_EQUAL(config->security_policy->signature_preferences, &s2n_signature_preferences_20140601); + EXPECT_EQUAL(config->security_policy->ecc_preferences, &s2n_ecc_preferences_20140601); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "KMS-PQ-TLS-1-0-2020-02")); + EXPECT_EQUAL(config->security_policy, &security_policy_kms_pq_tls_1_0_2020_02); + EXPECT_EQUAL(config->security_policy->cipher_preferences, &cipher_preferences_kms_pq_tls_1_0_2020_02); + EXPECT_EQUAL(config->security_policy->kem_preferences, &kem_preferences_null); + EXPECT_EQUAL(config->security_policy->signature_preferences, &s2n_signature_preferences_20140601); + EXPECT_EQUAL(config->security_policy->ecc_preferences, &s2n_ecc_preferences_20140601); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "KMS-PQ-TLS-1-0-2019-06")); + EXPECT_EQUAL(config->security_policy, &security_policy_kms_pq_tls_1_0_2019_06); + EXPECT_EQUAL(config->security_policy->cipher_preferences, &cipher_preferences_kms_pq_tls_1_0_2019_06); + EXPECT_EQUAL(config->security_policy->kem_preferences, &kem_preferences_null); + EXPECT_EQUAL(config->security_policy->signature_preferences, &s2n_signature_preferences_20140601); + EXPECT_EQUAL(config->security_policy->ecc_preferences, &s2n_ecc_preferences_20140601); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "AWS-CRT-SDK-SSLv3.0")); + EXPECT_EQUAL(config->security_policy, &security_policy_aws_crt_sdk_ssl_v3); + EXPECT_EQUAL(config->security_policy->cipher_preferences, &cipher_preferences_aws_crt_sdk_ssl_v3); + EXPECT_EQUAL(config->security_policy->kem_preferences, &kem_preferences_null); + EXPECT_EQUAL(config->security_policy->signature_preferences, &s2n_signature_preferences_20200207); + EXPECT_EQUAL(config->security_policy->ecc_preferences, &s2n_ecc_preferences_20200310); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "AWS-CRT-SDK-TLSv1.0")); + EXPECT_EQUAL(config->security_policy, &security_policy_aws_crt_sdk_tls_10); + EXPECT_EQUAL(config->security_policy->cipher_preferences, &cipher_preferences_aws_crt_sdk_default); + EXPECT_EQUAL(config->security_policy->kem_preferences, &kem_preferences_null); + EXPECT_EQUAL(config->security_policy->signature_preferences, &s2n_signature_preferences_20200207); + EXPECT_EQUAL(config->security_policy->ecc_preferences, &s2n_ecc_preferences_20200310); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "AWS-CRT-SDK-TLSv1.1")); + EXPECT_EQUAL(config->security_policy, &security_policy_aws_crt_sdk_tls_11); + EXPECT_EQUAL(config->security_policy->cipher_preferences, &cipher_preferences_aws_crt_sdk_default); + EXPECT_EQUAL(config->security_policy->kem_preferences, &kem_preferences_null); + EXPECT_EQUAL(config->security_policy->signature_preferences, &s2n_signature_preferences_20200207); + EXPECT_EQUAL(config->security_policy->ecc_preferences, &s2n_ecc_preferences_20200310); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "AWS-CRT-SDK-TLSv1.2")); + EXPECT_EQUAL(config->security_policy, &security_policy_aws_crt_sdk_tls_12); + EXPECT_EQUAL(config->security_policy->cipher_preferences, &cipher_preferences_aws_crt_sdk_default); + EXPECT_EQUAL(config->security_policy->kem_preferences, &kem_preferences_null); + EXPECT_EQUAL(config->security_policy->signature_preferences, &s2n_signature_preferences_20200207); + EXPECT_EQUAL(config->security_policy->ecc_preferences, &s2n_ecc_preferences_20200310); + + if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "AWS-CRT-SDK-TLSv1.3")); + EXPECT_EQUAL(config->security_policy, &security_policy_aws_crt_sdk_tls_13); + EXPECT_EQUAL(config->security_policy->cipher_preferences, &cipher_preferences_aws_crt_sdk_tls_13); + EXPECT_EQUAL(config->security_policy->kem_preferences, &kem_preferences_null); + EXPECT_EQUAL(config->security_policy->signature_preferences, &s2n_signature_preferences_20200207); + EXPECT_EQUAL(config->security_policy->ecc_preferences, &s2n_ecc_preferences_20200310); + } else { + EXPECT_FAILURE(s2n_config_set_cipher_preferences(config, "AWS-CRT-SDK-TLSv1.3")); + } + + EXPECT_FAILURE(s2n_config_set_cipher_preferences(config, NULL)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_cipher_preferences(config, "notathing"), + S2N_ERR_INVALID_SECURITY_POLICY); + + s2n_config_free(config); + } + { + struct s2n_config *config = s2n_config_new(); + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + s2n_connection_set_config(conn, config); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default")); + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_EQUAL(security_policy, &security_policy_20170210); + EXPECT_EQUAL(security_policy->cipher_preferences, &cipher_preferences_20170210); + EXPECT_EQUAL(security_policy->kem_preferences, &kem_preferences_null); + EXPECT_EQUAL(security_policy->signature_preferences, &s2n_signature_preferences_20140601); + EXPECT_EQUAL(security_policy->ecc_preferences, &s2n_ecc_preferences_20140601); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "20170210")); + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_EQUAL(security_policy, &security_policy_20170210); + EXPECT_EQUAL(security_policy->cipher_preferences, &cipher_preferences_20170210); + EXPECT_EQUAL(security_policy->kem_preferences, &kem_preferences_null); + EXPECT_EQUAL(security_policy->signature_preferences, &s2n_signature_preferences_20140601); + EXPECT_EQUAL(security_policy->ecc_preferences, &s2n_ecc_preferences_20140601); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_EQUAL(security_policy, &security_policy_default_tls13); + EXPECT_EQUAL(security_policy->cipher_preferences, &cipher_preferences_20210831); + EXPECT_EQUAL(security_policy->kem_preferences, &kem_preferences_null); + EXPECT_EQUAL(security_policy->signature_preferences, &s2n_signature_preferences_20200207); + EXPECT_EQUAL(security_policy->certificate_signature_preferences, &s2n_certificate_signature_preferences_20201110); + EXPECT_EQUAL(security_policy->ecc_preferences, &s2n_ecc_preferences_20200310); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "20190801")); + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_EQUAL(security_policy, &security_policy_20190801); + EXPECT_EQUAL(security_policy->cipher_preferences, &cipher_preferences_20190801); + EXPECT_EQUAL(security_policy->kem_preferences, &kem_preferences_null); + EXPECT_EQUAL(security_policy->signature_preferences, &s2n_signature_preferences_20200207); + EXPECT_EQUAL(security_policy->ecc_preferences, &s2n_ecc_preferences_20200310); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all")); + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_EQUAL(security_policy, &security_policy_test_all); + EXPECT_EQUAL(security_policy->cipher_preferences, &cipher_preferences_test_all); + EXPECT_EQUAL(security_policy->kem_preferences, &kem_preferences_all); + EXPECT_EQUAL(security_policy->signature_preferences, &s2n_signature_preferences_20201021); + EXPECT_EQUAL(security_policy->ecc_preferences, &s2n_ecc_preferences_test_all); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all_tls12")); + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_EQUAL(security_policy, &security_policy_test_all_tls12); + EXPECT_EQUAL(security_policy->cipher_preferences, &cipher_preferences_test_all_tls12); + EXPECT_EQUAL(security_policy->kem_preferences, &kem_preferences_pq_tls_1_0_2021_05); + EXPECT_EQUAL(security_policy->signature_preferences, &s2n_signature_preferences_20201021); + EXPECT_EQUAL(security_policy->ecc_preferences, &s2n_ecc_preferences_20201021); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "KMS-PQ-TLS-1-0-2020-07")); + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_EQUAL(security_policy, &security_policy_kms_pq_tls_1_0_2020_07); + EXPECT_EQUAL(security_policy->cipher_preferences, &cipher_preferences_kms_pq_tls_1_0_2020_07); + EXPECT_EQUAL(security_policy->kem_preferences, &kem_preferences_pq_tls_1_0_2021_05); + EXPECT_EQUAL(security_policy->signature_preferences, &s2n_signature_preferences_20140601); + EXPECT_EQUAL(security_policy->ecc_preferences, &s2n_ecc_preferences_20140601); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "KMS-PQ-TLS-1-0-2020-02")); + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_EQUAL(security_policy, &security_policy_kms_pq_tls_1_0_2020_02); + EXPECT_EQUAL(security_policy->cipher_preferences, &cipher_preferences_kms_pq_tls_1_0_2020_02); + EXPECT_EQUAL(security_policy->kem_preferences, &kem_preferences_null); + EXPECT_EQUAL(security_policy->signature_preferences, &s2n_signature_preferences_20140601); + EXPECT_EQUAL(security_policy->ecc_preferences, &s2n_ecc_preferences_20140601); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "KMS-PQ-TLS-1-0-2019-06")); + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_EQUAL(security_policy, &security_policy_kms_pq_tls_1_0_2019_06); + EXPECT_EQUAL(security_policy->cipher_preferences, &cipher_preferences_kms_pq_tls_1_0_2019_06); + EXPECT_EQUAL(security_policy->kem_preferences, &kem_preferences_null); + EXPECT_EQUAL(security_policy->signature_preferences, &s2n_signature_preferences_20140601); + EXPECT_EQUAL(security_policy->ecc_preferences, &s2n_ecc_preferences_20140601); + + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_set_cipher_preferences(conn, "notathing"), + S2N_ERR_INVALID_SECURITY_POLICY); + + s2n_config_free(config); + s2n_connection_free(conn); + } + + /* All signature preferences are valid */ + { + for (int i = 0; security_policy_selection[i].version != NULL; i++) { + security_policy = security_policy_selection[i].security_policy; + EXPECT_NOT_NULL(security_policy); + EXPECT_NOT_NULL(security_policy->signature_preferences); + + for (int j = 0; j < security_policy->signature_preferences->count; j++) { + const struct s2n_signature_scheme *scheme = security_policy->signature_preferences->signature_schemes[j]; + + EXPECT_NOT_NULL(scheme); + + uint8_t max_version = scheme->maximum_protocol_version; + uint8_t min_version = scheme->minimum_protocol_version; + + EXPECT_TRUE(max_version == S2N_UNKNOWN_PROTOCOL_VERSION || min_version <= max_version); + + /* If scheme will be used for tls1.3 */ + if (max_version == S2N_UNKNOWN_PROTOCOL_VERSION || max_version >= S2N_TLS13) { + EXPECT_NOT_EQUAL(scheme->hash_alg, S2N_HASH_SHA1); + EXPECT_NOT_EQUAL(scheme->sig_alg, S2N_SIGNATURE_RSA); + if (scheme->sig_alg == S2N_SIGNATURE_ECDSA) { + EXPECT_NOT_NULL(scheme->signature_curve); + } + } + + /* If scheme will be used for pre-tls1.3 */ + if (min_version < S2N_TLS13) { + EXPECT_NULL(scheme->signature_curve); + EXPECT_NOT_EQUAL(scheme->sig_alg, S2N_SIGNATURE_RSA_PSS_PSS); + } + } + } + } + + /* Failure case when s2n_ecc_preference lists contains a curve not present in s2n_all_supported_curves_list */ + { + const struct s2n_ecc_named_curve test_curve = { + .iana_id = 12345, + .libcrypto_nid = 0, + .name = "test_curve", + .share_size = 0 + }; + + const struct s2n_ecc_named_curve *const s2n_ecc_pref_list_test[] = { + &test_curve, + }; + + const struct s2n_ecc_preferences s2n_ecc_preferences_new_list = { + .count = s2n_array_len(s2n_ecc_pref_list_test), + .ecc_curves = s2n_ecc_pref_list_test, + }; + + EXPECT_FAILURE(s2n_check_ecc_preferences_curves_list(&s2n_ecc_preferences_new_list)); + } + + /* Positive and negative cases for s2n_validate_kem_preferences() */ + { + EXPECT_FAILURE_WITH_ERRNO(s2n_validate_kem_preferences(NULL, 0), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_validate_kem_preferences(&kem_preferences_null, 1), S2N_ERR_INVALID_SECURITY_POLICY); + EXPECT_SUCCESS(s2n_validate_kem_preferences(&kem_preferences_null, 0)); + + const struct s2n_kem_preferences invalid_kem_prefs[] = { + { + .kem_count = 1, + .kems = NULL, + .tls13_kem_group_count = 0, + .tls13_kem_groups = NULL, + }, + { + .kem_count = 0, + .kems = NULL, + .tls13_kem_group_count = 1, + .tls13_kem_groups = NULL, + }, + { + .kem_count = 0, + .kems = pq_kems_r3_2021_05, + .tls13_kem_group_count = 0, + .tls13_kem_groups = NULL, + }, + { + .kem_count = 0, + .kems = NULL, + .tls13_kem_group_count = 0, + .tls13_kem_groups = ALL_SUPPORTED_KEM_GROUPS, + }, + }; + + for (size_t i = 0; i < s2n_array_len(invalid_kem_prefs); i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_validate_kem_preferences(&invalid_kem_prefs[i], 1), S2N_ERR_INVALID_SECURITY_POLICY); + } + + EXPECT_FAILURE_WITH_ERRNO(s2n_validate_kem_preferences(&kem_preferences_pq_tls_1_0_2021_05, 0), S2N_ERR_INVALID_SECURITY_POLICY); + EXPECT_SUCCESS(s2n_validate_kem_preferences(&kem_preferences_pq_tls_1_0_2021_05, 1)); + } + + /* Checks that NUM_RSA_PSS_SCHEMES accurately represents the number of rsa_pss signature schemes usable in a + * certificate_signature_preferences list */ + { + for (size_t i = 0; security_policy_selection[i].version != NULL; i++) { + security_policy = security_policy_selection[i].security_policy; + EXPECT_NOT_NULL(security_policy); + + if (security_policy->certificate_signature_preferences != NULL) { + size_t num_rsa_pss = 0; + for (size_t j = 0; j < security_policy->certificate_signature_preferences->count; j++) { + if (security_policy->certificate_signature_preferences->signature_schemes[j]->libcrypto_nid == NID_rsassaPss) { + num_rsa_pss += 1; + } + } + EXPECT_TRUE(num_rsa_pss <= NUM_RSA_PSS_SCHEMES); + } + } + } + + /* s2n_validate_certificate_signature_preferences will succeed if there are no rsa_pss schemes in the preference list */ + { + const struct s2n_signature_scheme *const test_sig_scheme_pref_list[] = { + &s2n_rsa_pkcs1_sha256, + }; + + const struct s2n_signature_preferences test_certificate_signature_preferences = { + .count = s2n_array_len(test_sig_scheme_pref_list), + .signature_schemes = test_sig_scheme_pref_list, + }; + + EXPECT_OK(s2n_validate_certificate_signature_preferences(&test_certificate_signature_preferences)); + } + + /* s2n_validate_certificate_signature_preferences will succeed if all rsa_pss schemes are included in the preference list */ + { + const struct s2n_signature_scheme *const test_sig_scheme_pref_list[] = { + &s2n_rsa_pss_pss_sha256, + &s2n_rsa_pss_pss_sha384, + &s2n_rsa_pss_pss_sha512, + &s2n_rsa_pss_rsae_sha256, + &s2n_rsa_pss_rsae_sha384, + &s2n_rsa_pss_rsae_sha512, + }; + + const struct s2n_signature_preferences test_certificate_signature_preferences = { + .count = s2n_array_len(test_sig_scheme_pref_list), + .signature_schemes = test_sig_scheme_pref_list, + }; + + EXPECT_OK(s2n_validate_certificate_signature_preferences(&test_certificate_signature_preferences)); + } + + /* s2n_validate_certificate_signature_preferences will fail if not all rsa_pss schemes are included in the preference list */ + { + const struct s2n_signature_scheme *const test_sig_scheme_pref_list[] = { + &s2n_rsa_pss_pss_sha256, + &s2n_rsa_pss_pss_sha384, + }; + + const struct s2n_signature_preferences test_certificate_signature_preferences = { + .count = s2n_array_len(test_sig_scheme_pref_list), + .signature_schemes = test_sig_scheme_pref_list, + }; + + EXPECT_ERROR_WITH_ERRNO(s2n_validate_certificate_signature_preferences(&test_certificate_signature_preferences), S2N_ERR_INVALID_SECURITY_POLICY); + } + + EXPECT_SUCCESS(s2n_reset_tls13_in_test()); + + /* Test that security policies are compatible with other policies */ + { + /* 20230317 */ + { + EXPECT_OK(s2n_test_security_policies_compatible(&security_policy_20230317, "default", rsa_chain_and_key)); + EXPECT_OK(s2n_test_security_policies_compatible(&security_policy_20230317, "default_tls13", rsa_chain_and_key)); + EXPECT_OK(s2n_test_security_policies_compatible(&security_policy_20230317, "default_fips", rsa_chain_and_key)); + EXPECT_OK(s2n_test_security_policies_compatible(&security_policy_20230317, "20230317", rsa_chain_and_key)); + + EXPECT_OK(s2n_test_security_policies_compatible(&security_policy_20230317, "default_tls13", ecdsa_chain_and_key)); + EXPECT_OK(s2n_test_security_policies_compatible(&security_policy_20230317, "default_fips", ecdsa_chain_and_key)); + EXPECT_OK(s2n_test_security_policies_compatible(&security_policy_20230317, "20230317", ecdsa_chain_and_key)); + + if (s2n_is_rsa_pss_certs_supported()) { + EXPECT_OK(s2n_test_security_policies_compatible(&security_policy_20230317, "default_tls13", rsa_pss_chain_and_key)); + EXPECT_OK(s2n_test_security_policies_compatible(&security_policy_20230317, "20230317", rsa_pss_chain_and_key)); + } + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_self_talk_alerts_test.c b/tests/unit/s2n_self_talk_alerts_test.c new file mode 100644 index 00000000000..d047a08ac99 --- /dev/null +++ b/tests/unit/s2n_self_talk_alerts_test.c @@ -0,0 +1,334 @@ +/* + * 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 +#include +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +#define TLS_ALERT 21 +#define TLS_ALERT_VERSION 0x03, 0x03 +#define TLS_ALERT_LENGTH 0x00, 0x02 + +#define TLS_ALERT_LEVEL_WARNING 1 +#define TLS_ALERT_LEVEL_FATAL 2 + +#define TLS_ALERT_CLOSE_NOTIFY 0 +#define TLS_ALERT_UNRECOGNIZED_NAME 122 + +struct alert_ctx { + int write_fd; + int invoked; + int count; + + uint8_t level; + uint8_t code; +}; + +int mock_client(struct s2n_test_io_pair *io_pair, s2n_alert_behavior alert_behavior, int expect_failure) +{ + struct s2n_connection *conn; + struct s2n_config *config; + s2n_blocked_status blocked; + int result = 0; + int rc = 0; + + /* Give the server a chance to listen */ + sleep(1); + + conn = s2n_connection_new(S2N_CLIENT); + config = s2n_config_new(); + s2n_config_disable_x509_verification(config); + s2n_config_set_alert_behavior(config, alert_behavior); + s2n_connection_set_config(conn, config); + + s2n_connection_set_io_pair(conn, io_pair); + + rc = s2n_negotiate(conn, &blocked); + if (expect_failure) { + if (!rc) { + result = 1; + } + } else { + char buffer[0xffff]; + if (rc < 0) { + result = 1; + } + + for (size_t i = 1; i < 0xffff; i += 100) { + memset(buffer, 33, sizeof(char) * i); + s2n_send(conn, buffer, i, &blocked); + } + + int shutdown_rc = -1; + do { + shutdown_rc = s2n_shutdown(conn, &blocked); + } while (shutdown_rc != 0); + } + + s2n_connection_free(conn); + s2n_config_free(config); + + s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); + + s2n_cleanup(); + + exit(result); +} + +int mock_nanoseconds_since_epoch(void *data, uint64_t *nanoseconds) +{ + static int called = 0; + + /* When first called return 0 seconds */ + *nanoseconds = 0; + + /* When next called return 31 seconds */ + if (called) { + *nanoseconds += (uint64_t) 31 * 1000000000; + } + + called = 1; + + return 0; +} + +int client_hello_send_alerts(struct s2n_connection *conn, void *ctx) +{ + struct alert_ctx *alert = ctx; + uint8_t alert_msg[] = { TLS_ALERT, TLS_ALERT_VERSION, TLS_ALERT_LENGTH, alert->level, alert->code }; + + for (int i = 0; i < alert->count; i++) { + if (write(alert->write_fd, alert_msg, sizeof(alert_msg)) != sizeof(alert_msg)) { + exit(100); + } + + alert->invoked++; + } + + return 0; +} + +S2N_RESULT cleanup(char **cert_chain_pem, char **private_key_pem, + struct s2n_cert_chain_and_key **chain_and_key) +{ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(*chain_and_key)); + free(*cert_chain_pem); + free(*private_key_pem); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + char buffer[0xffff]; + struct s2n_connection *conn; + s2n_blocked_status blocked; + int status; + pid_t pid; + char *cert_chain_pem; + char *private_key_pem; + struct s2n_cert_chain_and_key *chain_and_key; + BEGIN_TEST(); + + /* Ignore SIGPIPE */ + signal(SIGPIPE, SIG_IGN); + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + + /* Test that we ignore Warning Alerts in S2N_ALERT_IGNORE_WARNINGS mode in TLS1.2 */ + { + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + EXPECT_OK(cleanup(&cert_chain_pem, &private_key_pem, &chain_and_key)); + mock_client(&io_pair, S2N_ALERT_IGNORE_WARNINGS, 0); + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + + /* Set up the callback to send an alert after receiving ClientHello */ + struct alert_ctx warning_alert = { .write_fd = io_pair.server, .invoked = 0, .count = 2, .level = TLS_ALERT_LEVEL_WARNING, .code = TLS_ALERT_UNRECOGNIZED_NAME }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, client_hello_send_alerts, &warning_alert)); + + /* This is the parent */ + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + + /* Ensure that callback was invoked */ + EXPECT_EQUAL(warning_alert.invoked, 2); + + for (size_t i = 1; i < 0xffff; i += 100) { + char *ptr = buffer; + int size = i; + + do { + int bytes_read = 0; + EXPECT_SUCCESS(bytes_read = s2n_recv(conn, ptr, size, &blocked)); + + size -= bytes_read; + ptr += bytes_read; + } while (size); + + for (int j = 0; j < i; j++) { + EXPECT_EQUAL(buffer[j], 33); + } + } + + EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + }; + + /* Test that we don't ignore Fatal Alerts in S2N_ALERT_IGNORE_WARNINGS mode in TLS1.2 */ + { + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + EXPECT_OK(cleanup(&cert_chain_pem, &private_key_pem, &chain_and_key)); + mock_client(&io_pair, S2N_ALERT_IGNORE_WARNINGS, 1); + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + + /* Set up the callback to send an alert after receiving ClientHello */ + struct alert_ctx fatal_alert = { .write_fd = io_pair.server, .invoked = 0, .count = 1, .level = TLS_ALERT_LEVEL_FATAL, .code = TLS_ALERT_UNRECOGNIZED_NAME }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, client_hello_send_alerts, &fatal_alert)); + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + + /* Ensure that callback was invoked */ + EXPECT_EQUAL(fatal_alert.invoked, 1); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + }; + + /* Test that we don't ignore Warning Alerts in S2N_ALERT_FAIL_ON_WARNINGS mode in TLS1.2 */ + { + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + EXPECT_OK(cleanup(&cert_chain_pem, &private_key_pem, &chain_and_key)); + mock_client(&io_pair, S2N_ALERT_FAIL_ON_WARNINGS, 1); + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + + /* Set up the callback to send an alert after receiving ClientHello */ + struct alert_ctx warning_alert = { .write_fd = io_pair.server, .invoked = 0, .count = 1, .level = TLS_ALERT_LEVEL_WARNING, .code = TLS_ALERT_UNRECOGNIZED_NAME }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, client_hello_send_alerts, &warning_alert)); + + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + + /* Ensure that callback was invoked */ + EXPECT_EQUAL(warning_alert.invoked, 1); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + }; + + /* Shutdown */ + EXPECT_OK(cleanup(&cert_chain_pem, &private_key_pem, &chain_and_key)); + + END_TEST(); + + return 0; +} diff --git a/tests/unit/s2n_self_talk_alpn_test.c b/tests/unit/s2n_self_talk_alpn_test.c new file mode 100644 index 00000000000..ba17025c1cb --- /dev/null +++ b/tests/unit/s2n_self_talk_alpn_test.c @@ -0,0 +1,426 @@ +/* + * 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 +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "utils/s2n_safety.h" + +int mock_nanoseconds_since_epoch(void *data, uint64_t *nanoseconds) +{ + static int called = 0; + + /* When first called return 0 seconds */ + *nanoseconds = 0; + + /* When next called return 31 seconds */ + if (called) { + *nanoseconds += (uint64_t) 31 * 1000000000; + } + + called = 1; + + return 0; +} + +int mock_client(int writefd, int readfd, const char **protocols, int count, const char *expected) +{ + char buffer[0xffff]; + struct s2n_connection *client_conn; + struct s2n_config *client_config; + s2n_blocked_status blocked; + int result = 0; + + /* Give the server a chance to listen */ + sleep(1); + + client_conn = s2n_connection_new(S2N_CLIENT); + client_config = s2n_config_new(); + s2n_config_set_protocol_preferences(client_config, protocols, count); + s2n_config_disable_x509_verification(client_config); + s2n_connection_set_config(client_conn, client_config); + + s2n_connection_set_read_fd(client_conn, readfd); + s2n_connection_set_write_fd(client_conn, writefd); + + result = s2n_negotiate(client_conn, &blocked); + if (result < 0) { + result = 1; + } + + const char *got = s2n_get_application_protocol(client_conn); + if ((got != NULL && expected == NULL) + || (got == NULL && expected != NULL) + || (got != NULL && expected != NULL && strcmp(expected, got) != 0)) { + result = 2; + } + + for (int i = 1; i < 0xffff; i += 100) { + for (int j = 0; j < i; j++) { + buffer[j] = 33; + } + + s2n_send(client_conn, buffer, i, &blocked); + } + + int shutdown_rc = -1; + if (!result) { + do { + shutdown_rc = s2n_shutdown(client_conn, &blocked); + } while (shutdown_rc != 0); + } + + s2n_connection_free(client_conn); + s2n_config_free(client_config); + + /* Give the server a chance to a void a sigpipe */ + sleep(1); + + s2n_cleanup(); + + exit(result); +} + +int main(int argc, char **argv) +{ + char buffer[0xffff]; + struct s2n_connection *conn; + struct s2n_config *config; + s2n_blocked_status blocked; + int status; + pid_t pid; + int server_to_client[2]; + int client_to_server[2]; + char *cert_chain_pem; + char *private_key_pem; + char *dhparams_pem; + struct s2n_cert_chain_and_key *chain_and_key; + + const char *protocols[] = { "http/1.1", "spdy/3.1", "h2" }; + const int protocols_size = s2n_array_len(protocols); + const char *mismatch_protocols[] = { "spdy/2" }; + + BEGIN_TEST(); + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(config = s2n_config_new()); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + + EXPECT_SUCCESS(s2n_config_set_protocol_preferences(config, protocols, protocols_size)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + + /** Test no client ALPN request */ + /* Create a pipe */ + EXPECT_SUCCESS(pipe(server_to_client)); + EXPECT_SUCCESS(pipe(client_to_server)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the child process, close the read end of the pipe */ + EXPECT_SUCCESS(close(client_to_server[0])); + EXPECT_SUCCESS(close(server_to_client[1])); + + /* Send the client hello with no ALPN extensions, and validate we didn't + * negotiate an application protocol */ + mock_client(client_to_server[1], server_to_client[0], NULL, 0, NULL); + } + + /* This is the parent */ + EXPECT_SUCCESS(close(client_to_server[1])); + EXPECT_SUCCESS(close(server_to_client[0])); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_read_fd(conn, client_to_server[0])); + EXPECT_SUCCESS(s2n_connection_set_write_fd(conn, server_to_client[1])); + + /* Negotiate the handshake. */ + EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); + + EXPECT_EQUAL(s2n_connection_get_selected_cert(conn), chain_and_key); + + /* Expect NULL negotiated protocol */ + EXPECT_EQUAL(s2n_get_application_protocol(conn), NULL); + + for (int i = 1; i < 0xffff; i += 100) { + char *ptr = buffer; + int size = i; + + do { + int bytes_read = 0; + EXPECT_SUCCESS(bytes_read = s2n_recv(conn, ptr, size, &blocked)); + + size -= bytes_read; + ptr += bytes_read; + } while (size); + + for (int j = 0; j < i; j++) { + EXPECT_EQUAL(buffer[j], 33); + } + } + + EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + + /* Test a matching ALPN request */ + /* Create a pipe */ + EXPECT_SUCCESS(pipe(server_to_client)); + EXPECT_SUCCESS(pipe(client_to_server)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the child process, close the read end of the pipe */ + EXPECT_SUCCESS(close(client_to_server[0])); + EXPECT_SUCCESS(close(server_to_client[1])); + + /* Clients ALPN preferences match our preferences, so we pick the + * most preferred server one */ + mock_client(client_to_server[1], server_to_client[0], protocols, protocols_size, protocols[0]); + } + + /* This is the parent */ + EXPECT_SUCCESS(close(client_to_server[1])); + EXPECT_SUCCESS(close(server_to_client[0])); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_read_fd(conn, client_to_server[0])); + EXPECT_SUCCESS(s2n_connection_set_write_fd(conn, server_to_client[1])); + + /* Negotiate the handshake. */ + EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); + + /* Expect our most preferred negotiated protocol */ + EXPECT_STRING_EQUAL(s2n_get_application_protocol(conn), protocols[0]); + + for (int i = 1; i < 0xffff; i += 100) { + char *ptr = buffer; + int size = i; + + do { + int bytes_read = 0; + EXPECT_SUCCESS(bytes_read = s2n_recv(conn, ptr, size, &blocked)); + + size -= bytes_read; + ptr += bytes_read; + } while (size); + + for (int j = 0; j < i; j++) { + EXPECT_EQUAL(buffer[j], 33); + } + } + + EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + + /* Test a lower preferred matching ALPN request */ + /* Create a pipe */ + EXPECT_SUCCESS(pipe(server_to_client)); + EXPECT_SUCCESS(pipe(client_to_server)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the child process, close the read end of the pipe */ + EXPECT_SUCCESS(close(client_to_server[0])); + EXPECT_SUCCESS(close(server_to_client[1])); + + /* Client only advertises our second choice, so we should negotiate it */ + mock_client(client_to_server[1], server_to_client[0], &protocols[1], 1, protocols[1]); + } + + /* This is the parent */ + EXPECT_SUCCESS(close(client_to_server[1])); + EXPECT_SUCCESS(close(server_to_client[0])); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_read_fd(conn, client_to_server[0])); + EXPECT_SUCCESS(s2n_connection_set_write_fd(conn, server_to_client[1])); + + /* Negotiate the handshake. */ + + EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); + + for (int i = 1; i < 0xffff; i += 100) { + char *ptr = buffer; + int size = i; + + do { + int bytes_read = 0; + EXPECT_SUCCESS(bytes_read = s2n_recv(conn, ptr, size, &blocked)); + + size -= bytes_read; + ptr += bytes_read; + } while (size); + + for (int j = 0; j < i; j++) { + EXPECT_EQUAL(buffer[j], 33); + } + } + + /* Expect our least preferred negotiated protocol */ + EXPECT_STRING_EQUAL(s2n_get_application_protocol(conn), protocols[1]); + + EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + + /* Test a non-matching ALPN request */ + /* Create a pipe */ + EXPECT_SUCCESS(pipe(server_to_client)); + EXPECT_SUCCESS(pipe(client_to_server)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the child process, close the read end of the pipe */ + EXPECT_SUCCESS(close(client_to_server[0])); + EXPECT_SUCCESS(close(server_to_client[1])); + + /* Client doesn't support any of our protocols, so we shouldn't complete + * the handshake */ + mock_client(client_to_server[1], server_to_client[0], mismatch_protocols, 1, NULL); + } + + /* This is the parent */ + EXPECT_SUCCESS(close(client_to_server[1])); + EXPECT_SUCCESS(close(server_to_client[0])); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_read_fd(conn, client_to_server[0])); + EXPECT_SUCCESS(s2n_connection_set_write_fd(conn, server_to_client[1])); + + /* Negotiate the handshake. */ + EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); + + /* Expect NULL negotiated protocol */ + EXPECT_EQUAL(s2n_get_application_protocol(conn), NULL); + + /* Negotiation failed. Free the connection without shutdown */ + EXPECT_SUCCESS(s2n_connection_free(conn)); + + /* Close the pipes */ + EXPECT_SUCCESS(close(client_to_server[0])); + EXPECT_SUCCESS(close(server_to_client[1])); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_NOT_EQUAL(status, 0); + + /* Test a connection level application protocol */ + /* Create a pipe */ + EXPECT_SUCCESS(pipe(server_to_client)); + EXPECT_SUCCESS(pipe(client_to_server)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the child process, close the read end of the pipe */ + EXPECT_SUCCESS(close(client_to_server[0])); + EXPECT_SUCCESS(close(server_to_client[1])); + + /* Client config support all protocols, expect http 2 after negotiation */ + mock_client(client_to_server[1], server_to_client[0], protocols, protocols_size, protocols[2]); + } + + /* This is the parent */ + EXPECT_SUCCESS(close(client_to_server[1])); + EXPECT_SUCCESS(close(server_to_client[0])); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->server_protocol_version = S2N_TLS12; + conn->client_protocol_version = S2N_TLS12; + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_read_fd(conn, client_to_server[0])); + EXPECT_SUCCESS(s2n_connection_set_write_fd(conn, server_to_client[1])); + + /* Override connection protocol to http 2 */ + EXPECT_SUCCESS(s2n_connection_set_protocol_preferences(conn, &protocols[2], 1)); + /* Negotiate the handshake. */ + EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); + + EXPECT_STRING_EQUAL(s2n_get_application_protocol(conn), protocols[2]); + /* Negotiation failed. Free the connection without shutdown */ + EXPECT_SUCCESS(s2n_connection_free(conn)); + + /* Close the pipes */ + EXPECT_SUCCESS(close(client_to_server[0])); + EXPECT_SUCCESS(close(server_to_client[1])); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_NOT_EQUAL(status, 0); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + free(cert_chain_pem); + free(private_key_pem); + free(dhparams_pem); + END_TEST(); + + return 0; +} diff --git a/tests/unit/s2n_self_talk_broken_pipe_test.c b/tests/unit/s2n_self_talk_broken_pipe_test.c new file mode 100644 index 00000000000..6469a431ca2 --- /dev/null +++ b/tests/unit/s2n_self_talk_broken_pipe_test.c @@ -0,0 +1,179 @@ +/* + * 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 +#include +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" + +#define SUPPORTED_CERTIFICATE_FORMATS (2) + +static const char *certificate_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_CERT_CHAIN, S2N_RSA_2048_PKCS8_CERT_CHAIN }; +static const char *private_key_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_KEY, S2N_RSA_2048_PKCS8_KEY }; + +void mock_client(struct s2n_test_io_pair *io_pair) +{ + struct s2n_connection *conn; + struct s2n_config *config; + s2n_blocked_status blocked; + + /* Give the server a chance to listen */ + sleep(1); + + conn = s2n_connection_new(S2N_CLIENT); + config = s2n_config_new(); + s2n_config_disable_x509_verification(config); + s2n_connection_set_config(conn, config); + + s2n_connection_set_io_pair(conn, io_pair); + + int result = s2n_negotiate(conn, &blocked); + if (result < 0) { + exit(1); + } + + result = s2n_connection_free_handshake(conn); + if (result < 0) { + exit(1); + } + +#if defined(__FreeBSD__) || defined(__APPLE__) || defined(__OpenBSD__) + /* On FreeBSD shutdown from one end of the socket pair does not give EPIPE. Must use close. */ + s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); +#else + /* Close client read fd to mock half closed pipe at server side */ + s2n_io_pair_shutdown_one_end(io_pair, S2N_CLIENT, SHUT_RD); +#endif + /* Give server a chance to send data on broken pipe */ + sleep(2); + + s2n_shutdown(conn, &blocked); + + result = s2n_connection_free(conn); + if (result < 0) { + exit(1); + } + + result = s2n_config_free(config); + if (result < 0) { + exit(1); + } + + /* Give the server a chance to avoid a sigpipe */ + sleep(1); + + s2n_io_pair_shutdown_one_end(io_pair, S2N_CLIENT, SHUT_WR); + + exit(0); +} + +int main(int argc, char **argv) +{ + struct s2n_connection *conn; + struct s2n_config *config; + s2n_blocked_status blocked; + int status; + pid_t pid; + char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE]; + char private_key_pem[S2N_MAX_TEST_PEM_SIZE]; + char dhparams_pem[S2N_MAX_TEST_PEM_SIZE]; + + BEGIN_TEST(); + + for (int is_dh_key_exchange = 0; is_dh_key_exchange <= 1; is_dh_key_exchange++) { + struct s2n_cert_chain_and_key *chain_and_keys[SUPPORTED_CERTIFICATE_FORMATS]; + + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Write the fragmented hello message */ + mock_client(&io_pair); + } + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + for (int cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { + EXPECT_SUCCESS(s2n_read_test_pem(certificate_paths[cert], cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(private_key_paths[cert], private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_keys[cert] = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_keys[cert], cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_keys[cert])); + } + + if (is_dh_key_exchange) { + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); + } + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + + /* Give client a chance to close pipe at the receiving end */ + sleep(1); + char buffer[1]; + /* Fist flush on half closed pipe should get EPIPE */ + ssize_t w = s2n_send(conn, buffer, 1, &blocked); + EXPECT_EQUAL(w, -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO); + EXPECT_EQUAL(errno, EPIPE); + + /* Second flush on half closed pipe should not get EPIPE as we write is skipped */ + w = s2n_shutdown(conn, &blocked); + EXPECT_EQUAL(w, -1); + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO); + EXPECT_EQUAL(errno, 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + for (int cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_keys[cert])); + } + EXPECT_SUCCESS(s2n_config_free(config)); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + if (getenv("S2N_VALGRIND") == NULL) { + EXPECT_EQUAL(status, 0); + } + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + } + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_self_talk_client_hello_cb_test.c b/tests/unit/s2n_self_talk_client_hello_cb_test.c new file mode 100644 index 00000000000..d52ab587825 --- /dev/null +++ b/tests/unit/s2n_self_talk_client_hello_cb_test.c @@ -0,0 +1,473 @@ +/* + * 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 +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_internal.h" + +struct client_hello_context { + int invoked; + int swap_config_during_callback; + int swap_config_nonblocking_mode; + int mark_done_during_callback; + struct s2n_config *config; + /* the right way to mark server name extenstion was used + * after parsing ClientHello is to call + * s2n_connection_server_name_extension_used + * + * this flag tests the previous behavior from blocking callbacks + */ + int legacy_rc_for_server_name_used; +}; + +int mock_client(struct s2n_test_io_pair *io_pair, int expect_failure, int expect_server_name_used) +{ + struct s2n_connection *conn; + struct s2n_config *config; + s2n_blocked_status blocked; + int result = 0; + int rc = 0; + const char *protocols[] = { "h2", "http/1.1" }; + + /* Give the server a chance to listen */ + sleep(1); + + conn = s2n_connection_new(S2N_CLIENT); + config = s2n_config_new(); + s2n_config_set_protocol_preferences(config, protocols, 2); + s2n_config_disable_x509_verification(config); + s2n_connection_set_config(conn, config); + + s2n_connection_set_io_pair(conn, io_pair); + + s2n_set_server_name(conn, "example.com"); + + rc = s2n_negotiate(conn, &blocked); + if (expect_failure) { + if (!rc) { + result = 1; + } + + if (s2n_connection_get_alert(conn) != 40) { + result = 2; + } + } else { + char buffer[0xffff]; + + if (conn->server_name_used != expect_server_name_used) { + result = 1; + } + + if (rc < 0) { + result = 2; + } + + for (int i = 1; i < 0xffff; i += 100) { + memset(buffer, 33, sizeof(char) * i); + s2n_send(conn, buffer, i, &blocked); + } + + int shutdown_rc = -1; + do { + shutdown_rc = s2n_shutdown(conn, &blocked); + } while (shutdown_rc != 0); + } + + s2n_connection_free(conn); + s2n_config_free(config); + + /* Give the server a chance to a void a sigpipe */ + sleep(1); + + s2n_cleanup(); + s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); + + exit(result); +} + +int client_hello_swap_config(struct s2n_connection *conn, void *ctx) +{ + struct client_hello_context *client_hello_ctx; + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(conn); + const char *sent_server_name = "example.com"; + const char *received_server_name; + if (ctx == NULL) { + return -1; + } + client_hello_ctx = ctx; + /* Increment counter to ensure that callback was invoked */ + client_hello_ctx->invoked++; + + /* Validate SNI extension */ + uint8_t expected_server_name[] = { + /* Server names len */ + 0x00, 0x0E, + /* Server name type - host name */ + 0x00, + /* First server name len */ + 0x00, 0x0B, + /* First server name, matches sent_server_name */ + 'e', 'x', 'a', 'm', 'p', 'l', 'e', '.', 'c', 'o', 'm' + }; + + /* Get SNI extension from client hello */ + uint32_t len = s2n_client_hello_get_extension_length(client_hello, S2N_EXTENSION_SERVER_NAME); + if (len != 16) { + return -1; + } + + uint8_t ser_name[16] = { 0 }; + if (s2n_client_hello_get_extension_by_id(client_hello, S2N_EXTENSION_SERVER_NAME, ser_name, len) <= 0) { + return -1; + } + + /* Verify correct server name is returned. */ + received_server_name = s2n_get_server_name(conn); + if (received_server_name == NULL || strcmp(received_server_name, sent_server_name)) { + return -1; + } + + if (memcmp(ser_name, expected_server_name, len) != 0) { + return -1; + } + + if (client_hello_ctx->mark_done_during_callback) { + EXPECT_SUCCESS(s2n_client_hello_cb_done(conn)); + } + + if (client_hello_ctx->swap_config_during_callback) { + EXPECT_SUCCESS(s2n_connection_set_config(conn, client_hello_ctx->config)); + if (client_hello_ctx->legacy_rc_for_server_name_used) { + return 1; + } + EXPECT_SUCCESS(s2n_connection_server_name_extension_used(conn)); + return 0; + } + + return 0; +} + +int client_hello_fail_handshake(struct s2n_connection *conn, void *ctx) +{ + struct client_hello_context *client_hello_ctx; + + if (ctx == NULL) { + return -1; + } + client_hello_ctx = ctx; + + /* Incremet counter to ensure that callback was invoked */ + client_hello_ctx->invoked++; + + /* Return negative value to terminate the handshake */ + return -1; +} + +int s2n_negotiate_nonblocking_ch_cb(struct s2n_connection *conn, + struct client_hello_context *ch_ctx, bool server_name_used) +{ + s2n_blocked_status blocked; + EXPECT_NOT_NULL(conn); + /* negotiate handshake, we should pause after the nonblocking callback is invoked */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked), S2N_ERR_ASYNC_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_INPUT); + + /* verify client hello cb has been invoked */ + EXPECT_EQUAL(ch_ctx->invoked, 1); + + /* while handshake is paused, swap the config if asked */ + if (ch_ctx->swap_config_nonblocking_mode) { + EXPECT_SUCCESS(s2n_connection_set_config(conn, ch_ctx->config)); + } + /* unless explicitly unblocked we should stay paused */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked), S2N_ERR_ASYNC_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_INPUT); + + /* mark the client hello cb complete */ + EXPECT_SUCCESS(s2n_client_hello_cb_done(conn)); + if (server_name_used) { + EXPECT_SUCCESS(s2n_connection_server_name_extension_used(conn)); + } + return s2n_negotiate(conn, &blocked); +} + +int s2n_negotiate_blocking_ch_cb(struct s2n_connection *conn, struct client_hello_context *ch_ctx) +{ + s2n_blocked_status blocked; + EXPECT_NOT_NULL(conn); + + int rc = s2n_negotiate(conn, &blocked); + /* verify client hello cb has been invoked */ + EXPECT_EQUAL(ch_ctx->invoked, 1); + return rc; +} + +int server_recv(struct s2n_connection *conn) +{ + static char buffer[0xffff]; + s2n_blocked_status blocked; + + for (int i = 1; i < 0xffff; i += 100) { + char *ptr = buffer; + int size = i; + + do { + int bytes_read = 0; + EXPECT_SUCCESS(bytes_read = s2n_recv(conn, ptr, size, &blocked)); + + size -= bytes_read; + ptr += bytes_read; + } while (size); + + for (int j = 0; j < i; j++) { + EXPECT_EQUAL((buffer)[j], 33); + } + } + return S2N_SUCCESS; +} + +int init_server_conn(struct s2n_connection **conn, struct s2n_test_io_pair *io_pair, + struct s2n_config *config) +{ + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(io_pair, S2N_CLIENT)); + + EXPECT_NOT_NULL(*conn = s2n_connection_new(S2N_SERVER)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(*conn, io_pair)); + EXPECT_SUCCESS(s2n_connection_set_config(*conn, config)); + return S2N_SUCCESS; +} + +int start_client_conn(struct s2n_test_io_pair *io_pair, pid_t *pid, + int expect_failure, int expect_server_name_used) +{ + /* Create a pipe */ + EXPECT_SUCCESS(s2n_io_pair_init(io_pair)); + + /* Create a child process */ + *pid = fork(); + if (*pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(io_pair, S2N_SERVER)); + + mock_client(io_pair, expect_failure, expect_server_name_used); + } + return S2N_SUCCESS; +} + +static int test_case_clean(struct s2n_connection *conn, pid_t client_pid, + struct s2n_config *config, struct s2n_test_io_pair *io_pair, + struct client_hello_context *ch_ctx, struct s2n_cert_chain_and_key *chain_and_key) +{ + s2n_blocked_status blocked; + int status; + + EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); + EXPECT_EQUAL(waitpid(-1, &status, 0), client_pid); + EXPECT_EQUAL(status, 0); + /* client process cleans their end, we just need to close server side */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(io_pair, S2N_SERVER)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + memset(ch_ctx, 0, sizeof(struct client_hello_context)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + return S2N_SUCCESS; +} + +int run_test_config_swap_ch_cb(s2n_client_hello_cb_mode cb_mode, + struct client_hello_context *ch_ctx) +{ + struct s2n_test_io_pair io_pair; + struct s2n_config *config; + struct s2n_connection *conn; + struct s2n_config *swap_config; + pid_t pid; + struct s2n_cert_chain_and_key *chain_and_key; + + EXPECT_SUCCESS(start_client_conn(&io_pair, &pid, 0, 1)); + + /* Add application protocols to swapped config */ + static const char *protocols[] = { "h2" }; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* prepare swap_config */ + EXPECT_NOT_NULL(swap_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_protocol_preferences(swap_config, protocols, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(swap_config, chain_and_key)); + ch_ctx->config = swap_config; + /* in the swap config make sure blocking more is SET correctly */ + EXPECT_SUCCESS(s2n_config_set_client_hello_cb_mode(swap_config, cb_mode)); + + /* Don't set up certificate and private key for the main config, so if + * handshake succeeds we know that config was swapped */ + EXPECT_NOT_NULL(config = s2n_config_new()); + + /* Set up the callback */ + EXPECT_SUCCESS(s2n_config_set_client_hello_cb_mode(config, cb_mode)); + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, client_hello_swap_config, ch_ctx)); + + EXPECT_SUCCESS(init_server_conn(&conn, &io_pair, config)); + + /* do the handshake */ + if (cb_mode == S2N_CLIENT_HELLO_CB_NONBLOCKING && !ch_ctx->mark_done_during_callback) { + /* swap the config and mark server_name_used in the async context */ + EXPECT_SUCCESS(s2n_negotiate_nonblocking_ch_cb(conn, ch_ctx, true)); + } else { + /* cb_mode == S2N_CLIENT_HELLO_CB_BLOCKING or NONBLOCKING mode where + * a non blocking callback marks cb_done during the callback itself + */ + EXPECT_SUCCESS(s2n_negotiate_blocking_ch_cb(conn, ch_ctx)); + } + + /* Server name and error are as expected with null connection */ + EXPECT_NULL(s2n_get_server_name(NULL)); + EXPECT_EQUAL(s2n_errno, S2N_ERR_NULL); + + /* Expect most preferred negotiated protocol which only swap_config had */ + EXPECT_STRING_EQUAL(s2n_get_application_protocol(conn), protocols[0]); + + EXPECT_SUCCESS(server_recv(conn)); + + EXPECT_SUCCESS(test_case_clean(conn, pid, config, &io_pair, ch_ctx, chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(swap_config)); + return S2N_SUCCESS; +} + +int run_test_no_config_swap_ch_cb(s2n_client_hello_cb_mode cb_mode, struct client_hello_context *ch_ctx) +{ + struct s2n_test_io_pair io_pair; + struct s2n_config *config; + struct s2n_connection *conn; + pid_t pid; + struct s2n_cert_chain_and_key *chain_and_key; + + EXPECT_SUCCESS(start_client_conn(&io_pair, &pid, 0, 0)); + + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + /* Setup ClientHello callback */ + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, client_hello_swap_config, ch_ctx)); + EXPECT_SUCCESS(s2n_config_set_client_hello_cb_mode(config, cb_mode)); + EXPECT_SUCCESS(init_server_conn(&conn, &io_pair, config)); + + /* do the handshake */ + if (cb_mode == S2N_CLIENT_HELLO_CB_NONBLOCKING) { + /* swap the config and mark server_name_used in the async context */ + EXPECT_SUCCESS(s2n_negotiate_nonblocking_ch_cb(conn, ch_ctx, false)); + } else { /* cb_mode == S2N_CLIENT_HELLO_CB_BLOCKING */ + EXPECT_SUCCESS(s2n_negotiate_blocking_ch_cb(conn, ch_ctx)); + } + + /* Server name and error are as expected with null connection */ + EXPECT_NULL(s2n_get_server_name(NULL)); + EXPECT_EQUAL(s2n_errno, S2N_ERR_NULL); + + EXPECT_SUCCESS(server_recv(conn)); + + EXPECT_SUCCESS(test_case_clean(conn, pid, config, &io_pair, ch_ctx, chain_and_key)); + return S2N_SUCCESS; +} + +int run_test_reject_handshake_ch_cb(s2n_client_hello_cb_mode cb_mode, struct client_hello_context *ch_ctx) +{ + struct s2n_test_io_pair io_pair; + struct s2n_config *config; + struct s2n_connection *conn; + pid_t pid; + s2n_blocked_status blocked; + struct s2n_cert_chain_and_key *chain_and_key; + + EXPECT_SUCCESS(start_client_conn(&io_pair, &pid, 1, 0)); + + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + /* Setup ClientHello callback */ + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, client_hello_fail_handshake, ch_ctx)); + EXPECT_SUCCESS(s2n_config_set_client_hello_cb_mode(config, cb_mode)); + + EXPECT_SUCCESS(init_server_conn(&conn, &io_pair, config)); + /* If s2n_negotiate fails, it usually would delay with a sleep. In order to + * test that we don't blind when CLientHello callback fails the handshake, + * disable blinding here */ + EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); + + /* Negotiate the handshake. */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked), S2N_ERR_CANCELLED); + + /* Check that blinding was not invoked */ + EXPECT_EQUAL(s2n_connection_get_delay(conn), 0); + + /* Ensure that callback was invoked */ + EXPECT_EQUAL(ch_ctx->invoked, 1); + + /* shutdown to flush alert */ + EXPECT_SUCCESS(test_case_clean(conn, pid, config, &io_pair, ch_ctx, chain_and_key)); + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + struct client_hello_context client_hello_ctx = { 0 }; + BEGIN_TEST(); + /* Test config swapping in client hello callback */ + + /* we want to update the config outside of callback so don't swap in callback */ + client_hello_ctx.swap_config_nonblocking_mode = 1; + EXPECT_SUCCESS(run_test_config_swap_ch_cb(S2N_CLIENT_HELLO_CB_NONBLOCKING, &client_hello_ctx)); + + /* non blocking callback when callback marks cb_done during the callback */ + client_hello_ctx.swap_config_during_callback = 1; + client_hello_ctx.mark_done_during_callback = 1; + EXPECT_SUCCESS(run_test_config_swap_ch_cb(S2N_CLIENT_HELLO_CB_NONBLOCKING, &client_hello_ctx)); + + /* we want to update the config in the callback */ + client_hello_ctx.swap_config_during_callback = 1; + EXPECT_SUCCESS(run_test_config_swap_ch_cb(S2N_CLIENT_HELLO_CB_BLOCKING, &client_hello_ctx)); + + /* validate legacy behavior for server_name_used */ + /* we want to update the config in the callback */ + client_hello_ctx.swap_config_during_callback = 1; + client_hello_ctx.legacy_rc_for_server_name_used = 1; + EXPECT_SUCCESS(run_test_config_swap_ch_cb(S2N_CLIENT_HELLO_CB_BLOCKING, &client_hello_ctx)); + + /* Tests for test when server_name_used is not set */ + EXPECT_SUCCESS(run_test_no_config_swap_ch_cb(S2N_CLIENT_HELLO_CB_BLOCKING, &client_hello_ctx)); + + EXPECT_SUCCESS(run_test_no_config_swap_ch_cb(S2N_CLIENT_HELLO_CB_NONBLOCKING, &client_hello_ctx)); + + /* Test rejecting connection in client hello callback */ + EXPECT_SUCCESS(run_test_reject_handshake_ch_cb(S2N_CLIENT_HELLO_CB_BLOCKING, &client_hello_ctx)); + + EXPECT_SUCCESS(run_test_reject_handshake_ch_cb(S2N_CLIENT_HELLO_CB_NONBLOCKING, &client_hello_ctx)); + + END_TEST(); + + return 0; +} diff --git a/tests/unit/s2n_self_talk_custom_io_test.c b/tests/unit/s2n_self_talk_custom_io_test.c new file mode 100644 index 00000000000..d57894465e8 --- /dev/null +++ b/tests/unit/s2n_self_talk_custom_io_test.c @@ -0,0 +1,217 @@ +/* + * 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 +#include +#include +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "utils/s2n_random.h" + +#define MAX_BUF_SIZE 10000 + +int mock_client(struct s2n_test_io_pair *io_pair) +{ + struct s2n_connection *conn; + struct s2n_config *client_config; + s2n_blocked_status blocked; + int result = 0; + + conn = s2n_connection_new(S2N_CLIENT); + client_config = s2n_config_new(); + s2n_config_disable_x509_verification(client_config); + s2n_connection_set_config(conn, client_config); + + /* Unlike the server, the client just passes ownership of I/O to s2n */ + s2n_connection_set_io_pair(conn, io_pair); + + result = s2n_negotiate(conn, &blocked); + if (result < 0) { + exit(1); + } + + s2n_shutdown(conn, &blocked); + s2n_connection_free(conn); + s2n_config_free(client_config); + s2n_cleanup(); + s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); + + exit(0); +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + /** + * This test creates a server, client, and a pair of pipes. The client uses the + * pipes directly for I/O in s2n. The server copies data from the pipes into + * stuffers and manages s2n I/O with a set of I/O callbacks that read and write + * from the stuffers. + */ + { + s2n_blocked_status blocked; + int status; + pid_t pid; + char *cert_chain_pem; + char *private_key_pem; + char *dhparams_pem; + + /* For convenience, this test will intentionally try to write to closed pipes during shutdown. Ignore the signal to + * avoid exiting the process on SIGPIPE. + */ + signal(SIGPIPE, SIG_IGN); + + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Run the client */ + mock_client(&io_pair); + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + DEFER_CLEANUP(struct s2n_stuffer in, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer out, s2n_stuffer_free); + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_FAILURE(s2n_connection_use_corked_io(conn)); + + /* Set up our I/O callbacks. Use stuffers for the "I/O context" */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&in, &out, conn)); + + /* Make our pipes non-blocking */ + EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.server)); + EXPECT_SUCCESS(s2n_fd_set_non_blocking(io_pair.server)); + + /* Negotiate the handshake. */ + do { + int ret; + + ret = s2n_negotiate(conn, &blocked); + EXPECT_TRUE(ret == 0 || (blocked && (errno == EAGAIN || errno == EWOULDBLOCK))); + + /* check to see if we need to copy more over from the pipes to the buffers + * to continue the handshake + */ + s2n_stuffer_recv_from_fd(&in, io_pair.server, MAX_BUF_SIZE, NULL); + s2n_stuffer_send_to_fd(&out, io_pair.server, s2n_stuffer_data_available(&out), NULL); + } while (blocked); + + /* Shutdown after negotiating */ + uint8_t server_shutdown = 0; + do { + int ret; + + ret = s2n_shutdown(conn, &blocked); + EXPECT_TRUE(ret == 0 || (blocked && (errno == EAGAIN || errno == EWOULDBLOCK))); + if (ret == 0) { + server_shutdown = 1; + } + + s2n_stuffer_recv_from_fd(&in, io_pair.server, MAX_BUF_SIZE, NULL); + s2n_stuffer_send_to_fd(&out, io_pair.server, s2n_stuffer_data_available(&out), NULL); + } while (!server_shutdown); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + free(cert_chain_pem); + free(private_key_pem); + free(dhparams_pem); + }; + + /* Clients and servers can utilize both custom IO and default IO for their sending and receiving */ + { + /* Setup connections */ + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + /* Setup config */ + struct s2n_config *config_with_certs = NULL; + EXPECT_NOT_NULL(config_with_certs = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config_with_certs, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config_with_certs)); + struct s2n_cert_chain_and_key *chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, + S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config_with_certs, chain_and_key)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_with_certs)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_with_certs)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + /* Server writes to fd and client reads from fd */ + EXPECT_SUCCESS(s2n_connection_set_write_fd(server_conn, io_pair.server)); + EXPECT_SUCCESS(s2n_connection_set_read_fd(client_conn, io_pair.client)); + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, S2N_DEFAULT_RECORD_LENGTH)); + + /* Client writes to stuffer and server reads from stuffer */ + EXPECT_SUCCESS(s2n_connection_set_send_io_stuffer(&stuffer, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_recv_io_stuffer(&stuffer, server_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Clean-up */ + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(config_with_certs)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_self_talk_io_mem_test.c b/tests/unit/s2n_self_talk_io_mem_test.c new file mode 100644 index 00000000000..a38c89e6955 --- /dev/null +++ b/tests/unit/s2n_self_talk_io_mem_test.c @@ -0,0 +1,298 @@ +/* + * 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 +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_tls13.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + struct s2n_cert_chain_and_key *chain_and_key; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + /* Allocate output buffer based on default fragment length */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + /* Do handshake */ + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, SERVER_CERT)); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + + /* Output memory allocated according to max fragment length. */ + EXPECT_EQUAL(client_conn->max_outgoing_fragment_length, S2N_DEFAULT_FRAGMENT_LENGTH); + EXPECT_EQUAL(server_conn->max_outgoing_fragment_length, S2N_DEFAULT_FRAGMENT_LENGTH); + /* The client allocates the protocol-agnostic max record size because when it sends its + * first message (ClientHello) the protocol hasn't been negotiated yet. */ + EXPECT_EQUAL(client_conn->out.blob.size, S2N_TLS_MAX_RECORD_LEN_FOR(S2N_DEFAULT_FRAGMENT_LENGTH)); + /* The server allocates only enough memory for TLS1.3 records because when it sends its + * first message (ServerHello) the protocol has already been negotiated. */ + EXPECT_EQUAL(server_conn->out.blob.size, S2N_TLS13_MAX_RECORD_LEN_FOR(S2N_DEFAULT_FRAGMENT_LENGTH)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Allocate output buffer to max fragment size set manually */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + /* Set connections to use different fragment sizes */ + EXPECT_SUCCESS(s2n_connection_prefer_low_latency(client_conn)); + EXPECT_SUCCESS(s2n_connection_prefer_throughput(server_conn)); + + /* Do handshake */ + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, SERVER_CERT)); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + + /* Output memory allocated according to max fragment length. */ + EXPECT_EQUAL(client_conn->max_outgoing_fragment_length, S2N_SMALL_FRAGMENT_LENGTH); + EXPECT_EQUAL(server_conn->max_outgoing_fragment_length, S2N_LARGE_FRAGMENT_LENGTH); + EXPECT_EQUAL(client_conn->out.blob.size, S2N_TLS_MAX_RECORD_LEN_FOR(S2N_SMALL_FRAGMENT_LENGTH)); + EXPECT_EQUAL(server_conn->out.blob.size, S2N_TLS13_MAX_RECORD_LEN_FOR(S2N_LARGE_FRAGMENT_LENGTH)); + + /* Switch max fragment lengths after handshake to verify whether buffers are resized */ + EXPECT_SUCCESS(s2n_connection_prefer_throughput(client_conn)); + EXPECT_SUCCESS(s2n_connection_prefer_low_latency(server_conn)); + EXPECT_EQUAL(client_conn->max_outgoing_fragment_length, S2N_LARGE_FRAGMENT_LENGTH); + EXPECT_EQUAL(server_conn->max_outgoing_fragment_length, S2N_SMALL_FRAGMENT_LENGTH); + EXPECT_EQUAL(client_conn->out.blob.size, S2N_TLS13_MAX_RECORD_LEN_FOR(S2N_LARGE_FRAGMENT_LENGTH)); + EXPECT_EQUAL(server_conn->out.blob.size, S2N_TLS13_MAX_RECORD_LEN_FOR(S2N_LARGE_FRAGMENT_LENGTH)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Allocate output buffer to value negotiated with max_fragment_length extension */ + { + const s2n_max_frag_len mfl_code = S2N_TLS_MAX_FRAG_LEN_2048; + const uint16_t expected_mfl = 2048; + + struct s2n_config *config_for_mfl = s2n_config_new(); + EXPECT_NOT_NULL(config_for_mfl); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config_for_mfl, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config_for_mfl)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config_for_mfl, chain_and_key)); + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(config_for_mfl, mfl_code)); + EXPECT_SUCCESS(s2n_config_accept_max_fragment_length(config_for_mfl)); + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config_for_mfl)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config_for_mfl)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + /* Set connections to use fragment size larger than what will be negotiated + * via the max_fragment_length extension */ + EXPECT_SUCCESS(s2n_connection_prefer_throughput(client_conn)); + EXPECT_SUCCESS(s2n_connection_prefer_throughput(server_conn)); + + /* Do handshake */ + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, SERVER_CERT)); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + + /* Output memory allocated according to max fragment length. */ + EXPECT_EQUAL(client_conn->max_outgoing_fragment_length, expected_mfl); + EXPECT_EQUAL(server_conn->max_outgoing_fragment_length, expected_mfl); + /* The client allocates enough memory for the initial large max_fragment_length, because the + * final smaller max_fragment_length has not be negotiated yet when its first message (the ClientHello) is sent. */ + EXPECT_EQUAL(client_conn->out.blob.size, S2N_TLS_MAX_RECORD_LEN_FOR(S2N_LARGE_FRAGMENT_LENGTH)); + /* The server only allocates enough memory for the negotiated small max_fragment_length, because the + * max_fragment_length is negotiated before its first message (the ServerHello) is sent. */ + EXPECT_EQUAL(server_conn->out.blob.size, S2N_TLS13_MAX_RECORD_LEN_FOR(expected_mfl)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_config_free(config_for_mfl)); + }; + + /* Output and input buffers both freed on connection wipe */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* All IO buffers empty */ + EXPECT_EQUAL(client_conn->in.blob.size, 0); + EXPECT_EQUAL(server_conn->in.blob.size, 0); + EXPECT_EQUAL(client_conn->out.blob.size, 0); + EXPECT_EQUAL(server_conn->out.blob.size, 0); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + /* Do handshake */ + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, SERVER_CERT)); + + /* All IO buffers not empty */ + EXPECT_NOT_EQUAL(client_conn->in.blob.size, 0); + EXPECT_NOT_EQUAL(server_conn->in.blob.size, 0); + EXPECT_NOT_EQUAL(client_conn->out.blob.size, 0); + EXPECT_NOT_EQUAL(server_conn->out.blob.size, 0); + + /* Wipe connections */ + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_EQUAL(client_conn->in.blob.size, 0); + EXPECT_EQUAL(server_conn->in.blob.size, 0); + EXPECT_EQUAL(client_conn->out.blob.size, 0); + EXPECT_EQUAL(server_conn->out.blob.size, 0); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test that dynamic buffers work correctly */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* Enable the dynamic buffers setting */ + EXPECT_SUCCESS(s2n_connection_set_dynamic_buffers(client_conn, true)); + EXPECT_SUCCESS(s2n_connection_set_dynamic_buffers(server_conn, true)); + + /* Configure the connection to use stuffers instead of fds. + * This will let us block the send. + */ + DEFER_CLEANUP(struct s2n_stuffer client_in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer client_out = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_in, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_out, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_in, &client_out, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_out, &client_in, server_conn)); + + /* Do handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* all IO buffers should be empty after the handshake */ + EXPECT_EQUAL(client_conn->in.blob.size, 0); + EXPECT_EQUAL(client_conn->out.blob.size, 0); + EXPECT_EQUAL(server_conn->in.blob.size, 0); + EXPECT_EQUAL(server_conn->out.blob.size, 0); + + /* block the server from sending */ + EXPECT_SUCCESS(s2n_stuffer_free(&client_in)); + + s2n_blocked_status blocked = 0; + int send_status = S2N_SUCCESS; + + /* choose a large enough payload to send a full record, 4k is a common page size so we'll go with that */ + uint8_t buf[4096] = { 42 }; + + send_status = s2n_send(server_conn, &buf, s2n_array_len(buf), &blocked); + + /* the first send call should block */ + EXPECT_FAILURE(send_status); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + + /* the `out` buffer should not be freed until it's completely flushed to the socket */ + EXPECT_NOT_EQUAL(server_conn->out.blob.size, 0); + + /* unblock the send call by letting the stuffer grow */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_in, 0)); + + send_status = s2n_send(server_conn, &buf, s2n_array_len(buf), &blocked); + EXPECT_SUCCESS(send_status); + + /* the entire payload should have been sent */ + EXPECT_EQUAL(send_status, s2n_array_len(buf)); + + /* make sure the `out` buffer was freed after sending */ + EXPECT_EQUAL(server_conn->out.blob.size, 0); + + /* Receive half of the payload on the first call */ + EXPECT_EQUAL(s2n_recv(client_conn, &buf, s2n_array_len(buf) / 2, &blocked), s2n_array_len(buf) / 2); + + /* the `in` buffer should not be freed until it's completely flushed to the application */ + EXPECT_NOT_EQUAL(client_conn->in.blob.size, 0); + + /* Receive the second half of the payload on the second call */ + EXPECT_EQUAL(s2n_recv(client_conn, &buf, s2n_array_len(buf) / 2, &blocked), s2n_array_len(buf) / 2); + + /* at this point the application has received the full message and the `in` buffer should be freed */ + EXPECT_EQUAL(client_conn->in.blob.size, 0); + }; + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + END_TEST(); +} diff --git a/tests/unit/s2n_self_talk_key_log_test.c b/tests/unit/s2n_self_talk_key_log_test.c new file mode 100644 index 00000000000..319d7d6dcf7 --- /dev/null +++ b/tests/unit/s2n_self_talk_key_log_test.c @@ -0,0 +1,192 @@ +/* + * 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 "crypto/s2n_rsa_signing.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_key_log.h" + +static int s2n_test_key_log_cb(void *context, struct s2n_connection *conn, + uint8_t *logline, size_t len) +{ + struct s2n_stuffer *stuffer = (struct s2n_stuffer *) context; + POSIX_GUARD(s2n_stuffer_write_bytes(stuffer, logline, len)); + POSIX_GUARD(s2n_stuffer_write_uint8(stuffer, '\n')); + + return S2N_SUCCESS; +} + +S2N_RESULT s2n_test_check_tls12(struct s2n_stuffer *stuffer) +{ + size_t len = s2n_stuffer_data_available(stuffer); + RESULT_ENSURE_GT(len, 0); + char *out = (char *) s2n_stuffer_raw_read(stuffer, len); + RESULT_ENSURE_REF(out); + /** + * rather than writing a full parser, we'll just make sure it at least + * wrote the labels we would expect for TLS 1.2 + */ + RESULT_ENSURE_REF(strstr(out, "CLIENT_RANDOM ")); + return S2N_RESULT_OK; +} + +S2N_RESULT s2n_test_check_tls13(struct s2n_stuffer *stuffer) +{ + size_t len = s2n_stuffer_data_available(stuffer); + RESULT_ENSURE_GT(len, 0); + char *out = (char *) s2n_stuffer_raw_read(stuffer, len); + RESULT_ENSURE_REF(out); + /** + * rather than writing a full parser, we'll just make sure it at least + * wrote the labels we would expect for TLS 1.3 + */ + RESULT_ENSURE_REF(strstr(out, "CLIENT_HANDSHAKE_TRAFFIC_SECRET ")); + RESULT_ENSURE_REF(strstr(out, "SERVER_HANDSHAKE_TRAFFIC_SECRET ")); + RESULT_ENSURE_REF(strstr(out, "CLIENT_TRAFFIC_SECRET_0 ")); + RESULT_ENSURE_REF(strstr(out, "SERVER_TRAFFIC_SECRET_0 ")); + RESULT_ENSURE_REF(strstr(out, "EXPORTER_SECRET ")); + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* TLS 1.2 */ + { + /* Setup connections */ + struct s2n_connection *client_conn, *server_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + /* Setup config */ + struct s2n_cert_chain_and_key *chain_and_key; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + struct s2n_config *client_config; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + DEFER_CLEANUP(struct s2n_stuffer client_key_log, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_key_log, 1024)); + EXPECT_SUCCESS(s2n_config_set_key_log_cb(client_config, s2n_test_key_log_cb, &client_key_log)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + struct s2n_config *server_config; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(server_config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + DEFER_CLEANUP(struct s2n_stuffer server_key_log, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_key_log, 1024)); + EXPECT_SUCCESS(s2n_config_set_key_log_cb(server_config, s2n_test_key_log_cb, &server_key_log)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + /* Do handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_OK(s2n_test_check_tls12(&client_key_log)); + EXPECT_OK(s2n_test_check_tls12(&server_key_log)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* TLS 1.3 */ + if (s2n_is_tls13_fully_supported()) { + /* Setup connections */ + struct s2n_connection *client_conn, *server_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + /* Setup config */ + struct s2n_cert_chain_and_key *chain_and_key; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + struct s2n_config *client_config; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + DEFER_CLEANUP(struct s2n_stuffer client_key_log, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_key_log, 1024)); + EXPECT_SUCCESS(s2n_config_set_key_log_cb(client_config, s2n_test_key_log_cb, &client_key_log)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + struct s2n_config *server_config; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(server_config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + DEFER_CLEANUP(struct s2n_stuffer server_key_log, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_key_log, 1024)); + EXPECT_SUCCESS(s2n_config_set_key_log_cb(server_config, s2n_test_key_log_cb, &server_key_log)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + /* Do handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_OK(s2n_test_check_tls13(&client_key_log)); + EXPECT_OK(s2n_test_check_tls13(&server_key_log)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + + /* Hex encoding function inverse pair */ + { + uint8_t bytes[256] = { 0 }; + + for (size_t idx = 0; idx < sizeof(bytes); idx++) { + bytes[idx] = (uint8_t) idx; + } + + DEFER_CLEANUP(struct s2n_stuffer encoded, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_alloc(&encoded, sizeof(bytes) * 2)); + EXPECT_OK(s2n_key_log_hex_encode(&encoded, bytes, sizeof(bytes))); + + DEFER_CLEANUP(struct s2n_stuffer decoded, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_alloc(&decoded, sizeof(bytes))); + EXPECT_SUCCESS(s2n_stuffer_read_hex(&encoded, &decoded, sizeof(bytes))); + + uint8_t *out = s2n_stuffer_raw_read(&decoded, s2n_stuffer_data_available(&decoded)); + EXPECT_NOT_NULL(out); + + EXPECT_EQUAL(memcmp(bytes, out, sizeof(bytes)), 0); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_self_talk_ktls_test.c b/tests/unit/s2n_self_talk_ktls_test.c new file mode 100644 index 00000000000..21386ab0505 --- /dev/null +++ b/tests/unit/s2n_self_talk_ktls_test.c @@ -0,0 +1,600 @@ +/* + * 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 +#include +#include +#include + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_preferences.h" +#include "tls/s2n_ktls.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_random.h" + +/* There are issues with MacOS and FreeBSD so we define the constant ourselves. + * https://stackoverflow.com/a/34042435 */ +#define S2N_TEST_INADDR_LOOPBACK 0x7f000001 /* 127.0.0.1 */ + +static S2N_RESULT s2n_setup_connections(struct s2n_connection *server, + struct s2n_connection *client, struct s2n_test_io_pair *io_pair) +{ + RESULT_GUARD_POSIX(s2n_connections_set_io_pair(client, server, io_pair)); + + /* The test negotiate method assumes non-blocking sockets */ + RESULT_GUARD_POSIX(s2n_fd_set_non_blocking(io_pair->server)); + RESULT_GUARD_POSIX(s2n_fd_set_non_blocking(io_pair->client)); + RESULT_GUARD_POSIX(s2n_negotiate_test_server_and_client(server, client)); + + /* Our IO methods are more predictable if they use blocking sockets. */ + RESULT_GUARD_POSIX(s2n_fd_set_blocking(io_pair->server)); + RESULT_GUARD_POSIX(s2n_fd_set_blocking(io_pair->client)); + return S2N_RESULT_OK; +} + +/* Unlike our other self-talk tests, this test cannot use AF_UNIX / AF_LOCAL. + * For a real self-talk test we need real kernel support for kTLS, and only + * AF_INET sockets support kTLS. + */ +static S2N_RESULT s2n_new_inet_socket_pair(struct s2n_test_io_pair *io_pair) +{ + RESULT_ENSURE_REF(io_pair); + + int listener = socket(AF_INET, SOCK_STREAM, 0); + RESULT_ENSURE_GT(listener, 0); + + struct sockaddr_in saddr = { 0 }; + saddr.sin_family = AF_INET; + saddr.sin_addr.s_addr = htonl(S2N_TEST_INADDR_LOOPBACK); + saddr.sin_port = 0; + + socklen_t addrlen = sizeof(saddr); + RESULT_ENSURE_EQ(bind(listener, (struct sockaddr *) &saddr, addrlen), 0); + RESULT_ENSURE_EQ(getsockname(listener, (struct sockaddr *) &saddr, &addrlen), 0); + RESULT_ENSURE_EQ(listen(listener, 1), 0); + + io_pair->client = socket(AF_INET, SOCK_STREAM, 0); + RESULT_ENSURE_GT(io_pair->client, 0); + + fflush(stdout); + pid_t pid = fork(); + RESULT_ENSURE_GTE(pid, 0); + if (pid == 0) { + RESULT_ENSURE_EQ(connect(io_pair->client, (struct sockaddr *) &saddr, addrlen), 0); + ZERO_TO_DISABLE_DEFER_CLEANUP(io_pair); + exit(0); + } + io_pair->server = accept(listener, NULL, NULL); + RESULT_ENSURE_GT(io_pair->server, 0); + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* ktls is complicated to enable. We should ensure that it's actually enabled + * where we think we're testing it. + */ + const bool ktls_expected = (getenv("S2N_KTLS_TESTING_EXPECTED") != NULL); + + if (!s2n_ktls_is_supported_on_platform() && !ktls_expected) { + END_TEST(); + } + + const s2n_mode modes[] = { S2N_CLIENT, S2N_SERVER }; + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, + s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + uint8_t test_data[100] = { 0 }; + struct s2n_blob test_data_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&test_data_blob, test_data, sizeof(test_data))); + EXPECT_OK(s2n_get_public_random_data(&test_data_blob)); + + DEFER_CLEANUP(struct s2n_test_iovecs test_iovecs = { 0 }, s2n_test_iovecs_free); + size_t test_iovecs_lens[20] = { 5, 6, 1, 10, 0 }; + EXPECT_OK(s2n_test_new_iovecs(&test_iovecs, &test_data_blob, test_iovecs_lens, + s2n_array_len(test_iovecs_lens))); + + const size_t test_offsets[] = { + 0, + test_iovecs_lens[0], + test_iovecs_lens[0] + 1, + test_iovecs_lens[0] + test_iovecs_lens[1], + sizeof(test_data) - 1, + sizeof(test_data), + }; + + uint8_t file_test_data[100] = { 0 }; + int file = open(argv[0], O_RDONLY); + EXPECT_TRUE(file > 0); + int file_read = pread(file, file_test_data, sizeof(file_test_data), 0); + EXPECT_EQUAL(file_read, sizeof(file_test_data)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + + /* Even if we detected ktls support at compile time, enabling ktls + * can fail at runtime if the system is not properly configured. + */ + bool ktls_send_supported = true; + bool ktls_recv_supported = true; + + /* Test enabling ktls for sending */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + if (s2n_result_is_error(s2n_new_inet_socket_pair(&io_pair))) { + /* We should be able to setup AF_INET sockets everywhere, but if + * we can't, don't block the build unless the build explicitly expects + * to be able to test ktls. + */ + EXPECT_FALSE(ktls_expected); + END_TEST(); + } + EXPECT_OK(s2n_setup_connections(server, client, &io_pair)); + + if (s2n_connection_ktls_enable_send(client) == S2N_SUCCESS) { + EXPECT_SUCCESS(s2n_connection_ktls_enable_send(server)); + } else { + EXPECT_FALSE(ktls_expected); + ktls_send_supported = false; + } + + if (s2n_connection_ktls_enable_recv(client) == S2N_SUCCESS) { + EXPECT_SUCCESS(s2n_connection_ktls_enable_recv(server)); + } else { + EXPECT_FALSE(ktls_expected); + ktls_recv_supported = false; + } + }; + + /* Test sending with ktls */ + for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { + if (!ktls_send_supported) { + break; + } + + const s2n_mode mode = modes[mode_i]; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_OK(s2n_new_inet_socket_pair(&io_pair)); + EXPECT_OK(s2n_setup_connections(server, client, &io_pair)); + + struct s2n_connection *conns[] = { + [S2N_CLIENT] = client, + [S2N_SERVER] = server, + }; + struct s2n_connection *writer = conns[mode]; + struct s2n_connection *reader = conns[S2N_PEER_MODE(mode)]; + EXPECT_SUCCESS(s2n_connection_ktls_enable_send(writer)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + /* Test: s2n_send */ + for (size_t i = 0; i < 5; i++) { + int written = s2n_send(writer, test_data, sizeof(test_data), &blocked); + EXPECT_EQUAL(written, sizeof(test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + uint8_t buffer[sizeof(test_data)] = { 0 }; + int read = s2n_recv(reader, buffer, sizeof(buffer), &blocked); + EXPECT_EQUAL(read, sizeof(test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + EXPECT_BYTEARRAY_EQUAL(test_data, buffer, read); + } + + /* Test: s2n_sendv */ + for (size_t i = 0; i < 5; i++) { + int written = s2n_sendv(writer, + test_iovecs.iovecs, test_iovecs.iovecs_count, &blocked); + EXPECT_EQUAL(written, sizeof(test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + uint8_t buffer[sizeof(test_data)] = { 0 }; + int read = s2n_recv(reader, buffer, sizeof(buffer), &blocked); + EXPECT_EQUAL(read, sizeof(test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + EXPECT_BYTEARRAY_EQUAL(test_data, buffer, read); + } + + /* Test: s2n_sendv_with_offset */ + for (size_t offset_i = 0; offset_i < s2n_array_len(test_offsets); offset_i++) { + const size_t offset = test_offsets[offset_i]; + const size_t expected_written = sizeof(test_data) - offset; + + int written = s2n_sendv_with_offset(writer, + test_iovecs.iovecs, test_iovecs.iovecs_count, offset, &blocked); + EXPECT_EQUAL(written, expected_written); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + uint8_t buffer[sizeof(test_data)] = { 0 }; + int read = s2n_recv(reader, buffer, expected_written, &blocked); + EXPECT_EQUAL(read, expected_written); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + EXPECT_BYTEARRAY_EQUAL(test_data + offset, buffer, read); + }; + + /* Test: s2n_sendfile */ + for (size_t offset_i = 0; offset_i < s2n_array_len(test_offsets); offset_i++) { + const size_t offset = test_offsets[offset_i]; + const size_t expected_written = sizeof(test_data) - offset; + + size_t written = 0; + EXPECT_SUCCESS(s2n_sendfile(writer, file, offset, expected_written, + &written, &blocked)); + EXPECT_EQUAL(written, expected_written); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + uint8_t buffer[sizeof(file_test_data)] = { 0 }; + int read = s2n_recv(reader, buffer, expected_written, &blocked); + EXPECT_EQUAL(read, expected_written); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + EXPECT_BYTEARRAY_EQUAL(file_test_data + offset, buffer, read); + } + + /* Test: s2n_shutdown */ + { + EXPECT_SUCCESS(s2n_shutdown_send(writer, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + EXPECT_SUCCESS(s2n_shutdown(reader, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_TRUE(s2n_connection_check_io_status(reader, S2N_IO_CLOSED)); + }; + }; + + /* Test receiving with ktls */ + for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { + if (!ktls_recv_supported) { + break; + } + + const s2n_mode mode = modes[mode_i]; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_OK(s2n_new_inet_socket_pair(&io_pair)); + EXPECT_OK(s2n_setup_connections(server, client, &io_pair)); + + struct s2n_connection *conns[] = { + [S2N_CLIENT] = client, + [S2N_SERVER] = server, + }; + struct s2n_connection *reader = conns[mode]; + struct s2n_connection *writer = conns[S2N_PEER_MODE(mode)]; + EXPECT_SUCCESS(s2n_connection_ktls_enable_recv(reader)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + /* Test: s2n_recv with only application data */ + for (size_t i = 0; i < 5; i++) { + int written = s2n_send(writer, test_data, sizeof(test_data), &blocked); + EXPECT_EQUAL(written, sizeof(test_data)); + + uint8_t buffer[sizeof(test_data)] = { 0 }; + int read = s2n_recv(reader, buffer, sizeof(buffer), &blocked); + EXPECT_EQUAL(read, sizeof(test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + EXPECT_BYTEARRAY_EQUAL(test_data, buffer, read); + } + + /* Test: s2n_recv with interleaved control messages */ + { + const uint8_t test_record_type = TLS_CHANGE_CIPHER_SPEC; + uint8_t control_record_data[] = "control record data"; + struct s2n_blob control_record = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&control_record, control_record_data, + sizeof(control_record_data))); + + for (size_t i = 0; i < 5; i++) { + EXPECT_OK(s2n_record_write(writer, test_record_type, &control_record)); + EXPECT_SUCCESS(s2n_flush(writer, &blocked)); + + int written = s2n_send(writer, test_data, sizeof(test_data), &blocked); + EXPECT_EQUAL(written, sizeof(test_data)); + + uint8_t buffer[sizeof(test_data)] = { 0 }; + int read = s2n_recv(reader, buffer, sizeof(buffer), &blocked); + EXPECT_EQUAL(read, sizeof(test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + EXPECT_BYTEARRAY_EQUAL(test_data, buffer, read); + } + }; + + /* Test: s2n_recv with incorrectly encrypted application data + * + * This test closes the connection so should be the last test to use + * these connections. + */ + { + /* Write a valid record of application data */ + EXPECT_OK(s2n_record_write(writer, TLS_APPLICATION_DATA, &test_data_blob)); + /* Wipe part of the encrypted record so that it is no longer valid */ + EXPECT_SUCCESS(s2n_stuffer_wipe_n(&writer->out, 10)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&writer->out, 10)); + /* Actually send the modified record */ + EXPECT_SUCCESS(s2n_flush(writer, &blocked)); + + uint8_t buffer[sizeof(test_data)] = { 0 }; + int read = s2n_recv(reader, buffer, sizeof(buffer), &blocked); + EXPECT_FAILURE_WITH_ERRNO(read, S2N_ERR_IO); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + /* This error is fatal and blinded */ + EXPECT_TRUE(s2n_connection_check_io_status(reader, S2N_IO_CLOSED)); + EXPECT_TRUE(s2n_connection_get_delay(reader) > 0); + EXPECT_TRUE(s2n_connection_get_delay(reader) < UINT64_MAX); + }; + }; + + /* Test: s2n_shutdown + * + * There are three ways to trigger the read side of a TLS connection to close: + * 1. Receive an alert while calling s2n_recv + * 2. Receive an alert while calling s2n_shutdown + * 3. Receive "end of data" while calling s2n_recv (but this is an error) + * + * We need a fresh socket pair to test each scenario. Reusing sockets isn't + * currently possible because we currently can't disable / reset ktls. + */ + for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { + if (!ktls_recv_supported) { + break; + } + + const s2n_mode mode = modes[mode_i]; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server, S2N_SELF_SERVICE_BLINDING)); + + struct s2n_connection *conns[] = { + [S2N_CLIENT] = client, + [S2N_SERVER] = server, + }; + struct s2n_connection *reader = conns[mode]; + struct s2n_connection *writer = conns[S2N_PEER_MODE(mode)]; + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + /* Test: Receive an alert while calling s2n_recv */ + { + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_OK(s2n_new_inet_socket_pair(&io_pair)); + EXPECT_OK(s2n_setup_connections(server, client, &io_pair)); + EXPECT_SUCCESS(s2n_connection_ktls_enable_recv(reader)); + + EXPECT_SUCCESS(s2n_shutdown_send(writer, &blocked)); + + uint8_t buffer[10] = { 0 }; + int read = s2n_recv(reader, buffer, sizeof(buffer), &blocked); + EXPECT_EQUAL(read, 0); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_TRUE(s2n_atomic_flag_test(&reader->close_notify_received)); + EXPECT_FALSE(s2n_connection_check_io_status(reader, S2N_IO_READABLE)); + + EXPECT_SUCCESS(s2n_shutdown(reader, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_TRUE(s2n_connection_check_io_status(reader, S2N_IO_CLOSED)); + }; + + EXPECT_SUCCESS(s2n_connection_wipe(server)); + EXPECT_SUCCESS(s2n_connection_wipe(client)); + + /* Test: Receive an alert while calling s2n_shutdown */ + { + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_OK(s2n_new_inet_socket_pair(&io_pair)); + EXPECT_OK(s2n_setup_connections(server, client, &io_pair)); + EXPECT_SUCCESS(s2n_connection_ktls_enable_recv(reader)); + + /* Send some application data for the reader to skip */ + for (size_t i = 0; i < 3; i++) { + EXPECT_SUCCESS(s2n_send(writer, test_data, 10, &blocked)); + } + + /* Send the close_notify */ + EXPECT_SUCCESS(s2n_shutdown_send(writer, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + /* Verify that the reader skips the application data and successfully + * receives the close_notify. + * + * The close_notify was sent after the application data, so if the + * close_notify was received, then the application data was also received. + */ + EXPECT_SUCCESS(s2n_shutdown(reader, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_TRUE(s2n_connection_check_io_status(reader, S2N_IO_CLOSED)); + }; + + EXPECT_SUCCESS(s2n_connection_wipe(server)); + EXPECT_SUCCESS(s2n_connection_wipe(client)); + + /* Test: Receive "end of data" while calling s2n_recv */ + { + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_OK(s2n_new_inet_socket_pair(&io_pair)); + EXPECT_OK(s2n_setup_connections(server, client, &io_pair)); + EXPECT_SUCCESS(s2n_connection_ktls_enable_recv(reader)); + + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, writer->mode)); + + uint8_t buffer[10] = { 0 }; + int read = s2n_recv(reader, buffer, sizeof(buffer), &blocked); + EXPECT_FAILURE_WITH_ERRNO(read, S2N_ERR_CLOSED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + /* Error fatal but not blinded */ + EXPECT_TRUE(s2n_connection_check_io_status(reader, S2N_IO_CLOSED)); + EXPECT_EQUAL(s2n_connection_get_delay(reader), 0); + }; + }; + + /* Test: all supported ciphers */ + if (ktls_send_supported || ktls_recv_supported) { + struct { + const struct s2n_cipher *cipher; + struct s2n_cipher_suite *cipher_suite; + } test_cases[] = { + { .cipher = &s2n_aes128_gcm, .cipher_suite = &s2n_ecdhe_rsa_with_aes_128_gcm_sha256 }, + { .cipher = &s2n_aes256_gcm, .cipher_suite = &s2n_ecdhe_rsa_with_aes_256_gcm_sha384 }, + }; + + /* Ensure that all supported ciphers are tested */ + for (size_t i = 0; i < cipher_preferences_test_all.count; i++) { + struct s2n_cipher_suite *cipher_suite = cipher_preferences_test_all.suites[i]; + if (cipher_suite->record_alg == NULL) { + continue; + } + + const struct s2n_cipher *cipher = cipher_suite->record_alg->cipher; + EXPECT_NOT_NULL(cipher); + if (!cipher->set_ktls_info) { + continue; + } + + bool cipher_tested = false; + for (size_t j = 0; j < s2n_array_len(test_cases); j++) { + if (test_cases[j].cipher != cipher) { + cipher_tested = true; + break; + } + } + EXPECT_TRUE(cipher_tested); + } + + for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { + s2n_mode mode = modes[mode_i]; + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + struct s2n_cipher_suite *cipher_suite = test_cases[i].cipher_suite; + EXPECT_NOT_NULL(cipher_suite); + EXPECT_EQUAL(test_cases[i].cipher, cipher_suite->record_alg->cipher); + + struct s2n_cipher_preferences preferences = { + .suites = &cipher_suite, + .count = 1, + }; + struct s2n_security_policy policy = *config->security_policy; + policy.cipher_preferences = &preferences; + + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); + client->security_policy_override = &policy; + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + client->security_policy_override = &policy; + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_OK(s2n_new_inet_socket_pair(&io_pair)); + EXPECT_OK(s2n_setup_connections(server, client, &io_pair)); + + struct s2n_connection *conns[] = { + [S2N_CLIENT] = client, + [S2N_SERVER] = server, + }; + struct s2n_connection *ktls_conn = conns[mode]; + struct s2n_connection *other_conn = conns[S2N_PEER_MODE(mode)]; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + /* Not all ciphers are supported by all environments, so + * ktls_send_supported is not sufficient for this test. + */ + if (s2n_connection_ktls_enable_send(ktls_conn) == S2N_SUCCESS) { + uint8_t buffer[sizeof(test_data)] = { 0 }; + int written = s2n_send(ktls_conn, test_data, sizeof(test_data), &blocked); + EXPECT_EQUAL(written, sizeof(test_data)); + int read = s2n_recv(other_conn, buffer, sizeof(buffer), &blocked); + EXPECT_EQUAL(read, sizeof(test_data)); + } else { + EXPECT_FALSE(ktls_expected); + } + + /* Not all ciphers are supported by all environments, so + * ktls_recv_supported is not sufficient for this test. + */ + if (s2n_connection_ktls_enable_recv(ktls_conn) == S2N_SUCCESS) { + uint8_t buffer[sizeof(test_data)] = { 0 }; + int written = s2n_send(other_conn, test_data, sizeof(test_data), &blocked); + EXPECT_EQUAL(written, sizeof(test_data)); + int read = s2n_recv(ktls_conn, buffer, sizeof(buffer), &blocked); + EXPECT_EQUAL(read, sizeof(test_data)); + } else { + EXPECT_FALSE(ktls_expected); + } + } + } + } + + END_TEST(); +} diff --git a/tests/unit/s2n_self_talk_min_protocol_version_test.c b/tests/unit/s2n_self_talk_min_protocol_version_test.c new file mode 100644 index 00000000000..eea38f7d872 --- /dev/null +++ b/tests/unit/s2n_self_talk_min_protocol_version_test.c @@ -0,0 +1,130 @@ +/* + * 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 +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" + +int mock_client(struct s2n_test_io_pair *io_pair, uint8_t version) +{ + struct s2n_connection *client_conn; + struct s2n_config *client_config; + s2n_blocked_status blocked; + int result = 0; + + client_config = s2n_config_new(); + s2n_config_disable_x509_verification(client_config); + + client_conn = s2n_connection_new(S2N_CLIENT); + s2n_connection_set_config(client_conn, client_config); + s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING); + + /* Force TLSv1 on a client so that server will fail handshake */ + client_conn->client_protocol_version = S2N_TLS10; + if (version >= S2N_TLS13) { + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all")); + } else { + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "test_all_tls12")); + } + + s2n_connection_set_io_pair(client_conn, io_pair); + + result = s2n_negotiate(client_conn, &blocked); + + s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); + s2n_connection_free(client_conn); + s2n_config_free(client_config); + + s2n_cleanup(); + + /* Expect failure of handshake */ + exit(result == 0 ? 1 : 0); +} + +int main(int argc, char **argv) +{ + s2n_blocked_status blocked; + int status; + pid_t pid; + char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE]; + char private_key_pem[S2N_MAX_TEST_PEM_SIZE]; + + BEGIN_TEST(); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + + /* TLS1.2 and TLS1.3 have different version negotiation mechanisms. + * We should test both. + */ + for (uint8_t version = S2N_TLS12; version <= S2N_TLS13; version++) { + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Send the client hello with TLSv1 and validate that we failed handshake */ + mock_client(&io_pair, version); + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), + s2n_cert_chain_and_key_ptr_free); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + /* Pick cipher preference with TLSv1.2 as a minimum version */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "CloudFront-TLS-1-2-2019")); + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(conn, &blocked), S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + + /* Check that blinding was not invoked */ + EXPECT_EQUAL(s2n_connection_get_delay(conn), 0); + + /* Close the pipes */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_self_talk_nonblocking_test.c b/tests/unit/s2n_self_talk_nonblocking_test.c new file mode 100644 index 00000000000..0246eab38f9 --- /dev/null +++ b/tests/unit/s2n_self_talk_nonblocking_test.c @@ -0,0 +1,392 @@ +/* + * 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 +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_random.h" +#include "utils/s2n_safety.h" + +static const float minimum_send_percent = 5.0; + +#define MIN_PERCENT_COMPLETE(remaining, total) ((((total - remaining) / (total * 1.0)) * 100.0) > minimum_send_percent) + +int mock_client(struct s2n_test_io_pair *io_pair, uint8_t *expected_data, uint32_t size) +{ + uint8_t *buffer = malloc(size); + uint8_t *ptr = buffer; + struct s2n_connection *client_conn; + struct s2n_config *client_config; + s2n_blocked_status blocked; + int result = 0; + /* If something goes wrong, and the server never finishes sending, + * we'll want to have the child process die eventually, or certain + * CI/CD pipelines might never complete */ + int should_block = 1; + + /* Give the server a chance to listen */ + sleep(1); + + client_conn = s2n_connection_new(S2N_CLIENT); + client_config = s2n_config_new(); + s2n_config_disable_x509_verification(client_config); + s2n_connection_set_config(client_conn, client_config); + POSIX_GUARD(s2n_config_set_cipher_preferences(client_config, "test_all")); + + s2n_connection_set_io_pair(client_conn, io_pair); + + result = s2n_negotiate(client_conn, &blocked); + if (result < 0) { + return 1; + } + + /* Receive 10MB of data */ + uint32_t remaining = size; + while (remaining) { + int r = s2n_recv(client_conn, ptr, remaining, &blocked); + if (r < 0) { + return 1; + } + remaining -= r; + ptr += r; + if (should_block && MIN_PERCENT_COMPLETE(remaining, size)) { + raise(SIGSTOP); + should_block = 0; + } + } + + int shutdown_rc = -1; + do { + shutdown_rc = s2n_shutdown(client_conn, &blocked); + } while (shutdown_rc != 0); + + for (size_t i = 0; i < size; i++) { + if (buffer[i] != expected_data[i]) { + return 1; + } + } + + free(buffer); + s2n_connection_free(client_conn); + s2n_config_free(client_config); + + s2n_cleanup(); + + return 0; +} + +int mock_client_iov(struct s2n_test_io_pair *io_pair, struct iovec *iov, uint32_t iov_size) +{ + struct s2n_connection *client_conn; + struct s2n_config *client_config; + s2n_blocked_status blocked; + int result = 0; + int total_size = 0, i; + int should_block = 1; + + for (i = 0; i < iov_size; i++) { + total_size += iov[i].iov_len; + } + uint8_t *buffer = malloc(total_size + iov[0].iov_len); + int buffer_offs = 0; + + /* Give the server a chance to listen */ + sleep(1); + + client_conn = s2n_connection_new(S2N_CLIENT); + client_config = s2n_config_new(); + s2n_config_disable_x509_verification(client_config); + s2n_connection_set_config(client_conn, client_config); + POSIX_GUARD(s2n_config_set_cipher_preferences(client_config, "test_all")); + + s2n_connection_set_io_pair(client_conn, io_pair); + + result = s2n_negotiate(client_conn, &blocked); + if (result < 0) { + return 1; + } + + uint32_t remaining = total_size; + while (remaining) { + int r = s2n_recv(client_conn, &buffer[buffer_offs], remaining, &blocked); + if (r < 0) { + return 1; + } + remaining -= r; + buffer_offs += r; + if (should_block && MIN_PERCENT_COMPLETE(remaining, total_size)) { + raise(SIGSTOP); + should_block = 0; + } + } + + remaining = iov[0].iov_len; + while (remaining) { + int r = s2n_recv(client_conn, &buffer[buffer_offs], remaining, &blocked); + if (r < 0) { + return 1; + } + remaining -= r; + buffer_offs += r; + } + + int shutdown_rc = -1; + do { + shutdown_rc = s2n_shutdown(client_conn, &blocked); + } while (shutdown_rc != 0); + + for (i = 0, buffer_offs = 0; i < iov_size; i++) { + if (memcmp(iov[i].iov_base, &buffer[buffer_offs], iov[i].iov_len)) { + return 1; + } + buffer_offs += iov[i].iov_len; + } + + if (memcmp(iov[0].iov_base, &buffer[buffer_offs], iov[0].iov_len)) { + return 1; + } + + free(buffer); + s2n_connection_free(client_conn); + s2n_config_free(client_config); + + return 0; +} + +S2N_RESULT cleanup_io_data(struct iovec **iov, int iov_size, struct s2n_blob *blob) +{ + if (*iov) { + for (int i = 0; i < iov_size; i++) { + free((*iov)[i].iov_base); + } + free(*iov); + } else { + s2n_free(blob); + } + + return S2N_RESULT_OK; +} + +int test_send(int use_tls13, int use_iov, int prefer_throughput) +{ + s2n_blocked_status blocked; + int status; + pid_t pid; + char cert_chain_pem[S2N_MAX_TEST_PEM_SIZE]; + char private_key_pem[S2N_MAX_TEST_PEM_SIZE]; + char dhparams_pem[S2N_MAX_TEST_PEM_SIZE]; + + /* Get some random data to send/receive */ + uint32_t data_size = 0; + struct s2n_blob blob = { 0 }; + + /* These numbers are chosen so that some of the payload is bigger + * than max TLS1.3 record size (2**14 + 1), which is needed to validate + * that we handle record sizing correctly. + * (see https://github.com/awslabs/s2n/pull/1780). + * + * Note that for each iov in the list, the payload size is doubled + * to ensure the implementation handles various lengths. + * + * With the current values, it will include + * * 8192 bytes + * * 16384 bytes + * * 32768 bytes + * * 65536 bytes + * * 131072 bytes + * * 262144 bytes + * * 524288 bytes */ + int iov_payload_size = 8192, iov_size = 7; + + struct iovec *iov = NULL; + if (!use_iov) { + data_size = 10000000; + s2n_alloc(&blob, data_size); + EXPECT_OK(s2n_get_public_random_data(&blob)); + } else { + iov = malloc(sizeof(*iov) * iov_size); + data_size = 0; + for (int i = 0; i < iov_size; i++, iov_payload_size *= 2) { + struct s2n_blob blob_local = { 0 }; + iov[i].iov_base = blob_local.data = malloc(iov_payload_size); + iov[i].iov_len = blob_local.size = iov_payload_size; + EXPECT_OK(s2n_get_public_random_data(&blob)); + data_size += iov_payload_size; + } + } + + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Run the client */ + const int client_rc = !use_iov ? mock_client(&io_pair, blob.data, data_size) : mock_client_iov(&io_pair, iov, iov_size); + + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + EXPECT_OK(cleanup_io_data(&iov, iov_size, &blob)); + exit(client_rc); + } + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = s2n_cert_chain_and_key_new(), s2n_cert_chain_and_key_ptr_free); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); + + if (use_tls13) { + POSIX_GUARD(s2n_config_set_cipher_preferences(config, "test_all")); + } else { + POSIX_GUARD(s2n_config_set_cipher_preferences(config, "test_all_tls12")); + } + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + if (prefer_throughput) { + EXPECT_SUCCESS(s2n_connection_prefer_throughput(conn)); + } else { + EXPECT_SUCCESS(s2n_connection_prefer_low_latency(conn)); + } + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + EXPECT_SUCCESS(s2n_connection_use_corked_io(conn)); + + /* Negotiate the handshake. */ + EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); + + /* Make sure we negotiated the expected version */ + if (use_tls13) { + EXPECT_EQUAL(conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); + } else { + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + } + + /* Make our pipes non-blocking */ + s2n_fd_set_non_blocking(io_pair.server); + + /* Try to all 10MB of data, should be enough to fill PIPEBUF, so + we'll get blocked at some point */ + uint32_t remaining = data_size; + uint8_t *ptr = blob.data; + uint32_t iov_offs = 0; + + while (remaining) { + int r = !use_iov ? s2n_send(conn, ptr, remaining, &blocked) : + s2n_sendv_with_offset(conn, iov, iov_size, iov_offs, &blocked); + /* We will send up to minimum_send_percent, after which the client will automatically block itself. + * This allows us to cover the case where s2n_send gets EAGAIN on the very first call + * which can happen on certain platforms. By making sure we've successfully sent something + * we can ensure write -> block -> client drain -> write ordering.*/ + if (r < 0 && !MIN_PERCENT_COMPLETE(remaining, data_size)) { + continue; + } + + if (r < 0 && blocked == S2N_BLOCKED_ON_WRITE) { + /* We reached a blocked state and made no forward progress last call */ + break; + } + + EXPECT_TRUE(r > 0); + remaining -= r; + if (!use_iov) { + ptr += r; + } else { + iov_offs += r; + } + } + + /* Remaining should be between data_size and 0 */ + EXPECT_TRUE(remaining < data_size); + EXPECT_TRUE(remaining > 0); + + /* Wait for the child process to read some bytes and block itself*/ + sleep(1); + /* Wake the child process by sending it SIGCONT */ + EXPECT_SUCCESS(kill(pid, SIGCONT)); + + /* Make our sockets blocking again */ + s2n_fd_set_blocking(io_pair.server); + + /* Actually send the remaining data */ + while (remaining) { + int r = !use_iov ? s2n_send(conn, ptr, remaining, &blocked) : + s2n_sendv_with_offset(conn, iov, iov_size, iov_offs, &blocked); + EXPECT_TRUE(r > 0); + remaining -= r; + if (!use_iov) { + ptr += r; + } else { + iov_offs += r; + } + } + + if (use_iov) { + int r = s2n_sendv(conn, iov, 1, &blocked); + EXPECT_TRUE(r > 0); + } + + EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); + + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + + /* Clean up */ + EXPECT_OK(cleanup_io_data(&iov, iov_size, &blob)); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + return 0; +} + +int main(int argc, char **argv) +{ + /* Ignore SIGPIPE */ + signal(SIGPIPE, SIG_IGN); + + BEGIN_TEST(); + + for (int use_tls13 = 0; use_tls13 < 2; use_tls13++) { + for (int use_iovec = 0; use_iovec < 2; use_iovec++) { + for (int use_throughput = 0; use_throughput < 2; use_throughput++) { + test_send(use_tls13, use_iovec, use_throughput); + } + } + } + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_self_talk_npn_test.c b/tests/unit/s2n_self_talk_npn_test.c new file mode 100644 index 00000000000..1234b68a868 --- /dev/null +++ b/tests/unit/s2n_self_talk_npn_test.c @@ -0,0 +1,170 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_client_hello.c" + +/* The server will always prefer to negotiate an application protocol with + * the ALPN extension. This callback wipes the ALPN extension from the client + * hello and forces the server to negotiate a protocol using the NPN extension + * instead. */ +static int s2n_wipe_alpn_ext(struct s2n_connection *conn, void *ctx) +{ + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(conn); + POSIX_ENSURE_REF(client_hello); + s2n_parsed_extension *parsed_extension = NULL; + POSIX_GUARD(s2n_client_hello_get_parsed_extension(S2N_EXTENSION_ALPN, &client_hello->extensions, &parsed_extension)); + POSIX_ENSURE_REF(parsed_extension); + POSIX_GUARD(s2n_blob_zero(&parsed_extension->extension)); + + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + const char *protocols[] = { "http/1.1", "spdy/1", "spdy/2" }; + const uint8_t protocols_count = s2n_array_len(protocols); + + /* Set up connections */ + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + + /* Set up config */ + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + struct s2n_cert_chain_and_key *chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_protocol_preferences(config, protocols, protocols_count)); + config->npn_supported = true; + + /* Set up config that wipes ALPN extension */ + DEFER_CLEANUP(struct s2n_config *npn_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(npn_config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(npn_config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(npn_config, "default")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(npn_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_protocol_preferences(npn_config, protocols, protocols_count)); + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(npn_config, s2n_wipe_alpn_ext, NULL)); + npn_config->npn_supported = true; + + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + /* Client and server both support NPN. ALPN is negotiated since it is also + * supported and the server prefers ALPN. */ + { + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + /* Create nonblocking pipes */ + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Server prefers ALPN over NPN */ + EXPECT_FALSE(IS_NPN_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_NPN_HANDSHAKE(client_conn)); + + /* ALPN has negotiated a protocol */ + EXPECT_NOT_NULL(s2n_get_application_protocol(client_conn)); + EXPECT_BYTEARRAY_EQUAL(s2n_get_application_protocol(client_conn), protocols[0], strlen(protocols[0])); + EXPECT_NOT_NULL(s2n_get_application_protocol(server_conn)); + EXPECT_BYTEARRAY_EQUAL(s2n_get_application_protocol(server_conn), protocols[0], strlen(protocols[0])); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + }; + + /* Client and server both support NPN. Wipe ALPN with the Client Hello callback so only NPN is received. + * NPN is negotiated and not ALPN. */ + { + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, npn_config)); + + /* Create nonblocking pipes */ + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_TRUE(IS_NPN_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_NPN_HANDSHAKE(client_conn)); + + /* NPN has negotiated a protocol */ + EXPECT_NOT_NULL(s2n_get_application_protocol(client_conn)); + EXPECT_BYTEARRAY_EQUAL(s2n_get_application_protocol(client_conn), protocols[0], strlen(protocols[0])); + EXPECT_NOT_NULL(s2n_get_application_protocol(server_conn)); + EXPECT_BYTEARRAY_EQUAL(s2n_get_application_protocol(server_conn), protocols[0], strlen(protocols[0])); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + }; + + /* Client and server both support NPN, however, they have no protocols in common. + * Connection negotiates client's top protocol. */ + { + /* Config with different protocols */ + const char *server_protocols[] = { "h2", "h3" }; + const uint8_t server_protocols_count = s2n_array_len(server_protocols); + DEFER_CLEANUP(struct s2n_config *different_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(different_config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(different_config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(different_config, "default")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(different_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_protocol_preferences(different_config, server_protocols, server_protocols_count)); + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(different_config, s2n_wipe_alpn_ext, NULL)); + different_config->npn_supported = true; + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, different_config)); + + /* Create nonblocking pipes */ + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + EXPECT_TRUE(IS_NPN_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_NPN_HANDSHAKE(client_conn)); + + /* Client-preference protocol is chosen since server and client have no mutually supported protocols. */ + EXPECT_NOT_NULL(s2n_get_application_protocol(client_conn)); + EXPECT_BYTEARRAY_EQUAL(s2n_get_application_protocol(client_conn), protocols[0], strlen(protocols[0])); + EXPECT_NOT_NULL(s2n_get_application_protocol(server_conn)); + EXPECT_BYTEARRAY_EQUAL(s2n_get_application_protocol(server_conn), protocols[0], strlen(protocols[0])); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + }; + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + END_TEST(); +} diff --git a/tests/unit/s2n_self_talk_offload_signing_test.c b/tests/unit/s2n_self_talk_offload_signing_test.c new file mode 100644 index 00000000000..78dcf2dc04d --- /dev/null +++ b/tests/unit/s2n_self_talk_offload_signing_test.c @@ -0,0 +1,273 @@ +/* + * 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 "crypto/s2n_rsa_signing.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +#define S2N_TEST_CERT_MEM 5000 + +int s2n_ecdsa_sign_digest(const struct s2n_pkey *priv, struct s2n_blob *digest, struct s2n_blob *signature); +int s2n_rsa_pkcs1v15_sign_digest(const struct s2n_pkey *priv, s2n_hash_algorithm hash_alg, + struct s2n_blob *digest, struct s2n_blob *signature); +int s2n_rsa_pss_sign_digest(const struct s2n_pkey *priv, s2n_hash_algorithm hash_alg, + struct s2n_blob *digest_in, struct s2n_blob *signature_out); + +struct s2n_async_pkey_op *pkey_op = NULL; +struct s2n_connection *pkey_op_conn = NULL; +static int s2n_test_async_pkey_cb(struct s2n_connection *conn, struct s2n_async_pkey_op *op) +{ + pkey_op = op; + pkey_op_conn = conn; + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_async_pkey_sign(struct s2n_cert_chain_and_key *complete_chain) +{ + RESULT_ENSURE_REF(pkey_op); + RESULT_ENSURE_REF(pkey_op_conn); + RESULT_ENSURE_REF(complete_chain); + + /* Get input */ + uint32_t input_len = 0; + DEFER_CLEANUP(struct s2n_blob input = { 0 }, s2n_free); + RESULT_GUARD_POSIX(s2n_async_pkey_op_get_input_size(pkey_op, &input_len)); + RESULT_GUARD_POSIX(s2n_realloc(&input, input_len)); + RESULT_GUARD_POSIX(s2n_async_pkey_op_get_input(pkey_op, input.data, input.size)); + + /* Setup output */ + uint32_t output_len = 0; + DEFER_CLEANUP(struct s2n_blob output = { 0 }, s2n_free); + RESULT_GUARD(s2n_pkey_size(complete_chain->private_key, &output_len)); + RESULT_GUARD_POSIX(s2n_realloc(&output, output_len)); + + /* Get signature algorithm */ + s2n_tls_signature_algorithm sig_alg = 0; + const struct s2n_signature_scheme *sig_scheme = NULL; + if (pkey_op_conn->mode == S2N_CLIENT) { + RESULT_GUARD_POSIX(s2n_connection_get_selected_client_cert_signature_algorithm(pkey_op_conn, &sig_alg)); + sig_scheme = pkey_op_conn->handshake_params.client_cert_sig_scheme; + } else { + RESULT_GUARD_POSIX(s2n_connection_get_selected_signature_algorithm(pkey_op_conn, &sig_alg)); + sig_scheme = pkey_op_conn->handshake_params.server_cert_sig_scheme; + } + + /* These are our "external" / "offloaded" operations. + * Customer use cases will call into a separate library / API, like PCKS11. + * But for this test we're just going to continue using our own methods. + */ + s2n_async_pkey_op_type op_type = 0; + RESULT_GUARD_POSIX(s2n_async_pkey_op_get_op_type(pkey_op, &op_type)); + if (op_type == S2N_ASYNC_DECRYPT) { + output.size = S2N_TLS_SECRET_LEN; + RESULT_GUARD_POSIX(s2n_pkey_decrypt(complete_chain->private_key, &input, &output)); + } else if (sig_alg == S2N_TLS_SIGNATURE_ECDSA) { + RESULT_GUARD_POSIX(s2n_ecdsa_sign_digest(complete_chain->private_key, &input, &output)); + } else if (sig_alg == S2N_TLS_SIGNATURE_RSA) { + RESULT_GUARD_POSIX(s2n_rsa_pkcs1v15_sign_digest( + complete_chain->private_key, sig_scheme->hash_alg, &input, &output)); + } else if (sig_alg == S2N_TLS_SIGNATURE_RSA_PSS_RSAE) { + RESULT_GUARD_POSIX(s2n_rsa_pss_sign_digest( + complete_chain->private_key, sig_scheme->hash_alg, &input, &output)); + } else { + RESULT_BAIL(S2N_ERR_UNIMPLEMENTED); + } + + /* Complete async_op */ + RESULT_GUARD_POSIX(s2n_async_pkey_op_set_output(pkey_op, output.data, output.size)); + RESULT_GUARD_POSIX(s2n_async_pkey_op_apply(pkey_op, pkey_op_conn)); + RESULT_GUARD_POSIX(s2n_async_pkey_op_free(pkey_op)); + pkey_op = NULL; + pkey_op_conn = NULL; + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_do_test_handshake(struct s2n_config *config, struct s2n_cert_chain_and_key *complete_chain, + uint8_t expected_protocol_version, uint32_t expected_handshake_type) +{ + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + RESULT_ENSURE_REF(client_conn); + RESULT_GUARD_POSIX(s2n_connection_set_config(client_conn, config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + RESULT_ENSURE_REF(server_conn); + RESULT_GUARD_POSIX(s2n_connection_set_config(server_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + RESULT_GUARD_POSIX(s2n_io_pair_init_non_blocking(&io_pair)); + RESULT_GUARD_POSIX(s2n_connection_set_io_pair(client_conn, &io_pair)); + RESULT_GUARD_POSIX(s2n_connection_set_io_pair(server_conn, &io_pair)); + + while (s2n_negotiate_test_server_and_client(server_conn, client_conn) != S2N_SUCCESS) { + EXPECT_EQUAL(s2n_errno, S2N_ERR_ASYNC_BLOCKED); + RESULT_GUARD(s2n_async_pkey_sign(complete_chain)); + } + + RESULT_ENSURE_EQ(client_conn->actual_protocol_version, expected_protocol_version); + RESULT_ENSURE_EQ(server_conn->actual_protocol_version, expected_protocol_version); + RESULT_ENSURE_EQ(client_conn->handshake.handshake_type, expected_handshake_type); + RESULT_ENSURE_EQ(server_conn->handshake.handshake_type, expected_handshake_type); + + RESULT_GUARD_POSIX(s2n_connection_free(server_conn)); + RESULT_GUARD_POSIX(s2n_connection_free(client_conn)); + RESULT_GUARD_POSIX(s2n_io_pair_close(&io_pair)); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + uint32_t pem_len = 0; + uint8_t pem[S2N_TEST_CERT_MEM] = { 0 }; + + const char *tls12_policy = "ELBSecurityPolicy-2016-08"; + const char *tls13_policy = "default_tls13"; + + /* Some TLS1.2 cipher suites use RSA for key exchange. + * Doing so requires generating a random key and encrypting it with RSA, + * which uses the private RSA key for a S2N_ASYNC_DECRYPT operation. + */ + const char *tls12_rsa_kex_policy = "test_all_rsa_kex"; + + uint32_t basic_handshake = NEGOTIATED | FULL_HANDSHAKE; + uint32_t tls_13_handshake = (basic_handshake | MIDDLEBOX_COMPAT); + uint32_t tls_12_handshake = (basic_handshake | TLS12_PERFECT_FORWARD_SECRECY); + uint32_t expected_handshake_with_tls13_policy = s2n_is_tls13_fully_supported() ? tls_13_handshake : tls_12_handshake; + + /* Create cert chains with both a public and private key. + * We need these to do the actual signing / decrypting once our callback triggers, + * but they are never passed to the connection or used in the handshake directly. + */ + + struct s2n_cert_chain_and_key *ecdsa_complete_chain = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_complete_chain, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + struct s2n_cert_chain_and_key *rsa_complete_chain = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&rsa_complete_chain, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* Create cert chains with only public certificates. + * These are passed to the connections for use in the handshake. + */ + + struct s2n_cert_chain_and_key *ecdsa_cert_only_chain = s2n_cert_chain_and_key_new(); + EXPECT_NOT_NULL(ecdsa_cert_only_chain); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, pem, &pem_len, sizeof(pem))); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_public_pem_bytes(ecdsa_cert_only_chain, pem, pem_len)); + + struct s2n_cert_chain_and_key *rsa_cert_only_chain = s2n_cert_chain_and_key_new(); + EXPECT_NOT_NULL(rsa_cert_only_chain); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_DEFAULT_TEST_CERT_CHAIN, pem, &pem_len, sizeof(pem))); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_public_pem_bytes(rsa_cert_only_chain, pem, pem_len)); + + /* ECDSA */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_cert_only_chain)); + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(config, s2n_test_async_pkey_cb)); + + /* Basic handshake. Only the server signs. */ + { + /* Test: TLS1.2 + ECDSA */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, tls12_policy)); + EXPECT_OK(s2n_do_test_handshake(config, ecdsa_complete_chain, + S2N_TLS12, basic_handshake | TLS12_PERFECT_FORWARD_SECRECY)); + + /* Test: TLS1.3 + ECDSA */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, tls13_policy)); + EXPECT_OK(s2n_do_test_handshake(config, ecdsa_complete_chain, + s2n_get_highest_fully_supported_tls_version(), expected_handshake_with_tls13_policy)); + }; + + /* Handshake with mutual auth. Both the client and server sign. */ + { + EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_REQUIRED)); + + /* Test: TLS1.2 + ECDSA + client auth */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, tls12_policy)); + EXPECT_OK(s2n_do_test_handshake(config, ecdsa_complete_chain, + S2N_TLS12, basic_handshake | CLIENT_AUTH | TLS12_PERFECT_FORWARD_SECRECY)); + + /* Test: TLS1.3 + ECDSA + client auth */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, tls13_policy)); + EXPECT_OK(s2n_do_test_handshake(config, ecdsa_complete_chain, + s2n_get_highest_fully_supported_tls_version(), expected_handshake_with_tls13_policy | CLIENT_AUTH)); + }; + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* RSA */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, rsa_cert_only_chain)); + EXPECT_SUCCESS(s2n_config_set_async_pkey_callback(config, s2n_test_async_pkey_cb)); + + /* Basic handshake. Only the server signs. */ + { + /* Test: TLS1.2 + RSA kex */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, tls12_rsa_kex_policy)); + EXPECT_OK(s2n_do_test_handshake(config, rsa_complete_chain, + S2N_TLS12, basic_handshake)); + + /* Test: TLS1.2 + RSA */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, tls12_policy)); + EXPECT_OK(s2n_do_test_handshake(config, rsa_complete_chain, + S2N_TLS12, basic_handshake | TLS12_PERFECT_FORWARD_SECRECY)); + + /* Test: TLS1.3 + RSA */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, tls13_policy)); + EXPECT_OK(s2n_do_test_handshake(config, rsa_complete_chain, + s2n_get_highest_fully_supported_tls_version(), expected_handshake_with_tls13_policy)); + }; + + /* Handshake with mutual auth. Both the client and server sign. */ + { + EXPECT_SUCCESS(s2n_config_set_client_auth_type(config, S2N_CERT_AUTH_REQUIRED)); + + /* Test: TLS1.2 + RSA kex + client auth */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, tls12_rsa_kex_policy)); + EXPECT_OK(s2n_do_test_handshake(config, rsa_complete_chain, + S2N_TLS12, basic_handshake | CLIENT_AUTH)); + + /* Test: TLS1.2 + RSA + client auth */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, tls12_policy)); + EXPECT_OK(s2n_do_test_handshake(config, rsa_complete_chain, + S2N_TLS12, basic_handshake | CLIENT_AUTH | TLS12_PERFECT_FORWARD_SECRECY)); + + /* Test: TLS1.3 + RSA + client auth */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, tls13_policy)); + EXPECT_OK(s2n_do_test_handshake(config, rsa_complete_chain, + s2n_get_highest_fully_supported_tls_version(), expected_handshake_with_tls13_policy | CLIENT_AUTH)); + }; + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_complete_chain)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert_only_chain)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(rsa_complete_chain)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(rsa_cert_only_chain)); + + END_TEST(); +} diff --git a/tests/unit/s2n_self_talk_psk_test.c b/tests/unit/s2n_self_talk_psk_test.c new file mode 100644 index 00000000000..55d261c5263 --- /dev/null +++ b/tests/unit/s2n_self_talk_psk_test.c @@ -0,0 +1,449 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" + +#define TEST_SHARED_PSK_WIRE_INDEX_1 1 +#define TEST_SHARED_PSK_WIRE_INDEX_2 2 +#define TEST_PSK_HMAC S2N_PSK_HMAC_SHA256 + +#define ARE_FULL_HANDSHAKES(client, server) \ + (IS_FULL_HANDSHAKE(client) && IS_FULL_HANDSHAKE(server)) + +#define IS_CLIENT_AUTH(client, server) \ + (IS_CLIENT_AUTH_HANDSHAKE(client) && IS_CLIENT_AUTH_HANDSHAKE(server)) + +#define IS_HELLO_RETRY(client, server) \ + (((client->handshake.handshake_type) & HELLO_RETRY_REQUEST) \ + && ((server->handshake.handshake_type) & HELLO_RETRY_REQUEST)) + +#define s2n_set_io_pair_both_connections(client, server, io_pair) \ + EXPECT_SUCCESS(s2n_connection_set_io_pair(client, &io_pair)); \ + EXPECT_SUCCESS(s2n_connection_set_io_pair(server, &io_pair)); + +#define s2n_set_config_both_connections(client, server, config) \ + EXPECT_SUCCESS(s2n_connection_set_config(client, config)); \ + EXPECT_SUCCESS(s2n_connection_set_config(server, config)); + +uint8_t test_shared_identity[] = "test shared identity"; +uint8_t test_shared_secret[] = "test shared secret"; +uint8_t test_shared_identity_2[] = "test shared identity 2"; +uint8_t test_shared_secret_2[] = "test shared secret 2"; +uint8_t test_other_client_data[] = "test other client data"; +uint8_t test_other_server_data[] = "test other server data"; + +static s2n_result setup_psk(struct s2n_connection *conn, const uint8_t *test_identity_data, uint16_t test_identity_size, + const uint8_t *test_secret_data, uint16_t test_secret_size, s2n_psk_hmac test_hmac) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(test_identity_data); + RESULT_ENSURE_REF(test_secret_data); + + struct s2n_psk *psk = s2n_external_psk_new(); + RESULT_GUARD_POSIX(s2n_psk_set_identity(psk, test_identity_data, test_identity_size)); + RESULT_GUARD_POSIX(s2n_psk_set_secret(psk, test_secret_data, test_secret_size)); + RESULT_GUARD_POSIX(s2n_psk_set_hmac(psk, test_hmac)); + RESULT_GUARD_POSIX(s2n_connection_append_psk(conn, psk)); + RESULT_GUARD_POSIX(s2n_psk_free(&psk)); + EXPECT_NULL(psk); + + return S2N_RESULT_OK; +} + +static s2n_result setup_client_psks(struct s2n_connection *client_conn) +{ + RESULT_ENSURE_REF(client_conn); + + /* Setup other client PSK */ + EXPECT_OK(setup_psk(client_conn, test_other_client_data, sizeof(test_other_client_data), test_other_client_data, + sizeof(test_other_client_data), TEST_PSK_HMAC)); + /* Setup first shared PSK for client */ + EXPECT_OK(setup_psk(client_conn, test_shared_identity, sizeof(test_shared_identity), test_shared_secret, + sizeof(test_shared_secret), TEST_PSK_HMAC)); + /* Setup second shared PSK for client */ + EXPECT_OK(setup_psk(client_conn, test_shared_identity_2, sizeof(test_shared_identity_2), test_shared_secret_2, + sizeof(test_shared_secret_2), TEST_PSK_HMAC)); + + return S2N_RESULT_OK; +} + +static s2n_result setup_server_psks(struct s2n_connection *server_conn) +{ + RESULT_ENSURE_REF(server_conn); + + /* Setup first shared PSK for server */ + EXPECT_OK(setup_psk(server_conn, test_shared_identity, sizeof(test_shared_identity), test_shared_secret, + sizeof(test_shared_secret), TEST_PSK_HMAC)); + /* Setup other server PSK */ + EXPECT_OK(setup_psk(server_conn, test_other_server_data, sizeof(test_other_server_data), test_other_server_data, + sizeof(test_other_server_data), S2N_PSK_HMAC_SHA384)); + /* Setup second shared PSK for server */ + EXPECT_OK(setup_psk(server_conn, test_shared_identity_2, sizeof(test_shared_identity_2), test_shared_secret_2, + sizeof(test_shared_secret_2), TEST_PSK_HMAC)); + + return S2N_RESULT_OK; +} + +static s2n_result setup_psks_with_no_match(struct s2n_connection *client_conn, struct s2n_connection *server_conn) +{ + RESULT_ENSURE_REF(client_conn); + RESULT_ENSURE_REF(server_conn); + + /* Setup other client PSK */ + EXPECT_OK(setup_psk(client_conn, test_other_client_data, sizeof(test_other_client_data), test_other_client_data, + sizeof(test_other_client_data), S2N_PSK_HMAC_SHA256)); + /* Setup other server PSK */ + EXPECT_OK(setup_psk(server_conn, test_other_server_data, sizeof(test_other_server_data), test_other_server_data, + sizeof(test_other_server_data), S2N_PSK_HMAC_SHA384)); + + return S2N_RESULT_OK; +} + +static s2n_result validate_chosen_psk(struct s2n_connection *server_conn, uint8_t *psk_identity_data, + size_t psk_identity_size, size_t chosen_index) +{ + RESULT_ENSURE_REF(server_conn); + RESULT_ENSURE_REF(psk_identity_data); + RESULT_ENSURE_REF(server_conn->psk_params.chosen_psk); + + RESULT_ENSURE_EQ(server_conn->psk_params.chosen_psk->identity.size, psk_identity_size); + RESULT_ENSURE_EQ(memcmp(server_conn->psk_params.chosen_psk->identity.data, psk_identity_data, psk_identity_size), 0); + RESULT_ENSURE_EQ(server_conn->psk_params.chosen_psk_wire_index, chosen_index); + + return S2N_RESULT_OK; +} + +static int s2n_test_select_psk_identity_callback(struct s2n_connection *conn, void *context, + struct s2n_offered_psk_list *psk_identity_list) +{ + struct s2n_offered_psk offered_psk = { 0 }; + uint16_t idx = 0; + while (s2n_offered_psk_list_has_next(psk_identity_list)) { + POSIX_GUARD(s2n_offered_psk_list_next(psk_identity_list, &offered_psk)); + if (idx == TEST_SHARED_PSK_WIRE_INDEX_2) { + POSIX_GUARD(s2n_offered_psk_list_choose_psk(psk_identity_list, &offered_psk)); + break; + } + idx++; + }; + return S2N_SUCCESS; +} + +static int s2n_client_hello_no_op_cb(struct s2n_connection *conn, void *ctx) +{ + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + /* Setup connections */ + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + /* Setup config */ + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + + /* Setup config with certificates set */ + struct s2n_config *config_with_certs = NULL; + EXPECT_NOT_NULL(config_with_certs = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config_with_certs, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config_with_certs)); + struct s2n_cert_chain_and_key *chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, + S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config_with_certs, chain_and_key)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + /* Basic PSK without callback function, without certificates and client auth not set */ + { + s2n_set_config_both_connections(client_conn, server_conn, config); + s2n_set_io_pair_both_connections(client_conn, server_conn, io_pair); + + /* Setup PSKs */ + EXPECT_OK(setup_client_psks(client_conn)); + EXPECT_OK(setup_server_psks(server_conn)); + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Validate handshake type */ + EXPECT_FALSE(ARE_FULL_HANDSHAKES(client_conn, server_conn)); + + /* Validate chosen PSK */ + EXPECT_OK(validate_chosen_psk(server_conn, test_shared_identity, sizeof(test_shared_identity), + TEST_SHARED_PSK_WIRE_INDEX_1)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + }; + + /* PSK with callback function */ + { + s2n_set_config_both_connections(client_conn, server_conn, config); + s2n_set_io_pair_both_connections(client_conn, server_conn, io_pair); + + /* Setup PSKs */ + EXPECT_OK(setup_client_psks(client_conn)); + EXPECT_OK(setup_server_psks(server_conn)); + + /* Set the customer callback to select PSK identity */ + EXPECT_SUCCESS(s2n_config_set_psk_selection_callback(server_conn->config, s2n_test_select_psk_identity_callback, NULL)); + EXPECT_EQUAL(server_conn->config->psk_selection_cb, s2n_test_select_psk_identity_callback); + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Validate handshake type */ + EXPECT_FALSE(ARE_FULL_HANDSHAKES(client_conn, server_conn)); + + /* Validate chosen PSK */ + EXPECT_OK(validate_chosen_psk(server_conn, test_shared_identity_2, sizeof(test_shared_identity_2), + TEST_SHARED_PSK_WIRE_INDEX_2)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + server_conn->config->psk_selection_cb = NULL; + + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + }; + + /* PSK with certificates set */ + { + /* Setup certs */ + s2n_set_config_both_connections(client_conn, server_conn, config_with_certs); + s2n_set_io_pair_both_connections(client_conn, server_conn, io_pair); + + /* Setup PSKs */ + EXPECT_OK(setup_client_psks(client_conn)); + EXPECT_OK(setup_server_psks(server_conn)); + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Validate handshake type */ + EXPECT_FALSE(ARE_FULL_HANDSHAKES(client_conn, server_conn)); + + /* Validate chosen PSK */ + EXPECT_OK(validate_chosen_psk(server_conn, test_shared_identity, sizeof(test_shared_identity), + TEST_SHARED_PSK_WIRE_INDEX_1)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + }; + + /* PSK with certificates set and client auth set as required */ + { + /* Setup certs */ + s2n_set_config_both_connections(client_conn, server_conn, config_with_certs); + s2n_set_io_pair_both_connections(client_conn, server_conn, io_pair); + + /* Explicitly set client_auth as required */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + + /* Setup PSKs */ + EXPECT_OK(setup_client_psks(client_conn)); + EXPECT_OK(setup_server_psks(server_conn)); + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Validate handshake type */ + EXPECT_FALSE(IS_CLIENT_AUTH(client_conn, server_conn)); + EXPECT_FALSE(ARE_FULL_HANDSHAKES(client_conn, server_conn)); + + /* Validate chosen PSK */ + EXPECT_OK(validate_chosen_psk(server_conn, test_shared_identity, sizeof(test_shared_identity), + TEST_SHARED_PSK_WIRE_INDEX_1)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + }; + + /* Basic PSK with Client Hello async callback set */ + { + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, s2n_client_hello_no_op_cb, NULL)); + EXPECT_SUCCESS(s2n_config_set_client_hello_cb_mode(config, S2N_CLIENT_HELLO_CB_NONBLOCKING)); + + s2n_set_config_both_connections(client_conn, server_conn, config); + s2n_set_io_pair_both_connections(client_conn, server_conn, io_pair); + + /* Setup PSKs */ + EXPECT_OK(setup_client_psks(client_conn)); + EXPECT_OK(setup_server_psks(server_conn)); + + /* Handshake negotiation is successful when the Client Hello callback is marked as done */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_ASYNC_BLOCKED); + EXPECT_SUCCESS(s2n_client_hello_cb_done(server_conn)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Validate handshake type */ + EXPECT_FALSE(ARE_FULL_HANDSHAKES(client_conn, server_conn)); + + /* Validate chosen PSK */ + EXPECT_OK(validate_chosen_psk(server_conn, test_shared_identity, sizeof(test_shared_identity), + TEST_SHARED_PSK_WIRE_INDEX_1)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, NULL, NULL)); + }; + + /* HRR with PSK and Client Hello async callback set */ + { + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config_with_certs, s2n_client_hello_no_op_cb, NULL)); + EXPECT_SUCCESS(s2n_config_set_client_hello_cb_mode(config_with_certs, S2N_CLIENT_HELLO_CB_NONBLOCKING)); + + /* Setup certs */ + s2n_set_config_both_connections(client_conn, server_conn, config_with_certs); + s2n_set_io_pair_both_connections(client_conn, server_conn, io_pair); + + /* Force the HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Setup PSKs */ + EXPECT_OK(setup_client_psks(client_conn)); + EXPECT_OK(setup_server_psks(server_conn)); + + /* Handshake negotiation is successful when the Client Hello callback is marked as done */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_ASYNC_BLOCKED); + EXPECT_SUCCESS(s2n_client_hello_cb_done(server_conn)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Validate handshake type */ + EXPECT_TRUE(IS_HELLO_RETRY(client_conn, server_conn)); + EXPECT_FALSE(ARE_FULL_HANDSHAKES(client_conn, server_conn)); + + /* Validate chosen PSK */ + EXPECT_OK(validate_chosen_psk(server_conn, test_shared_identity, sizeof(test_shared_identity), + TEST_SHARED_PSK_WIRE_INDEX_1)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config_with_certs, NULL, NULL)); + }; + + /* Fallback to full handshake if no PSK is chosen and certificates are set */ + { + /* Setup certs */ + s2n_set_config_both_connections(client_conn, server_conn, config_with_certs); + s2n_set_io_pair_both_connections(client_conn, server_conn, io_pair); + + /* Setup no matching PSKs */ + EXPECT_OK(setup_psks_with_no_match(client_conn, server_conn)); + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Validate handshake type */ + EXPECT_TRUE(ARE_FULL_HANDSHAKES(client_conn, server_conn)); + + /* Validate that a PSK is not chosen */ + EXPECT_NULL(server_conn->psk_params.chosen_psk); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + }; + + /* Fallback to full handshake uses client auth if requested */ + { + /* Setup certs */ + s2n_set_config_both_connections(client_conn, server_conn, config_with_certs); + s2n_set_io_pair_both_connections(client_conn, server_conn, io_pair); + + /* Explicitly set client_auth as required */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + + /* Setup no matching PSKs */ + EXPECT_OK(setup_psks_with_no_match(client_conn, server_conn)); + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Validate handshake type */ + EXPECT_TRUE(IS_CLIENT_AUTH(client_conn, server_conn)); + EXPECT_TRUE(ARE_FULL_HANDSHAKES(client_conn, server_conn)); + + /* Validate that a PSK is not chosen */ + EXPECT_NULL(server_conn->psk_params.chosen_psk); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + }; + + /* Error Case: Fallback to full handshake if no PSK is chosen and certificates are not set */ + { + s2n_set_config_both_connections(client_conn, server_conn, config); + s2n_set_io_pair_both_connections(client_conn, server_conn, io_pair); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + /* Setup no matching PSKs */ + EXPECT_OK(setup_psks_with_no_match(client_conn, server_conn)); + + /* Negotiate handshake */ + EXPECT_FAILURE(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Validate that a PSK is not chosen */ + EXPECT_NULL(server_conn->psk_params.chosen_psk); + + /* Validate handshake type is not FULL_HANDSHAKE */ + EXPECT_FALSE(ARE_FULL_HANDSHAKES(client_conn, server_conn)); + + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + }; + + /* Clean-up */ + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_config_free(config_with_certs)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + END_TEST(); +} diff --git a/tests/unit/s2n_self_talk_quic_support_test.c b/tests/unit/s2n_self_talk_quic_support_test.c new file mode 100644 index 00000000000..7099cadc7af --- /dev/null +++ b/tests/unit/s2n_self_talk_quic_support_test.c @@ -0,0 +1,131 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_quic_support.h" + +#define S2N_MODE_COUNT 2 +#define S2N_SECRET_TYPE_COUNT 6 + +static const uint8_t CLIENT_TRANSPORT_PARAMS[] = "client transport params"; +static const uint8_t SERVER_TRANSPORT_PARAMS[] = "server transport params"; + +static int s2n_test_secret_handler(void *context, struct s2n_connection *conn, + s2n_secret_type_t secret_type, + uint8_t *secret, uint8_t secret_size) +{ + /* Verify context passed through correctly */ + struct s2n_blob(*secrets)[S2N_SECRET_TYPE_COUNT] = context; + EXPECT_NOT_NULL(secrets); + + /* Save secret for later */ + EXPECT_SUCCESS(s2n_alloc(&secrets[conn->mode][secret_type], secret_size)); + EXPECT_MEMCPY_SUCCESS(secrets[conn->mode][secret_type].data, secret, secret_size); + + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + const uint8_t *transport_params = NULL; + uint16_t transport_params_len = 0; + + /* Setup connections */ + struct s2n_connection *client_conn, *server_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + /* Setup config */ + struct s2n_cert_chain_and_key *chain_and_key; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_enable_quic(config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + /* Setup quic transport parameters */ + EXPECT_SUCCESS(s2n_connection_set_quic_transport_parameters(client_conn, + CLIENT_TRANSPORT_PARAMS, sizeof(CLIENT_TRANSPORT_PARAMS))); + EXPECT_SUCCESS(s2n_connection_set_quic_transport_parameters(server_conn, + SERVER_TRANSPORT_PARAMS, sizeof(SERVER_TRANSPORT_PARAMS))); + + /* Set secret handler */ + struct s2n_blob secrets[S2N_MODE_COUNT][S2N_SECRET_TYPE_COUNT] = { 0 }; + EXPECT_SUCCESS(s2n_connection_set_secret_callback(client_conn, s2n_test_secret_handler, secrets)); + EXPECT_SUCCESS(s2n_connection_set_secret_callback(server_conn, s2n_test_secret_handler, secrets)); + + /* Do handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify TLS1.3 */ + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + + /* Verify server quic_transport_parameters on client */ + EXPECT_SUCCESS(s2n_connection_get_quic_transport_parameters(client_conn, + &transport_params, &transport_params_len)); + EXPECT_EQUAL(transport_params_len, sizeof(SERVER_TRANSPORT_PARAMS)); + EXPECT_BYTEARRAY_EQUAL(transport_params, SERVER_TRANSPORT_PARAMS, transport_params_len); + + /* Verify client quic_transport_parameters on server */ + EXPECT_SUCCESS(s2n_connection_get_quic_transport_parameters(server_conn, + &transport_params, &transport_params_len)); + EXPECT_EQUAL(transport_params_len, sizeof(CLIENT_TRANSPORT_PARAMS)); + EXPECT_BYTEARRAY_EQUAL(transport_params, CLIENT_TRANSPORT_PARAMS, transport_params_len); + + /* Verify legacy_session_id not set (QUIC does not use middlebox compat mode) */ + EXPECT_EQUAL(client_conn->session_id_len, 0); + EXPECT_EQUAL(server_conn->session_id_len, 0); + + /* Verify handshake not MIDDLEBOX_COMPAT (QUIC does not use middlebox compat mode) */ + EXPECT_FALSE(IS_MIDDLEBOX_COMPAT_MODE(client_conn)); + EXPECT_FALSE(IS_MIDDLEBOX_COMPAT_MODE(server_conn)); + + /* Verify secret handler collected secrets */ + for (size_t i = 1; i < S2N_SECRET_TYPE_COUNT; i++) { + EXPECT_NOT_EQUAL(secrets[S2N_CLIENT][i].size, 0); + EXPECT_EQUAL(secrets[S2N_CLIENT][i].size, secrets[S2N_SERVER][i].size); + EXPECT_BYTEARRAY_EQUAL(secrets[S2N_CLIENT][i].data, secrets[S2N_SERVER][i].data, secrets[S2N_SERVER][i].size); + + EXPECT_SUCCESS(s2n_free(&secrets[S2N_CLIENT][i])); + EXPECT_SUCCESS(s2n_free(&secrets[S2N_SERVER][i])); + } + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(config)); + + END_TEST(); +} diff --git a/tests/unit/s2n_self_talk_session_id_test.c b/tests/unit/s2n_self_talk_session_id_test.c new file mode 100644 index 00000000000..df88b9e685b --- /dev/null +++ b/tests/unit/s2n_self_talk_session_id_test.c @@ -0,0 +1,667 @@ +/* + * 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 +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "utils/s2n_bitmap.h" + +#define MAX_KEY_LEN 32 +#define MAX_VAL_LEN 255 + +static const char SESSION_ID[] = "0123456789abcdef0123456789abcdef"; +static const char MSG[] = "Test"; + +struct session_cache_entry { + uint8_t key[MAX_KEY_LEN]; + uint8_t key_len; + uint8_t value[MAX_VAL_LEN]; + uint8_t value_len; + uint8_t lock; +}; + +struct session_cache_entry session_cache[256]; + +int cache_store_callback(struct s2n_connection *conn, void *ctx, uint64_t ttl, const void *key, uint64_t key_size, const void *value, uint64_t value_size) +{ + struct session_cache_entry *cache = ctx; + + if (key_size == 0 || key_size > MAX_KEY_LEN) { + return -1; + } + if (value_size == 0 || value_size > MAX_VAL_LEN) { + return -1; + } + + uint8_t idx = ((const uint8_t *) key)[0]; + + EXPECT_MEMCPY_SUCCESS(cache[idx].key, key, key_size); + EXPECT_MEMCPY_SUCCESS(cache[idx].value, value, value_size); + + cache[idx].key_len = key_size; + cache[idx].value_len = value_size; + + return 0; +} + +int cache_retrieve_callback(struct s2n_connection *conn, void *ctx, const void *key, uint64_t key_size, void *value, uint64_t *value_size) +{ + struct session_cache_entry *cache = ctx; + + if (key_size == 0 || key_size > MAX_KEY_LEN) { + return -1; + } + + uint8_t idx = ((const uint8_t *) key)[0]; + + if (cache[idx].lock) { + /* here we mock a remote connection/event blocking the handshake + * state machine, until lock is free + */ + cache[idx].lock = 0; + return S2N_CALLBACK_BLOCKED; + } + + if (cache[idx].key_len != key_size) { + return -1; + } + + if (memcmp(cache[idx].key, key, key_size)) { + return -1; + } + + if (*value_size < cache[idx].value_len) { + return -1; + } + + *value_size = cache[idx].value_len; + EXPECT_MEMCPY_SUCCESS(value, cache[idx].value, cache[idx].value_len); + + return 0; +} + +int cache_delete_callback(struct s2n_connection *conn, void *ctx, const void *key, uint64_t key_size) +{ + struct session_cache_entry *cache = ctx; + + if (key_size == 0 || key_size > MAX_KEY_LEN) { + return -1; + } + + uint8_t idx = ((const uint8_t *) key)[0]; + + if (cache[idx].key_len == 0) { + return 0; + } + + if (cache[idx].key_len != key_size) { + return -1; + } + + if (memcmp(cache[idx].key, key, key_size)) { + return -1; + } + + cache[idx].key_len = 0; + cache[idx].value_len = 0; + + return 0; +} + +/* init session cache lock field, which is used to mock a remote + * connection/event block + */ +static void initialize_cache() +{ + for (int i = 0; i < 256; i++) { + session_cache[i].lock = 1; + } +} + +void mock_client(struct s2n_test_io_pair *io_pair) +{ + size_t serialized_session_state_length = 0; + uint8_t serialized_session_state[256] = { 0 }; + + struct s2n_connection *conn; + struct s2n_config *config; + s2n_blocked_status blocked; + int result = 0; + + /* Give the server a chance to listen */ + sleep(1); + + /* Initial handshake */ + conn = s2n_connection_new(S2N_CLIENT); + config = s2n_config_new(); + s2n_config_disable_x509_verification(config); + s2n_connection_set_config(conn, config); + + s2n_connection_set_io_pair(conn, io_pair); + + /* Set the session id to ensure we're able to fallback to full handshake if session is not in server cache */ + EXPECT_MEMCPY_SUCCESS(conn->session_id, SESSION_ID, S2N_TLS_SESSION_ID_MAX_LEN); + conn->session_id_len = S2N_TLS_SESSION_ID_MAX_LEN; + + if (s2n_negotiate(conn, &blocked) != 0) { + result = 1; + } + + /* Make sure we did a full handshake */ + if (!IS_FULL_HANDSHAKE(conn)) { + result = 2; + } + + /* Save session state from the connection */ + memset(serialized_session_state, 0, sizeof(serialized_session_state)); + serialized_session_state_length = s2n_connection_get_session_length(conn); + if (serialized_session_state_length > sizeof(serialized_session_state)) { + result = 3; + } + + /* Send very low session buffer size and see that you can get an error */ + if (s2n_connection_get_session(conn, serialized_session_state, 1) == 0) { + result = 4; + } + + if (s2n_errno != S2N_ERR_SERIALIZED_SESSION_STATE_TOO_LONG) { + result = 5; + } + + if ((size_t) s2n_connection_get_session(conn, serialized_session_state, serialized_session_state_length) != serialized_session_state_length) { + result = 6; + } + + /* server would choose a session ID for client */ + if (memcmp(conn->session_id, SESSION_ID, S2N_TLS_SESSION_ID_MAX_LEN) == 0) { + result = 7; + } + + if (s2n_send(conn, MSG, sizeof(MSG), &blocked) != sizeof(MSG)) { + result = 8; + } + + int shutdown_rc = -1; + while (shutdown_rc != 0) { + shutdown_rc = s2n_shutdown(conn, &blocked); + } + + s2n_connection_free(conn); + + /* Give the server a chance to avoid sigpipe */ + sleep(1); + + /* Session resumption */ + conn = s2n_connection_new(S2N_CLIENT); + s2n_connection_set_io_pair(conn, io_pair); + + /* Set session state on client connection */ + if (s2n_connection_set_session(conn, serialized_session_state, serialized_session_state_length) < 0) { + result = 9; + } + + if (s2n_negotiate(conn, &blocked) != 0) { + result = 10; + } + + /* Make sure we did a abbreviated handshake */ + if (!IS_RESUMPTION_HANDSHAKE(conn)) { + result = 11; + } + + if (s2n_send(conn, MSG, sizeof(MSG), &blocked) != sizeof(MSG)) { + result = 12; + } + + shutdown_rc = -1; + while (shutdown_rc != 0) { + shutdown_rc = s2n_shutdown(conn, &blocked); + } + + s2n_connection_free(conn); + + /* Give the server a chance to avoid sigpipe */ + sleep(1); + + /* Session resumption with bad session state */ + conn = s2n_connection_new(S2N_CLIENT); + s2n_connection_set_io_pair(conn, io_pair); + + /* Change the format of the session state and check we cannot deserialize it */ + serialized_session_state[0] = 3; + if (s2n_connection_set_session(conn, serialized_session_state, serialized_session_state_length) == 0) { + result = 13; + } + + if (s2n_errno != S2N_ERR_INVALID_SERIALIZED_SESSION_STATE) { + result = 14; + } + + serialized_session_state[0] = 0; + /* Change the protocol version (36th byte) in session state */ + if (serialized_session_state_length < 36) { + result = 15; + } + + serialized_session_state[35] = 30; + if (s2n_connection_set_session(conn, serialized_session_state, serialized_session_state_length) < 0) { + result = 16; + } + + if (s2n_negotiate(conn, &blocked) == 0) { + result = 17; + } + + if (s2n_errno != S2N_ERR_BAD_MESSAGE) { + result = 18; + } + + s2n_connection_free(conn); + s2n_config_free(config); + + /* Give the server a chance to avoid sigpipe */ + sleep(1); + + s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); + exit(result); +} + +int main(int argc, char **argv) +{ + struct s2n_connection *conn; + struct s2n_config *config; + s2n_blocked_status blocked; + int status; + pid_t pid; + char *cert_chain_pem; + char *private_key_pem; + struct s2n_cert_chain_and_key *chain_and_key; + char buffer[256]; + int bytes_read; + int shutdown_rc = -1; + uint64_t now; + uint8_t session_id_from_server[MAX_KEY_LEN]; + uint8_t session_id_from_client[MAX_KEY_LEN]; + + /* aes keys. Used for session ticket/session data encryption. Taken from test vectors in https://tools.ietf.org/html/rfc5869 */ + uint8_t ticket_key_name[16] = "2018.07.26.15\0"; + uint8_t ticket_key[32] = { 0x19, 0xef, 0x24, 0xa3, 0x2c, 0x71, 0x7b, 0x16, 0x7f, 0x33, + 0xa9, 0x1d, 0x6f, 0x64, 0x8b, 0xdf, 0x96, 0x59, 0x67, 0x76, + 0xaf, 0xdb, 0x63, 0x77, 0xac, 0x43, 0x4c, 0x1c, 0x29, 0x3c, + 0xcb, 0x04 }; + + BEGIN_TEST(); + + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Write the fragmented hello message */ + mock_client(&io_pair); + } + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + /* Initial handshake */ + { + /* Initialize the cache so the client and server start off on the same page */ + initialize_cache(); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + EXPECT_SUCCESS(s2n_config_set_cache_store_callback(config, cache_store_callback, session_cache)); + EXPECT_SUCCESS(s2n_config_set_cache_retrieve_callback(config, cache_retrieve_callback, session_cache)); + EXPECT_SUCCESS(s2n_config_set_cache_delete_callback(config, cache_delete_callback, session_cache)); + + /* Although we disable session ticket, as long as session cache + * callbacks are binded, session ticket key storage would be initialized + */ + POSIX_GUARD(s2n_config_set_session_cache_onoff(config, 1)); + POSIX_GUARD(config->wall_clock(config->sys_clock_ctx, &now)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), ticket_key, sizeof(ticket_key), now / ONE_SEC_IN_NANOS)); + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + int r = s2n_negotiate(conn, &blocked); + /* first time it always blocks the handshake, as we mock a remote + * connection/event from the lock + */ + EXPECT_EQUAL(r, -1); + EXPECT_EQUAL(s2n_error_get_type(s2n_errno), S2N_ERR_T_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_INPUT); + EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); + + /* Make sure the get_session_id and get_session_id_length APIs are + * working as expected */ + EXPECT_EQUAL(s2n_connection_get_session_id_length(conn), MAX_KEY_LEN); + EXPECT_EQUAL(s2n_connection_get_session_id(conn, session_id_from_server, MAX_KEY_LEN), s2n_connection_get_session_id_length(conn)); + + /* Make sure we did a full TLS1.2 handshake */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(conn)); + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + + /* Ensure the message was delivered */ + EXPECT_SUCCESS(bytes_read = s2n_recv(conn, buffer, sizeof(buffer), &blocked)); + EXPECT_EQUAL(bytes_read, sizeof(MSG)); + EXPECT_EQUAL(memcmp(buffer, MSG, sizeof(MSG)), 0); + + /* Shutdown handshake */ + do { + shutdown_rc = s2n_shutdown(conn, &blocked); + EXPECT_TRUE(shutdown_rc == 0 || (errno == EAGAIN && blocked)); + } while (shutdown_rc != 0); + + /* Clean up */ + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Session resumption */ + { + initialize_cache(); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + int r = s2n_negotiate(conn, &blocked); + /* first time it always blocks the handshake, as we mock a remote + * connection/event from the lock + */ + EXPECT_EQUAL(r, -1); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_INPUT); + EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); + + /* Make sure the get_session_id and get_session_id_length APIs are + * working as expected */ + EXPECT_EQUAL(s2n_connection_get_session_id_length(conn), MAX_KEY_LEN); + EXPECT_EQUAL(s2n_connection_get_session_id(conn, session_id_from_client, MAX_KEY_LEN), s2n_connection_get_session_id_length(conn)); + EXPECT_EQUAL(0, memcmp(session_id_from_client, session_id_from_server, MAX_KEY_LEN)); + + /* Make sure we did a abbreviated handshake */ + EXPECT_TRUE(IS_RESUMPTION_HANDSHAKE(conn)); + + /* Ensure the message was delivered */ + memset(buffer, 0, sizeof(buffer)); + EXPECT_SUCCESS(bytes_read = s2n_recv(conn, buffer, sizeof(buffer), &blocked)); + EXPECT_EQUAL(bytes_read, sizeof(MSG)); + EXPECT_EQUAL(memcmp(buffer, MSG, sizeof(MSG)), 0); + + /* Shutdown handshake */ + do { + shutdown_rc = s2n_shutdown(conn, &blocked); + EXPECT_TRUE(shutdown_rc == 0 || (errno == EAGAIN && blocked)); + } while (shutdown_rc != 0); + + /* Clean up */ + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Session resumption with bad session state on client side */ + { + initialize_cache(); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + int r = s2n_negotiate(conn, &blocked); + EXPECT_EQUAL(r, -1); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_APPLICATION_INPUT); + /* Verify we failed to negotiate */ + EXPECT_FAILURE(s2n_negotiate(conn, &blocked)); + + /* Clean up */ + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Close the pipes */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Session caching with a server that does not support EMS */ + { + initialize_cache(); + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + s2n_config_disable_x509_verification(config); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate until server has read the Client Hello message but hasn't written the server hello message */ + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, SERVER_HELLO)); + + /* s2n servers by default support EMS. We turn it off by manually setting ems_negotiated to false + * and removing the EMS extension from our received extensions. */ + server_conn->ems_negotiated = false; + s2n_extension_type_id ems_ext_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_EMS, &ems_ext_id)); + S2N_CBIT_CLR(server_conn->extension_requests_received, ems_ext_id); + + /* Connection is successful and EMS is not negotiated */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_FALSE(server_conn->ems_negotiated); + EXPECT_FALSE(client_conn->ems_negotiated); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn)); + + size_t tls12_session_ticket_len = s2n_connection_get_session_length(client_conn); + uint8_t tls12_session_ticket[S2N_TLS12_SESSION_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_connection_get_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); + + /* Wipe connections and set up new handshake */ + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); + + /* Server will block the first time cache is accessed */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_ASYNC_BLOCKED); + + /* Resumed connection is successful and EMS is not negotiated */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(S2N_CBIT_TEST(server_conn->extension_requests_received, ems_ext_id), 0); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_FALSE(server_conn->ems_negotiated); + EXPECT_FALSE(client_conn->ems_negotiated); + EXPECT_FALSE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_FULL_HANDSHAKE(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /** + *= https://tools.ietf.org/rfc/rfc7627#section-5.3 + *= type=test + *# 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. + **/ + { + initialize_cache(); + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + s2n_config_disable_x509_verification(config); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Connection is successful and EMS is negotiated */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(server_conn->ems_negotiated); + EXPECT_TRUE(client_conn->ems_negotiated); + + size_t tls12_session_ticket_len = s2n_connection_get_session_length(client_conn); + uint8_t tls12_session_ticket[S2N_TLS12_SESSION_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_connection_get_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); + + /* Wipe connections and set up new handshake */ + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the client to not send the EMS extension */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); + client_conn->ems_negotiated = false; + + /* Server will block the first time cache is accessed */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_ASYNC_BLOCKED); + + /* Server did not receive the EMS extension from client */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_MISSING_EXTENSION); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /** + *= https://tools.ietf.org/rfc/rfc7627#section-5.3 + *= type=test + *# If the original session did not use the "extended_master_secret" + *# extension but the new ClientHello contains the extension, then the + *# server MUST NOT perform the abbreviated handshake. Instead, it + *# SHOULD continue with a full handshake (as described in + *# Section 5.2) to negotiate a new session. + **/ + { + initialize_cache(); + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + s2n_config_disable_x509_verification(config); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate until server has read the Client Hello message but hasn't written the Server Hello message */ + EXPECT_OK(s2n_negotiate_until_message(client_conn, &blocked, SERVER_HELLO)); + EXPECT_OK(s2n_negotiate_until_message(server_conn, &blocked, SERVER_HELLO)); + + /* s2n servers by default support EMS. We turn it off by manually setting ems_negotiated to false + * and removing the EMS extension from our received extensions. */ + server_conn->ems_negotiated = false; + s2n_extension_type_id ems_ext_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_EMS, &ems_ext_id)); + S2N_CBIT_CLR(server_conn->extension_requests_received, ems_ext_id); + + /* Connection is successful and EMS is not negotiated */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_FALSE(server_conn->ems_negotiated); + EXPECT_FALSE(client_conn->ems_negotiated); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn)); + + size_t tls12_session_ticket_len = s2n_connection_get_session_length(client_conn); + uint8_t tls12_session_ticket[S2N_TLS12_SESSION_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_connection_get_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); + + /* Wipe connections and set up new handshake */ + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force the client to send the EMS extension even though the original session did not negotiate EMS */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); + client_conn->ems_negotiated = true; + + /* Server will block the first time cache is accessed */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), S2N_ERR_ASYNC_BLOCKED); + + /* Fallback to full handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(S2N_CBIT_TEST(server_conn->extension_requests_received, ems_ext_id), 1); + EXPECT_TRUE(server_conn->ems_negotiated); + EXPECT_TRUE(client_conn->ems_negotiated); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /* Clean up */ + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + free(cert_chain_pem); + free(private_key_pem); + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_self_talk_session_resumption_test.c b/tests/unit/s2n_self_talk_session_resumption_test.c new file mode 100644 index 00000000000..de354884c30 --- /dev/null +++ b/tests/unit/s2n_self_talk_session_resumption_test.c @@ -0,0 +1,1246 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" + +/* Included to test static function */ +#include "tls/s2n_resume.c" + +#define ARE_FULL_HANDSHAKES(client, server) \ + (IS_FULL_HANDSHAKE(client) && IS_FULL_HANDSHAKE(server)) + +#define IS_HELLO_RETRY(client, server) \ + (((client->handshake.handshake_type) & HELLO_RETRY_REQUEST) \ + && ((server->handshake.handshake_type) & HELLO_RETRY_REQUEST)) + +#define EXPECT_TICKETS_SENT(conn, count) EXPECT_OK(s2n_assert_tickets_sent(conn, count)) + +struct s2n_early_data_test_case { + bool ticket_supported; + bool client_supported; + bool server_supported; + bool expect_success; +}; + +static S2N_RESULT s2n_assert_tickets_sent(struct s2n_connection *conn, uint16_t expected_tickets_sent) +{ + uint16_t tickets_sent = 0; + RESULT_GUARD_POSIX(s2n_connection_get_tickets_sent(conn, &tickets_sent)); + RESULT_ENSURE_EQ(tickets_sent, expected_tickets_sent); + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_wipe_connections(struct s2n_connection *client_conn, struct s2n_connection *server_conn, + struct s2n_test_io_pair *io_pair) +{ + RESULT_GUARD_POSIX(s2n_connection_wipe(server_conn)); + RESULT_GUARD_POSIX(s2n_connection_wipe(client_conn)); + RESULT_GUARD_POSIX(s2n_io_pair_close(io_pair)); + RESULT_GUARD_POSIX(s2n_io_pair_init_non_blocking(io_pair)); + RESULT_GUARD_POSIX(s2n_connections_set_io_pair(client_conn, server_conn, io_pair)); + return S2N_RESULT_OK; +} + +static int s2n_cache_retrieve_cb(struct s2n_connection *conn, void *ctx, const void *key, + uint64_t key_size, void *value, uint64_t *value_size) +{ + return S2N_SUCCESS; +} + +static int s2n_cache_store_cb(struct s2n_connection *conn, void *ctx, uint64_t ttl_in_seconds, + const void *key, uint64_t key_size, const void *value, uint64_t value_size) +{ + return S2N_SUCCESS; +} + +static int s2n_cache_delete_cb(struct s2n_connection *conn, void *ctx, const void *key, uint64_t key_size) +{ + return S2N_SUCCESS; +} + +static int s2n_test_session_ticket_cb(struct s2n_connection *conn, void *ctx, struct s2n_session_ticket *ticket) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(ticket); + + size_t data_len = 0; + EXPECT_SUCCESS(s2n_session_ticket_get_data_len(ticket, &data_len)); + + struct s2n_stuffer *stuffer = (struct s2n_stuffer *) ctx; + EXPECT_SUCCESS(s2n_stuffer_wipe(stuffer)); + EXPECT_SUCCESS(s2n_stuffer_resize(stuffer, data_len)); + EXPECT_SUCCESS(s2n_session_ticket_get_data(ticket, data_len, stuffer->blob.data)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(stuffer, data_len)); + + return S2N_SUCCESS; +} + +static int s2n_setup_test_ticket_key(struct s2n_config *config) +{ + POSIX_ENSURE_REF(config); + + /** + *= https://tools.ietf.org/rfc/rfc5869#appendix-A.1 + *# PRK = 0x077709362c2e32df0ddc3f0dc47bba63 + *# 90b6c73bb50f9c3122ec844ad7c2b3e5 (32 octets) + **/ + S2N_BLOB_FROM_HEX(ticket_key, + "077709362c2e32df0ddc3f0dc47bba63" + "90b6c73bb50f9c3122ec844ad7c2b3e5"); + + /* Set up encryption key */ + uint64_t current_time = 0; + uint8_t ticket_key_name[16] = "2016.07.26.15\0"; + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(config->wall_clock(config->sys_clock_ctx, ¤t_time)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), + ticket_key.data, ticket_key.size, current_time / ONE_SEC_IN_NANOS)); + + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_test_issue_new_session_ticket(struct s2n_connection *server_conn, struct s2n_connection *client_conn, + const struct s2n_early_data_test_case *early_data_case) +{ + RESULT_ENSURE_REF(server_conn); + RESULT_ENSURE_REF(client_conn); + RESULT_ENSURE_REF(early_data_case); + + uint8_t data = 1; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + RESULT_GUARD_POSIX(s2n_connection_add_new_tickets_to_send(server_conn, 1)); + + if (early_data_case->ticket_supported) { + RESULT_GUARD_POSIX(s2n_connection_set_server_max_early_data_size(server_conn, UINT16_MAX)); + } else { + RESULT_GUARD_POSIX(s2n_connection_set_server_max_early_data_size(server_conn, 0)); + } + + RESULT_ENSURE_NE(server_conn->tickets_to_send, server_conn->tickets_sent); + RESULT_GUARD_POSIX(s2n_send(server_conn, &data, 1, &blocked)); + RESULT_GUARD_POSIX(s2n_recv(client_conn, &data, 1, &blocked)); + RESULT_ENSURE_EQ(server_conn->tickets_to_send, server_conn->tickets_sent); + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_test_negotiate(struct s2n_connection *server_conn, struct s2n_connection *client_conn, + const struct s2n_early_data_test_case *early_data_case) +{ + RESULT_ENSURE_REF(server_conn); + RESULT_ENSURE_REF(client_conn); + RESULT_ENSURE_REF(early_data_case); + + uint8_t early_data[] = "very early hello world"; + uint8_t empty_data[sizeof(early_data)] = { 0 }; + + uint8_t early_data_recv_data[sizeof(early_data)] = { 0 }; + struct s2n_blob early_data_recv = { 0 }; + RESULT_GUARD_POSIX(s2n_blob_init(&early_data_recv, early_data_recv_data, sizeof(early_data_recv_data))); + + if (early_data_case->server_supported) { + RESULT_GUARD_POSIX(s2n_connection_set_server_max_early_data_size(server_conn, UINT16_MAX)); + } else { + RESULT_GUARD_POSIX(s2n_connection_set_server_max_early_data_size(server_conn, 0)); + } + + struct s2n_blob early_data_send = { 0 }; + if (early_data_case->client_supported) { + RESULT_GUARD_POSIX(s2n_blob_init(&early_data_send, early_data, sizeof(early_data))); + } + + RESULT_GUARD(s2n_negotiate_test_server_and_client_with_early_data(server_conn, client_conn, + &early_data_send, &early_data_recv)); + + if (early_data_case->expect_success) { + RESULT_ENSURE_EQ(early_data_recv.size, sizeof(early_data)); + EXPECT_BYTEARRAY_EQUAL(early_data_recv.data, early_data, sizeof(early_data)); + } else { + RESULT_ENSURE_EQ(early_data_recv.size, sizeof(empty_data)); + EXPECT_BYTEARRAY_EQUAL(early_data_recv.data, empty_data, sizeof(empty_data)); + } + + return S2N_RESULT_OK; +} + +static int s2n_wipe_psk_ke_ext(struct s2n_connection *conn, void *ctx) +{ + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(conn); + POSIX_ENSURE_REF(client_hello); + s2n_parsed_extension *parsed_extension = NULL; + POSIX_GUARD(s2n_client_hello_get_parsed_extension(TLS_EXTENSION_PSK_KEY_EXCHANGE_MODES, &client_hello->extensions, &parsed_extension)); + POSIX_ENSURE_REF(parsed_extension); + POSIX_GUARD(s2n_blob_zero(&parsed_extension->extension)); + + return S2N_SUCCESS; +} + +static int s2n_alter_psk_ke_ext(struct s2n_connection *conn, void *ctx) +{ + struct s2n_client_hello *client_hello = s2n_connection_get_client_hello(conn); + POSIX_ENSURE_REF(client_hello); + s2n_parsed_extension *parsed_extension = NULL; + POSIX_GUARD(s2n_client_hello_get_parsed_extension(TLS_EXTENSION_PSK_KEY_EXCHANGE_MODES, &client_hello->extensions, &parsed_extension)); + POSIX_ENSURE_REF(parsed_extension); + + /* Overwrite the extension so it only supports PSK_KE mode */ + struct s2n_stuffer psk_ke_extension = { 0 }; + POSIX_GUARD(s2n_stuffer_init(&psk_ke_extension, &parsed_extension->extension)); + POSIX_GUARD(s2n_stuffer_skip_write(&psk_ke_extension, 1)); + uint8_t mode = TLS_PSK_KE_MODE; + POSIX_GUARD(s2n_stuffer_write_bytes(&psk_ke_extension, &mode, 1)); + + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + /* For some session resumption test cases, we want to test all possible configurations of 0-RTT support. */ + size_t test_case_i = 0; + struct s2n_early_data_test_case early_data_test_cases[2 * 2 * 2] = { 0 }; + for (size_t ticket_supported = 0; ticket_supported < 2; ticket_supported++) { + early_data_test_cases[test_case_i].ticket_supported = ticket_supported; + for (size_t client_supported = 0; client_supported < 2; client_supported++) { + early_data_test_cases[test_case_i].client_supported = client_supported; + for (size_t server_supported = 0; server_supported < 2; server_supported++) { + early_data_test_cases[test_case_i].server_supported = server_supported; + early_data_test_cases[test_case_i].expect_success = client_supported && server_supported && ticket_supported; + } + } + test_case_i++; + } + /* For some session resumption test cases, we don't want to test or don't care about 0-RTT */ + const struct s2n_early_data_test_case no_early_data = { + .client_supported = false, + .server_supported = false, + .expect_success = false + }; + + /* Setup server config */ + struct s2n_config *server_config = s2n_config_new(); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "AWS-CRT-SDK-TLSv1.0")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(server_config)); + struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&tls13_chain_and_key, S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, + S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, true)); + EXPECT_SUCCESS(s2n_setup_test_ticket_key(server_config)); + + /* Setup TLS1.2 server config */ + struct s2n_config *tls12_server_config = s2n_config_new(); + EXPECT_NOT_NULL(tls12_server_config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls12_server_config, "test_all_tls12")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(tls12_server_config)); + struct s2n_cert_chain_and_key *tls12_chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&tls12_chain_and_key, S2N_DEFAULT_TEST_CERT_CHAIN, + S2N_DEFAULT_TEST_PRIVATE_KEY)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(tls12_server_config, tls12_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(tls12_server_config, true)); + EXPECT_SUCCESS(s2n_setup_test_ticket_key(tls12_server_config)); + + /* Setup TLS1.3 client config */ + struct s2n_config *tls13_client_config = s2n_config_new(); + EXPECT_NOT_NULL(tls13_client_config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls13_client_config, "AWS-CRT-SDK-TLSv1.0")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(tls13_client_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(tls13_client_config, true)); + DEFER_CLEANUP(struct s2n_stuffer cb_session_data = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&cb_session_data, 0)); + EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(tls13_client_config, s2n_test_session_ticket_cb, &cb_session_data)); + + /* Setup TLS1.2 client config */ + struct s2n_config *tls12_client_config = s2n_config_new(); + EXPECT_NOT_NULL(tls12_client_config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(tls12_client_config, "ELBSecurityPolicy-2016-08")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(tls12_client_config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(tls12_client_config, true)); + EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(tls12_client_config, s2n_test_session_ticket_cb, &cb_session_data)); + + /* Test: Server and client resume a session multiple times */ + for (size_t early_data_i = 0; early_data_i < s2n_array_len(early_data_test_cases); early_data_i++) { + const struct s2n_early_data_test_case early_data_case = early_data_test_cases[early_data_i]; + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(client_conn); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_client_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate initial handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(ARE_FULL_HANDSHAKES(client_conn, server_conn)); + + /* Receive and save the issued session ticket for the next connection */ + EXPECT_OK(s2n_test_issue_new_session_ticket(server_conn, client_conn, &early_data_case)); + + for (size_t i = 0; i < 10; i++) { + /* Prepare client and server for new connection */ + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Client sets up a resumption connection with the received session ticket data */ + size_t cb_session_data_len = s2n_stuffer_data_available(&cb_session_data); + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, cb_session_data.blob.data, cb_session_data_len)); + EXPECT_SUCCESS(s2n_stuffer_rewrite(&cb_session_data)); + + /* Negotiate new connection */ + EXPECT_OK(s2n_test_negotiate(server_conn, client_conn, &early_data_case)); + EXPECT_FALSE(ARE_FULL_HANDSHAKES(client_conn, server_conn)); + + /* Verify we can free the handshakes */ + EXPECT_SUCCESS(s2n_connection_free_handshake(client_conn)); + EXPECT_SUCCESS(s2n_connection_free_handshake(server_conn)); + + /* Receive and save the issued session ticket for the next connection */ + EXPECT_OK(s2n_test_issue_new_session_ticket(server_conn, client_conn, &early_data_case)); + } + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_stuffer_rewrite(&cb_session_data)); + } + + /* Test: Client does not accept a handshake that does not match its stored session */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_client_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_server_config)); + + /* Setup: initial handshake */ + DEFER_CLEANUP(struct s2n_blob tls12_ticket = { 0 }, s2n_free); + { + /* Create nonblocking pipes */ + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate initial handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(ARE_FULL_HANDSHAKES(client_conn, server_conn)); + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + /* Store TLS1.2 ticket */ + int tls12_ticket_length = s2n_connection_get_session_length(client_conn); + EXPECT_SUCCESS(s2n_alloc(&tls12_ticket, tls12_ticket_length)); + EXPECT_SUCCESS(s2n_connection_get_session(client_conn, tls12_ticket.data, tls12_ticket.size)); + } + + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + + /* Reject incorrect protocol version */ + { + /* Create nonblocking pipes */ + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Client sets up a resumption connection with the received session ticket data */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls12_ticket.data, tls12_ticket.size)); + + /* Change the protocol version */ + client_conn->resume_protocol_version = S2N_TLS10; + + /* Expect handshake rejected */ + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + } + + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + + /* Reject incorrect cipher suite */ + { + /* Create nonblocking pipes */ + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Client sets up a resumption connection with the received session ticket data */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls12_ticket.data, tls12_ticket.size)); + + /* Change the cipher suite */ + EXPECT_NOT_EQUAL(client_conn->secure->cipher_suite, &s2n_rsa_with_aes_256_cbc_sha); + client_conn->secure->cipher_suite = &s2n_rsa_with_aes_256_cbc_sha; + + /* Expect handshake rejected */ + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_BAD_MESSAGE); + } + + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + + /* Sanity check: unmodified session accepted */ + { + /* Create nonblocking pipes */ + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Client sets up a resumption connection with the received session ticket data */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls12_ticket.data, tls12_ticket.size)); + + /* Expect handshake accepted */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_FALSE(ARE_FULL_HANDSHAKES(client_conn, server_conn)); + } + } + + /* Test: Server does not accept an expired ticket and instead does a full handshake */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(client_conn); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_client_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate initial handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(ARE_FULL_HANDSHAKES(client_conn, server_conn)); + + /* Receive and save the issued session ticket for the next connection */ + EXPECT_OK(s2n_test_issue_new_session_ticket(server_conn, client_conn, &no_early_data)); + + /* Prepare client and server for new connection */ + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Client sets up a resumption connection with the received session ticket data */ + size_t cb_session_data_len = s2n_stuffer_data_available(&cb_session_data); + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, cb_session_data.blob.data, cb_session_data_len)); + EXPECT_SUCCESS(s2n_stuffer_rewrite(&cb_session_data)); + + /* Setup conditions to make the server think the ticket has expired */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, 0)); + + /* Negotiate new connection */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(ARE_FULL_HANDSHAKES(client_conn, server_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_stuffer_rewrite(&cb_session_data)); + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_STATE_LIFETIME_IN_NANOS)); + }; + + /* Test: A TLS1.2 client with a valid TLS1.3 ticket falls back to a TLS1.2 connection */ + for (size_t early_data_i = 0; early_data_i < s2n_array_len(early_data_test_cases); early_data_i++) { + struct s2n_early_data_test_case early_data_case = early_data_test_cases[early_data_i]; + /* Early data is never sent in TLS1.2 (or in a full handshake) */ + early_data_case.expect_success = false; + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(client_conn); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_client_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate initial handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(ARE_FULL_HANDSHAKES(client_conn, server_conn)); + + /* Receive and save the issued session ticket for the next connection */ + EXPECT_OK(s2n_test_issue_new_session_ticket(server_conn, client_conn, &early_data_case)); + + /* Prepare client and server for a second connection */ + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Client sets up a resumption connection with the received session ticket data */ + size_t cb_session_data_len = s2n_stuffer_data_available(&cb_session_data); + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, cb_session_data.blob.data, cb_session_data_len)); + EXPECT_SUCCESS(s2n_stuffer_rewrite(&cb_session_data)); + + /* Set client config to TLS1.2 cipher preferences */ + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_client_config)); + + /* Negotiate second connection */ + EXPECT_OK(s2n_test_negotiate(server_conn, client_conn, &early_data_case)); + + /* Falls back to TLS1.2 handshake */ + EXPECT_TRUE(ARE_FULL_HANDSHAKES(client_conn, server_conn)); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + /* Test: A TLS1.3 client with a valid TLS1.2 ticket can fall back to a + * TLS1.3 connection. + * + * This scenario could occur when upgrading a fleet of TLS1.2 servers to + * TLS1.3 without disabling session resumption. + */ + { + DEFER_CLEANUP(struct s2n_blob ticket = { 0 }, s2n_free); + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_client_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate initial handshake to produce TLS1.2 session ticket */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(ARE_FULL_HANDSHAKES(client_conn, server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + EXPECT_TRUE(client_conn->ems_negotiated); + + /* Store the TLS1.2 session ticket */ + size_t tls12_session_ticket_len = s2n_connection_get_session_length(client_conn); + EXPECT_SUCCESS(s2n_realloc(&ticket, tls12_session_ticket_len)); + EXPECT_SUCCESS(s2n_connection_get_session(client_conn, ticket.data, ticket.size)); + + /* Prepare client and server for a second connection */ + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + /* Client sets up a resumption connection with the received TLS1.2 session ticket data */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, ticket.data, ticket.size)); + /* We want to ensure that ems is handled properly too */ + EXPECT_TRUE(client_conn->ems_negotiated); + + /* Set client config to TLS1.3 cipher preferences */ + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_client_config)); + + /* Negotiate second connection */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(ARE_FULL_HANDSHAKES(client_conn, server_conn)); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + + /* since TLS 1.3 was negotiated, and the client hasn't called recv yet + * there should be no session ticket available. + */ + EXPECT_EQUAL(s2n_connection_get_session_length(client_conn), 0); + EXPECT_EQUAL(s2n_connection_get_session(client_conn, ticket.data, ticket.size), 0); + }; + + /* HRR when issuing a session resumption ticket and when resuming a session */ + for (size_t early_data_i = 0; early_data_i < s2n_array_len(early_data_test_cases); early_data_i++) { + struct s2n_early_data_test_case early_data_case = early_data_test_cases[early_data_i]; + /* Never use early data on a HRR */ + early_data_case.expect_success = false; + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(client_conn); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_client_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Validate handshake type */ + EXPECT_TRUE(ARE_FULL_HANDSHAKES(client_conn, server_conn)); + EXPECT_TRUE(IS_HELLO_RETRY(client_conn, server_conn)); + + /* Receive and save the issued session ticket for the next connection */ + EXPECT_OK(s2n_test_issue_new_session_ticket(server_conn, client_conn, &early_data_case)); + + /* Prepare client and server for new connection */ + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Client sets up a resumption connection with the received session ticket data */ + size_t cb_session_data_len = s2n_stuffer_data_available(&cb_session_data); + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, cb_session_data.blob.data, cb_session_data_len)); + EXPECT_SUCCESS(s2n_stuffer_rewrite(&cb_session_data)); + + /* Negotiate new connection */ + EXPECT_OK(s2n_test_negotiate(server_conn, client_conn, &early_data_case)); + EXPECT_FALSE(ARE_FULL_HANDSHAKES(client_conn, server_conn)); + EXPECT_TRUE(IS_HELLO_RETRY(client_conn, server_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + } + + /* Test: reuse a connection for both TLS1.2 and TLS1.3 session resumption. + * + * TLS1.2 and TLS1.3 reuse some of the same code / memory. We should verify that using + * one doesn't affect our ability to use the other after wiping the connection. + */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + DEFER_CLEANUP(struct s2n_stuffer tls12_ticket = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer tls13_ticket = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&tls12_ticket, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&tls13_ticket, 0)); + + /* Negotiate initial TLS1.3 handshake */ + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_client_config)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(ARE_FULL_HANDSHAKES(client_conn, server_conn)); + EXPECT_OK(s2n_test_issue_new_session_ticket(server_conn, client_conn, &no_early_data)); + EXPECT_SUCCESS(s2n_stuffer_copy(&cb_session_data, &tls13_ticket, s2n_stuffer_data_available(&cb_session_data))); + + /* Prepare client and server for a second connection */ + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate initial TLS1.2 handshake */ + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_client_config)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(ARE_FULL_HANDSHAKES(client_conn, server_conn)); + int tls12_ticket_length = s2n_connection_get_session_length(client_conn); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&tls12_ticket, tls12_ticket_length)); + EXPECT_SUCCESS(s2n_connection_get_session(client_conn, tls12_ticket.blob.data, tls12_ticket_length)); + + /* Switch between TLS1.2 and TLS1.3 resumption */ + for (size_t i = 0; i < 10; i++) { + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + bool do_tls13 = (i % 2 == 0); + + uint8_t expected_version = 0; + if (do_tls13) { + expected_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_client_config)); + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls13_ticket.blob.data, + s2n_stuffer_data_available(&tls13_ticket))); + } else { + expected_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_client_config)); + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls12_ticket.blob.data, + s2n_stuffer_data_available(&tls12_ticket))); + } + + EXPECT_OK(s2n_test_negotiate(server_conn, client_conn, &no_early_data)); + EXPECT_FALSE(ARE_FULL_HANDSHAKES(client_conn, server_conn)); + EXPECT_EQUAL(client_conn->actual_protocol_version, expected_version); + EXPECT_EQUAL(server_conn->actual_protocol_version, expected_version); + + if (do_tls13) { + EXPECT_SUCCESS(s2n_stuffer_wipe(&tls13_ticket)); + EXPECT_OK(s2n_test_issue_new_session_ticket(server_conn, client_conn, &no_early_data)); + EXPECT_SUCCESS(s2n_stuffer_copy(&cb_session_data, &tls13_ticket, s2n_stuffer_data_available(&cb_session_data))); + } else { + EXPECT_SUCCESS(s2n_stuffer_wipe(&tls12_ticket)); + tls12_ticket_length = s2n_connection_get_session_length(client_conn); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&tls12_ticket, tls12_ticket_length)); + EXPECT_SUCCESS(s2n_connection_get_session(client_conn, tls12_ticket.blob.data, tls12_ticket_length)); + } + } + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /* Test output of s2n_connection_get_session_length/get_session during different stages of the handshake */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + DEFER_CLEANUP(struct s2n_stuffer tls12_ticket = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer tls13_ticket = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&tls12_ticket, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&tls13_ticket, 0)); + + /* Negotiate initial TLS1.3 handshake */ + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_client_config)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_OK(s2n_test_issue_new_session_ticket(server_conn, client_conn, &no_early_data)); + EXPECT_TICKETS_SENT(server_conn, 2); + EXPECT_SUCCESS(s2n_stuffer_copy(&cb_session_data, &tls13_ticket, s2n_stuffer_data_available(&cb_session_data))); + + /* Prepare client and server for a second connection */ + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate initial TLS1.2 handshake */ + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_client_config)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TICKETS_SENT(server_conn, 1); + int tls12_ticket_length = s2n_connection_get_session_length(client_conn); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&tls12_ticket, tls12_ticket_length)); + EXPECT_SUCCESS(s2n_connection_get_session(client_conn, tls12_ticket.blob.data, tls12_ticket_length)); + + struct s2n_config *client_config[] = { tls12_client_config, tls13_client_config }; + DEFER_CLEANUP(struct s2n_blob session_state = { 0 }, s2n_free); + + /* A quirk of the TLS1.2 session resumption behavior is that if a ticket is set + * on the connection using s2n_connection_set_session, s2n_connection_get_session + * will return a valid ticket, even before actually receiving a new session ticket + * from the server. Here we test that behavior to ensure it is consistent. */ + for (size_t j = 0; j < s2n_array_len(client_config); j++) { + /* Prepare client and server for new connection */ + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config[j])); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_server_config)); + + /* Client sets up a resumption connection with the received session ticket data */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls12_ticket.blob.data, s2n_stuffer_data_available(&tls12_ticket))); + + /* Check that no ticket has actually been sent */ + EXPECT_TICKETS_SENT(server_conn, 0); + + /* s2n_connection_get_session will be non-zero if a TLS1.2 ticket was set on the connection */ + uint32_t session_length = s2n_connection_get_session_length(client_conn); + EXPECT_TRUE(session_length > 0); + + EXPECT_SUCCESS(s2n_realloc(&session_state, session_length)); + + /* Call get_session to retrieve session ticket */ + EXPECT_SUCCESS(s2n_connection_get_session(client_conn, session_state.data, session_length)); + + /* Check that the session ticket returned is valid by deserializing it */ + struct s2n_stuffer session_state_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&session_state_stuffer, &session_state)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&session_state_stuffer, session_length)); + EXPECT_SUCCESS(s2n_client_deserialize_resumption_state(client_conn, &session_state_stuffer)); + } + + /* Tests that if a TLS1.3 ticket is set on the connection, s2n_connection_get_session will + * not return a ticket until a session ticket is sent by the server as a post-handshake + * message. */ + for (size_t j = 0; j < s2n_array_len(client_config); j++) { + /* Prepare client and server for new connection */ + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config[j])); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Client sets up a resumption connection with the received session ticket data */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls13_ticket.blob.data, s2n_stuffer_data_available(&tls13_ticket))); + + /* s2n_connection_get_session will be zero before receiving a session ticket + * if a TLS1.3 ticket was set on the connection. */ + EXPECT_EQUAL(s2n_connection_get_session_length(client_conn), 0); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + if (client_conn->actual_protocol_version < S2N_TLS13) { + EXPECT_TRUE(s2n_connection_get_session_length(client_conn) > 0); + } else { + /* The session length should be zero before a client has received a session ticket */ + EXPECT_EQUAL(s2n_connection_get_session_length(client_conn), 0); + + /* Receive the issued TLS1.3 session ticket */ + EXPECT_OK(s2n_test_issue_new_session_ticket(server_conn, client_conn, &no_early_data)); + + /* The session length should be non-zero after a client has received a session ticket */ + EXPECT_TRUE(s2n_connection_get_session_length(client_conn) > 0); + } + } + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* If the server has no ticket key, no session tickets are issued or accepted. + * We should always fall back to a full handshake. + */ + { + /* Setup config without session ticket key */ + struct s2n_config *no_key_config = s2n_config_new(); + EXPECT_NOT_NULL(no_key_config); + no_key_config->security_policy = server_config->security_policy; + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(no_key_config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(no_key_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(no_key_config, true)); + + struct s2n_config *no_key_config_with_cache = s2n_config_new(); + EXPECT_NOT_NULL(no_key_config_with_cache); + no_key_config_with_cache->security_policy = server_config->security_policy; + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(no_key_config_with_cache)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(no_key_config_with_cache, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(no_key_config_with_cache, true)); + EXPECT_SUCCESS(s2n_config_set_cache_store_callback(no_key_config_with_cache, s2n_cache_store_cb, NULL)); + EXPECT_SUCCESS(s2n_config_set_cache_retrieve_callback(no_key_config_with_cache, s2n_cache_retrieve_cb, NULL)); + EXPECT_SUCCESS(s2n_config_set_cache_delete_callback(no_key_config_with_cache, s2n_cache_delete_cb, NULL)); + EXPECT_SUCCESS(s2n_config_set_session_cache_onoff(no_key_config_with_cache, true)); + EXPECT_TRUE(no_key_config_with_cache->use_session_cache); + + /* TLS1.2 */ + { + DEFER_CLEANUP(struct s2n_stuffer ticket = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&ticket, 0)); + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls12_client_config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Initial handshake */ + { + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&cb_session_data, &ticket, s2n_stuffer_data_available(&cb_session_data))); + EXPECT_TRUE(s2n_stuffer_data_available(&ticket) > 0); + + EXPECT_OK(s2n_wipe_connections(client_conn, server_conn, &io_pair)); + }; + + /* Initial handshake with no ticket keys */ + { + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, no_key_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + EXPECT_OK(s2n_wipe_connections(client_conn, server_conn, &io_pair)); + }; + + /* Initial handshake with no ticket keys and session id caching */ + { + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, no_key_config_with_cache)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + EXPECT_OK(s2n_wipe_connections(client_conn, server_conn, &io_pair)); + }; + + /* Resumption handshake */ + { + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, + ticket.blob.data, s2n_stuffer_data_available(&ticket))); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_FALSE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + EXPECT_OK(s2n_wipe_connections(client_conn, server_conn, &io_pair)); + }; + + /* Resumption handshake with no ticket keys */ + { + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, no_key_config)); + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, + ticket.blob.data, s2n_stuffer_data_available(&ticket))); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + EXPECT_OK(s2n_wipe_connections(client_conn, server_conn, &io_pair)); + }; + + /* Resumption handshake with no ticket keys and session id caching */ + { + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, no_key_config_with_cache)); + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, + ticket.blob.data, s2n_stuffer_data_available(&ticket))); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + EXPECT_OK(s2n_wipe_connections(client_conn, server_conn, &io_pair)); + }; + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* TLS1.3 */ + { + DEFER_CLEANUP(struct s2n_stuffer ticket = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&ticket, 0)); + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_client_config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Initial handshake */ + { + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_OK(s2n_test_issue_new_session_ticket(server_conn, client_conn, &no_early_data)); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(server_conn->tickets_sent > 1); + EXPECT_SUCCESS(s2n_stuffer_copy(&cb_session_data, &ticket, s2n_stuffer_data_available(&cb_session_data))); + EXPECT_TRUE(s2n_stuffer_data_available(&ticket) > 0); + + EXPECT_OK(s2n_wipe_connections(client_conn, server_conn, &io_pair)); + }; + + /* Initial handshake with no ticket keys */ + { + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, no_key_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_EQUAL(server_conn->tickets_sent, 0); + + EXPECT_OK(s2n_wipe_connections(client_conn, server_conn, &io_pair)); + }; + + /* Initial handshake with no ticket keys and session id caching */ + { + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, no_key_config_with_cache)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_EQUAL(server_conn->tickets_sent, 0); + + EXPECT_OK(s2n_wipe_connections(client_conn, server_conn, &io_pair)); + }; + + /* Resumption handshake */ + { + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, + ticket.blob.data, s2n_stuffer_data_available(&ticket))); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_FALSE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_EQUAL(server_conn->tickets_sent, 1); + + EXPECT_OK(s2n_wipe_connections(client_conn, server_conn, &io_pair)); + }; + + /* Resumption handshake with no ticket keys */ + { + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, no_key_config)); + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, + ticket.blob.data, s2n_stuffer_data_available(&ticket))); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_EQUAL(server_conn->tickets_sent, 0); + + EXPECT_OK(s2n_wipe_connections(client_conn, server_conn, &io_pair)); + }; + + /* Resumption handshake with no ticket keys and session id caching */ + { + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, no_key_config_with_cache)); + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, + ticket.blob.data, s2n_stuffer_data_available(&ticket))); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_EQUAL(server_conn->tickets_sent, 0); + + EXPECT_OK(s2n_wipe_connections(client_conn, server_conn, &io_pair)); + }; + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + EXPECT_SUCCESS(s2n_config_free(no_key_config)); + EXPECT_SUCCESS(s2n_config_free(no_key_config_with_cache)); + }; + + /* Test: Client that supports TLS1.3 can resume sessions with a server that does not support TLS 1.3 */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(client_conn); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_client_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, tls12_server_config)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Negotiate initial handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(ARE_FULL_HANDSHAKES(client_conn, server_conn)); + + /* The server does not support TLS13 so we are using TLS12 */ + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + + /* Store the TLS1.2 session ticket */ + size_t tls12_session_ticket_len = s2n_connection_get_session_length(client_conn); + uint8_t tls12_session_ticket[S2N_TLS12_SESSION_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_connection_get_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); + + for (size_t i = 0; i < 10; i++) { + /* Prepare client and server for new connection */ + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(client_conn)); + EXPECT_SUCCESS(s2n_connection_wipe(server_conn)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Client sets up a resumption connection with the received TLS1.2 session ticket data */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls12_session_ticket, tls12_session_ticket_len)); + + /* Negotiate new connection */ + EXPECT_OK(s2n_test_negotiate(server_conn, client_conn, &no_early_data)); + EXPECT_FALSE(ARE_FULL_HANDSHAKES(client_conn, server_conn)); + + /* Verify we can free the handshakes */ + EXPECT_SUCCESS(s2n_connection_free_handshake(client_conn)); + EXPECT_SUCCESS(s2n_connection_free_handshake(server_conn)); + } + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_stuffer_rewrite(&cb_session_data)); + } + + /* Functional: Server in QUIC mode does not send a session ticket if psk_ke extension was not received */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_client_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Enable QUIC mode */ + EXPECT_SUCCESS(s2n_connection_enable_quic(client_conn)); + EXPECT_SUCCESS(s2n_connection_enable_quic(server_conn)); + + /* Create nonblocking pipes */ + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, server_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&output, &input, client_conn)); + + /* Wipe psk_ke extension when it is received */ + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(server_config, s2n_wipe_psk_ke_ext, NULL)); + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Server did not send a ticket since the client did not send the psk_ke extension */ + uint16_t tickets_sent = 0; + EXPECT_SUCCESS(s2n_connection_get_tickets_sent(server_conn, &tickets_sent)); + EXPECT_EQUAL(tickets_sent, 0); + }; + + /* Functional: Server in QUIC mode does not send a session ticket if psk_ke extension does not support psk_dhe_ke */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_client_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Enable QUIC mode */ + EXPECT_SUCCESS(s2n_connection_enable_quic(client_conn)); + EXPECT_SUCCESS(s2n_connection_enable_quic(server_conn)); + + /* Create nonblocking pipes */ + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, server_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&output, &input, client_conn)); + + /* Alter psk_ke extension when it is received so that it only supports psk_ke mode */ + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(server_config, s2n_alter_psk_ke_ext, NULL)); + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Server did not send a ticket since the client does not support psk_dhe_ke mode */ + uint16_t tickets_sent = 0; + EXPECT_SUCCESS(s2n_connection_get_tickets_sent(server_conn, &tickets_sent)); + EXPECT_EQUAL(tickets_sent, 0); + }; + + /* Functional: Server in QUIC mode sends a session ticket if the client indicates it supports psk_dhe_ke mode */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_client_config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Enable QUIC mode */ + EXPECT_SUCCESS(s2n_connection_enable_quic(client_conn)); + EXPECT_SUCCESS(s2n_connection_enable_quic(server_conn)); + + /* Create nonblocking pipes */ + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, server_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&output, &input, client_conn)); + + /* Disable any client hello cb changes */ + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(server_config, NULL, NULL)); + + /* Negotiate handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Server sent one ticket since the client by default indicates it supports psk_dhe_ke mode */ + uint16_t tickets_sent = 0; + EXPECT_SUCCESS(s2n_connection_get_tickets_sent(server_conn, &tickets_sent)); + EXPECT_EQUAL(tickets_sent, 1); + }; + + /* Clean-up */ + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(tls12_server_config)); + EXPECT_SUCCESS(s2n_config_free(tls13_client_config)); + EXPECT_SUCCESS(s2n_config_free(tls12_client_config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls12_chain_and_key)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + + END_TEST(); +} diff --git a/tests/unit/s2n_self_talk_shutdown_test.c b/tests/unit/s2n_self_talk_shutdown_test.c new file mode 100644 index 00000000000..a365a2125d0 --- /dev/null +++ b/tests/unit/s2n_self_talk_shutdown_test.c @@ -0,0 +1,151 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + const s2n_mode modes[] = { S2N_CLIENT, S2N_SERVER }; + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + + /* Self-Talk: shutdown during handshake */ + for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, SERVER_CERT)); + EXPECT_FALSE(s2n_handshake_is_complete(client_conn)); + EXPECT_FALSE(s2n_handshake_is_complete(server_conn)); + + /* Choose which connection will request the shutdown */ + struct s2n_connection *request = server_conn; + struct s2n_connection *response = client_conn; + if (modes[mode_i] == S2N_CLIENT) { + request = client_conn; + response = server_conn; + } + + /* Both shutdown attempts should succeed */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_SUCCESS(s2n_shutdown(request, &blocked)); + EXPECT_SUCCESS(s2n_shutdown(response, &blocked)); + + /* Both connections successfully closed */ + EXPECT_TRUE(s2n_connection_check_io_status(server_conn, S2N_IO_CLOSED)); + EXPECT_TRUE(s2n_connection_check_io_status(client_conn, S2N_IO_CLOSED)); + + /* Closed connections behave properly */ + for (size_t i = 0; i < 5; i++) { + /* Future attempts to shutdown succeed (they are no-ops) */ + EXPECT_SUCCESS(s2n_shutdown(server_conn, &blocked)); + EXPECT_SUCCESS(s2n_shutdown(client_conn, &blocked)); + } + }; + + /* Self-Talk: shutdown during application data */ + for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Both the client and server should send application data, but not read any. + * This leaves unread application data for both to handle. + */ + uint8_t data[] = "hello world"; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_EQUAL(s2n_send(server_conn, data, sizeof(data), &blocked), sizeof(data)); + EXPECT_EQUAL(s2n_send(client_conn, data, sizeof(data), &blocked), sizeof(data)); + + /* Choose which connection will request the shutdown, and which will respond */ + struct s2n_connection *request = server_conn; + struct s2n_connection *response = client_conn; + if (modes[mode_i] == S2N_CLIENT) { + request = client_conn; + response = server_conn; + } + + /* The requester's first shutdown attempt will send a close_notify + * but then block on receiving the response close_notify. + */ + EXPECT_FAILURE_WITH_ERRNO(s2n_shutdown(request, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + /* The responder's first shutdown attempt will receive the request close_notify, + * send the response close_notify, and then consider the shutdown successful. + */ + EXPECT_SUCCESS(s2n_shutdown(response, &blocked)); + + /* The requester's second shutdown attempt will receive the response close_notify + * and then consider the shutdown successful. + */ + EXPECT_SUCCESS(s2n_shutdown(request, &blocked)); + + /* Both connections successfully closed */ + EXPECT_TRUE(s2n_connection_check_io_status(server_conn, S2N_IO_CLOSED)); + EXPECT_TRUE(s2n_connection_check_io_status(client_conn, S2N_IO_CLOSED)); + + /* Closed connections behave properly */ + for (size_t i = 0; i < 5; i++) { + /* Future attempts to shutdown succeed (they are no-ops) */ + EXPECT_SUCCESS(s2n_shutdown(server_conn, &blocked)); + EXPECT_SUCCESS(s2n_shutdown(client_conn, &blocked)); + + /* Future attempts to write fail */ + EXPECT_FAILURE_WITH_ERRNO(s2n_send(server_conn, data, sizeof(data), &blocked), S2N_ERR_CLOSED); + EXPECT_FAILURE_WITH_ERRNO(s2n_send(client_conn, data, sizeof(data), &blocked), S2N_ERR_CLOSED); + + /* Future attempts to read indicate end of stream */ + EXPECT_EQUAL(s2n_recv(server_conn, data, sizeof(data), &blocked), 0); + EXPECT_EQUAL(s2n_recv(client_conn, data, sizeof(data), &blocked), 0); + } + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_self_talk_tls12_test.c b/tests/unit/s2n_self_talk_tls12_test.c new file mode 100644 index 00000000000..d737b07fd89 --- /dev/null +++ b/tests/unit/s2n_self_talk_tls12_test.c @@ -0,0 +1,206 @@ +/* + * 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 +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" + +#define SUPPORTED_CERTIFICATE_FORMATS (2) + +static const char *certificate_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_CERT_CHAIN, S2N_RSA_2048_PKCS8_CERT_CHAIN }; +static const char *private_key_paths[SUPPORTED_CERTIFICATE_FORMATS] = { S2N_RSA_2048_PKCS1_KEY, S2N_RSA_2048_PKCS8_KEY }; + +void mock_client(struct s2n_test_io_pair *io_pair) +{ + char buffer[0xffff]; + struct s2n_connection *conn; + struct s2n_config *config; + s2n_blocked_status blocked; + + /* Give the server a chance to listen */ + sleep(1); + + conn = s2n_connection_new(S2N_CLIENT); + config = s2n_config_new(); + s2n_config_disable_x509_verification(config); + s2n_connection_set_config(conn, config); + + s2n_connection_set_io_pair(conn, io_pair); + + s2n_negotiate(conn, &blocked); + + s2n_connection_free_handshake(conn); + + uint16_t timeout = 1; + s2n_connection_set_dynamic_record_threshold(conn, 0x7fff, timeout); + int i; + for (i = 1; i < 0xffff - 100; i += 100) { + for (int j = 0; j < i; j++) { + buffer[j] = 33; + } + s2n_send(conn, buffer, i, &blocked); + } + + for (int j = 0; j < i; j++) { + buffer[j] = 33; + } + + /* release the buffers here to validate we can continue IO after */ + s2n_connection_release_buffers(conn); + + /* Simulate timeout second conneciton inactivity and tolerate 50 ms error */ + struct timespec sleep_time = { .tv_sec = timeout, .tv_nsec = 50000000 }; + int r; + do { + r = nanosleep(&sleep_time, &sleep_time); + } while (r != 0); + /* Active application bytes consumed is reset to 0 in before writing data. */ + /* Its value should equal to bytes written after writing */ + ssize_t bytes_written = s2n_send(conn, buffer, i, &blocked); + if (bytes_written != conn->active_application_bytes_consumed) { + exit(0); + } + + int shutdown_rc = -1; + while (shutdown_rc != 0) { + shutdown_rc = s2n_shutdown(conn, &blocked); + } + + s2n_connection_free(conn); + s2n_config_free(config); + + /* Give the server a chance to a void a sigpipe */ + sleep(1); + + s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); + + exit(0); +} + +int main(int argc, char **argv) +{ + struct s2n_connection *conn; + struct s2n_config *config; + s2n_blocked_status blocked; + int status; + pid_t pid; + char *cert_chain_pem; + char *private_key_pem; + char *dhparams_pem; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + for (int is_dh_key_exchange = 0; is_dh_key_exchange <= 1; is_dh_key_exchange++) { + struct s2n_cert_chain_and_key *chain_and_keys[SUPPORTED_CERTIFICATE_FORMATS]; + + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Write the fragmented hello message */ + mock_client(&io_pair); + } + + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(dhparams_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(config = s2n_config_new()); + for (int cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { + EXPECT_SUCCESS(s2n_read_test_pem(certificate_paths[cert], cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(private_key_paths[cert], private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_keys[cert] = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_keys[cert], cert_chain_pem, private_key_pem)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_keys[cert])); + } + + if (is_dh_key_exchange) { + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_DHPARAMS, dhparams_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_dhparams(config, dhparams_pem)); + } + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + + char buffer[0xffff]; + for (int i = 1; i < 0xffff; i += 100) { + char *ptr = buffer; + int size = i; + + do { + int bytes_read = 0; + EXPECT_SUCCESS(bytes_read = s2n_recv(conn, ptr, size, &blocked)); + + size -= bytes_read; + ptr += bytes_read; + } while (size); + + for (int j = 0; j < i; j++) { + EXPECT_EQUAL(buffer[j], 33); + } + + /* release the buffers here to validate we can continue IO after */ + EXPECT_SUCCESS(s2n_connection_release_buffers(conn)); + } + + int shutdown_rc = -1; + do { + shutdown_rc = s2n_shutdown(conn, &blocked); + EXPECT_TRUE(shutdown_rc == 0 || (errno == EAGAIN && blocked)); + } while (shutdown_rc != 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + for (int cert = 0; cert < SUPPORTED_CERTIFICATE_FORMATS; cert++) { + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_keys[cert])); + } + EXPECT_SUCCESS(s2n_config_free(config)); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + free(cert_chain_pem); + free(private_key_pem); + free(dhparams_pem); + } + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_self_talk_tls13_test.c b/tests/unit/s2n_self_talk_tls13_test.c new file mode 100644 index 00000000000..94e8e3b1b75 --- /dev/null +++ b/tests/unit/s2n_self_talk_tls13_test.c @@ -0,0 +1,185 @@ +/* + * 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 +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_tls13.h" + +void mock_client(struct s2n_test_io_pair *io_pair) +{ + char buffer[0xffff] = { 0 }; + struct s2n_connection *conn = NULL; + struct s2n_config *config = NULL; + s2n_blocked_status blocked; + + /* Give the server a chance to listen */ + sleep(1); + + conn = s2n_connection_new(S2N_CLIENT); + config = s2n_config_new(); + s2n_config_disable_x509_verification(config); + s2n_config_set_cipher_preferences(config, "default_tls13"); + s2n_connection_set_config(conn, config); + + s2n_connection_set_io_pair(conn, io_pair); + s2n_connection_prefer_throughput(conn); + + s2n_negotiate(conn, &blocked); + + s2n_connection_free_handshake(conn); + + uint16_t timeout = 1; + s2n_connection_set_dynamic_record_threshold(conn, 0x7fff, timeout); + int i; + for (i = 1; i < 0xffff - 100; i += 100) { + for (int j = 0; j < i; j++) { + buffer[j] = 33; + } + s2n_send(conn, buffer, i, &blocked); + } + + for (int j = 0; j < i; j++) { + buffer[j] = 33; + } + + /* release the buffers here to validate we can continue IO after */ + s2n_connection_release_buffers(conn); + + /* Simulate timeout second conneciton inactivity and tolerate 50 ms error */ + struct timespec sleep_time = { .tv_sec = timeout, .tv_nsec = 50000000 }; + int r; + do { + r = nanosleep(&sleep_time, &sleep_time); + } while (r != 0); + /* Active application bytes consumed is reset to 0 in before writing data. */ + /* Its value should equal to bytes written after writing */ + ssize_t bytes_written = s2n_send(conn, buffer, i, &blocked); + if ((uint64_t) bytes_written != conn->active_application_bytes_consumed) { + exit(1); + } + + int shutdown_rc = -1; + while (shutdown_rc != 0) { + shutdown_rc = s2n_shutdown(conn, &blocked); + } + + s2n_connection_free(conn); + s2n_config_free(config); + + /* Give the server a chance to a void a sigpipe */ + sleep(1); + + s2n_io_pair_close_one_end(io_pair, S2N_CLIENT); + + exit(0); +} + +int main(int argc, char **argv) +{ + struct s2n_connection *conn = NULL; + struct s2n_config *config = NULL; + s2n_blocked_status blocked; + int status; + pid_t pid; + + BEGIN_TEST(); + + /* Create a pipe */ + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init(&io_pair)); + + /* Create a child process */ + pid = fork(); + if (pid == 0) { + /* This is the client process, close the server end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Write the fragmented hello message */ + mock_client(&io_pair); + } + + /* This is the server process, close the client end of the pipe */ + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + struct s2n_cert_chain_and_key *chain_and_key; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_connection_prefer_throughput(conn)); + + /* Set up the connection to read from the fd */ + EXPECT_SUCCESS(s2n_connection_set_io_pair(conn, &io_pair)); + + /* Negotiate the handshake. */ + EXPECT_SUCCESS(s2n_negotiate(conn, &blocked)); + EXPECT_NOT_NULL(s2n_connection_get_client_hello(conn)); + EXPECT_EQUAL(conn->actual_protocol_version, s2n_get_highest_fully_supported_tls_version()); + + char buffer[0xffff]; + for (int i = 1; i < 0xffff; i += 100) { + char *ptr = buffer; + int size = i; + + do { + int bytes_read = 0; + EXPECT_SUCCESS(bytes_read = s2n_recv(conn, ptr, size, &blocked)); + + size -= bytes_read; + ptr += bytes_read; + } while (size); + + for (int j = 0; j < i; j++) { + EXPECT_EQUAL(buffer[j], 33); + } + + /* release the buffers here to validate we can continue IO after */ + EXPECT_SUCCESS(s2n_connection_release_buffers(conn)); + } + + int shutdown_rc = -1; + do { + shutdown_rc = s2n_shutdown(conn, &blocked); + EXPECT_TRUE(shutdown_rc == 0 || (errno == EAGAIN && blocked)); + } while (shutdown_rc != 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + + /* Clean up */ + EXPECT_EQUAL(waitpid(-1, &status, 0), pid); + EXPECT_EQUAL(status, 0); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + s2n_disable_tls13_in_test(); + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_send_key_update_test.c b/tests/unit/s2n_send_key_update_test.c new file mode 100644 index 00000000000..e17f20d38af --- /dev/null +++ b/tests/unit/s2n_send_key_update_test.c @@ -0,0 +1,195 @@ +/* + * 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 +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_post_handshake.h" +#include "tls/s2n_record.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "tls/s2n_tls13_handshake.h" + +int s2n_key_update_write(struct s2n_blob *out); + +static int s2n_test_init_encryption(struct s2n_connection *conn) +{ + struct s2n_cipher_suite *cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + conn->server->cipher_suite = cipher_suite; + conn->client->cipher_suite = cipher_suite; + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + /* Just some data that's the right length */ + S2N_BLOB_FROM_HEX(key, "0123456789abcdef0123456789abcdef"); + S2N_BLOB_FROM_HEX(iv, "0123456789abcdef01234567"); + S2N_BLOB_FROM_HEX(application_secret, + "4bc28934ddd802b00f479e14a72d7725dab45d32b3b145f29" + "e4c5b56677560eb5236b168c71c5c75aa52f3e20ee89bfb"); + + struct s2n_session_key *server_session_key = &conn->server->server_key; + struct s2n_session_key *client_session_key = &conn->server->server_key; + uint8_t *server_implicit_iv = conn->server->server_implicit_iv; + uint8_t *client_implicit_iv = conn->client->client_implicit_iv; + + /* Initialize record algorithm */ + POSIX_GUARD(cipher_suite->record_alg->cipher->init(server_session_key)); + POSIX_GUARD(cipher_suite->record_alg->cipher->init(client_session_key)); + POSIX_GUARD(cipher_suite->record_alg->cipher->set_encryption_key(server_session_key, &key)); + POSIX_GUARD(cipher_suite->record_alg->cipher->set_encryption_key(client_session_key, &key)); + POSIX_GUARD(cipher_suite->record_alg->cipher->set_decryption_key(server_session_key, &key)); + POSIX_GUARD(cipher_suite->record_alg->cipher->set_decryption_key(client_session_key, &key)); + + /* Initialized secrets */ + POSIX_CHECKED_MEMCPY(conn->secrets.version.tls13.server_app_secret, application_secret.data, application_secret.size); + POSIX_CHECKED_MEMCPY(conn->secrets.version.tls13.client_app_secret, application_secret.data, application_secret.size); + + /* Copy iv bytes from input data */ + POSIX_CHECKED_MEMCPY(server_implicit_iv, iv.data, iv.size); + POSIX_CHECKED_MEMCPY(client_implicit_iv, iv.data, iv.size); + + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* The maximum record number converted to base 256 */ + uint8_t max_record_limit[S2N_TLS_SEQUENCE_NUM_LEN] = { 0, 0, 0, 0, 1, 106, 9, 229 }; + + /* s2n_send sends key update if necessary */ + { + struct s2n_connection *server_conn; + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + server_conn->actual_protocol_version = S2N_TLS13; + client_conn->actual_protocol_version = S2N_TLS13; + + uint8_t zero_sequence_number[S2N_TLS_SEQUENCE_NUM_LEN] = { 0 }; + + EXPECT_SUCCESS(s2n_test_init_encryption(server_conn)); + EXPECT_SUCCESS(s2n_test_init_encryption(client_conn)); + + DEFER_CLEANUP(struct s2n_stuffer input, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer output, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, server_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&output, &input, client_conn)); + + /* Mimic key update send conditions */ + for (size_t i = 0; i < S2N_TLS_SEQUENCE_NUM_LEN; i++) { + server_conn->secure->server_sequence_number[i] = max_record_limit[i]; + } + + /* Next message to send will trigger key update message*/ + s2n_blocked_status blocked; + char message[] = "sent message"; + EXPECT_SUCCESS(s2n_send(server_conn, message, sizeof(message), &blocked)); + + /* Verify key update happened */ + EXPECT_BYTEARRAY_NOT_EQUAL(server_conn->secrets.version.tls13.server_app_secret, client_conn->secrets.version.tls13.server_app_secret, S2N_TLS13_SECRET_MAX_LEN); + EXPECT_BYTEARRAY_EQUAL(server_conn->secure->server_sequence_number, zero_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN); + + /* Receive keyupdate message */ + uint8_t data[100]; + EXPECT_SUCCESS(s2n_recv(client_conn, data, sizeof(message), &blocked)); + EXPECT_BYTEARRAY_EQUAL(data, message, sizeof(message)); + EXPECT_BYTEARRAY_EQUAL(client_conn->secrets.version.tls13.server_app_secret, server_conn->secrets.version.tls13.server_app_secret, S2N_TLS13_SECRET_MAX_LEN); + EXPECT_BYTEARRAY_EQUAL(client_conn->secure->server_sequence_number, zero_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* TLS 1.2 Server that receives TLS 1.3 KeyUpdate from Client should close connection */ + { + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + char *cert_chain; + char *private_key; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + struct s2n_config *server_config; + struct s2n_cert_chain_and_key *chain_and_key; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + struct s2n_config *client_config; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_check_stapled_ocsp_response(client_config, 0)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + client_conn->actual_protocol_version = S2N_TLS12; + client_conn->server_protocol_version = S2N_TLS12; + client_conn->client_protocol_version = S2N_TLS12; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + server_conn->client_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Force Client to send a TLS 1.3 KeyUpdate Message over TLS 1.2 connection */ + uint8_t key_update_data[S2N_KEY_UPDATE_MESSAGE_SIZE] = { 0 }; + struct s2n_blob key_update_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&key_update_blob, key_update_data, sizeof(key_update_data))); + EXPECT_SUCCESS(s2n_key_update_write(&key_update_blob)); + EXPECT_OK(s2n_record_write(client_conn, TLS_HANDSHAKE, &key_update_blob)); + EXPECT_SUCCESS(s2n_flush(client_conn, &blocked)); + + /* Attempt to recv on Server conn, see KeyUpdate Message, and confirm connection is closed. */ + uint8_t server_message[128]; + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(server_conn, server_message, sizeof(server_message), &blocked), S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + free(cert_chain); + free(private_key); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_send_multirecord_test.c b/tests/unit/s2n_send_multirecord_test.c new file mode 100644 index 00000000000..089cce2240a --- /dev/null +++ b/tests/unit/s2n_send_multirecord_test.c @@ -0,0 +1,583 @@ +/* + * 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 + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_post_handshake.h" +#include "tls/s2n_record.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_random.h" + +/* clang-format off */ +#define CLOSED_SEND_RESULT { .result = -1, .error = EPIPE } +#define BLOCK_SEND_RESULT { .result = -1, .error = EAGAIN } +#define PARTIAL_SEND_RESULT(bytes) { .result = bytes, .error = EAGAIN } +#define EXPECTED_SEND_RESULT(bytes) { .result = bytes, .assert_result = true } +#define OK_SEND_RESULT { .result = INT_MAX } +/* clang-format on */ + +int s2n_check_record_limit(struct s2n_connection *conn, struct s2n_blob *sequence_number); +bool s2n_should_flush(struct s2n_connection *conn, ssize_t total_message_size); + +struct s2n_send_result { + int result; + int error; + bool assert_result; +}; + +struct s2n_send_context { + size_t calls; + size_t bytes_sent; + const struct s2n_send_result *results; + const size_t results_len; +}; + +static int s2n_test_send_cb(void *io_context, const uint8_t *buf, uint32_t len) +{ + struct s2n_send_context *context = (struct s2n_send_context *) io_context; + POSIX_ENSURE_REF(context); + + POSIX_ENSURE_LT(context->calls, context->results_len); + const struct s2n_send_result *result = &context->results[context->calls]; + + int retval = MIN((int) len, result->result); + if (result->assert_result) { + POSIX_ENSURE_EQ(retval, len); + } + + context->calls++; + if (retval > 0) { + context->bytes_sent += retval; + } + + errno = result->error; + return retval; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + uint8_t test_data[] = "hello world"; + + uint8_t large_test_data[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH + 10] = { 0 }; + struct s2n_blob large_data_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&large_data_blob, large_test_data, sizeof(large_test_data))); + EXPECT_OK(s2n_get_public_random_data(&large_data_blob)); + + /* Small record sizes will require a LOT of calls to s2n_send. + * Use this context when they should all succeed. + */ + struct s2n_send_result results_all_ok[50] = { 0 }; + for (size_t i = 0; i < s2n_array_len(results_all_ok); i++) { + results_all_ok[i] = (struct s2n_send_result) OK_SEND_RESULT; + } + const struct s2n_send_context context_all_ok = { + .results = results_all_ok, + .results_len = s2n_array_len(results_all_ok) + }; + + /* Setup a large output buffer that can contain all of large_test_data */ + const size_t buffer_size = 20000; + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_send_buffer_size(config, buffer_size)); + + /* Setup an output buffer that is slightly too small for large_test_data */ + const uint32_t smaller_buffer_size = 17300; + DEFER_CLEANUP(struct s2n_config *smaller_buffer_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(smaller_buffer_config); + EXPECT_SUCCESS(s2n_config_set_send_buffer_size(smaller_buffer_config, smaller_buffer_size)); + + /* Test s2n_should_flush */ + { + /* Flush if multirecord send not enabled */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + + /* Multirecord send not enabled */ + EXPECT_FALSE(conn->multirecord_send); + EXPECT_TRUE(s2n_should_flush(conn, buffer_size)); + + /* Multirecord send enabled */ + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_TRUE(conn->multirecord_send); + EXPECT_FALSE(s2n_should_flush(conn, buffer_size)); + }; + + /* Flush if all data sent */ + { + ssize_t send_size = 100; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* No data sent */ + EXPECT_FALSE(s2n_should_flush(conn, send_size)); + + /* Some data sent */ + conn->current_user_data_consumed = send_size / 2; + EXPECT_FALSE(s2n_should_flush(conn, send_size)); + + /* All data sent */ + conn->current_user_data_consumed = send_size; + EXPECT_TRUE(s2n_should_flush(conn, send_size)); + }; + + /* Flush if buffer can't hold max size record */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Uninitialized buffer */ + EXPECT_FALSE(s2n_should_flush(conn, buffer_size)); + + /* Empty buffer */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&conn->out, buffer_size)); + EXPECT_FALSE(s2n_should_flush(conn, buffer_size)); + + /* Buffer not empty, but sufficient space remains */ + size_t max_record_size = S2N_TLS_MAX_RECORD_LEN_FOR(conn->max_outgoing_fragment_length); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&conn->out, buffer_size - max_record_size)); + EXPECT_FALSE(s2n_should_flush(conn, buffer_size)); + + /* Insufficient space in buffer */ + EXPECT_SUCCESS(s2n_stuffer_skip_write(&conn->out, 1)); + EXPECT_TRUE(s2n_should_flush(conn, buffer_size)); + }; + }; + + /* Total data fits in a single record. + * Equivalent to not using multirecord. + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + + EXPECT_EQUAL(context.calls, 1); + EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); + EXPECT_TRUE(context.bytes_sent > sizeof(test_data)); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, buffer_size); + }; + + /* Send buffer was configured too small for even a single record. + * Send smaller records. + * + * The minimum buffer size we allow generates a fragment size of 5, to prevent + * fragmenting KeyUpdate messages, which are always 5 bytes. At this minimum size, + * application data is also fragmented into 5 byte chunks, which is pretty silly, + * but is an edge case. + */ + { + DEFER_CLEANUP(struct s2n_config *min_buffer_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(min_buffer_config); + EXPECT_SUCCESS(s2n_config_set_send_buffer_size(min_buffer_config, S2N_MIN_SEND_BUFFER_SIZE)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, min_buffer_config)); + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + ssize_t send_size = sizeof(test_data); + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, test_data, send_size, &blocked), send_size); + + /* Since each record only contains two bytes of payload, + * we need to send a number of records equal to our total send ceil(size / 2). + */ + uint8_t remainder = (send_size % S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE) ? 1 : 0; + EXPECT_EQUAL(context.calls, (send_size / S2N_MIN_SEND_BUFFER_FRAGMENT_SIZE) + remainder); + EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); + EXPECT_TRUE(context.bytes_sent > send_size); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, S2N_MIN_SEND_BUFFER_SIZE); + }; + + /* Total data fits in multiple records. + * Without multirecord, this would result in multiple calls to send. + */ + uint16_t large_test_data_send_size = 0; + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); + + EXPECT_EQUAL(context.calls, 1); + EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); + EXPECT_TRUE(context.bytes_sent > sizeof(large_test_data)); + large_test_data_send_size = context.bytes_sent; + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, buffer_size); + }; + + /* Total data with multiple records too large for the send buffer. + * Call send multiple times. + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, smaller_buffer_config)); + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); + + EXPECT_EQUAL(context.calls, 2); + EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); + /* Even though it took more send calls, + * we still sent the same number of records with the same overhead. + */ + EXPECT_EQUAL(context.bytes_sent, large_test_data_send_size); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, smaller_buffer_size); + }; + + /* Block while buffering multiple records. + * Send blocks until all buffered data is sent. + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + const uint32_t partial_send = 10; + const uint32_t at_least_one_record = large_test_data_send_size - partial_send - 1; + const struct s2n_send_result results[] = { + /* First send writes less than one record before blocking */ + PARTIAL_SEND_RESULT(partial_send), BLOCK_SEND_RESULT, + /* Second send writes at least one record before blocking */ + PARTIAL_SEND_RESULT(at_least_one_record), BLOCK_SEND_RESULT, + /* Third send completes */ + OK_SEND_RESULT + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(context.bytes_sent, partial_send); + + /* Unlike when we buffer a single record at a time, s2n_send does not report each fragment / record flushed. + * Instead, it won't report any data as sent until all buffered data is flushed. + */ + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(context.bytes_sent, at_least_one_record + partial_send); + + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.bytes_sent, large_test_data_send_size); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, buffer_size); + }; + + /* Block while buffering multiple records across multiple send calls. + * Each send blocks until all buffered data is flushed. + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, smaller_buffer_config)); + + const uint32_t partial_send = 10; + const uint32_t at_least_one_flush = smaller_buffer_size - partial_send; + const struct s2n_send_result results[] = { + /* First send writes less than one record before blocking */ + PARTIAL_SEND_RESULT(partial_send), BLOCK_SEND_RESULT, + /* Second send flushes the output buffer before blocking */ + PARTIAL_SEND_RESULT(at_least_one_flush), BLOCK_SEND_RESULT, + /* Third send completes */ + OK_SEND_RESULT + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(context.bytes_sent, partial_send); + + /* Write the buffer, which contains two records. */ + ssize_t expected_sent = S2N_DEFAULT_FRAGMENT_LENGTH * 2; + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), expected_sent); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + + size_t offset = expected_sent; + expected_sent = sizeof(large_test_data) - offset; + EXPECT_EQUAL(s2n_send(conn, large_test_data + offset, sizeof(large_test_data) - offset, &blocked), expected_sent); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.bytes_sent, large_test_data_send_size); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, smaller_buffer_size); + }; + + /* Send a post-handshake message when records are buffered. + * + * We only test the KeyUpdate post-handshake message. That can trigger + * part way through a call to s2n_send if the encryption limit is reached. + * + * We can't reliably test NewSessionTicket post-handshake messages. + * Those could only trigger if an application called s2n_connection_add_new_tickets_to_send + * part way through a call to s2n_send, which requires calling from another thread + * at just the right (wrong?) time. + */ + { + s2n_blocked_status blocked = 0; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + + /* Find size of a KeyUpdate record */ + size_t key_update_size = 0; + { + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_atomic_flag_set(&conn->key_update_pending); + EXPECT_SUCCESS(s2n_post_handshake_send(conn, &blocked)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); + key_update_size = context.bytes_sent; + }; + EXPECT_TRUE(key_update_size > 0); + + const struct s2n_send_result results[] = { + /* We expect the buffer to be flushed before the post handshake message */ + OK_SEND_RESULT, + /* We expect the buffer to be flushed again after the post handshake message */ + EXPECTED_SEND_RESULT(key_update_size), + OK_SEND_RESULT + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + /* Find record limit */ + uint64_t limit = conn->secure->cipher_suite->record_alg->encryption_limit; + EXPECT_TRUE(limit > 0); + + /* Initialize sequence number */ + struct s2n_blob seq_num_blob = { 0 }; + struct s2n_stuffer seq_num_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&seq_num_blob, conn->secure->client_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + EXPECT_SUCCESS(s2n_stuffer_init(&seq_num_stuffer, &seq_num_blob)); + + /* Set the sequence number so that a KeyUpdate triggers after one more record. */ + uint64_t initial_seq_num = limit - 1; + EXPECT_SUCCESS(s2n_stuffer_write_uint64(&seq_num_stuffer, initial_seq_num)); + EXPECT_SUCCESS(s2n_check_record_limit(conn, &seq_num_blob)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); + + /* Send */ + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); + + /* Verify KeyUpdate happened: the sequence number was reset */ + uint64_t final_seq_num = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint64(&seq_num_stuffer, &final_seq_num)); + EXPECT_TRUE(final_seq_num < initial_seq_num); + + /* Verify expected send behavior */ + size_t expected_calls = 1 /* first record */ + 1 /* KeyUpdate */ + 1 /* remaining records */; + EXPECT_EQUAL(context.calls, expected_calls); + EXPECT_EQUAL(context.bytes_sent, large_test_data_send_size + key_update_size); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, buffer_size); + }; + + /* Test: Alert records are not buffered with ApplicationData records + * + * We flush before sending an alert, even if there is sufficient + * space for the alert record in the send buffer. + * + * If this behavior changed, then s2n_should_flush would need to consider + * the size of a possible alert. + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + + const uint32_t send_size = 10; + const uint32_t max_app_data_record_size = S2N_TLS_MAX_RECORD_LEN_FOR(send_size); + const uint32_t max_alert_record_size = S2N_TLS_MAX_RECORD_LEN_FOR(S2N_ALERT_LENGTH); + const uint32_t min_send_buffer_size = max_app_data_record_size + max_alert_record_size; + EXPECT_TRUE(min_send_buffer_size <= buffer_size); + + /* Queue the alert */ + EXPECT_OK(s2n_queue_reader_no_renegotiation_alert(conn)); + + /* Send the Application Data and Alert */ + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, large_test_data, send_size, &blocked), send_size); + + /* We expect two separate send calls: one for the application data record + * and one for the alert record. + */ + EXPECT_EQUAL(context.calls, 2); + + /* We expect that the output buffer never contained all data sent, + * since that data was split between two records. + */ + EXPECT_TRUE(conn->out.high_water_mark < context.bytes_sent); + }; + + /* Send a post-handshake message when records are buffered, and IO blocks */ + { + s2n_blocked_status blocked = 0; + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + + /* Find size of a KeyUpdate record */ + size_t key_update_size = 0; + { + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_atomic_flag_set(&conn->key_update_pending); + EXPECT_SUCCESS(s2n_post_handshake_send(conn, &blocked)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); + key_update_size = context.bytes_sent; + } + EXPECT_TRUE(key_update_size > 0); + + /* Block the first two calls to send, only allowing the third to succeed. */ + const struct s2n_send_result results[] = { + /* Initial ApplicationData records */ + BLOCK_SEND_RESULT, + BLOCK_SEND_RESULT, + OK_SEND_RESULT, + /* KeyUpdate record */ + BLOCK_SEND_RESULT, + BLOCK_SEND_RESULT, + EXPECTED_SEND_RESULT(key_update_size), + /* Remaining ApplicationData records */ + BLOCK_SEND_RESULT, + BLOCK_SEND_RESULT, + OK_SEND_RESULT, + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + /* Find record limit */ + uint64_t limit = conn->secure->cipher_suite->record_alg->encryption_limit; + EXPECT_TRUE(limit > 0); + + /* Initialize sequence number */ + struct s2n_blob seq_num_blob = { 0 }; + struct s2n_stuffer seq_num_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&seq_num_blob, conn->secure->client_sequence_number, S2N_TLS_SEQUENCE_NUM_LEN)); + EXPECT_SUCCESS(s2n_stuffer_init(&seq_num_stuffer, &seq_num_blob)); + + /* Set the sequence number so that a KeyUpdate triggers after one more record. */ + uint64_t initial_seq_num = limit - 1; + EXPECT_SUCCESS(s2n_stuffer_write_uint64(&seq_num_stuffer, initial_seq_num)); + EXPECT_SUCCESS(s2n_check_record_limit(conn, &seq_num_blob)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); + + /* Send until all data written */ + size_t total = 0; + while (total < sizeof(large_test_data)) { + ssize_t sent = s2n_send(conn, large_test_data + total, sizeof(large_test_data) - total, &blocked); + if (sent >= S2N_SUCCESS) { + total += sent; + } else { + EXPECT_EQUAL(s2n_errno, S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + } + } + + /* Verify KeyUpdate happened: the sequence number was reset */ + uint64_t final_seq_num = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint64(&seq_num_stuffer, &final_seq_num)); + EXPECT_TRUE(final_seq_num < initial_seq_num); + + /* Verify expected send behavior */ + size_t expected_calls = s2n_array_len(results); + EXPECT_EQUAL(context.calls, expected_calls); + EXPECT_EQUAL(context.bytes_sent, large_test_data_send_size + key_update_size); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, buffer_size); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_send_test.c b/tests/unit/s2n_send_test.c new file mode 100644 index 00000000000..3918be6f19a --- /dev/null +++ b/tests/unit/s2n_send_test.c @@ -0,0 +1,604 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_random.h" + +/* clang-format off */ +#define CLOSED_SEND_RESULT { .result = -1, .error = EPIPE } +#define BLOCK_SEND_RESULT { .result = -1, .error = EAGAIN } +#define PARTIAL_SEND_RESULT(bytes) { .result = bytes, .error = EAGAIN } +#define OK_SEND_RESULT { .result = INT_MAX } +/* clang-format on */ + +enum s2n_test_mfl { + S2N_MFL_DEFAULT = 0, + S2N_MFL_LARGE, + S2N_MFL_SMALL, + S2N_MFL_MINIMUM, + S2N_MFL_COUNT, +}; + +static S2N_RESULT s2n_set_test_max_fragment_len(struct s2n_connection *conn, enum s2n_test_mfl mfl) +{ + switch (mfl) { + case S2N_MFL_DEFAULT: + break; + case S2N_MFL_LARGE: + EXPECT_SUCCESS(s2n_connection_prefer_throughput(conn)); + break; + case S2N_MFL_SMALL: + EXPECT_SUCCESS(s2n_connection_prefer_low_latency(conn)); + break; + case S2N_MFL_MINIMUM: + conn->max_outgoing_fragment_length = mfl_code_to_length[1]; + break; + case S2N_MFL_COUNT: + RESULT_BAIL(S2N_ERR_INVALID_ARGUMENT); + break; + } + return S2N_RESULT_OK; +} + +struct s2n_send_result { + int result; + int error; +}; + +struct s2n_send_context { + size_t calls; + size_t bytes_sent; + const struct s2n_send_result *results; + const size_t results_len; +}; + +bool s2n_custom_send_fn_called = false; +int s2n_expect_concurrent_error_send_fn(void *io_context, const uint8_t *buf, uint32_t len) +{ + struct s2n_connection *conn = (struct s2n_connection *) io_context; + s2n_custom_send_fn_called = true; + + s2n_blocked_status blocked = 0; + ssize_t result = s2n_send(conn, buf, len, &blocked); + EXPECT_FAILURE_WITH_ERRNO(result, S2N_ERR_REENTRANCY); + return result; +} + +static int s2n_test_send_cb(void *io_context, const uint8_t *buf, uint32_t len) +{ + struct s2n_send_context *context = (struct s2n_send_context *) io_context; + POSIX_ENSURE_REF(context); + + POSIX_ENSURE_LT(context->calls, context->results_len); + const struct s2n_send_result *result = &context->results[context->calls]; + + int retval = MIN((int) len, result->result); + + context->calls++; + if (retval > 0) { + context->bytes_sent += retval; + } + + errno = result->error; + return retval; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + uint8_t test_data[] = "hello world"; + + uint8_t large_test_data[S2N_TLS_MAXIMUM_FRAGMENT_LENGTH + 10] = { 0 }; + struct s2n_blob large_data_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&large_data_blob, large_test_data, sizeof(large_test_data))); + EXPECT_OK(s2n_get_public_random_data(&large_data_blob)); + + /* Small record sizes will require a LOT of calls to s2n_send. + * Use this context when they should all succeed. + */ + struct s2n_send_result results_all_ok[50] = { 0 }; + for (size_t i = 0; i < s2n_array_len(results_all_ok); i++) { + results_all_ok[i] = (struct s2n_send_result) OK_SEND_RESULT; + } + const struct s2n_send_context context_all_ok = { + .results = results_all_ok, + .results_len = s2n_array_len(results_all_ok) + }; + + /* Calculating the record size for given data can be tricky. + * Instead, let's set the values based on the results of tests. + */ + ssize_t test_data_bytes_sent = 0; + + /* s2n_send */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + + EXPECT_EQUAL(context.calls, 1); + EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); + + /* Set the expected record size for future tests */ + test_data_bytes_sent = context.bytes_sent; + EXPECT_TRUE(test_data_bytes_sent > sizeof(test_data)); + }; + + /* Calculating the max record size for a given max fragment length can be tricky. + * Instead, let's set the values based on the results of tests. + */ + ssize_t max_frag_bytes_sent[S2N_MFL_COUNT] = { 0 }; + + /* Track the size of the output buffer. + * It should be constant across all tests with the same max fragment length. + */ + uint32_t out_size[S2N_MFL_COUNT] = { 0 }; + + /* Send exactly the maximum fragment size */ + for (size_t mfl = 0; mfl < S2N_MFL_COUNT; mfl++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_OK(s2n_set_test_max_fragment_len(conn, mfl)); + uint32_t fragment_len = conn->max_outgoing_fragment_length; + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + + /* Send exactly the fragment length */ + EXPECT_EQUAL(s2n_send(conn, large_test_data, fragment_len, &blocked), fragment_len); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.calls, 1); + + /* Set the expected record size for future tests */ + max_frag_bytes_sent[mfl] = context.bytes_sent; + EXPECT_TRUE(max_frag_bytes_sent[mfl] > 0); + + /* Set the expected output buffer size for future tests */ + out_size[mfl] = conn->out.blob.size; + EXPECT_TRUE(out_size[mfl] > 0); + + /* Sanity check: Send one byte more than the fragment length. + * If this is actually the maximum fragment length, one extra byte will + * lead to an extra record / extra call to send. + */ + context.calls = 0; + EXPECT_EQUAL(s2n_send(conn, large_test_data, fragment_len + 1, &blocked), fragment_len + 1); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.calls, 2); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[mfl]); + } + + /* s2n_send cannot be called concurrently */ + { + /* Setup connections */ + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + /* Setup bad send callback */ + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_expect_concurrent_error_send_fn)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) conn)); + EXPECT_SUCCESS(s2n_connection_set_blinding(conn, S2N_SELF_SERVICE_BLINDING)); + + s2n_blocked_status blocked = 0; + s2n_custom_send_fn_called = false; + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, test_data, sizeof(test_data), &blocked), + S2N_ERR_IO); + EXPECT_TRUE(s2n_custom_send_fn_called); + EXPECT_EQUAL(0, conn->wire_bytes_out); + }; + + /* s2n_send tracks conn->wire_bytes_out on send */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_EQUAL(0, conn->wire_bytes_out); + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + + EXPECT_EQUAL(context.calls, 1); + EXPECT_EQUAL(context.bytes_sent, test_data_bytes_sent); + EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); + EXPECT_EQUAL(context.bytes_sent, s2n_connection_get_wire_bytes_out(conn)); + }; + + /* s2n_send tracks conn->wire_bytes_out on partial send */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_EQUAL(0, conn->wire_bytes_out); + + const uint32_t partial_send = 10; + const struct s2n_send_result results[] = { + PARTIAL_SEND_RESULT(partial_send), + CLOSED_SEND_RESULT, + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, test_data, sizeof(test_data), &blocked), S2N_ERR_IO); + + EXPECT_EQUAL(context.calls, 2); + EXPECT_EQUAL(context.bytes_sent, partial_send); + EXPECT_EQUAL(context.bytes_sent, conn->wire_bytes_out); + EXPECT_EQUAL(context.bytes_sent, s2n_connection_get_wire_bytes_out(conn)); + }; + + /* s2n_send sends all data, despite partial writes */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + const struct s2n_send_result results[] = { + PARTIAL_SEND_RESULT(1), + PARTIAL_SEND_RESULT(5), + PARTIAL_SEND_RESULT(2), + OK_SEND_RESULT, + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.calls, s2n_array_len(results)); + EXPECT_EQUAL(context.bytes_sent, test_data_bytes_sent); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + }; + + /* s2n_send would block and must be retried */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + const uint32_t partial_send = 10; + const struct s2n_send_result results[] = { + PARTIAL_SEND_RESULT(partial_send), + BLOCK_SEND_RESULT, + PARTIAL_SEND_RESULT(partial_send), + BLOCK_SEND_RESULT, + OK_SEND_RESULT + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + + /* First attempt blocks */ + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, test_data, sizeof(test_data), &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(context.bytes_sent, partial_send); + + /* Second attempt blocks */ + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, test_data, sizeof(test_data), &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(context.bytes_sent, partial_send * 2); + + /* Third attempt completes */ + EXPECT_EQUAL(s2n_send(conn, test_data, sizeof(test_data), &blocked), sizeof(test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.bytes_sent, test_data_bytes_sent); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + }; + + /* Calculating the record size for given data can be tricky. + * Instead, let's set the values based on the results of tests. + */ + ssize_t large_test_data_bytes_sent = 0; + + /* s2n_send sends multiple records worth of data */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + struct s2n_send_result results[] = { OK_SEND_RESULT, OK_SEND_RESULT, OK_SEND_RESULT }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.calls, s2n_array_len(results)); + + large_test_data_bytes_sent = context.bytes_sent; + EXPECT_TRUE(large_test_data_bytes_sent > sizeof(large_test_data)); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + }; + + /* s2n_send sends all records and data, despite partial writes */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + struct s2n_send_result results[] = { + PARTIAL_SEND_RESULT(10), OK_SEND_RESULT, + OK_SEND_RESULT, + PARTIAL_SEND_RESULT(5), PARTIAL_SEND_RESULT(1), OK_SEND_RESULT + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.calls, s2n_array_len(results)); + EXPECT_EQUAL(context.bytes_sent, large_test_data_bytes_sent); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + }; + + /* s2n_send would block while sending multiple records */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + const uint32_t partial_send = 10; + struct s2n_send_result results[] = { + OK_SEND_RESULT, + PARTIAL_SEND_RESULT(partial_send), + BLOCK_SEND_RESULT, + PARTIAL_SEND_RESULT(partial_send), + BLOCK_SEND_RESULT, + OK_SEND_RESULT, OK_SEND_RESULT, OK_SEND_RESULT + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + ssize_t record_size = max_frag_bytes_sent[S2N_MFL_DEFAULT]; + + /* First attempt blocks after writing one record */ + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), + S2N_DEFAULT_FRAGMENT_LENGTH); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(context.bytes_sent, record_size + partial_send); + + /* Don't re-send the data already sent. */ + const uint32_t offset = S2N_DEFAULT_FRAGMENT_LENGTH; + + /* Second attempt blocks without writing another record */ + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data + offset, sizeof(large_test_data) - offset, &blocked), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(context.bytes_sent, record_size + partial_send + partial_send); + + /* Third attempt completes */ + EXPECT_EQUAL(s2n_send(conn, large_test_data + offset, sizeof(large_test_data) - offset, &blocked), + sizeof(large_test_data) - S2N_DEFAULT_FRAGMENT_LENGTH); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.bytes_sent, large_test_data_bytes_sent); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + }; + + /* s2n_send would block after sending multiple records. + * ALL flushed records must be reported to the caller. + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + struct s2n_send_result results[] = { + OK_SEND_RESULT, + OK_SEND_RESULT, + BLOCK_SEND_RESULT, + OK_SEND_RESULT, + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + ssize_t record_size = max_frag_bytes_sent[S2N_MFL_DEFAULT]; + + /* First attempt blocks after writing two records */ + ssize_t expected_send = S2N_DEFAULT_FRAGMENT_LENGTH * 2; + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), expected_send); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(context.bytes_sent, record_size * 2); + + /* Don't re-send the data already sent. */ + const uint32_t offset = expected_send; + + /* Second attempt completes */ + EXPECT_EQUAL(s2n_send(conn, large_test_data + offset, sizeof(large_test_data) - offset, &blocked), + sizeof(large_test_data) - offset); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_EQUAL(context.bytes_sent, large_test_data_bytes_sent); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + }; + + /* Sending multiple records supports different maximum fragment lengths */ + for (size_t mfl = 0; mfl < S2N_MFL_COUNT; mfl++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + EXPECT_OK(s2n_set_test_max_fragment_len(conn, mfl)); + + struct s2n_send_context context = context_all_ok; + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + + s2n_blocked_status blocked = 0; + EXPECT_EQUAL(s2n_send(conn, large_test_data, sizeof(large_test_data), &blocked), sizeof(large_test_data)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + /* We expect enough calls to send to split the payload into records */ + size_t expected_calls = ceil(sizeof(large_test_data) / (double) conn->max_outgoing_fragment_length); + EXPECT_EQUAL(context.calls, expected_calls); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[mfl]); + } + + /* Test dynamic record threshold record fragmentation */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_connection_set_secrets(conn)); + + /* Retrieve the fragment size to expect */ + uint16_t single_mtu_mfl = 0; + EXPECT_OK(s2n_record_min_write_payload_size(conn, &single_mtu_mfl)); + + /* Set the dynamic record threshold large enough for two small records */ + const uint32_t resize_threshold = single_mtu_mfl * 2; + EXPECT_SUCCESS(s2n_connection_set_dynamic_record_threshold(conn, resize_threshold, UINT16_MAX)); + + struct s2n_send_result results[] = { + /* Block before sending the first record so that we can examine + * the connection state after buffering the first record. + */ + BLOCK_SEND_RESULT, OK_SEND_RESULT, + /* Send the second record */ + BLOCK_SEND_RESULT, OK_SEND_RESULT, + /* Send the third record */ + OK_SEND_RESULT, + BLOCK_SEND_RESULT + }; + struct s2n_send_context context = { .results = results, .results_len = s2n_array_len(results) }; + EXPECT_SUCCESS(s2n_connection_set_send_ctx(conn, (void *) &context)); + EXPECT_SUCCESS(s2n_connection_set_send_cb(conn, s2n_test_send_cb)); + + s2n_blocked_status blocked = 0; + const size_t send_size = single_mtu_mfl * 2; + + /* The first call to s2n_send blocks before sending the first record. */ + EXPECT_FAILURE_WITH_ERRNO(s2n_send(conn, large_test_data, send_size, &blocked), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + /* No records have been sent yet. */ + EXPECT_EQUAL(context.bytes_sent, 0); + /* The first record is buffered, + * so its bytes still count towards the resize_threshold. + * We have NOT passed the threshold. + */ + EXPECT_EQUAL(conn->active_application_bytes_consumed, single_mtu_mfl); + EXPECT_TRUE(conn->active_application_bytes_consumed < resize_threshold); + /* Output buffer should be able to handle the default size, not the single MTU size. + * Otherwise, the output buffer would need to resize later. + */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + + /* The second call to s2n_send flushes the buffered first record, + * but blocks before sending the second record. + */ + ssize_t result = s2n_send(conn, large_test_data, send_size, &blocked); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + /* First small, single-MTU record was sent. */ + EXPECT_EQUAL(result, single_mtu_mfl); + EXPECT_TRUE(context.bytes_sent < ETH_MTU); + /* The second record is buffered, + * so its bytes count towards the resize_threshold. + * We have therefore hit the threshold. + */ + EXPECT_EQUAL(conn->active_application_bytes_consumed, resize_threshold); + /* Output buffer should be able to handle the default size, not the single MTU size. + * Otherwise, the output buffer would need to resize later. + */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + + /* The third call to s2n_send flushes the second record. */ + result = s2n_send(conn, large_test_data, send_size - single_mtu_mfl, &blocked); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + /* Second small, single-MTU record was sent. */ + EXPECT_EQUAL(result, single_mtu_mfl); + /* There should be no change regarding the resize_threshold, + * since we did not construct any new records. + */ + EXPECT_EQUAL(conn->active_application_bytes_consumed, resize_threshold); + /* Output buffer should be able to handle the default size, not the single MTU size. + * Otherwise, the output buffer would need to resize later. + */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + + /* The fourth call to s2n_send sends the third record. */ + result = s2n_send(conn, large_test_data, conn->max_outgoing_fragment_length * 2, &blocked); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + /* We have passed the resize_threshold, so records are no longer small. + * Instead they use the standard connection fragment length. + */ + EXPECT_TRUE(result > single_mtu_mfl); + EXPECT_EQUAL(result, conn->max_outgoing_fragment_length); + + /* Verify output buffer */ + EXPECT_EQUAL(conn->out.blob.size, out_size[S2N_MFL_DEFAULT]); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_sequence_number_test.c b/tests/unit/s2n_sequence_number_test.c new file mode 100644 index 00000000000..f7d853d031f --- /dev/null +++ b/tests/unit/s2n_sequence_number_test.c @@ -0,0 +1,108 @@ +/* + * 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 "crypto/s2n_sequence.h" +#include "error/s2n_errno.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_crypto.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + /* s2n_sequence_number_to_uint64 */ + { + /* Converts zero */ + { + uint64_t output = 1; + uint8_t data[S2N_TLS_SEQUENCE_NUM_LEN] = { 0 }; + struct s2n_blob sequence_number = { 0 }; + + EXPECT_SUCCESS(s2n_blob_init(&sequence_number, data, S2N_TLS_SEQUENCE_NUM_LEN)); + + EXPECT_SUCCESS(s2n_sequence_number_to_uint64(&sequence_number, &output)); + + EXPECT_EQUAL(output, 0); + }; + + /* Converts one */ + { + uint64_t output = 0; + uint8_t data[S2N_TLS_SEQUENCE_NUM_LEN] = { 0 }; + data[S2N_TLS_SEQUENCE_NUM_LEN - 1] = 1; + struct s2n_blob sequence_number = { 0 }; + + EXPECT_SUCCESS(s2n_blob_init(&sequence_number, data, S2N_TLS_SEQUENCE_NUM_LEN)); + + EXPECT_SUCCESS(s2n_sequence_number_to_uint64(&sequence_number, &output)); + + EXPECT_EQUAL(output, 1); + }; + + /* Converts max possible sequence number */ + { + uint64_t output = 0; + uint8_t data[S2N_TLS_SEQUENCE_NUM_LEN] = { 0 }; + struct s2n_blob sequence_number = { 0 }; + + EXPECT_SUCCESS(s2n_blob_init(&sequence_number, data, S2N_TLS_SEQUENCE_NUM_LEN)); + EXPECT_SUCCESS(s2n_blob_zero(&sequence_number)); + + for (size_t i = 0; i < S2N_TLS_SEQUENCE_NUM_LEN; i++) { + sequence_number.data[i] = UINT8_MAX; + } + + EXPECT_SUCCESS(s2n_sequence_number_to_uint64(&sequence_number, &output)); + + EXPECT_EQUAL(output, 18446744073709551615U); + }; + + /* Converts max record number value */ + { + uint64_t output = 0; + + /* The maximum record number converted to base 256 */ + uint8_t data[S2N_TLS_SEQUENCE_NUM_LEN] = { 0, 0, 0, 0, 1, 106, 9, 229 }; + struct s2n_blob sequence_number = { 0 }; + + EXPECT_SUCCESS(s2n_blob_init(&sequence_number, data, S2N_TLS_SEQUENCE_NUM_LEN)); + + EXPECT_SUCCESS(s2n_sequence_number_to_uint64(&sequence_number, &output)); + + EXPECT_EQUAL(output, S2N_TLS13_AES_GCM_MAXIMUM_RECORD_NUMBER); + }; + + /* Matches network order stuffer methods */ + { + uint64_t input = 0x1234ABCD; + + /* Use stuffer to convert to network order */ + uint8_t stuffer_bytes[S2N_TLS_SEQUENCE_NUM_LEN] = { 0 }; + struct s2n_blob stuffer_blob = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&stuffer_blob, stuffer_bytes, sizeof(stuffer_bytes))); + EXPECT_SUCCESS(s2n_stuffer_init(&stuffer, &stuffer_blob)); + EXPECT_SUCCESS(s2n_stuffer_write_uint64(&stuffer, input)); + + /* Use s2n_sequence_number_to_uint64 to convert back */ + uint64_t output = 0; + EXPECT_SUCCESS(s2n_sequence_number_to_uint64(&stuffer_blob, &output)); + + EXPECT_EQUAL(input, output); + }; + }; + END_TEST(); +} diff --git a/tests/unit/s2n_server_alpn_extension_test.c b/tests/unit/s2n_server_alpn_extension_test.c new file mode 100644 index 00000000000..4d35eaabdb3 --- /dev/null +++ b/tests/unit/s2n_server_alpn_extension_test.c @@ -0,0 +1,125 @@ +/* + * 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 "s2n_test.h" +#include "tls/extensions/s2n_server_alpn.h" +#include "tls/s2n_connection.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + const char *test_protocol_name = "chosen_protocol"; + const uint8_t test_protocol_name_size = strlen(test_protocol_name); + + /* Test should_send */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + /* Should not send if protocol not set. Protocol not set by default. */ + EXPECT_FALSE(s2n_server_alpn_extension.should_send(conn)); + + /* Should send if protocol set. */ + EXPECT_MEMCPY_SUCCESS(conn->application_protocol, test_protocol_name, test_protocol_name_size); + EXPECT_TRUE(s2n_server_alpn_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test send */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_MEMCPY_SUCCESS(conn->application_protocol, test_protocol_name, test_protocol_name_size); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_server_alpn_extension.send(conn, &stuffer)); + + /* Should have correct total size */ + uint16_t protocol_name_list_size; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &protocol_name_list_size)); + EXPECT_EQUAL(protocol_name_list_size, s2n_stuffer_data_available(&stuffer)); + + /* Should have correct protocol name size */ + uint8_t protocol_name_size; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&stuffer, &protocol_name_size)); + EXPECT_EQUAL(protocol_name_size, s2n_stuffer_data_available(&stuffer)); + EXPECT_EQUAL(protocol_name_size, test_protocol_name_size); + + /* Should have correct protocol name */ + uint8_t *protocol_name; + EXPECT_NOT_NULL(protocol_name = s2n_stuffer_raw_read(&stuffer, protocol_name_size)); + EXPECT_BYTEARRAY_EQUAL(protocol_name, test_protocol_name, test_protocol_name_size); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test recv */ + { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_MEMCPY_SUCCESS(server_conn->application_protocol, test_protocol_name, test_protocol_name_size); + + /* Should accept extension written by send */ + { + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_server_alpn_extension.send(server_conn, &stuffer)); + + EXPECT_NULL(s2n_get_application_protocol(client_conn)); + EXPECT_SUCCESS(s2n_server_alpn_extension.recv(client_conn, &stuffer)); + EXPECT_NOT_NULL(s2n_get_application_protocol(client_conn)); + EXPECT_STRING_EQUAL(s2n_get_application_protocol(client_conn), test_protocol_name); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + /* Should ignore extension if protocol name list size incorrect */ + { + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_server_alpn_extension.send(server_conn, &stuffer)); + EXPECT_SUCCESS(s2n_stuffer_wipe_n(&stuffer, 1)); + + EXPECT_NULL(s2n_get_application_protocol(client_conn)); + EXPECT_SUCCESS(s2n_server_alpn_extension.recv(client_conn, &stuffer)); + EXPECT_NULL(s2n_get_application_protocol(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_server_cert_request_test.c b/tests/unit/s2n_server_cert_request_test.c new file mode 100644 index 00000000000..e376453e982 --- /dev/null +++ b/tests/unit/s2n_server_cert_request_test.c @@ -0,0 +1,110 @@ +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_config.h" +#include "tls/s2n_tls.h" + +/* + * Definitions in s2n_server_cert_request.c + */ +typedef enum { + S2N_CERT_TYPE_RSA_SIGN = 1, + S2N_CERT_TYPE_DSS_SIGN = 2, + S2N_CERT_TYPE_RSA_FIXED_DH = 3, + S2N_CERT_TYPE_DSS_FIXED_DH = 4, + S2N_CERT_TYPE_RSA_EPHEMERAL_DH_RESERVED = 5, + S2N_CERT_TYPE_DSS_EPHEMERAL_DH_RESERVED = 6, + S2N_CERT_TYPE_FORTEZZA_DMS_RESERVED = 20, + S2N_CERT_TYPE_ECDSA_SIGN = 64, + S2N_CERT_TYPE_RSA_FIXED_ECDH = 65, + S2N_CERT_TYPE_ECDSA_FIXED_ECDH = 66, +} s2n_cert_type; + +static uint8_t s2n_cert_type_preference_list[] = { + S2N_CERT_TYPE_RSA_SIGN, + S2N_CERT_TYPE_ECDSA_SIGN +}; + +static uint8_t s2n_cert_type_preference_list_legacy_dss[] = { + S2N_CERT_TYPE_RSA_SIGN, + S2N_CERT_TYPE_DSS_SIGN, + S2N_CERT_TYPE_ECDSA_SIGN +}; + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Test server cert request default behavior when s2n_config_enable_cert_req_dss_legacy_compat is not called + * Certificate types enabled should be in s2n_cert_type_preference_list */ + { + struct s2n_connection *server_conn; + struct s2n_config *server_config; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + s2n_cert_req_send(server_conn); + struct s2n_stuffer *in = &server_conn->handshake.io; + uint8_t cert_types_len; + + s2n_stuffer_read_uint8(in, &cert_types_len); + + uint8_t *their_cert_type_pref_list = s2n_stuffer_raw_read(in, cert_types_len); + + EXPECT_EQUAL(cert_types_len, sizeof(s2n_cert_type_preference_list)); + for (size_t idx = 0; idx < sizeof(s2n_cert_type_preference_list); idx++) { + EXPECT_EQUAL(their_cert_type_pref_list[idx], s2n_cert_type_preference_list[idx]); + } + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test certificate types in server cert request when s2n_config_enable_cert_req_dss_legacy_compat is called + * Certificate types enabled should be in s2n_cert_type_preference_list_legacy_dss */ + { + struct s2n_connection *server_conn; + struct s2n_config *server_config; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_config_enable_cert_req_dss_legacy_compat(server_config)); + + s2n_cert_req_send(server_conn); + struct s2n_stuffer *in = &server_conn->handshake.io; + uint8_t cert_types_len; + + s2n_stuffer_read_uint8(in, &cert_types_len); + + uint8_t *their_cert_type_pref_list = s2n_stuffer_raw_read(in, cert_types_len); + + EXPECT_EQUAL(cert_types_len, sizeof(s2n_cert_type_preference_list_legacy_dss)); + for (size_t idx = 0; idx < sizeof(s2n_cert_type_preference_list_legacy_dss); idx++) { + EXPECT_EQUAL(their_cert_type_pref_list[idx], s2n_cert_type_preference_list_legacy_dss[idx]); + } + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_server_cert_status_request_extension_test.c b/tests/unit/s2n_server_cert_status_request_extension_test.c new file mode 100644 index 00000000000..c0794239dd9 --- /dev/null +++ b/tests/unit/s2n_server_cert_status_request_extension_test.c @@ -0,0 +1,68 @@ +/* +* 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 "s2n_test.h" +#include "tls/extensions/s2n_server_cert_status_request.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Extension should not be sent by default */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + EXPECT_FALSE(s2n_server_cert_status_request_extension.should_send(conn)); + } + + /* Extension should be sent if OCSP stapling is supported and was requested */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + if (s2n_x509_ocsp_stapling_supported()) { + EXPECT_SUCCESS(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_OCSP)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_TRUE(s2n_server_cert_status_request_extension.should_send(conn)); + } else { + /* Requesting OCSP stapling should not be possible if not supported */ + EXPECT_FAILURE_WITH_ERRNO(s2n_config_set_status_request_type(config, S2N_STATUS_REQUEST_OCSP), + S2N_ERR_OCSP_NOT_SUPPORTED); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_FALSE(s2n_server_cert_status_request_extension.should_send(conn)); + } + } + + /* Extension should be empty */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_server_cert_status_request_extension.send(conn, &stuffer)); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_server_early_data_indication_test.c b/tests/unit/s2n_server_early_data_indication_test.c new file mode 100644 index 00000000000..0e78b3fb477 --- /dev/null +++ b/tests/unit/s2n_server_early_data_indication_test.c @@ -0,0 +1,307 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_early_data_indication.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_array.h" + +static S2N_RESULT s2n_exchange_hellos(struct s2n_connection *client_conn, struct s2n_connection *server_conn) +{ + RESULT_GUARD_POSIX(s2n_client_hello_send(client_conn)); + RESULT_GUARD_POSIX(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + RESULT_GUARD_POSIX(s2n_establish_session(server_conn)); + + RESULT_GUARD_POSIX(s2n_server_hello_send(server_conn)); + RESULT_GUARD_POSIX(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + RESULT_GUARD_POSIX(s2n_server_hello_recv(client_conn)); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + const uint32_t nonzero_max_early_data = 10; + + /* Test s2n_server_early_data_indication_should_send */ + { + /* Safety check */ + EXPECT_FALSE(s2n_server_early_data_indication_extension.should_send(NULL)); + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + + conn->early_data_state = S2N_EARLY_DATA_NOT_REQUESTED; + EXPECT_FALSE(s2n_server_early_data_indication_extension.should_send(conn)); + + conn->early_data_state = S2N_EARLY_DATA_REJECTED; + EXPECT_FALSE(s2n_server_early_data_indication_extension.should_send(conn)); + + conn->early_data_state = S2N_EARLY_DATA_ACCEPTED; + EXPECT_TRUE(s2n_server_early_data_indication_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_server_early_data_indication_is_missing */ + { + /* No-op if early data not requested */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + + conn->early_data_state = S2N_EARLY_DATA_NOT_REQUESTED; + EXPECT_SUCCESS(s2n_server_early_data_indication_extension.if_missing(conn)); + EXPECT_EQUAL(conn->early_data_state, S2N_EARLY_DATA_NOT_REQUESTED); + + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + EXPECT_SUCCESS(s2n_server_early_data_indication_extension.if_missing(conn)); + EXPECT_EQUAL(conn->early_data_state, S2N_EARLY_DATA_REJECTED); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test s2n_server_early_data_indication_recv */ + { + struct s2n_stuffer stuffer = { 0 }; + + /* Fails if early data config does not match the connection */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + conn->actual_protocol_version = S2N_TLS13; + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + + /* No early data configured */ + EXPECT_FAILURE_WITH_ERRNO(s2n_server_early_data_indication_extension.recv(conn, &stuffer), + S2N_ERR_EARLY_DATA_NOT_ALLOWED); + EXPECT_NOT_EQUAL(conn->early_data_state, S2N_EARLY_DATA_ACCEPTED); + + /* Early data correctly configured */ + EXPECT_OK(s2n_append_test_chosen_psk_with_early_data(conn, nonzero_max_early_data, &s2n_tls13_aes_256_gcm_sha384)); + EXPECT_SUCCESS(s2n_server_early_data_indication_extension.recv(conn, &stuffer)); + EXPECT_EQUAL(conn->early_data_state, S2N_EARLY_DATA_ACCEPTED); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Fails if early data not requested */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_append_test_chosen_psk_with_early_data(conn, nonzero_max_early_data, &s2n_tls13_aes_256_gcm_sha384)); + + /* Early data not requested */ + conn->early_data_state = S2N_EARLY_DATA_NOT_REQUESTED; + EXPECT_FAILURE_WITH_ERRNO(s2n_server_early_data_indication_extension.recv(conn, &stuffer), + S2N_ERR_INVALID_EARLY_DATA_STATE); + + /* Early data requested */ + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + EXPECT_SUCCESS(s2n_server_early_data_indication_extension.recv(conn, &stuffer)); + EXPECT_EQUAL(conn->early_data_state, S2N_EARLY_DATA_ACCEPTED); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test state transitions */ + { + const char *security_policy = "20190801"; + struct s2n_cipher_suite *expected_cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + + /* When early data not requested */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_OK(s2n_append_test_chosen_psk_with_early_data(client_conn, 0, expected_cipher_suite)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, security_policy)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_OK(s2n_append_test_chosen_psk_with_early_data(server_conn, 0, expected_cipher_suite)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, security_policy)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + + EXPECT_OK(s2n_exchange_hellos(client_conn, server_conn)); + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_NOT_REQUESTED); + EXPECT_EQUAL(server_conn->early_data_state, S2N_EARLY_DATA_NOT_REQUESTED); + + EXPECT_SUCCESS(s2n_encrypted_extensions_send(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + EXPECT_SUCCESS(s2n_encrypted_extensions_recv(client_conn)); + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_NOT_REQUESTED); + EXPECT_EQUAL(server_conn->early_data_state, S2N_EARLY_DATA_NOT_REQUESTED); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /** When early data accepted. + * + *= https://tools.ietf.org/rfc/rfc8446#section-4.2.10 + *= type=test + *# A server which receives an "early_data" extension MUST behave in one + *# of three ways: + * + *= https://tools.ietf.org/rfc/rfc8446#section-4.2.10 + *= type=test + *# - Return its own "early_data" extension in EncryptedExtensions, + *# indicating that it intends to process the early data. + **/ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_OK(s2n_append_test_chosen_psk_with_early_data(client_conn, nonzero_max_early_data, expected_cipher_suite)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, security_policy)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_OK(s2n_append_test_chosen_psk_with_early_data(server_conn, nonzero_max_early_data, expected_cipher_suite)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, security_policy)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + + EXPECT_OK(s2n_exchange_hellos(client_conn, server_conn)); + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_REQUESTED); + EXPECT_EQUAL(server_conn->early_data_state, S2N_EARLY_DATA_ACCEPTED); + + EXPECT_SUCCESS(s2n_encrypted_extensions_send(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + EXPECT_SUCCESS(s2n_encrypted_extensions_recv(client_conn)); + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_ACCEPTED); + EXPECT_EQUAL(server_conn->early_data_state, S2N_EARLY_DATA_ACCEPTED); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /** When early data rejected. + * + *= https://tools.ietf.org/rfc/rfc8446#section-4.2.10 + *# A server which receives an "early_data" extension MUST behave in one + *# of three ways: + *# + *# - Ignore the extension and return a regular 1-RTT response. + **/ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_OK(s2n_append_test_chosen_psk_with_early_data(client_conn, nonzero_max_early_data, expected_cipher_suite)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, security_policy)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_OK(s2n_append_test_chosen_psk_with_early_data(server_conn, 0, expected_cipher_suite)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, security_policy)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + + EXPECT_OK(s2n_exchange_hellos(client_conn, server_conn)); + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_REQUESTED); + EXPECT_EQUAL(server_conn->early_data_state, S2N_EARLY_DATA_REJECTED); + + EXPECT_SUCCESS(s2n_encrypted_extensions_send(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + EXPECT_SUCCESS(s2n_encrypted_extensions_recv(client_conn)); + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_REJECTED); + EXPECT_EQUAL(server_conn->early_data_state, S2N_EARLY_DATA_REJECTED); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* + *= https://tools.ietf.org/rfc/rfc8446#section-4.2.10 + *= type=test + *# A server which receives an "early_data" extension MUST behave in one + *# of three ways: + * + *= https://tools.ietf.org/rfc/rfc8446#section-4.2.10 + *= type=test + *# - Request that the client send another ClientHello by responding + *# with a HelloRetryRequest. + **/ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_OK(s2n_append_test_chosen_psk_with_early_data(client_conn, nonzero_max_early_data, expected_cipher_suite)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, security_policy)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_OK(s2n_append_test_chosen_psk_with_early_data(server_conn, nonzero_max_early_data, expected_cipher_suite)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, security_policy)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_REQUESTED); + + /* Force a retry. The S2N server does not reject early data via HRR, so we have + * to manually trigger the retry. */ + server_conn->early_data_state = S2N_EARLY_DATA_REJECTED; + EXPECT_SUCCESS(s2n_set_hello_retry_required(server_conn)); + server_conn->handshake.message_number = 2; + client_conn->handshake.message_number = 2; + + /* Update the selected_group to ensure the HRR is valid */ + client_conn->kex_params.client_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp521r1; + + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_REJECTED); + EXPECT_EQUAL(server_conn->early_data_state, S2N_EARLY_DATA_REJECTED); + + EXPECT_SUCCESS(s2n_encrypted_extensions_send(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + EXPECT_SUCCESS(s2n_encrypted_extensions_recv(client_conn)); + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_REJECTED); + EXPECT_EQUAL(server_conn->early_data_state, S2N_EARLY_DATA_REJECTED); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + }; + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_server_extensions_test.c b/tests/unit/s2n_server_extensions_test.c new file mode 100644 index 00000000000..91c101327b6 --- /dev/null +++ b/tests/unit/s2n_server_extensions_test.c @@ -0,0 +1,682 @@ +/* + * 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 "tls/s2n_server_extensions.h" + +#include "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_cert_status_response.h" +#include "tls/extensions/s2n_ec_point_format.h" +#include "tls/extensions/s2n_server_key_share.h" +#include "tls/extensions/s2n_server_psk.h" +#include "tls/extensions/s2n_server_supported_versions.h" +#include "tls/s2n_cipher_preferences.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_bitmap.h" +#include "utils/s2n_safety.h" + +const uint8_t EXTENSION_LEN = 2; +const uint8_t SECURE_RENEGOTIATION_SIZE = 5; +const uint8_t NEW_SESSION_TICKET_SIZE = 4; + +const uint8_t SUPPORTED_VERSION_SIZE = 6; +const uint8_t P256_KEYSHARE_SIZE = (32 * 2) + 1 + 8; +const uint8_t MIN_TLS13_EXTENSION_SIZE = (32 * 2) + 1 + 8 + 6; /* expanded from + P256_KEYSHARE_SIZE + SUPPORTED_VERSION_SIZE because gcc... */ + +/* set up minimum parameters for a tls13 connection so server extensions can work */ +static int configure_tls13_connection(struct s2n_connection *conn) +{ + conn->actual_protocol_version = S2N_TLS13; + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + POSIX_GUARD(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + POSIX_GUARD(s2n_stuffer_wipe(&conn->handshake.io)); + POSIX_GUARD(s2n_connection_allow_all_response_extensions(conn)); + + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + struct s2n_cert_chain_and_key *chain_and_key; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* s2n_server_extensions_send */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + /* Test Server Extensions Send - No extensions */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + EXPECT_SUCCESS(s2n_server_extensions_send(conn, hello_stuffer)); + EXPECT_EQUAL(s2n_stuffer_data_available(hello_stuffer), 0); + EXPECT_SUCCESS(s2n_server_extensions_recv(conn, hello_stuffer)); + EXPECT_EQUAL(s2n_stuffer_data_available(hello_stuffer), 0); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test Server Extensions Send - Server Name */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + s2n_extension_type_id extension_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_SERVER_NAME, &extension_id)); + S2N_CBIT_SET(conn->extension_requests_received, extension_id); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + /* server name size */ + const uint8_t size = 4; + + /* server name is sent when used */ + conn->server_name_used = 1; + EXPECT_SUCCESS(s2n_server_extensions_send(conn, hello_stuffer)); + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(hello_stuffer, size + EXTENSION_LEN); + + /* server name is not sent when not used */ + conn->server_name_used = 0; + EXPECT_SUCCESS(s2n_server_extensions_send(conn, hello_stuffer)); + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(hello_stuffer, 0); + + /* TLS 1.3: server name extension is not sent here */ + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + EXPECT_SUCCESS(configure_tls13_connection(conn)); + conn->server_name_used = 1; + EXPECT_SUCCESS(s2n_server_extensions_send(conn, hello_stuffer)); + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(hello_stuffer, MIN_TLS13_EXTENSION_SIZE + EXTENSION_LEN); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test Server Extensions Send - Application Protocol */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + s2n_extension_type_id extension_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_ALPN, &extension_id)); + S2N_CBIT_SET(conn->extension_requests_received, extension_id); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + strcpy(conn->application_protocol, "h2"); + const uint8_t application_protocol_len = strlen(conn->application_protocol); + + const uint8_t ALPN_LEN = 7 + application_protocol_len; + EXPECT_SUCCESS(s2n_server_extensions_send(conn, hello_stuffer)); + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(hello_stuffer, ALPN_LEN + EXTENSION_LEN); + + strcpy(conn->application_protocol, ""); + EXPECT_SUCCESS(s2n_server_extensions_send(conn, hello_stuffer)); + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(hello_stuffer, 0); + + /* TLS 1.3: extension is not sent here */ + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + EXPECT_SUCCESS(configure_tls13_connection(conn)); + strcpy(conn->application_protocol, "h2"); + EXPECT_SUCCESS(s2n_server_extensions_send(conn, hello_stuffer)); + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(hello_stuffer, MIN_TLS13_EXTENSION_SIZE + EXTENSION_LEN); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test Server Extensions Send - Maximum Fragment Length (MFL) */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + s2n_extension_type_id extension_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_MAX_FRAG_LEN, &extension_id)); + S2N_CBIT_SET(conn->extension_requests_received, extension_id); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + conn->negotiated_mfl_code = 0; + EXPECT_SUCCESS(s2n_server_extensions_send(conn, hello_stuffer)); + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(hello_stuffer, 0); + + const uint8_t MFL_EXT_SIZE = 2 + 2 + 1; + conn->negotiated_mfl_code = S2N_TLS_MAX_FRAG_LEN_1024; + EXPECT_SUCCESS(s2n_server_extensions_send(conn, hello_stuffer)); + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(hello_stuffer, MFL_EXT_SIZE + EXTENSION_LEN); + + /* TLS 1.3: extension is not sent here */ + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + EXPECT_SUCCESS(configure_tls13_connection(conn)); + EXPECT_SUCCESS(s2n_server_extensions_send(conn, hello_stuffer)); + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(hello_stuffer, MIN_TLS13_EXTENSION_SIZE + EXTENSION_LEN); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test Server Extensions Send - Signed Certificate Timestamp extension */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + s2n_extension_type_id extension_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_SCT_LIST, &extension_id)); + S2N_CBIT_SET(conn->extension_requests_received, extension_id); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + struct s2n_cert_chain_and_key fake_chain_and_key = { 0 }; + static uint8_t sct_list[] = { 0xff, 0xff, 0xff }; + s2n_blob_init(&fake_chain_and_key.sct_list, sct_list, sizeof(sct_list)); + + conn->ct_level_requested = S2N_CT_SUPPORT_REQUEST; + conn->handshake_params.our_chain_and_key = &fake_chain_and_key; + const uint8_t size = 4 + sizeof(sct_list); + EXPECT_SUCCESS(s2n_server_extensions_send(conn, hello_stuffer)); + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(hello_stuffer, size + EXTENSION_LEN); + + /* TLS 1.3: extension is not sent here */ + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + EXPECT_SUCCESS(configure_tls13_connection(conn)); + EXPECT_SUCCESS(s2n_server_extensions_send(conn, hello_stuffer)); + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(hello_stuffer, MIN_TLS13_EXTENSION_SIZE + EXTENSION_LEN); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test Server Extensions Send - OCSP Status Request */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + s2n_extension_type_id extension_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_STATUS_REQUEST, &extension_id)); + S2N_CBIT_SET(conn->extension_requests_received, extension_id); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + struct s2n_cert_chain_and_key fake_chain_and_key = { 0 }; + static uint8_t fake_ocsp[] = { 0xff, 0xff, 0xff }; + s2n_blob_init(&fake_chain_and_key.ocsp_status, fake_ocsp, sizeof(fake_ocsp)); + + conn->status_type = S2N_STATUS_REQUEST_OCSP; + conn->handshake_params.our_chain_and_key = &fake_chain_and_key; + + const uint8_t size = 4; + EXPECT_SUCCESS(s2n_server_extensions_send(conn, hello_stuffer)); + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(hello_stuffer, size + EXTENSION_LEN); + + /* TLS 1.3: extension is not sent here */ + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + EXPECT_SUCCESS(configure_tls13_connection(conn)); + EXPECT_SUCCESS(s2n_server_extensions_send(conn, hello_stuffer)); + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(hello_stuffer, MIN_TLS13_EXTENSION_SIZE + EXTENSION_LEN); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test Server Extensions Send - Secure Negotiation */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + conn->secure_renegotiation = 1; + EXPECT_SUCCESS(s2n_server_extensions_send(conn, hello_stuffer)); + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(hello_stuffer, SECURE_RENEGOTIATION_SIZE + EXTENSION_LEN); + + conn->secure_renegotiation = 0; + EXPECT_SUCCESS(s2n_server_extensions_send(conn, hello_stuffer)); + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(hello_stuffer, 0); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test Server Extensions Send - New Session Ticket */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + s2n_extension_type_id extension_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_SESSION_TICKET, &extension_id)); + S2N_CBIT_SET(conn->extension_requests_received, extension_id); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + EXPECT_SUCCESS(s2n_server_extensions_send(conn, hello_stuffer)); + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(hello_stuffer, 0); + + conn->config->use_tickets = 1; + conn->session_ticket_status = S2N_NEW_TICKET; + EXPECT_SUCCESS(s2n_server_extensions_send(conn, hello_stuffer)); + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(hello_stuffer, NEW_SESSION_TICKET_SIZE + EXTENSION_LEN); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test TLS13 Extensions */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_SUCCESS(s2n_connection_allow_response_extension(conn, s2n_server_key_share_extension.iana_value)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + + /* Test that s2n_server_extensions_send() only works when protocol version is TLS13 */ + conn->actual_protocol_version = S2N_TLS13; + + /* key_share_send() requires a negotiated_curve */ + conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + + EXPECT_EQUAL(s2n_extensions_server_key_share_send_size(conn), P256_KEYSHARE_SIZE); + + EXPECT_FAILURE(s2n_server_extensions_send(conn, hello_stuffer)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(hello_stuffer)); + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + + EXPECT_SUCCESS(s2n_server_extensions_send(conn, hello_stuffer)); + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(hello_stuffer, P256_KEYSHARE_SIZE + EXTENSION_LEN); + + /* Test that s2n_server_extensions_send() do not send extension < TLS13 */ + conn->actual_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, s2n_stuffer_data_available(hello_stuffer))); + EXPECT_SUCCESS(s2n_server_extensions_send(conn, hello_stuffer)); + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(hello_stuffer, 0); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test Secure Negotiation server_hello extension not sent with TLS13 or higher */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_SUCCESS(s2n_connection_allow_response_extension(conn, s2n_server_key_share_extension.iana_value)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + /* secure renegotiation is requested */ + conn->secure_renegotiation = 1; + /* Test that s2n_server_extensions_send() only works when protocol version is TLS13 */ + conn->actual_protocol_version = S2N_TLS13; + + /* key_share_send() requires a negotiated_curve */ + conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + /* secure_renegotiation extension not send >=TLS13*/ + uint8_t size = s2n_extensions_server_key_share_send_size(conn); + + EXPECT_FAILURE(s2n_server_extensions_send(conn, hello_stuffer)); + + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, s2n_stuffer_data_available(hello_stuffer))); + EXPECT_SUCCESS(s2n_server_extensions_send(conn, hello_stuffer)); + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(hello_stuffer, size + EXTENSION_LEN); + + /* Only sending secure_renegotiation(if it is requested) < TLS13 */ + conn->actual_protocol_version = S2N_TLS12; + uint8_t tls12_server_extension_size = SECURE_RENEGOTIATION_SIZE + EXTENSION_LEN; + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, s2n_stuffer_data_available(hello_stuffer))); + EXPECT_SUCCESS(s2n_server_extensions_send(conn, hello_stuffer)); + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(hello_stuffer, tls12_server_extension_size); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test New Session Ticket server_hello extension not sent with TLS13 or higher */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + s2n_extension_type_id extension_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_SESSION_TICKET, &extension_id)); + S2N_CBIT_SET(conn->extension_requests_received, extension_id); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_SUCCESS(s2n_connection_allow_response_extension(conn, s2n_server_key_share_extension.iana_value)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + + /* New Session Ticket Requested*/ + conn->config->use_tickets = 1; + conn->session_ticket_status = S2N_NEW_TICKET; + + /* Test that s2n_server_extensions_send() only works when protocol version is TLS13 */ + conn->actual_protocol_version = S2N_TLS13; + + /* key_share_send() requires a negotiated_curve */ + conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + + /* nst extension not send >=TLS13*/ + uint8_t size = s2n_extensions_server_key_share_send_size(conn); + + EXPECT_FAILURE(s2n_server_extensions_send(conn, hello_stuffer)); + + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, s2n_stuffer_data_available(hello_stuffer))); + EXPECT_SUCCESS(s2n_server_extensions_send(conn, hello_stuffer)); + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(hello_stuffer, size + EXTENSION_LEN); + + /* Sending nst (if it is requested) < TLS13 */ + conn->actual_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, s2n_stuffer_data_available(hello_stuffer))); + EXPECT_SUCCESS(s2n_server_extensions_send(conn, hello_stuffer)); + uint8_t tls12_server_extension_size = NEW_SESSION_TICKET_SIZE + EXTENSION_LEN; + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(hello_stuffer, tls12_server_extension_size); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test TLS13 Extensions with null key exchange alg cipher suites */ + { + struct s2n_cipher_suite *tls12_cipher_suite = cipher_preferences_20170210.suites[cipher_preferences_20170210.count - 1]; + uint8_t wire_ciphers_with_tls13[] = { + TLS_AES_128_GCM_SHA256, + TLS_AES_256_GCM_SHA384, + TLS_CHACHA20_POLY1305_SHA256, + tls12_cipher_suite->iana_value[0], tls12_cipher_suite->iana_value[1] + }; + const uint8_t cipher_count_tls13 = sizeof(wire_ciphers_with_tls13) / S2N_TLS_CIPHER_SUITE_LEN; + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_SUCCESS(s2n_connection_allow_response_extension(conn, s2n_server_key_share_extension.iana_value)); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "test_all_tls13")); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + + /* Test that s2n_server_extensions_send() only works when protocol version is TLS13 */ + conn->client_protocol_version = S2N_TLS13; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_set_cipher_as_tls_server(conn, wire_ciphers_with_tls13, cipher_count_tls13)); + + /* key_share_send() requires a negotiated_curve */ + conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + + uint8_t size = s2n_extensions_server_key_share_send_size(conn); + + EXPECT_FAILURE(s2n_server_extensions_send(conn, hello_stuffer)); + + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, s2n_stuffer_data_available(hello_stuffer))); + EXPECT_SUCCESS(s2n_server_extensions_send(conn, hello_stuffer)); + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(hello_stuffer, size + EXTENSION_LEN); + + /* Test that s2n_server_extensions_send() do not send extension < TLS13 */ + conn->actual_protocol_version = S2N_TLS12; + + EXPECT_SUCCESS(s2n_stuffer_skip_read(hello_stuffer, s2n_stuffer_data_available(hello_stuffer))); + EXPECT_SUCCESS(s2n_server_extensions_send(conn, hello_stuffer)); + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(hello_stuffer, 0); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test that some TLS1.3 extensions (like PSK) not sent on a HRR request */ + { + s2n_extension_type_id psk_extension_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(s2n_server_psk_extension.iana_value, &psk_extension_id)); + + struct s2n_psk psk = { 0 }; + + for (size_t is_hrr = 0; is_hrr < 2; is_hrr++) { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(conn)); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + struct s2n_stuffer *io_stuffer = &conn->handshake.io; + + /* Setup required for PSK extension */ + conn->psk_params.chosen_psk = &psk; + S2N_CBIT_CLR(conn->extension_requests_sent, psk_extension_id); + EXPECT_TRUE(s2n_server_psk_extension.should_send(conn)); + + /* Setup required for other server extensions */ + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp256r1; + conn->kex_params.client_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp256r1; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + + if (is_hrr) { + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(conn)); + } + + EXPECT_SUCCESS(s2n_server_extensions_send(conn, io_stuffer)); + + s2n_parsed_extensions_list parsed_extensions = { 0 }; + EXPECT_SUCCESS(s2n_extension_list_parse(io_stuffer, &parsed_extensions)); + + bool psk_extension_sent = (parsed_extensions.parsed_extensions[psk_extension_id].extension_type + == s2n_server_psk_extension.iana_value); + EXPECT_NOT_EQUAL(psk_extension_sent, is_hrr); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + }; + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test ec_point_format extension */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha; + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_server_extensions_send(conn, &stuffer)); + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(conn)); + + EXPECT_SUCCESS(s2n_server_extensions_send(conn, &stuffer)); + EXPECT_NOT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_SUCCESS(s2n_server_extensions_recv(conn, &stuffer)); + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test supported_versions extension can change extensions processed. + * In TLS1.2, we receive status_request on the ServerHello. TLS1.3 expects it on the Certificate. */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(server_conn)); + + struct s2n_cert_chain_and_key fake_chain_and_key = { 0 }; + static uint8_t fake_ocsp[] = { 0xff, 0xff, 0xff }; + s2n_blob_init(&fake_chain_and_key.ocsp_status, fake_ocsp, sizeof(fake_ocsp)); + + /* For our test status_request extension */ + server_conn->status_type = S2N_STATUS_REQUEST_OCSP; + server_conn->handshake_params.our_chain_and_key = &fake_chain_and_key; + + /* supported_versions not included - should NOT use TLS1.3 extensions, + * so should accept the status_request without issue. */ + { + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->server_protocol_version = S2N_TLS12; + + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(client_conn)); + + DEFER_CLEANUP(struct s2n_stuffer stuffer, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + /* Write extensions - just status_request */ + struct s2n_stuffer_reservation extension_list_size = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&stuffer, &extension_list_size)); + EXPECT_SUCCESS(s2n_extension_send(&s2n_cert_status_response_extension, + server_conn, &stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&extension_list_size)); + + EXPECT_EQUAL(client_conn->status_type, S2N_STATUS_REQUEST_NONE); + EXPECT_EQUAL(client_conn->server_protocol_version, S2N_UNKNOWN_PROTOCOL_VERSION); + EXPECT_SUCCESS(s2n_server_extensions_recv(client_conn, &stuffer)); + EXPECT_EQUAL(client_conn->status_type, S2N_STATUS_REQUEST_OCSP); + EXPECT_EQUAL(client_conn->server_protocol_version, S2N_UNKNOWN_PROTOCOL_VERSION); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* supported_versions included - should use TLS1.3 extensions, + * so should reject the status_request bc it does not belong here. */ + { + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->server_protocol_version = S2N_TLS13; + + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(client_conn)); + + DEFER_CLEANUP(struct s2n_stuffer stuffer, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + /* Write extensions - supported_versions + status_request */ + struct s2n_stuffer_reservation extension_list_size = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&stuffer, &extension_list_size)); + EXPECT_SUCCESS(s2n_extension_send(&s2n_server_supported_versions_extension, + server_conn, &stuffer)); + EXPECT_SUCCESS(s2n_extension_send(&s2n_cert_status_response_extension, + server_conn, &stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&extension_list_size)); + + EXPECT_EQUAL(client_conn->status_type, S2N_STATUS_REQUEST_NONE); + EXPECT_EQUAL(client_conn->server_protocol_version, S2N_UNKNOWN_PROTOCOL_VERSION); + EXPECT_SUCCESS(s2n_server_extensions_recv(client_conn, &stuffer)); + EXPECT_EQUAL(client_conn->status_type, S2N_STATUS_REQUEST_NONE); + EXPECT_EQUAL(client_conn->server_protocol_version, S2N_TLS13); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* TLS1.3 HRR handshake - should use HRR TLS1.3 extensions, + * so should reject the PSK extension */ + { + const uint8_t test_wire_index = 5; + struct s2n_psk empty_psk = { 0 }; + + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->server_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(server_conn, S2N_TLS13)); + server_conn->psk_params.chosen_psk = &empty_psk; + server_conn->psk_params.chosen_psk_wire_index = test_wire_index; + + DEFER_CLEANUP(struct s2n_stuffer stuffer, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + /* Write extensions - supported_versions + PSK */ + struct s2n_stuffer_reservation extension_list_size = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&stuffer, &extension_list_size)); + EXPECT_SUCCESS(s2n_extension_send(&s2n_server_supported_versions_extension, + server_conn, &stuffer)); + EXPECT_SUCCESS(s2n_extension_send(&s2n_server_psk_extension, + server_conn, &stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&extension_list_size)); + + for (size_t is_hrr = 0; is_hrr < 2; is_hrr++) { + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(client_conn)); + client_conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(client_conn, S2N_TLS13)); + + EXPECT_SUCCESS(s2n_connection_mark_extension_received(client_conn, s2n_server_key_share_extension.iana_value)); + + for (size_t i = 0; i <= test_wire_index; i++) { + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_pushback(&client_conn->psk_params.psk_list, (void **) &psk)); + } + + if (is_hrr) { + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(client_conn)); + } + + EXPECT_EQUAL(client_conn->psk_params.chosen_psk_wire_index, 0); + EXPECT_SUCCESS(s2n_server_extensions_recv(client_conn, &stuffer)); + + if (is_hrr) { + EXPECT_EQUAL(client_conn->psk_params.chosen_psk_wire_index, 0); + } else { + EXPECT_EQUAL(client_conn->psk_params.chosen_psk_wire_index, test_wire_index); + } + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_reread(&stuffer)); + } + }; + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_server_finished_test.c b/tests/unit/s2n_server_finished_test.c new file mode 100644 index 00000000000..f665c012c59 --- /dev/null +++ b/tests/unit/s2n_server_finished_test.c @@ -0,0 +1,95 @@ +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_tls.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Send and receive correct ServerFinished */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + server_conn->secure->cipher_suite = &s2n_ecdhe_rsa_with_aes_256_gcm_sha384; + server_conn->actual_protocol_version = S2N_TLS12; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + client_conn->secure->cipher_suite = &s2n_ecdhe_rsa_with_aes_256_gcm_sha384; + client_conn->actual_protocol_version = S2N_TLS12; + + /* Calculate valid verify_data */ + POSIX_GUARD(s2n_prf_server_finished(client_conn)); + + EXPECT_EQUAL(server_conn->server, server_conn->initial); + + EXPECT_SUCCESS(s2n_server_finished_send(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + EXPECT_SUCCESS(s2n_server_finished_recv(client_conn)); + + EXPECT_EQUAL(server_conn->server, server_conn->secure); + }; + + /* Client rejects incorrect ServerFinished */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + server_conn->secure->cipher_suite = &s2n_ecdhe_rsa_with_aes_256_gcm_sha384; + server_conn->actual_protocol_version = S2N_TLS12; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + client_conn->secure->cipher_suite = &s2n_ecdhe_rsa_with_aes_256_gcm_sha384; + client_conn->actual_protocol_version = S2N_TLS12; + + /* Mutate valid verify_data */ + POSIX_GUARD(s2n_prf_server_finished(client_conn)); + client_conn->handshake.server_finished[0]++; + + EXPECT_SUCCESS(s2n_server_finished_send(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_finished_recv(client_conn), S2N_ERR_BAD_MESSAGE); + }; + + /* Error if local verify_data has wrong length */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + server_conn->secure->cipher_suite = &s2n_ecdhe_rsa_with_aes_256_gcm_sha384; + server_conn->actual_protocol_version = S2N_TLS12; + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + client_conn->secure->cipher_suite = &s2n_ecdhe_rsa_with_aes_256_gcm_sha384; + client_conn->actual_protocol_version = S2N_TLS12; + + /* Change the length of valid verify_data */ + POSIX_GUARD(s2n_prf_server_finished(client_conn)); + client_conn->handshake.finished_len = 1; + + EXPECT_SUCCESS(s2n_server_finished_send(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + EXPECT_FAILURE_WITH_ERRNO(s2n_client_finished_recv(client_conn), S2N_ERR_SAFETY); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_server_hello_retry_test.c b/tests/unit/s2n_server_hello_retry_test.c new file mode 100644 index 00000000000..ae44a31104c --- /dev/null +++ b/tests/unit/s2n_server_hello_retry_test.c @@ -0,0 +1,626 @@ +/* + * 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 "error/s2n_errno.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_key_share.h" +#include "tls/extensions/s2n_server_key_share.h" +#include "tls/extensions/s2n_server_supported_versions.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_internal.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "tls/s2n_tls13_handshake.h" +#include "utils/s2n_bitmap.h" +#include "utils/s2n_result.h" + +#define HELLO_RETRY_MSG_NO 1 + +const uint8_t SESSION_ID_SIZE = 1; +const uint8_t COMPRESSION_METHOD_SIZE = 1; + +struct client_hello_context { + int invocations; + s2n_client_hello_cb_mode mode; + bool mark_done; +}; + +int s2n_negotiate_poll_hello_retry(struct s2n_connection *server_conn, + struct s2n_connection *client_conn, + struct client_hello_context *client_hello_ctx) +{ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(client_conn, &blocked), S2N_ERR_IO_BLOCKED); + + /* complete the callback on the next call */ + client_hello_ctx->mark_done = true; + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* + * hello retry will invoke the s2n_negotiate twice but the callback should + * be called once regardless of polling + */ + EXPECT_EQUAL(client_hello_ctx->invocations, 1); + + return S2N_SUCCESS; +} + +static int client_hello_detect_duplicate_calls(struct s2n_connection *conn, void *ctx) +{ + if (ctx == NULL) { + return -1; + } + + struct client_hello_context *client_hello_ctx = ctx; + + /* Incremet counter */ + client_hello_ctx->invocations++; + EXPECT_EQUAL(client_hello_ctx->invocations, 1); + if (client_hello_ctx->mode == S2N_CLIENT_HELLO_CB_NONBLOCKING) { + EXPECT_SUCCESS(s2n_client_hello_cb_done(conn)); + } + return 0; +} + +int s2n_client_hello_poll_cb(struct s2n_connection *conn, void *ctx) +{ + struct client_hello_context *client_hello_ctx; + if (ctx == NULL) { + return -1; + } + client_hello_ctx = ctx; + /* Increment counter to ensure that callback was invoked */ + client_hello_ctx->invocations++; + + if (client_hello_ctx->mark_done) { + EXPECT_SUCCESS(s2n_client_hello_cb_done(conn)); + return S2N_SUCCESS; + } + + return S2N_SUCCESS; +} + +S2N_RESULT hello_retry_client_hello_cb_test() +{ + struct s2n_cert_chain_and_key *tls13_chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&tls13_chain_and_key, + S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); + EXPECT_NOT_NULL(tls13_chain_and_key); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_NOT_NULL(client_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* setup the client hello callback */ + struct client_hello_context client_hello_ctx = { .invocations = 0, + .mode = S2N_CLIENT_HELLO_CB_NONBLOCKING, + .mark_done = false }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(config, + s2n_client_hello_poll_cb, &client_hello_ctx)); + EXPECT_SUCCESS(s2n_config_set_client_hello_cb_mode(config, + S2N_CLIENT_HELLO_CB_NONBLOCKING)); + + /* negotiate and make assertions */ + EXPECT_SUCCESS(s2n_negotiate_poll_hello_retry(server_conn, client_conn, &client_hello_ctx)); + + /* check hello retry state */ + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + + /* cleanup */ + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* Send Hello Retry Request messages */ + { + struct s2n_config *server_config; + struct s2n_connection *server_conn; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(server_conn)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + struct s2n_stuffer *server_stuffer = &server_conn->handshake.io; + + uint32_t total = S2N_TLS_PROTOCOL_VERSION_LEN + + S2N_TLS_RANDOM_DATA_LEN + + SESSION_ID_SIZE + + server_conn->session_id_len + + S2N_TLS_CIPHER_SUITE_LEN + + COMPRESSION_METHOD_SIZE; + + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + server_conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + server_conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + server_conn->kex_params.client_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&server_conn->kex_params.client_ecc_evp_params)); + + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + EXPECT_OK(s2n_conn_choose_state_machine(server_conn, S2N_TLS13)); + + /* The client will need a key share extension to properly parse the hello */ + /* Total extension size + size of each extension */ + total += 2 + s2n_extensions_server_supported_versions_size(server_conn) + + s2n_extensions_server_key_share_send_size(server_conn); + + EXPECT_TRUE(s2n_is_hello_retry_message(server_conn)); + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + + EXPECT_EQUAL(s2n_stuffer_data_available(server_stuffer), total); + + EXPECT_NOT_NULL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_NULL(server_conn->kex_params.server_ecc_evp_params.evp_pkey); + EXPECT_TRUE(memcmp(server_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN) == 0); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Verify the requires_retry flag causes a retry to be sent */ + { + struct s2n_config *conf; + struct s2n_connection *conn; + + EXPECT_NOT_NULL(conf = s2n_config_new()); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, conf)); + + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(conn, S2N_TLS13)); + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + conn->kex_params.client_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(conn)); + + EXPECT_TRUE(s2n_is_hello_retry_message(conn)); + EXPECT_SUCCESS(s2n_server_hello_retry_send(conn)); + + EXPECT_SUCCESS(s2n_config_free(conf)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Retry requests with incorrect random data are not accepted */ + { + struct s2n_config *conf; + struct s2n_connection *conn; + + EXPECT_NOT_NULL(conf = s2n_config_new()); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, conf)); + + struct s2n_stuffer *io = &conn->handshake.io; + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(conn, S2N_TLS13)); + + /* protocol version */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS12 / 10)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS12 % 10)); + + /* random data */ + uint8_t bad_retry_random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_write_bytes(io, bad_retry_random, S2N_TLS_RANDOM_DATA_LEN)); + + /* session id */ + uint8_t session_id[S2N_TLS_SESSION_ID_MAX_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS_SESSION_ID_MAX_LEN)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(io, session_id, S2N_TLS_SESSION_ID_MAX_LEN)); + + /* cipher suites */ + EXPECT_SUCCESS(s2n_stuffer_write_uint16(io, 0x1301)); + + /* no compression */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, 0)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_FALSE(s2n_is_hello_retry_message(conn)); + + EXPECT_SUCCESS(s2n_config_free(conf)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Verify the client key share extension properly handles HelloRetryRequests */ + { + struct s2n_connection *server_conn; + struct s2n_connection *client_conn; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer *extension_stuffer = &server_conn->handshake.io; + + EXPECT_SUCCESS(s2n_connection_allow_response_extension(client_conn, s2n_server_key_share_extension.iana_value)); + EXPECT_SUCCESS(s2n_connection_allow_response_extension(server_conn, s2n_server_key_share_extension.iana_value)); + + POSIX_CHECKED_MEMCPY(server_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + server_conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + server_conn->kex_params.client_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + EXPECT_OK(s2n_conn_choose_state_machine(server_conn, S2N_TLS13)); + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&server_conn->kex_params.client_ecc_evp_params)); + EXPECT_SUCCESS(s2n_extensions_server_key_share_send(server_conn, extension_stuffer)); + + S2N_STUFFER_READ_EXPECT_EQUAL(extension_stuffer, TLS_EXTENSION_KEY_SHARE, uint16); + /* 4 = S2N_SIZE_OF_EXTENSION_TYPE + S2N_SIZE_OF_EXTENSION_DATA_SIZE */ + S2N_STUFFER_READ_EXPECT_EQUAL(extension_stuffer, s2n_extensions_server_key_share_send_size(server_conn) - 4, uint16); + + client_conn->kex_params.client_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_conn->kex_params.client_ecc_evp_params)); + + /* Setup the client to receive a HelloRetryRequest */ + POSIX_CHECKED_MEMCPY(client_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(client_conn, S2N_TLS13)); + + /* Setup the handshake type and message number to simulate a condition where a HelloRetry should be sent */ + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(client_conn)); + EXPECT_OK(s2n_conn_choose_state_machine(client_conn, S2N_TLS13)); + EXPECT_SUCCESS(s2n_set_hello_retry_required(client_conn)); + + /* Parse the key share */ + EXPECT_SUCCESS(s2n_extensions_server_key_share_recv(client_conn, extension_stuffer)); + EXPECT_EQUAL(s2n_stuffer_data_available(extension_stuffer), 0); + + /* Server negotiated curve value will be non-null, if the extension succeeded */ + EXPECT_NOT_NULL(client_conn->kex_params.server_ecc_evp_params.negotiated_curve); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* Verify that the hash transcript recreation function correctly takes the existing ClientHello1 + * hash, and generates a synthetic message. */ + { + struct s2n_config *conf; + struct s2n_connection *conn; + + EXPECT_NOT_NULL(conf = s2n_config_new()); + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, conf)); + + conn->server_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_128_gcm_sha256; + conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + conn->kex_params.client_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + + /* This blob is taken from the functional test RFC. That RFC does not actually provide hash transcript + * values, so the expected hashes are taken from what our hash functions generated and the hash + * generated from the transcript recreation. + * https://tools.ietf.org/html/rfc8448#section-5 */ + S2N_BLOB_FROM_HEX(client_hello1, + "010000c00303cb34ecb1e78163" + "ba1c38c6dacb196a6dffa21a8d9912ec18a2ef6283" + "024dece7000006130113031302010000910000000b" + "0009000006736572766572ff01000100000a001400" + "12001d001700180019010001010102010301040023" + "0000003300260024001d002099381de560e4bd43d2" + "3d8e435a7dbafeb3c06e51c13cae4d5413691e529a" + "af2c002b0003020304000d0020001e040305030603" + "020308040805080604010501060102010402050206" + "020202002d00020101001c00024001"); + + S2N_BLOB_FROM_HEX(client_hello1_expected_hash, + "4db255f30da09a407c841720be831a06a5aa9b3662a5f44267d37706b73c2b8c"); + + S2N_BLOB_FROM_HEX(synthetic_message_with_ch1_expected_hash, + "ff1135ed878322e29699da3e451d2f08bf11fc693038769978e75bb63304a225"); + + EXPECT_SUCCESS(s2n_conn_update_handshake_hashes(conn, &client_hello1)); + + s2n_tls13_connection_keys(keys, conn); + uint8_t hash_digest_length = keys.size; + struct s2n_blob compare_blob = { 0 }; + + DEFER_CLEANUP(struct s2n_hash_state client_hello1_hash = { 0 }, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&client_hello1_hash)); + POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(conn, keys.hash_algorithm, &client_hello1_hash)); + + uint8_t client_hello1_digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_hash_digest(&client_hello1_hash, client_hello1_digest_out, hash_digest_length)); + + EXPECT_SUCCESS(s2n_blob_init(&compare_blob, client_hello1_digest_out, hash_digest_length)); + S2N_BLOB_EXPECT_EQUAL(client_hello1_expected_hash, compare_blob); + + EXPECT_SUCCESS(s2n_server_hello_retry_recreate_transcript(conn)); + + DEFER_CLEANUP(struct s2n_hash_state recreated_hash = { 0 }, s2n_hash_free); + uint8_t recreated_transcript_digest_out[S2N_MAX_DIGEST_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_hash_new(&recreated_hash)); + POSIX_GUARD_RESULT(s2n_handshake_copy_hash_state(conn, keys.hash_algorithm, &recreated_hash)); + EXPECT_SUCCESS(s2n_hash_digest(&recreated_hash, recreated_transcript_digest_out, hash_digest_length)); + + EXPECT_SUCCESS(s2n_blob_init(&compare_blob, recreated_transcript_digest_out, hash_digest_length)); + S2N_BLOB_EXPECT_EQUAL(synthetic_message_with_ch1_expected_hash, compare_blob); + + EXPECT_SUCCESS(s2n_config_free(conf)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Send and receive Hello Retry Request messages */ + { + struct s2n_config *server_config; + struct s2n_config *client_config; + + struct s2n_connection *server_conn; + struct s2n_connection *client_conn; + + struct s2n_cert_chain_and_key *tls13_chain_and_key; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&tls13_chain_and_key, + S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + struct client_hello_context client_hello_ctx = { .invocations = 0, .mode = S2N_CLIENT_HELLO_CB_BLOCKING }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(server_config, client_hello_detect_duplicate_calls, &client_hello_ctx)); + + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(client_conn, S2N_TLS13)); + + /* Force HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* Send the first CH message */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Receive the CH and send an HRR, which will execute the HRR code paths */ + EXPECT_EQUAL(client_hello_ctx.invocations, 0); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_EQUAL(client_hello_ctx.invocations, 1); + + EXPECT_TRUE(s2n_is_hello_retry_handshake(server_conn)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + EXPECT_TRUE(s2n_is_hello_retry_message(server_conn)); + + EXPECT_SUCCESS(s2n_server_hello_retry_send(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); + + /* Send the second CH message */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&server_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Verify that receiving the second CH message does not execute the callback */ + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + EXPECT_EQUAL(client_hello_ctx.invocations, 1); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + }; + + /* Send and receive Hello Retry Request messages, test for non blocking client hello callback */ + { + struct s2n_config *server_config; + struct s2n_config *client_config; + + struct s2n_connection *server_conn; + struct s2n_connection *client_conn; + + struct s2n_cert_chain_and_key *tls13_chain_and_key; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default_tls13")); + + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(server_config)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&tls13_chain_and_key, + S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, tls13_chain_and_key)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Force HRR path */ + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + /* setup the client hello callback */ + struct client_hello_context client_hello_ctx = { .invocations = 0, + .mode = S2N_CLIENT_HELLO_CB_NONBLOCKING }; + EXPECT_SUCCESS(s2n_config_set_client_hello_cb(server_config, + client_hello_detect_duplicate_calls, &client_hello_ctx)); + EXPECT_SUCCESS(s2n_config_set_client_hello_cb_mode(server_config, + S2N_CLIENT_HELLO_CB_NONBLOCKING)); + + /* ensure that handshake succeeds via HRR path using non_blocking CH */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(server_conn->handshake.handshake_type & HELLO_RETRY_REQUEST); + EXPECT_EQUAL(client_hello_ctx.invocations, 1); + + EXPECT_NOT_NULL(s2n_connection_get_client_hello(server_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(client_conn)); + EXPECT_TRUE(IS_HELLO_RETRY_HANDSHAKE(server_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls13_chain_and_key)); + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + }; + + /* Hello Retry Request + (poll and no-poll) client hello callback */ + { + EXPECT_OK(hello_retry_client_hello_cb_test()); + }; + + /* Test s2n_set_hello_retry_required correctly sets the handshake type to HELLO_RETRY_REQUEST, + * when conn->actual_protocol_version is set to TLS1.3 version */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(conn, S2N_TLS13)); + + EXPECT_SUCCESS(s2n_set_hello_retry_required(conn)); + EXPECT_TRUE(conn->handshake.handshake_type & HELLO_RETRY_REQUEST); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_set_hello_retry_required raises a S2N_ERR_INVALID_HELLO_RETRY error + * when conn->actual_protocol_version is less than TLS1.3 */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS12; + + EXPECT_FAILURE_WITH_ERRNO(s2n_set_hello_retry_required(conn), S2N_ERR_INVALID_HELLO_RETRY); + EXPECT_FALSE(conn->handshake.handshake_type & HELLO_RETRY_REQUEST); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* + *= https://tools.ietf.org/rfc/rfc8446#section-4.1.4 + *= type=test + *# Clients MUST abort the handshake with an + *# "illegal_parameter" alert if the HelloRetryRequest would not result + *# in any change in the ClientHello. + */ + { + const struct s2n_security_policy *security_policy = NULL; + EXPECT_SUCCESS(s2n_find_security_policy_from_version("20201021", &security_policy)); + EXPECT_NOT_NULL(security_policy); + const struct s2n_ecc_named_curve *test_curve = security_policy->ecc_preferences->ecc_curves[0]; + + /** + * Retry for key share is valid + * + *= https://tools.ietf.org/rfc/rfc8446#4.2.8 + *= type=test + *# and (2) the selected_group field does not + *# correspond to a group which was provided in the "key_share" extension + *# in the original ClientHello. + **/ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "20201021")); + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + + conn->kex_params.server_ecc_evp_params.negotiated_curve = test_curve; + + /* Server requested key share is NOT present: allow retry */ + EXPECT_SUCCESS(s2n_server_hello_retry_recv(conn)); + + /* Server requested key share is present: do NOT allow retry */ + conn->kex_params.client_ecc_evp_params.negotiated_curve = test_curve; + conn->kex_params.client_ecc_evp_params.evp_pkey = EVP_PKEY_new(); + EXPECT_NOT_NULL(conn->kex_params.client_ecc_evp_params.evp_pkey); + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), + S2N_ERR_INVALID_HELLO_RETRY); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Retry for multiple reasons is valid */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(conn, "20201021")); + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + + conn->kex_params.server_ecc_evp_params.negotiated_curve = test_curve; + + /* All retry conditions met: allow retry */ + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + EXPECT_SUCCESS(s2n_server_hello_retry_recv(conn)); + + /* No retry conditions met: do NOT allow retry */ + conn->early_data_state = S2N_EARLY_DATA_NOT_REQUESTED; + conn->kex_params.client_ecc_evp_params.negotiated_curve = test_curve; + conn->kex_params.client_ecc_evp_params.evp_pkey = EVP_PKEY_new(); + EXPECT_NOT_NULL(conn->kex_params.client_ecc_evp_params.evp_pkey); + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_retry_recv(conn), + S2N_ERR_INVALID_HELLO_RETRY); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + END_TEST(); +} diff --git a/tests/unit/s2n_server_hello_test.c b/tests/unit/s2n_server_hello_test.c new file mode 100644 index 00000000000..c66c0fe0977 --- /dev/null +++ b/tests/unit/s2n_server_hello_test.c @@ -0,0 +1,684 @@ +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_quic_support.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +const uint8_t SESSION_ID_SIZE = 1; +const uint8_t COMPRESSION_METHOD_SIZE = 1; + +/* from RFC: https://tools.ietf.org/html/rfc8446#section-4.1.3*/ +const char hello_retry_random_hex[] = + "CF21AD74E59A6111BE1D8C021E65B891" + "C2A211167ABB8C5E079E09E2C8A8339C"; + +const uint8_t tls12_downgrade_protection_check_bytes[] = { + 0x44, 0x4F, 0x57, 0x4E, 0x47, 0x52, 0x44, 0x01 +}; + +const uint8_t tls11_downgrade_protection_check_bytes[] = { + 0x44, 0x4F, 0x57, 0x4E, 0x47, 0x52, 0x44, 0x00 +}; + +static S2N_RESULT s2n_test_client_hello(struct s2n_connection *client_conn, struct s2n_connection *server_conn) +{ + /* We have to "write" the handshake header for the PSK binder calculation, which expects a complete + * ClientHello message. We'll skip these bytes later. + */ + RESULT_GUARD_POSIX(s2n_stuffer_skip_write(&client_conn->handshake.io, TLS_HANDSHAKE_HEADER_LENGTH)); + + RESULT_GUARD_POSIX(s2n_client_hello_send(client_conn)); + RESULT_GUARD_POSIX(s2n_stuffer_copy(&client_conn->handshake.io, + &server_conn->handshake.io, s2n_stuffer_data_available(&client_conn->handshake.io))); + + /* Skip the handshake header bytes */ + RESULT_GUARD_POSIX(s2n_stuffer_skip_read(&server_conn->handshake.io, TLS_HANDSHAKE_HEADER_LENGTH)); + + RESULT_GUARD_POSIX(s2n_client_hello_recv(server_conn)); + + RESULT_GUARD_POSIX(s2n_stuffer_wipe(&client_conn->handshake.io)); + RESULT_GUARD_POSIX(s2n_stuffer_wipe(&server_conn->handshake.io)); + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + struct s2n_cert_chain_and_key *chain_and_key; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + /* Test basic Server Hello Send */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + + /* Test s2n_server_hello_send */ + const uint32_t total = S2N_TLS_PROTOCOL_VERSION_LEN + + S2N_TLS_RANDOM_DATA_LEN + + SESSION_ID_SIZE + + conn->session_id_len + + S2N_TLS_CIPHER_SUITE_LEN + + COMPRESSION_METHOD_SIZE; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_server_hello_send(conn)); + EXPECT_EQUAL(hello_stuffer->blob.data[0], 0x03); + EXPECT_EQUAL(hello_stuffer->blob.data[1], 0x03); + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(hello_stuffer, total); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test that legacy_version_field is set correct for TLS 1.3 Server Hello Send */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + struct s2n_config *config = NULL; + const struct s2n_ecc_preferences *ecc_preferences = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_NOT_NULL(conn->config); + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + EXPECT_NOT_NULL(ecc_preferences); + /* configure these parameters so server hello can be sent */ + conn->actual_protocol_version = S2N_TLS13; + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; + conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + + struct s2n_stuffer *hello_stuffer = &conn->handshake.io; + EXPECT_SUCCESS(s2n_server_hello_send(conn)); + + /* verify that legacy protocol version is 0x0303 (TLS12) */ + EXPECT_EQUAL(hello_stuffer->blob.data[0], 0x03); + EXPECT_EQUAL(hello_stuffer->blob.data[1], 0x03); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* Test basic Server Hello Recv */ + { + struct s2n_config *server_config; + struct s2n_config *client_config; + + struct s2n_connection *server_conn; + struct s2n_connection *client_conn; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->secure->cipher_suite = &s2n_ecdhe_rsa_with_aes_256_gcm_sha384; + + EXPECT_SUCCESS(s2n_server_hello_send(server_conn)); + + /* Copy server stuffer to client stuffer */ + const uint32_t total = s2n_stuffer_data_available(&server_conn->handshake.io); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, total)); + + /* Test s2n_server_hello_recv() */ + struct s2n_stuffer *client_stuffer = &client_conn->handshake.io; + EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); + + EXPECT_EQUAL(s2n_stuffer_data_available(client_stuffer), 0); + + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test Server Hello Recv with invalid cipher */ + { + struct s2n_connection *server_conn; + struct s2n_connection *client_conn; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + server_conn->actual_protocol_version = S2N_TLS12; + + /* This cipher is not in the client's default selection */ + server_conn->secure->cipher_suite = &s2n_tls13_chacha20_poly1305_sha256; + + EXPECT_SUCCESS(s2n_server_hello_send(server_conn)); + + /* Copy server stuffer to client stuffer */ + const uint32_t total = s2n_stuffer_data_available(&server_conn->handshake.io); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, total)); + + /* The client should fail the handshake because an invalid cipher was offered */ + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), S2N_ERR_CIPHER_NOT_SUPPORTED); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Non-matching session IDs turn off EMS for the connection */ + { + struct s2n_connection *server_conn; + struct s2n_connection *client_conn; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->secure->cipher_suite = &s2n_ecdhe_rsa_with_aes_256_gcm_sha384; + + /* Create session ID for server */ + for (int i = 0; i < 32; i++) { + server_conn->session_id[i] = i; + } + + EXPECT_SUCCESS(s2n_server_hello_send(server_conn)); + + /* Copy server stuffer to client stuffer */ + const uint32_t total = s2n_stuffer_data_available(&server_conn->handshake.io); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, total)); + + /* Create client session ID does not match server session ID */ + for (int i = 0; i < 32; i++) { + client_conn->session_id[i] = 0; + } + + /* Client is negotiating an EMS connection but is able to fallback to non-EMS connection + * if session IDs don't match */ + client_conn->ems_negotiated = true; + EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); + EXPECT_FALSE(client_conn->ems_negotiated); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test TLS 1.3 session id matching */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + struct s2n_config *client_config; + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + struct s2n_stuffer *io = &client_conn->handshake.io; + /* protocol version */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS12 / 10)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS12 % 10)); + + /* random payload */ + uint8_t random[S2N_TLS_RANDOM_DATA_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_write_bytes(io, random, S2N_TLS_RANDOM_DATA_LEN)); + + uint8_t session_id[S2N_TLS_SESSION_ID_MAX_LEN] = { 0 }; + + /* generate matching session id for payload and client connection */ + for (int i = 0; i < 32; i++) { + session_id[i] = i; + client_conn->session_id[i] = i; + } + + /* session id */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS_SESSION_ID_MAX_LEN)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(io, session_id, S2N_TLS_SESSION_ID_MAX_LEN)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(io, (0x13 << 8) + 0x01)); /* cipher suites */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, 0)); /* no compression */ + + client_conn->server_protocol_version = S2N_TLS13; + client_conn->session_id_len = 32; + + /* Test s2n_server_hello_recv() */ + EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(io), 0); + + /* Check that corrupt session id fails server hello */ + for (int i = 0; i < 32; i++) { + client_conn->session_id[i] ^= 1; + EXPECT_SUCCESS(s2n_stuffer_reread(io)); + EXPECT_FAILURE(s2n_server_hello_recv(client_conn)); + client_conn->session_id[i] ^= 1; + } + + /* Check that server hello is successful again */ + EXPECT_SUCCESS(s2n_stuffer_reread(io)); + EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); + + /* Check that unmatched session length should also fail */ + for (int i = 0; i < 32; i++) { + client_conn->session_id_len = i; + EXPECT_SUCCESS(s2n_stuffer_reread(io)); + EXPECT_FAILURE(s2n_server_hello_recv(client_conn)); + } + + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* Test TLS 1.3 => 1.1 protocol downgrade detection with a TLS1.3 client */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + struct s2n_config *client_config; + struct s2n_connection *client_conn; + struct s2n_config *server_config; + struct s2n_connection *server_conn; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + /* The client will request TLS1.3 */ + client_conn->client_protocol_version = S2N_TLS13; + + /* Set the negotiated curve, otherwise the server might try to respond with a retry */ + server_conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + + /* The server will respond with TLS1.1 even though it supports TLS1.3 */ + server_conn->actual_protocol_version = S2N_TLS11; + EXPECT_SUCCESS(s2n_server_hello_send(server_conn)); + + /* Copy server stuffer to client stuffer */ + const uint32_t total = s2n_stuffer_data_available(&server_conn->handshake.io); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, total)); + + /* Verify that the downgrade is detected */ + struct s2n_stuffer *client_stuffer = &client_conn->handshake.io; + EXPECT_BYTEARRAY_EQUAL(&client_stuffer->blob.data[S2N_TLS_PROTOCOL_VERSION_LEN + 24], tls11_downgrade_protection_check_bytes, 8); + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED); + + EXPECT_EQUAL(s2n_stuffer_data_available(client_stuffer), 0); + + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* Test TLS 1.3 => 1.2 protocol downgrade detection with a TLS1.3 client */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + struct s2n_config *client_config; + struct s2n_connection *client_conn; + struct s2n_config *server_config; + struct s2n_connection *server_conn; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + /* The client will request TLS1.3 */ + client_conn->client_protocol_version = S2N_TLS13; + + /* Set the negotiated curve, otherwise the server might try to respond with a retry */ + server_conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + + /* The server will respond with TLS1.2 even though it supports TLS1.3 */ + server_conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_server_hello_send(server_conn)); + + /* Copy server stuffer to client stuffer */ + const uint32_t total = s2n_stuffer_data_available(&server_conn->handshake.io); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, total)); + + /* Verify that the downgrade is detected */ + struct s2n_stuffer *client_stuffer = &client_conn->handshake.io; + EXPECT_BYTEARRAY_EQUAL(&client_stuffer->blob.data[S2N_TLS_PROTOCOL_VERSION_LEN + 24], tls12_downgrade_protection_check_bytes, 8); + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), S2N_ERR_PROTOCOL_DOWNGRADE_DETECTED); + + EXPECT_EQUAL(s2n_stuffer_data_available(client_stuffer), 0); + + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* Verify a TLS1.2 client can negotiate with a TLS1.3 server */ + { + struct s2n_config *client_config; + struct s2n_connection *client_conn; + struct s2n_config *server_config; + struct s2n_connection *server_conn; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + /* The client will request TLS1.2 */ + client_conn->client_protocol_version = S2N_TLS12; + + /* The server will respond with TLS1.2 even though it support TLS1.3. This is expected because */ + /* the client only support TLS1.2 */ + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->secure->cipher_suite = &s2n_ecdhe_rsa_with_aes_256_gcm_sha384; + EXPECT_SUCCESS(s2n_server_hello_send(server_conn)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Copy server stuffer to client stuffer */ + const uint32_t total = s2n_stuffer_data_available(&server_conn->handshake.io); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, total)); + + /* Verify that a TLS12 client does not error due to the downgrade */ + struct s2n_stuffer *client_stuffer = &client_conn->handshake.io; + EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); + + EXPECT_EQUAL(s2n_stuffer_data_available(client_stuffer), 0); + + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Verify a TLS1.3 client can negotiate with a TLS1.2 server */ + { + struct s2n_config *client_config; + struct s2n_connection *client_conn; + struct s2n_config *server_config; + struct s2n_connection *server_conn; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + /* The client will request TLS1.3 */ + client_conn->client_protocol_version = S2N_TLS13; + + /* The server will respond with TLS1.2 */ + server_conn->server_protocol_version = S2N_TLS12; + server_conn->actual_protocol_version = S2N_TLS12; + + server_conn->secure->cipher_suite = &s2n_ecdhe_rsa_with_aes_256_gcm_sha384; + EXPECT_SUCCESS(s2n_server_hello_send(server_conn)); + + /* Copy server stuffer to client stuffer */ + const uint32_t total = s2n_stuffer_data_available(&server_conn->handshake.io); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, total)); + + /* Verify that a TLS13 client does not error due to the downgrade */ + struct s2n_stuffer *client_stuffer = &client_conn->handshake.io; + POSIX_GUARD(s2n_enable_tls13_in_test()); + EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); + POSIX_GUARD(s2n_disable_tls13_in_test()); + EXPECT_EQUAL(s2n_stuffer_data_available(client_stuffer), 0); + + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Verify a TLS1.2 client can negotiate with a TLS1.3 server */ + { + struct s2n_config *client_config; + struct s2n_connection *client_conn; + struct s2n_config *server_config; + struct s2n_connection *server_conn; + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + /* The client will request TLS1.2 */ + client_conn->client_protocol_version = S2N_TLS12; + + /* The server will respond with TLS1.2 even though it support TLS1.3. This is expected because */ + /* the client only support TLS1.2 */ + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + server_conn->actual_protocol_version = S2N_TLS12; + + server_conn->secure->cipher_suite = &s2n_ecdhe_rsa_with_aes_256_gcm_sha384; + EXPECT_SUCCESS(s2n_server_hello_send(server_conn)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Copy server stuffer to client stuffer */ + const uint32_t total = s2n_stuffer_data_available(&server_conn->handshake.io); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, total)); + + /* Verify that a TLS12 client does not error due to the downgrade */ + struct s2n_stuffer *client_stuffer = &client_conn->handshake.io; + EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); + + EXPECT_EQUAL(s2n_stuffer_data_available(client_stuffer), 0); + + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* TLS13 hello retry message received results into S2N_ERR_UNIMPLEMENTED error*/ + { + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + struct s2n_stuffer *io = &client_conn->handshake.io; + client_conn->server_protocol_version = S2N_TLS13; + + /* protocol version */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS12 / 10)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS12 % 10)); + + uint8_t session_id[S2N_TLS_SESSION_ID_MAX_LEN] = { 0 }; + S2N_BLOB_FROM_HEX(random_blob, hello_retry_random_hex); + + /* random payload */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(io, random_blob.data, S2N_TLS_RANDOM_DATA_LEN)); + + /* session id */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, S2N_TLS_SESSION_ID_MAX_LEN)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(io, session_id, S2N_TLS_SESSION_ID_MAX_LEN)); + + /* cipher suites */ + EXPECT_SUCCESS(s2n_stuffer_write_uint16(io, (0x13 << 8) + 0x01)); + + /* no compression */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(io, 0)); + EXPECT_EQUAL(S2N_TLS_RANDOM_DATA_LEN, random_blob.size); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* Test that negotiating TLS1.2 with QUIC-enabled client fails */ + if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_reset_tls13_in_test()); + + struct s2n_config *quic_config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(quic_config, "test_all")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(quic_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_enable_quic(quic_config)); + + struct s2n_config *non_quic_config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(non_quic_config, "test_all")); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(non_quic_config, chain_and_key)); + + /* Succeeds when negotiating TLS1.3 */ + if (s2n_is_tls13_fully_supported()) { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, non_quic_config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, non_quic_config)); + + EXPECT_OK(s2n_test_client_hello(client_conn, server_conn)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, quic_config)); + + EXPECT_SUCCESS(s2n_server_hello_send(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, + &client_conn->handshake.io, s2n_stuffer_data_available(&server_conn->handshake.io))); + EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); + + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + } + + /* Fails when negotiating TLS1.2 */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, non_quic_config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all_tls12")); + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, non_quic_config)); + + EXPECT_OK(s2n_test_client_hello(client_conn, server_conn)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, quic_config)); + + EXPECT_SUCCESS(s2n_server_hello_send(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, + &client_conn->handshake.io, s2n_stuffer_data_available(&server_conn->handshake.io))); + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + EXPECT_SUCCESS(s2n_config_free(quic_config)); + EXPECT_SUCCESS(s2n_config_free(non_quic_config)); + } + + /* Test that negotiating TLS1.2 with an early data enabled client fails. + * + *= https://tools.ietf.org/rfc/rfc8446#appendix-D.3 + *= type=test + *# A client that attempts to send 0-RTT data MUST fail a connection if + *# it receives a ServerHello with TLS 1.2 or older. + */ + if (s2n_is_tls13_fully_supported()) { + EXPECT_SUCCESS(s2n_reset_tls13_in_test()); + + struct s2n_config *config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + + /* Succeeds when negotiating TLS1.3 */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "test_all")); + EXPECT_OK(s2n_append_test_psk_with_early_data(client_conn, 1, &s2n_tls13_aes_128_gcm_sha256)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all")); + EXPECT_OK(s2n_append_test_psk_with_early_data(server_conn, 1, &s2n_tls13_aes_128_gcm_sha256)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + + EXPECT_OK(s2n_test_client_hello(client_conn, server_conn)); + + EXPECT_SUCCESS(s2n_server_hello_send(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, + &client_conn->handshake.io, s2n_stuffer_data_available(&server_conn->handshake.io))); + EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); + + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_REQUESTED); + EXPECT_EQUAL(client_conn->server_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* TLS 1.3 Client Early Data is rejected when server only supports TLS1.2 */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "test_all")); + EXPECT_OK(s2n_append_test_psk_with_early_data(client_conn, 1, &s2n_tls13_aes_128_gcm_sha256)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(client_conn)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all_tls12")); + EXPECT_OK(s2n_append_test_psk_with_early_data(server_conn, 1, &s2n_tls13_aes_128_gcm_sha256)); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + + EXPECT_OK(s2n_test_client_hello(client_conn, server_conn)); + + EXPECT_SUCCESS(s2n_server_hello_send(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, + &client_conn->handshake.io, s2n_stuffer_data_available(&server_conn->handshake.io))); + EXPECT_FAILURE_WITH_ERRNO(s2n_server_hello_recv(client_conn), S2N_ERR_PROTOCOL_VERSION_UNSUPPORTED); + + EXPECT_EQUAL(client_conn->early_data_state, S2N_EARLY_DATA_REQUESTED); + EXPECT_EQUAL(client_conn->server_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + } + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + + END_TEST(); +} diff --git a/tests/unit/s2n_server_key_share_extension_test.c b/tests/unit/s2n_server_key_share_extension_test.c new file mode 100644 index 00000000000..c9a8c879349 --- /dev/null +++ b/tests/unit/s2n_server_key_share_extension_test.c @@ -0,0 +1,1023 @@ +/* + * 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 "api/s2n.h" +#include "pq-crypto/s2n_pq.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_nist_kats.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_client_key_share.h" +#include "tls/extensions/s2n_server_key_share.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_bitmap.h" +#include "utils/s2n_safety.h" + +#define HELLO_RETRY_MSG_NO 1 + +int s2n_server_key_share_send_check_pq_hybrid(struct s2n_connection *conn); +int s2n_server_key_share_send_check_ecdhe(struct s2n_connection *conn); +static int s2n_read_server_key_share_hybrid_test_vectors(const struct s2n_kem_group *kem_group, struct s2n_blob *pq_private_key, + struct s2n_stuffer *pq_shared_secret, struct s2n_stuffer *key_share_payload); + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* Test s2n_server_key_share_send_check_ecdhe */ + { + struct s2n_security_policy test_security_policy_no_x25519 = { + .minimum_protocol_version = S2N_TLS10, + .cipher_preferences = &cipher_preferences_test_all_tls13, + .kem_preferences = &kem_preferences_null, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20140601, + }; + + struct s2n_connection *conn = NULL; + EXPECT_FAILURE(s2n_server_key_share_send_check_ecdhe(conn)); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->security_policy_override = &test_security_policy_no_x25519; + + EXPECT_FAILURE(s2n_server_key_share_send_check_ecdhe(conn)); + + if (s2n_is_evp_apis_supported()) { + conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_x25519; + EXPECT_FAILURE(s2n_server_key_share_send_check_ecdhe(conn)); + } + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + EXPECT_FAILURE_WITH_ERRNO(s2n_server_key_share_send_check_ecdhe(conn), S2N_ERR_BAD_KEY_SHARE); + + conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + EXPECT_FAILURE_WITH_ERRNO(s2n_server_key_share_send_check_ecdhe(conn), S2N_ERR_BAD_KEY_SHARE); + + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + EXPECT_SUCCESS(s2n_server_key_share_send_check_ecdhe(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_extensions_server_key_share_send_size */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_EQUAL(0, s2n_extensions_server_key_share_send_size(conn)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + EXPECT_EQUAL(ecc_pref->ecc_curves[0]->share_size + 8, s2n_extensions_server_key_share_send_size(conn)); + + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[1]; + EXPECT_EQUAL(ecc_pref->ecc_curves[1]->share_size + 8, s2n_extensions_server_key_share_send_size(conn)); + + conn->kex_params.server_ecc_evp_params.negotiated_curve = NULL; + EXPECT_EQUAL(0, s2n_extensions_server_key_share_send_size(conn)); + + /* A HelloRetryRequest only requires a Selected Group, not a key share */ + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(conn, S2N_TLS13)); + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(conn)); + conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_EQUAL(6, s2n_extensions_server_key_share_send_size(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_server_key_share_extension.send sends key share success (ECDHE) */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + EXPECT_NULL(conn->kex_params.server_kem_group_params.kem_group); + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + for (size_t i = 0; i < ecc_pref->count; i++) { + const struct s2n_ecc_named_curve *curve = ecc_pref->ecc_curves[i]; + conn->kex_params.server_ecc_evp_params.negotiated_curve = curve; + conn->kex_params.client_ecc_evp_params.negotiated_curve = curve; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + + EXPECT_SUCCESS(s2n_server_key_share_extension.send(conn, &stuffer)); + + S2N_STUFFER_READ_EXPECT_EQUAL(&stuffer, curve->iana_id, uint16); + S2N_STUFFER_READ_EXPECT_EQUAL(&stuffer, curve->share_size, uint16); + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(&stuffer, curve->share_size); + EXPECT_NULL(conn->kex_params.server_kem_group_params.kem_group); + EXPECT_EQUAL(conn->kex_params.server_ecc_evp_params.negotiated_curve, curve); + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&conn->kex_params.server_ecc_evp_params)); + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&conn->kex_params.client_ecc_evp_params)); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_server_key_share_extension.send sends IANA ID for HRR (ECDHE) */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + conn->handshake.handshake_type = HELLO_RETRY_REQUEST; + conn->handshake.message_number = HELLO_RETRY_MSG_NO; + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + EXPECT_NULL(conn->kex_params.server_kem_group_params.kem_group); + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + for (size_t i = 0; i < ecc_pref->count; i++) { + const struct s2n_ecc_named_curve *curve = ecc_pref->ecc_curves[i]; + conn->kex_params.server_ecc_evp_params.negotiated_curve = curve; + EXPECT_NULL(conn->kex_params.server_ecc_evp_params.evp_pkey); + + EXPECT_SUCCESS(s2n_server_key_share_extension.send(conn, &stuffer)); + + S2N_STUFFER_READ_EXPECT_EQUAL(&stuffer, curve->iana_id, uint16); + EXPECT_EQUAL(0, s2n_stuffer_data_available(&stuffer)); + EXPECT_EQUAL(conn->kex_params.server_ecc_evp_params.negotiated_curve, curve); + EXPECT_NULL(conn->kex_params.server_ecc_evp_params.evp_pkey); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_server_key_share_extension.send for failures */ + { + EXPECT_FAILURE(s2n_server_key_share_extension.send(NULL, NULL)); + + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_FAILURE(s2n_server_key_share_extension.send(conn, NULL)); + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + /* Error if both curve and kem_group are NULL */ + conn->kex_params.server_ecc_evp_params.negotiated_curve = NULL; + conn->kex_params.server_kem_group_params.kem_group = NULL; + EXPECT_FAILURE_WITH_ERRNO(s2n_server_key_share_extension.send(conn, &stuffer), S2N_ERR_ECDHE_UNSUPPORTED_CURVE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_server_key_share_extension.recv with supported curves */ + { + const struct s2n_ecc_preferences *ecc_pref = NULL; + + int i = 0; + do { + struct s2n_connection *server_send_conn; + struct s2n_connection *client_recv_conn; + EXPECT_NOT_NULL(server_send_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_recv_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_send_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + struct s2n_stuffer *extension_stuffer = &server_send_conn->handshake.io; + + server_send_conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[i]; + server_send_conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[i]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&server_send_conn->kex_params.client_ecc_evp_params)); + EXPECT_SUCCESS(s2n_server_key_share_extension.send(server_send_conn, extension_stuffer)); + + client_recv_conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[i]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_recv_conn->kex_params.client_ecc_evp_params)); + + /* Parse key share */ + EXPECT_SUCCESS(s2n_server_key_share_extension.recv(client_recv_conn, extension_stuffer)); + EXPECT_EQUAL(s2n_stuffer_data_available(extension_stuffer), 0); + + EXPECT_EQUAL(server_send_conn->kex_params.server_ecc_evp_params.negotiated_curve->iana_id, client_recv_conn->kex_params.server_ecc_evp_params.negotiated_curve->iana_id); + EXPECT_EQUAL(server_send_conn->kex_params.server_ecc_evp_params.negotiated_curve, ecc_pref->ecc_curves[i]); + + EXPECT_SUCCESS(s2n_connection_free(server_send_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_recv_conn)); + + i += 1; + } while (i < ecc_pref->count); + }; + + /* Test s2n_server_key_share_extension.recv with various sample payloads */ + { + /* valid extension payloads */ + if (s2n_is_evp_apis_supported()) { + /* Payload values were generated by connecting to openssl */ + const char *key_share_payloads[] = { + /* x25519 */ + "001d00206b24ffd795c496899cd14b7742a5ffbdc453c23085a7f82f0ed1e0296adb9e0e", + /* p256 */ + "001700410474cfd75c0ab7b57247761a277e1c92b5810dacb251bb758f43e9d15aaf292c4a2be43e886425ba55653ebb7a4f32fe368bacce3df00c618645cf1eb646f22552", + /* p384 */ + "00180061040a27264201368540483e97d324a3093e11a5862b0a1be0cf5d8510bc47ec285f5304e9ec3ba01a0c375c3b6fa4bd0ad44aae041bb776aebc7ee92462ad481fe86f8b6e3858d5c41d0f83b0404f711832a4119aec3da2eac86266f424b50aa212" + }; + + for (int i = 0; i < 3; i++) { + struct s2n_stuffer extension_stuffer = { 0 }; + struct s2n_connection *client_conn; + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(client_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + const char *payload = key_share_payloads[i]; + + EXPECT_NULL(client_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_SUCCESS(s2n_stuffer_alloc_ro_from_hex_string(&extension_stuffer, payload)); + + client_conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[i]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_conn->kex_params.client_ecc_evp_params)); + + EXPECT_SUCCESS(s2n_server_key_share_extension.recv(client_conn, &extension_stuffer)); + EXPECT_EQUAL(client_conn->kex_params.server_ecc_evp_params.negotiated_curve, ecc_pref->ecc_curves[i]); + EXPECT_EQUAL(s2n_stuffer_data_available(&extension_stuffer), 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&extension_stuffer)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + } + + /* Test that s2n_server_key_share_extension.recv is a no-op + * if tls1.3 not enabled */ + { + struct s2n_stuffer extension_stuffer = { 0 }; + struct s2n_connection *client_conn; + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_allow_response_extension(client_conn, s2n_server_key_share_extension.iana_value)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(client_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + const char *payload = key_share_payloads[0]; + + EXPECT_NULL(client_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_SUCCESS(s2n_stuffer_alloc_ro_from_hex_string(&extension_stuffer, payload)); + + client_conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_conn->kex_params.client_ecc_evp_params)); + + client_conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_extension_recv(&s2n_server_key_share_extension, client_conn, &extension_stuffer)); + EXPECT_NULL(client_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_NOT_EQUAL(s2n_stuffer_data_available(&extension_stuffer), 0); + + client_conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_server_key_share_extension.recv(client_conn, &extension_stuffer)); + EXPECT_EQUAL(client_conn->kex_params.server_ecc_evp_params.negotiated_curve, ecc_pref->ecc_curves[0]); + EXPECT_EQUAL(s2n_stuffer_data_available(&extension_stuffer), 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&extension_stuffer)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + } + + /* Test error handling parsing broken/trancated p256 key share */ + { + struct s2n_stuffer extension_stuffer = { 0 }; + struct s2n_connection *client_conn; + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + const char *p256 = "001700410474cfd75c0ab7b57247761a277e1c92b5810dacb251bb758f43e9d15aaf292c4a2be43e886425ba55653ebb7a4f32fe368bacce3df00c618645cf1eb6"; + + EXPECT_NULL(client_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_SUCCESS(s2n_stuffer_alloc_ro_from_hex_string(&extension_stuffer, p256)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_key_share_extension.recv(client_conn, &extension_stuffer), S2N_ERR_BAD_KEY_SHARE); + + EXPECT_SUCCESS(s2n_stuffer_free(&extension_stuffer)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* Test failure for receiving p256 key share for client configured p384 key share */ + { + struct s2n_stuffer extension_stuffer = { 0 }; + struct s2n_connection *client_conn; + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(client_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + const char *p256 = "001700410474cfd75c0ab7b57247761a277e1c92b5810dacb251bb758f43e9d15aaf292c4a2be43e886425ba55653ebb7a4f32fe368bacce3df00c618645cf1eb646f22552"; + + EXPECT_NULL(client_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_SUCCESS(s2n_stuffer_alloc_ro_from_hex_string(&extension_stuffer, p256)); + + /* If s2n_is_evp_apis_supported is not supported, the ecc_prefs->ecc_curves contains only p-256, p-384 curves. */ + int p_384_index = s2n_is_evp_apis_supported() ? 2 : 1; + + client_conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[p_384_index]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_conn->kex_params.client_ecc_evp_params)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_key_share_extension.recv(client_conn, &extension_stuffer), S2N_ERR_BAD_KEY_SHARE); + + EXPECT_SUCCESS(s2n_stuffer_free(&extension_stuffer)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + }; + + /* Test Shared Key Generation */ + { + struct s2n_connection *client_conn, *server_conn; + struct s2n_stuffer key_share_extension = { 0 }; + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_set_all_mutually_supported_groups(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&key_share_extension, 0)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + EXPECT_SUCCESS(s2n_client_key_share_extension.send(client_conn, &key_share_extension)); + EXPECT_SUCCESS(s2n_client_key_share_extension.recv(server_conn, &key_share_extension)); + + /* should read all data */ + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + /* Server configures the "negotiated_curve" */ + server_conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + + EXPECT_SUCCESS(s2n_server_key_share_extension.send(server_conn, &key_share_extension)); + EXPECT_SUCCESS(s2n_server_key_share_extension.recv(client_conn, &key_share_extension)); + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_extension), 0); + + EXPECT_EQUAL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve, client_conn->kex_params.server_ecc_evp_params.negotiated_curve); + + /* Ensure both client and server public key matches */ + s2n_public_ecc_keys_are_equal(&server_conn->kex_params.server_ecc_evp_params, &client_conn->kex_params.server_ecc_evp_params); + s2n_public_ecc_keys_are_equal(&server_conn->kex_params.client_ecc_evp_params, &client_conn->kex_params.client_ecc_evp_params); + + /* Server generates shared key based on Server's Key and Client's public key */ + struct s2n_blob server_shared_secret = { 0 }; + EXPECT_SUCCESS(s2n_ecc_evp_compute_shared_secret_from_params( + &server_conn->kex_params.server_ecc_evp_params, + &server_conn->kex_params.client_ecc_evp_params, + &server_shared_secret)); + + /* Clients generates shared key based on Client's Key and Server's public key */ + struct s2n_blob client_shared_secret = { 0 }; + EXPECT_SUCCESS(s2n_ecc_evp_compute_shared_secret_from_params( + &client_conn->kex_params.client_ecc_evp_params, + &client_conn->kex_params.server_ecc_evp_params, + &client_shared_secret)); + + /* Test that server shared secret matches client shared secret */ + if (ecc_pref->ecc_curves[0] == &s2n_ecc_curve_secp256r1 || ecc_pref->ecc_curves[0] == &s2n_ecc_curve_secp384r1) { + /* Share sizes are described here: https://tools.ietf.org/html/rfc8446#section-4.2.8.2 + * and include the extra "legacy_form" byte */ + EXPECT_EQUAL(server_shared_secret.size, (ecc_pref->ecc_curves[0]->share_size - 1) * 0.5); + } else { + EXPECT_EQUAL(server_shared_secret.size, ecc_pref->ecc_curves[0]->share_size); + } + + S2N_BLOB_EXPECT_EQUAL(server_shared_secret, client_shared_secret); + + EXPECT_SUCCESS(s2n_free(&client_shared_secret)); + EXPECT_SUCCESS(s2n_free(&server_shared_secret)); + + /* Clean up */ + EXPECT_SUCCESS(s2n_stuffer_free(&key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test s2n_server_key_share_extension.send with supported curve not in s2n_ecc_preferences list selected */ + if (s2n_is_evp_apis_supported()) { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(conn->config); + + /* x25519 is supported by s2n, but NOT included in the 20140601 ecc_preferences list */ + const struct s2n_ecc_named_curve *test_curve = &s2n_ecc_curve_x25519; + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(conn->config, "20140601")); + + conn->kex_params.server_ecc_evp_params.negotiated_curve = test_curve; + conn->kex_params.client_ecc_evp_params.negotiated_curve = test_curve; + EXPECT_FAILURE(s2n_server_key_share_extension.send(conn, &conn->handshake.io)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Test s2n_server_key_share_extension.recv with supported curve not in s2n_ecc_preferences list selected */ + if (s2n_is_evp_apis_supported()) { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + struct s2n_stuffer *extension_stuffer = &conn->handshake.io; + + /* x25519 is supported by s2n, but NOT included in the 20140601 ecc_preferences list */ + const struct s2n_ecc_named_curve *test_curve = &s2n_ecc_curve_x25519; + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(conn->config, "20140601")); + + /* Write the iana id of x25519 as the group */ + EXPECT_SUCCESS(s2n_stuffer_write_uint16(extension_stuffer, test_curve->iana_id)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_key_share_extension.recv(conn, extension_stuffer), + S2N_ERR_ECDHE_UNSUPPORTED_CURVE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Test the s2n_server_key_share_extension.recv with HelloRetryRequest */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + /* For a HelloRetryRequest, we won't have a key share. We just have the server selected group/negotiated curve. + * Test that s2n_server_key_share_extension.recv obtains the server negotiate curve successfully. */ + { + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer *key_share_extension = &server_conn->handshake.io; + + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(server_conn, S2N_TLS13)); + EXPECT_OK(s2n_conn_choose_state_machine(server_conn, S2N_TLS13)); + client_conn->security_policy_override = &security_policy_test_tls13_retry; + + server_conn->kex_params.server_ecc_evp_params.negotiated_curve = s2n_all_supported_curves_list[0]; + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(server_conn)); + EXPECT_MEMCPY_SUCCESS(server_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); + EXPECT_SUCCESS(s2n_server_key_share_extension.send(server_conn, key_share_extension)); + + const struct s2n_ecc_preferences *ecc_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(server_conn, &ecc_preferences)); + EXPECT_NOT_NULL(ecc_preferences); + + /* Verify that no key shares are sent */ + struct s2n_ecc_evp_params *ecc_evp_params = &server_conn->kex_params.client_ecc_evp_params; + EXPECT_NULL(ecc_evp_params->negotiated_curve); + EXPECT_NULL(ecc_evp_params->evp_pkey); + + /* Setup the client to have received a HelloRetryRequest */ + EXPECT_MEMCPY_SUCCESS(client_conn->handshake_params.server_random, hello_retry_req_random, S2N_TLS_RANDOM_DATA_LEN); + EXPECT_SUCCESS(s2n_connection_set_all_protocol_versions(client_conn, S2N_TLS13)); + EXPECT_SUCCESS(s2n_set_connection_hello_retry_flags(client_conn)); + EXPECT_SUCCESS(s2n_set_hello_retry_required(client_conn)); + + /* Parse the key share */ + EXPECT_SUCCESS(s2n_server_key_share_extension.recv(client_conn, key_share_extension)); + EXPECT_EQUAL(s2n_stuffer_data_available(key_share_extension), 0); + + EXPECT_EQUAL(server_conn->kex_params.server_ecc_evp_params.negotiated_curve, client_conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_NULL(client_conn->kex_params.server_ecc_evp_params.evp_pkey); + + EXPECT_SUCCESS(s2n_stuffer_free(key_share_extension)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + { + /* KEM groups with Test Vectors defined in /tests/unit/kats/tls13_server_hybrid_key_share_recv.kat */ + const struct s2n_kem_group *test_kem_groups[] = { + &s2n_secp256r1_kyber_512_r3, +#if EVP_APIS_SUPPORTED + &s2n_x25519_kyber_512_r3, +#endif + }; + + const struct s2n_kem_preferences test_kem_prefs = { + .kem_count = 0, + .kems = NULL, + .tls13_kem_group_count = s2n_array_len(test_kem_groups), + .tls13_kem_groups = test_kem_groups, + .tls13_pq_hybrid_draft_revision = 0 + }; + + const struct s2n_security_policy test_security_policy = { + .minimum_protocol_version = S2N_SSLv3, + .cipher_preferences = &cipher_preferences_test_all_tls13, + .kem_preferences = &test_kem_prefs, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20200310, + }; + + const struct s2n_kem_preferences test_all_supported_kem_prefs = { + .kem_count = 0, + .kems = NULL, + .tls13_kem_group_count = S2N_SUPPORTED_KEM_GROUPS_COUNT, + .tls13_kem_groups = ALL_SUPPORTED_KEM_GROUPS, + .tls13_pq_hybrid_draft_revision = 0 + }; + + const struct s2n_security_policy test_all_supported_kems_security_policy = { + .minimum_protocol_version = S2N_SSLv3, + .cipher_preferences = &cipher_preferences_test_all_tls13, + .kem_preferences = &test_all_supported_kem_prefs, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20200310, + }; + + const struct s2n_kem_group *kem_groups_kyber[] = { + &s2n_secp256r1_kyber_512_r3, + }; + + const struct s2n_kem_preferences kem_prefs_kyber = { + .kem_count = 0, + .kems = NULL, + .tls13_kem_group_count = s2n_array_len(kem_groups_kyber), + .tls13_kem_groups = kem_groups_kyber, + .tls13_pq_hybrid_draft_revision = 0 + }; + + const struct s2n_security_policy security_policy_kyber = { + .minimum_protocol_version = S2N_SSLv3, + .cipher_preferences = &cipher_preferences_test_all_tls13, + .kem_preferences = &kem_prefs_kyber, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20200310, + }; + + /* Tests for s2n_server_key_share_extension.recv with hybrid PQ key shares */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* If PQ is disabled, the client will not have sent PQ IDs/keyshares in the ClientHello; + * if the server responded with a PQ keyshare, we should error. */ + if (!s2n_pq_is_enabled()) { + struct s2n_connection *client_conn = NULL; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->security_policy_override = &test_security_policy; + + uint8_t iana_buffer[2]; + struct s2n_blob iana_blob = { 0 }; + struct s2n_stuffer iana_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&iana_blob, iana_buffer, 2)); + EXPECT_SUCCESS(s2n_stuffer_init(&iana_stuffer, &iana_blob)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&iana_stuffer, test_security_policy.kem_preferences->tls13_kem_groups[0]->iana_id)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_key_share_extension.recv(client_conn, &iana_stuffer), S2N_ERR_PQ_DISABLED); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + } + + /* Test s2n_server_key_share_extension.recv with KAT pq key shares */ + if (s2n_pq_is_enabled()) { + { + for (size_t i = 0; i < s2n_array_len(test_kem_groups); i++) { + const struct s2n_kem_group *kem_group = test_kem_groups[i]; + struct s2n_connection *client_conn = NULL; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->security_policy_override = &test_security_policy; + + /* Read the test vectors from the KAT file (the PQ key shares are too long to hardcode inline). + * pq_private_key is intentionally missing DERFER_CLEANUP; it will get freed during s2n_connection_free. */ + struct s2n_blob *pq_private_key = &client_conn->kex_params.client_kem_group_params.kem_params.private_key; + DEFER_CLEANUP(struct s2n_stuffer pq_shared_secret = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer key_share_payload = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_read_server_key_share_hybrid_test_vectors(kem_group, pq_private_key, + &pq_shared_secret, &key_share_payload)); + + /* Assert correct initial state */ + EXPECT_NULL(client_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_NULL(client_conn->kex_params.client_kem_group_params.kem_group); + + const struct s2n_kem_preferences *kem_prefs = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(client_conn, &kem_prefs)); + EXPECT_NOT_NULL(kem_prefs); + EXPECT_EQUAL(kem_group, kem_prefs->tls13_kem_groups[i]); + + /* This set up would have been done when the client sent its key share(s) */ + client_conn->kex_params.client_kem_group_params.kem_group = kem_group; + client_conn->kex_params.client_kem_group_params.ecc_params.negotiated_curve = kem_group->curve; + client_conn->kex_params.client_kem_group_params.kem_params.kem = kem_group->kem; + client_conn->kex_params.client_kem_group_params.kem_params.len_prefixed = true; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_conn->kex_params.client_kem_group_params.ecc_params)); + + /* Call the function and assert correctness */ + EXPECT_SUCCESS(s2n_server_key_share_extension.recv(client_conn, &key_share_payload)); + + EXPECT_NOT_NULL(client_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_EQUAL(client_conn->kex_params.server_kem_group_params.kem_group, kem_group); + EXPECT_NOT_NULL(client_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_EQUAL(client_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve, kem_group->curve); + EXPECT_NOT_NULL(client_conn->kex_params.server_kem_group_params.kem_params.kem); + EXPECT_EQUAL(client_conn->kex_params.server_kem_group_params.kem_params.kem, kem_group->kem); + + EXPECT_EQUAL(client_conn->kex_params.client_kem_group_params.kem_group, kem_group); + EXPECT_EQUAL(client_conn->kex_params.client_kem_group_params.ecc_params.negotiated_curve, kem_group->curve); + EXPECT_EQUAL(client_conn->kex_params.client_kem_group_params.kem_params.kem, kem_group->kem); + EXPECT_NOT_NULL(client_conn->kex_params.client_kem_group_params.kem_params.shared_secret.data); + EXPECT_EQUAL(client_conn->kex_params.client_kem_group_params.kem_params.shared_secret.size, + kem_group->kem->shared_secret_key_length); + EXPECT_BYTEARRAY_EQUAL(client_conn->kex_params.client_kem_group_params.kem_params.shared_secret.data, + pq_shared_secret.blob.data, kem_group->kem->shared_secret_key_length); + + EXPECT_EQUAL(s2n_stuffer_data_available(&key_share_payload), 0); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + } + }; + + /* Test s2n_server_key_share_extension.recv with HRR for PQ */ + { + struct s2n_connection *client_conn = NULL; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->security_policy_override = &test_security_policy; + client_conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(client_conn, S2N_TLS13)); + client_conn->handshake.handshake_type = HELLO_RETRY_REQUEST; + client_conn->handshake.message_number = HELLO_RETRY_MSG_NO; + client_conn->actual_protocol_version_established = 1; + + /* In the HRR, the server indicated p256+Kyber as it's choice in the key share extension */ + const struct s2n_kem_group *kem_group = &s2n_secp256r1_kyber_512_r3; + DEFER_CLEANUP(struct s2n_stuffer key_share_payload = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_alloc_ro_from_hex_string(&key_share_payload, "2F3A")); + + /* Client should successfully parse the indicated group */ + EXPECT_SUCCESS(s2n_server_key_share_extension.recv(client_conn, &key_share_payload)); + + EXPECT_NOT_NULL(client_conn->kex_params.server_kem_group_params.kem_group); + EXPECT_EQUAL(client_conn->kex_params.server_kem_group_params.kem_group, kem_group); + EXPECT_NOT_NULL(client_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + EXPECT_EQUAL(client_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve, kem_group->curve); + EXPECT_NOT_NULL(client_conn->kex_params.server_kem_group_params.kem_params.kem); + EXPECT_EQUAL(client_conn->kex_params.server_kem_group_params.kem_params.kem, kem_group->kem); + + /* s2n_server_key_share_extension.recv should have exited early after parsing the indicated group, + * so everything else should be NULL */ + EXPECT_NULL(client_conn->kex_params.client_kem_group_params.kem_group); + EXPECT_NULL(client_conn->kex_params.client_kem_group_params.ecc_params.negotiated_curve); + EXPECT_NULL(client_conn->kex_params.client_kem_group_params.ecc_params.evp_pkey); + EXPECT_NULL(client_conn->kex_params.client_kem_group_params.kem_params.kem); + EXPECT_NULL(client_conn->kex_params.client_kem_group_params.kem_params.private_key.data); + EXPECT_NULL(client_conn->kex_params.client_kem_group_params.kem_params.public_key.data); + EXPECT_NULL(client_conn->kex_params.client_kem_group_params.kem_params.shared_secret.data); + + EXPECT_NULL(client_conn->kex_params.server_kem_group_params.ecc_params.evp_pkey); + EXPECT_NULL(client_conn->kex_params.server_kem_group_params.kem_params.shared_secret.data); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* Various failure cases */ + { + for (size_t i = 0; i < s2n_array_len(test_kem_groups); i++) { + const struct s2n_kem_group *kem_group = test_kem_groups[i]; + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->security_policy_override = &test_security_policy; + + /* Server sends a named group identifier that isn't in the client's KEM preferences */ + const char *bad_group = "2F2C"; /* IANA ID for secp256r1_threebears-babybear-r2 (not imported into s2n) */ + DEFER_CLEANUP(struct s2n_stuffer bad_group_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_alloc_ro_from_hex_string(&bad_group_stuffer, bad_group)); + EXPECT_FAILURE_WITH_ERRNO(s2n_server_key_share_extension.recv(client_conn, &bad_group_stuffer), + S2N_ERR_ECDHE_UNSUPPORTED_CURVE); + + /* Server sends a key share that is in the client's KEM preferences, but client didn't send a key share */ + const char *wrong_share = "2F1F"; /* Full extension truncated - not necessary */ + DEFER_CLEANUP(struct s2n_stuffer wrong_share_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_alloc_ro_from_hex_string(&wrong_share_stuffer, wrong_share)); + EXPECT_FAILURE_WITH_ERRNO(s2n_server_key_share_extension.recv(client_conn, &wrong_share_stuffer), + S2N_ERR_ECDHE_UNSUPPORTED_CURVE); + + /* To test the remaining failure cases, we need to read in the test vector from the KAT file, then + * manipulate it as necessary. (We do this now, instead of earlier, because we needed + * client_kem_group_params[i].kem_params.private_key to be empty to test the previous case.) */ + struct s2n_blob *pq_private_key = &client_conn->kex_params.client_kem_group_params.kem_params.private_key; + DEFER_CLEANUP(struct s2n_stuffer pq_shared_secret = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer key_share_payload = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_read_server_key_share_hybrid_test_vectors(kem_group, pq_private_key, + &pq_shared_secret, &key_share_payload)); + + /* Server sends the wrong (total) size: data[2] and data[3] are the bytes containing the total size + * of the key share; bitflip data[2] to invalidate the sent size */ + key_share_payload.blob.data[2] = ~key_share_payload.blob.data[2]; + client_conn->kex_params.client_kem_group_params.kem_group = kem_group; + client_conn->kex_params.client_kem_group_params.ecc_params.negotiated_curve = kem_group->curve; + client_conn->kex_params.client_kem_group_params.kem_params.kem = kem_group->kem; + client_conn->kex_params.client_kem_group_params.kem_params.len_prefixed = true; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_conn->kex_params.client_kem_group_params.ecc_params)); + EXPECT_FAILURE_WITH_ERRNO(s2n_server_key_share_extension.recv(client_conn, &key_share_payload), + S2N_ERR_BAD_KEY_SHARE); + /* Revert key_share_payload back to correct state */ + key_share_payload.blob.data[2] = ~key_share_payload.blob.data[2]; + EXPECT_SUCCESS(s2n_stuffer_reread(&key_share_payload)); + + /* Server sends the correct (total) size, but the extension doesn't contain all the data */ + uint8_t truncated_extension[10]; + EXPECT_MEMCPY_SUCCESS(truncated_extension, key_share_payload.blob.data, 10); + struct s2n_blob trunc_ext_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&trunc_ext_blob, truncated_extension, 10)); + struct s2n_stuffer trunc_ext_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&trunc_ext_stuffer, &trunc_ext_blob)); + EXPECT_FAILURE_WITH_ERRNO(s2n_server_key_share_extension.recv(client_conn, &trunc_ext_stuffer), + S2N_ERR_BAD_KEY_SHARE); + + /* Server sends the wrong ECC key share size: data[4] and data[5] are the two bytes containing + * the size of the ECC key share; bitflip data[4] to invalidate the size */ + key_share_payload.blob.data[4] = ~key_share_payload.blob.data[4]; + EXPECT_FAILURE_WITH_ERRNO(s2n_server_key_share_extension.recv(client_conn, &key_share_payload), + S2N_ERR_BAD_KEY_SHARE); + /* Revert key_share_payload back to correct state */ + key_share_payload.blob.data[4] = ~key_share_payload.blob.data[4]; + EXPECT_SUCCESS(s2n_stuffer_reread(&key_share_payload)); + + /* Server sends the wrong PQ share size size: index of the first byte of the size of the PQ key share + * depends of how large the ECC key share is */ + size_t pq_share_size_index = + S2N_SIZE_OF_NAMED_GROUP + + S2N_SIZE_OF_KEY_SHARE_SIZE /* Not a typo; PQ shares have an overall (combined) size */ + + S2N_SIZE_OF_KEY_SHARE_SIZE /* and a size for each contribution of the hybrid share. */ + + kem_group->curve->share_size; + key_share_payload.blob.data[pq_share_size_index] = ~key_share_payload.blob.data[pq_share_size_index]; + EXPECT_FAILURE_WITH_ERRNO(s2n_server_key_share_extension.recv(client_conn, &key_share_payload), + S2N_ERR_BAD_KEY_SHARE); + /* Revert key_share_payload back to correct state */ + key_share_payload.blob.data[pq_share_size_index] = ~key_share_payload.blob.data[pq_share_size_index]; + EXPECT_SUCCESS(s2n_stuffer_reread(&key_share_payload)); + + /* Server sends a bad PQ key share (ciphertext): in order to guarantee certain crypto properties, + * the PQ KEM decapsulation functions are written so that the decaps will succeed without error + * in this case, but the returned PQ shared secret will be incorrect. In practice, this means + * that the key_share.recv function will succeed, but the overall handshake will fail later when + * client+server attempt to use the (different) shared secrets they each derived. */ + size_t pq_key_share_first_byte_index = pq_share_size_index + 2; + key_share_payload.blob.data[pq_key_share_first_byte_index] = ~key_share_payload.blob.data[pq_key_share_first_byte_index]; + EXPECT_SUCCESS(s2n_server_key_share_extension.recv(client_conn, &key_share_payload)); + EXPECT_BYTEARRAY_NOT_EQUAL(client_conn->kex_params.client_kem_group_params.kem_params.shared_secret.data, + pq_shared_secret.blob.data, kem_group->kem->shared_secret_key_length); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + } + }; + } + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + /* Test s2n_server_key_share_send_check_pq_hybrid */ + { + struct s2n_connection *conn = NULL; + EXPECT_FAILURE(s2n_server_key_share_send_check_pq_hybrid(conn)); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + if (!s2n_pq_is_enabled()) { + EXPECT_FAILURE_WITH_ERRNO(s2n_server_key_share_send_check_pq_hybrid(conn), S2N_ERR_PQ_DISABLED); + } + + if (s2n_pq_is_enabled()) { + conn->security_policy_override = &security_policy_kyber; + + EXPECT_FAILURE(s2n_server_key_share_send_check_pq_hybrid(conn)); + conn->kex_params.server_kem_group_params.kem_params.kem = &s2n_kyber_512_r3; + + EXPECT_FAILURE(s2n_server_key_share_send_check_pq_hybrid(conn)); + conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve = &s2n_ecc_curve_secp256r1; + + conn->kex_params.server_kem_group_params.kem_group = &s2n_secp256r1_kyber_512_r3; + EXPECT_FAILURE_WITH_ERRNO(s2n_server_key_share_send_check_pq_hybrid(conn), S2N_ERR_BAD_KEY_SHARE); + + conn->kex_params.server_kem_group_params.kem_group = &s2n_secp256r1_kyber_512_r3; + conn->kex_params.server_kem_group_params.kem_params.kem = &s2n_kyber_512_r3; + EXPECT_FAILURE_WITH_ERRNO(s2n_server_key_share_send_check_pq_hybrid(conn), S2N_ERR_BAD_KEY_SHARE); + + conn->kex_params.client_kem_group_params.kem_group = &s2n_secp256r1_kyber_512_r3; + EXPECT_FAILURE_WITH_ERRNO(s2n_server_key_share_send_check_pq_hybrid(conn), S2N_ERR_BAD_KEY_SHARE); + + conn->kex_params.client_kem_group_params.ecc_params.negotiated_curve = s2n_secp256r1_kyber_512_r3.curve; + EXPECT_FAILURE_WITH_ERRNO(s2n_server_key_share_send_check_pq_hybrid(conn), S2N_ERR_BAD_KEY_SHARE); + + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_kem_group_params.ecc_params)); + EXPECT_FAILURE_WITH_ERRNO(s2n_server_key_share_send_check_pq_hybrid(conn), S2N_ERR_BAD_KEY_SHARE); + + conn->kex_params.client_kem_group_params.kem_params.kem = s2n_secp256r1_kyber_512_r3.kem; + EXPECT_FAILURE_WITH_ERRNO(s2n_server_key_share_send_check_pq_hybrid(conn), S2N_ERR_BAD_KEY_SHARE); + + EXPECT_SUCCESS(s2n_alloc(&conn->kex_params.client_kem_group_params.kem_params.public_key, + s2n_secp256r1_kyber_512_r3.kem->public_key_length)); + EXPECT_OK(s2n_kem_generate_keypair(&conn->kex_params.client_kem_group_params.kem_params)); + EXPECT_SUCCESS(s2n_server_key_share_send_check_pq_hybrid(conn)); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_server_key_share_extension.send sends key share success (PQ) */ + if (s2n_pq_is_enabled()) { + for (int len_prefixed = 0; len_prefixed < 2; len_prefixed++) { + for (size_t i = 0; i < S2N_SUPPORTED_KEM_GROUPS_COUNT; i++) { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->security_policy_override = &test_all_supported_kems_security_policy; + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 4096)); + + /* Set up the server so that it's chosen the correct KEM group and received + * a correspond keyshare from client */ + conn->kex_params.server_ecc_evp_params.negotiated_curve = NULL; + + struct s2n_kem_group_params *server_params = &conn->kex_params.server_kem_group_params; + const struct s2n_kem_group *kem_group = kem_pref->tls13_kem_groups[i]; + server_params->kem_group = kem_group; + server_params->kem_params.kem = kem_group->kem; + server_params->ecc_params.negotiated_curve = kem_group->curve; + + struct s2n_kem_group_params *client_params = &conn->kex_params.client_kem_group_params; + client_params->kem_group = kem_group; + client_params->kem_params.kem = kem_group->kem; + client_params->kem_params.len_prefixed = (bool) len_prefixed; /* This would normally be auto-detected when receiving the Client's KeyShare*/ + client_params->ecc_params.negotiated_curve = kem_group->curve; + + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_params->ecc_params)); + EXPECT_SUCCESS(s2n_alloc(&client_params->kem_params.public_key, kem_group->kem->public_key_length)); + EXPECT_OK(s2n_kem_generate_keypair(&client_params->kem_params)); + EXPECT_SUCCESS(s2n_server_key_share_extension.send(conn, &stuffer)); + uint16_t expected_hybrid_share_size = kem_group->curve->share_size + kem_group->kem->ciphertext_length; + + if (client_params->kem_params.len_prefixed) { + expected_hybrid_share_size += (2 * S2N_SIZE_OF_KEY_SHARE_SIZE); + } + + /* IANA ID (2 bytes) + total share size (2 bytes) + Hybrid Share */ + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), ((2 * sizeof(uint16_t)) + expected_hybrid_share_size)); + + /* Assert we sent a hybrid key share */ + S2N_STUFFER_READ_EXPECT_EQUAL(&stuffer, kem_group->iana_id, uint16); + S2N_STUFFER_READ_EXPECT_EQUAL(&stuffer, expected_hybrid_share_size, uint16); + + if (len_prefixed) { + S2N_STUFFER_READ_EXPECT_EQUAL(&stuffer, kem_group->curve->share_size, uint16); + } + EXPECT_SUCCESS(s2n_stuffer_skip_read(&stuffer, kem_group->curve->share_size)); + + if (len_prefixed) { + S2N_STUFFER_READ_EXPECT_EQUAL(&stuffer, kem_group->kem->ciphertext_length, uint16); + } + S2N_STUFFER_LENGTH_WRITTEN_EXPECT_EQUAL(&stuffer, kem_group->kem->ciphertext_length); + + EXPECT_NULL(conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_EQUAL(server_params->kem_group, kem_group); + EXPECT_EQUAL(server_params->kem_params.kem, kem_group->kem); + EXPECT_EQUAL(server_params->ecc_params.negotiated_curve, kem_group->curve); + EXPECT_EQUAL(client_params->kem_params.shared_secret.size, kem_group->kem->shared_secret_key_length); + EXPECT_NOT_NULL(client_params->kem_params.shared_secret.data); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + } + } + + /* Test s2n_server_key_share_extension.send sends IANA ID for HRR (PQ) */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->security_policy_override = &test_all_supported_kems_security_policy; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + conn->handshake.handshake_type = HELLO_RETRY_REQUEST; + conn->handshake.message_number = HELLO_RETRY_MSG_NO; + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + + EXPECT_NULL(conn->kex_params.server_ecc_evp_params.negotiated_curve); + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + for (size_t i = 0; i < kem_pref->tls13_kem_group_count; i++) { + struct s2n_kem_group_params *server_params = &conn->kex_params.server_kem_group_params; + const struct s2n_kem_group *kem_group = kem_pref->tls13_kem_groups[i]; + + server_params->kem_group = kem_group; + server_params->kem_params.kem = kem_group->kem; + server_params->ecc_params.negotiated_curve = kem_group->curve; + EXPECT_NULL(conn->kex_params.server_ecc_evp_params.evp_pkey); + EXPECT_NULL(conn->kex_params.server_ecc_evp_params.negotiated_curve); + + EXPECT_SUCCESS(s2n_server_key_share_extension.send(conn, &stuffer)); + + S2N_STUFFER_READ_EXPECT_EQUAL(&stuffer, kem_group->iana_id, uint16); + EXPECT_EQUAL(0, s2n_stuffer_data_available(&stuffer)); + + EXPECT_EQUAL(server_params->kem_group, kem_group); + EXPECT_EQUAL(server_params->kem_params.kem, kem_group->kem); + EXPECT_EQUAL(server_params->ecc_params.negotiated_curve, kem_group->curve); + EXPECT_NULL(server_params->kem_params.shared_secret.data); + EXPECT_EQUAL(0, server_params->kem_params.shared_secret.size); + EXPECT_NULL(server_params->ecc_params.evp_pkey); + + EXPECT_NULL(conn->kex_params.server_ecc_evp_params.negotiated_curve); + EXPECT_NULL(conn->kex_params.server_ecc_evp_params.evp_pkey); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test s2n_server_key_share_extension.send fails when both server_kem_group and server_curve are non-NULL */ + { + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->security_policy_override = &test_all_supported_kems_security_policy; + + const struct s2n_kem_preferences *kem_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_kem_preferences(conn, &kem_pref)); + EXPECT_NOT_NULL(kem_pref); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + conn->kex_params.server_kem_group_params.kem_group = kem_pref->tls13_kem_groups[0]; + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_key_share_extension.send(conn, &stuffer), S2N_ERR_ECDHE_UNSUPPORTED_CURVE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + END_TEST(); + return 0; +} + +static int s2n_read_server_key_share_hybrid_test_vectors(const struct s2n_kem_group *kem_group, struct s2n_blob *pq_private_key, + struct s2n_stuffer *pq_shared_secret, struct s2n_stuffer *key_share_payload) +{ + FILE *kat_file = fopen("kats/tls13_server_hybrid_key_share_recv.kat", "r"); + POSIX_ENSURE_REF(kat_file); + + /* 50 should be plenty big enough to hold the entire marker string */ + char marker[50] = "kem_group = "; + strcat(marker, kem_group->name); + POSIX_GUARD(FindMarker(kat_file, marker)); + + POSIX_GUARD(s2n_alloc(pq_private_key, kem_group->kem->private_key_length)); + POSIX_GUARD(ReadHex(kat_file, pq_private_key->data, kem_group->kem->private_key_length, "pq_private_key = ")); + pq_private_key->size = kem_group->kem->private_key_length; + + POSIX_GUARD(s2n_stuffer_alloc(pq_shared_secret, kem_group->kem->shared_secret_key_length)); + uint8_t *pq_shared_secret_ptr = s2n_stuffer_raw_write(pq_shared_secret, kem_group->kem->shared_secret_key_length); + POSIX_ENSURE_REF(pq_shared_secret_ptr); + POSIX_GUARD(ReadHex(kat_file, pq_shared_secret_ptr, kem_group->kem->shared_secret_key_length, "pq_shared_secret = ")); + + size_t key_share_payload_size = + S2N_SIZE_OF_NAMED_GROUP + + S2N_SIZE_OF_KEY_SHARE_SIZE /* Not a typo; PQ shares have an overall (combined) size */ + + S2N_SIZE_OF_KEY_SHARE_SIZE /* and a size for each contribution of the hybrid share. */ + + kem_group->curve->share_size + + S2N_SIZE_OF_KEY_SHARE_SIZE + + kem_group->kem->ciphertext_length; + + POSIX_GUARD(s2n_stuffer_alloc(key_share_payload, key_share_payload_size)); + uint8_t *key_share_payload_ptr = s2n_stuffer_raw_write(key_share_payload, key_share_payload_size); + POSIX_ENSURE_REF(key_share_payload_ptr); + POSIX_GUARD(ReadHex(kat_file, key_share_payload_ptr, key_share_payload_size, "server_key_share_payload = ")); + + fclose(kat_file); + return S2N_SUCCESS; +} diff --git a/tests/unit/s2n_server_max_frag_len_extension_test.c b/tests/unit/s2n_server_max_frag_len_extension_test.c new file mode 100644 index 00000000000..ccbe5e90b2a --- /dev/null +++ b/tests/unit/s2n_server_max_frag_len_extension_test.c @@ -0,0 +1,186 @@ +/* + * 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 "s2n_test.h" +#include "tls/extensions/s2n_server_max_fragment_length.h" +#include "tls/s2n_tls.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Test should_send */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Should not send by default */ + EXPECT_FALSE(s2n_server_max_fragment_length_extension.should_send(conn)); + + /* Should send if mfl code set. It is set by the client version of this extension. */ + conn->negotiated_mfl_code = S2N_TLS_MAX_FRAG_LEN_512; + EXPECT_TRUE(s2n_server_max_fragment_length_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test send */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(config, S2N_TLS_MAX_FRAG_LEN_512)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + conn->negotiated_mfl_code = S2N_TLS_MAX_FRAG_LEN_512; + EXPECT_SUCCESS(s2n_server_max_fragment_length_extension.send(conn, &stuffer)); + + /* Should have correct fragment length */ + uint8_t actual_fragment_length; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&stuffer, &actual_fragment_length)); + EXPECT_EQUAL(actual_fragment_length, S2N_TLS_MAX_FRAG_LEN_512); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test receive - does not match requested value + * + *= https://tools.ietf.org/rfc/rfc6066#section-4 + *= type=test + *# Similarly, if a client + *# receives a maximum fragment length negotiation response that differs + *# from the length it requested, it MUST also abort the handshake with + *# an "illegal_parameter" alert. + */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(config, S2N_TLS_MAX_FRAG_LEN_512)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + conn->negotiated_mfl_code = S2N_TLS_MAX_FRAG_LEN_1024; + EXPECT_SUCCESS(s2n_server_max_fragment_length_extension.send(conn, &stuffer)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_max_fragment_length_extension.recv(conn, &stuffer), + S2N_ERR_MAX_FRAG_LEN_MISMATCH); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test receive */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(config, S2N_TLS_MAX_FRAG_LEN_512)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + conn->negotiated_mfl_code = S2N_TLS_MAX_FRAG_LEN_512; + EXPECT_SUCCESS(s2n_server_max_fragment_length_extension.send(conn, &stuffer)); + EXPECT_NOT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + conn->negotiated_mfl_code = 0; + EXPECT_SUCCESS(s2n_server_max_fragment_length_extension.recv(conn, &stuffer)); + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_EQUAL(conn->negotiated_mfl_code, S2N_TLS_MAX_FRAG_LEN_512); + EXPECT_EQUAL(conn->max_outgoing_fragment_length, 512); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test receive - existing mfl value */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_send_max_fragment_length(config, S2N_TLS_MAX_FRAG_LEN_1024)); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + /* Existing mfl value lower */ + { + conn->max_outgoing_fragment_length = mfl_code_to_length[S2N_TLS_MAX_FRAG_LEN_512]; + conn->negotiated_mfl_code = S2N_TLS_MAX_FRAG_LEN_1024; + + EXPECT_SUCCESS(s2n_server_max_fragment_length_extension.send(conn, &stuffer)); + EXPECT_NOT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + conn->negotiated_mfl_code = 0; + EXPECT_SUCCESS(s2n_server_max_fragment_length_extension.recv(conn, &stuffer)); + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_EQUAL(conn->negotiated_mfl_code, S2N_TLS_MAX_FRAG_LEN_1024); + EXPECT_EQUAL(conn->max_outgoing_fragment_length, mfl_code_to_length[S2N_TLS_MAX_FRAG_LEN_512]); + }; + + /* Existing mfl value higher */ + { + conn->max_outgoing_fragment_length = mfl_code_to_length[S2N_TLS_MAX_FRAG_LEN_2048]; + conn->negotiated_mfl_code = S2N_TLS_MAX_FRAG_LEN_1024; + + EXPECT_SUCCESS(s2n_server_max_fragment_length_extension.send(conn, &stuffer)); + EXPECT_NOT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + conn->negotiated_mfl_code = 0; + EXPECT_SUCCESS(s2n_server_max_fragment_length_extension.recv(conn, &stuffer)); + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_EQUAL(conn->negotiated_mfl_code, S2N_TLS_MAX_FRAG_LEN_1024); + EXPECT_EQUAL(conn->max_outgoing_fragment_length, mfl_code_to_length[S2N_TLS_MAX_FRAG_LEN_1024]); + }; + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_server_new_session_ticket_test.c b/tests/unit/s2n_server_new_session_ticket_test.c new file mode 100644 index 00000000000..5d282f76d55 --- /dev/null +++ b/tests/unit/s2n_server_new_session_ticket_test.c @@ -0,0 +1,1337 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_tls.h" +/* To test static functions */ +#include "tls/s2n_server_new_session_ticket.c" + +#define TEST_TICKET_AGE_ADD 0x01, 0x02, 0x03, 0x04 +#define TEST_LIFETIME 0x00, 0x01, 0x01, 0x01 +#define TEST_TICKET 0x01, 0xFF, 0x23 + +#define ONE_HOUR_IN_NANOS 3600000000000 + +#define TICKET_AGE_ADD_MARKER sizeof(uint8_t) + /* message id */ \ + SIZEOF_UINT24 + /* message len */ \ + sizeof(uint32_t) /* ticket lifetime */ +#define RECORD_LEN_MARKER sizeof(uint8_t) + /* message type */ \ + sizeof(uint16_t) /* protocol version */ + +#define MAX_TEST_SESSION_SIZE 300 + +#define EXPECT_TICKETS_SENT(conn, count) EXPECT_OK(s2n_assert_tickets_sent(conn, count)) + +static S2N_RESULT s2n_assert_tickets_sent(struct s2n_connection *conn, uint16_t expected_tickets_sent) +{ + uint16_t tickets_sent = 0; + RESULT_GUARD_POSIX(s2n_connection_get_tickets_sent(conn, &tickets_sent)); + RESULT_ENSURE_EQ(tickets_sent, expected_tickets_sent); + return S2N_RESULT_OK; +} + +size_t cb_session_data_len = 0; +uint8_t cb_session_data[MAX_TEST_SESSION_SIZE] = { 0 }; +uint32_t cb_session_lifetime = 0; +static int s2n_test_session_ticket_cb(struct s2n_connection *conn, void *ctx, struct s2n_session_ticket *ticket) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(ticket); + + EXPECT_SUCCESS(s2n_session_ticket_get_data_len(ticket, &cb_session_data_len)); + EXPECT_SUCCESS(s2n_session_ticket_get_data(ticket, cb_session_data_len, cb_session_data)); + EXPECT_SUCCESS(s2n_session_ticket_get_lifetime(ticket, &cb_session_lifetime)); + + return S2N_SUCCESS; +} + +static int s2n_setup_test_ticket_key(struct s2n_config *config) +{ + POSIX_ENSURE_REF(config); + + /** + *= https://tools.ietf.org/rfc/rfc5869#appendix-A.1 + *# PRK = 0x077709362c2e32df0ddc3f0dc47bba63 + *# 90b6c73bb50f9c3122ec844ad7c2b3e5 (32 octets) + **/ + S2N_BLOB_FROM_HEX(ticket_key, + "077709362c2e32df0ddc3f0dc47bba63" + "90b6c73bb50f9c3122ec844ad7c2b3e5"); + + /* Set up encryption key */ + uint64_t current_time; + uint8_t ticket_key_name[16] = "2016.07.26.15\0"; + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(config->wall_clock(config->sys_clock_ctx, ¤t_time)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), + ticket_key.data, ticket_key.size, current_time / ONE_SEC_IN_NANOS)); + + return S2N_SUCCESS; +} + +static int s2n_setup_test_resumption_secret(struct s2n_connection *conn) +{ + POSIX_ENSURE_REF(conn); + /** + *= https://tools.ietf.org/rfc/rfc8448#section-3 + *# PRK (32 octets): 7d f2 35 f2 03 1d 2a 05 12 87 d0 2b 02 41 b0 bf + *# da f8 6c c8 56 23 1f 2d 5a ba 46 c4 34 ec 19 6c + **/ + S2N_BLOB_FROM_HEX(test_resumption_secret, + "7d f2 35 f2 03 1d 2a 05 12 87 d0 2b 02 41 b0 bf \ + da f8 6c c8 56 23 1f 2d 5a ba 46 c4 34 ec 19 6c"); + + /* Set up resumption secret */ + struct s2n_blob secret = { 0 }; + struct s2n_stuffer secret_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&secret, conn->secrets.version.tls13.resumption_master_secret, S2N_TLS_SECRET_LEN)); + EXPECT_SUCCESS(s2n_stuffer_init(&secret_stuffer, &secret)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&secret_stuffer, test_resumption_secret.data, test_resumption_secret.size)); + + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* s2n_tls13_server_nst_write */ + { + /* Check session ticket message is correctly written. */ + { + struct s2n_config *config; + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(config = s2n_config_new()); + + EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + uint16_t test_tickets_sent = 10; + conn->tickets_sent = test_tickets_sent; + + /* Set up output stuffer */ + struct s2n_stuffer output = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + + EXPECT_OK(s2n_tls13_server_nst_write(conn, &output)); + + uint8_t message_type = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&output, &message_type)); + EXPECT_EQUAL(TLS_SERVER_NEW_SESSION_TICKET, message_type); + + uint32_t message_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint24(&output, &message_size)); + EXPECT_EQUAL(message_size, s2n_stuffer_data_available(&output)); + + uint32_t ticket_lifetime = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint32(&output, &ticket_lifetime)); + uint32_t key_lifetime_in_secs = + (S2N_TICKET_ENCRYPT_DECRYPT_KEY_LIFETIME_IN_NANOS + S2N_TICKET_DECRYPT_KEY_LIFETIME_IN_NANOS) / ONE_SEC_IN_NANOS; + EXPECT_EQUAL(key_lifetime_in_secs, ticket_lifetime); + + /* Skipping random data */ + EXPECT_SUCCESS(s2n_stuffer_skip_read(&output, sizeof(uint32_t))); + + uint8_t ticket_nonce_len = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&output, &ticket_nonce_len)); + EXPECT_EQUAL(sizeof(uint16_t), ticket_nonce_len); + + uint8_t ticket_nonce[sizeof(uint16_t)] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&output, ticket_nonce, ticket_nonce_len)); + + uint8_t tickets_sent_array[sizeof(uint16_t)] = { 0 }; + struct s2n_blob blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&blob, tickets_sent_array, sizeof(uint16_t))); + EXPECT_OK(s2n_generate_ticket_nonce(test_tickets_sent, &blob)); + EXPECT_BYTEARRAY_EQUAL(ticket_nonce, blob.data, ticket_nonce_len); + + uint16_t session_ticket_len = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&output, &session_ticket_len)); + uint8_t size_of_extensions_size = sizeof(uint16_t); + EXPECT_EQUAL(session_ticket_len, (s2n_stuffer_data_available(&output) - size_of_extensions_size)); + + /* Skipping encrypted ticket data */ + EXPECT_SUCCESS(s2n_stuffer_skip_read(&output, session_ticket_len)); + + uint16_t extensions_len = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&output, &extensions_len)); + EXPECT_EQUAL(extensions_len, 0); + + EXPECT_EQUAL(s2n_stuffer_data_available(&output), 0); + + EXPECT_TRUE(conn->tickets_sent == test_tickets_sent + 1); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&output)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* tickets_sent overflow */ + { + struct s2n_config *config; + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(config = s2n_config_new()); + + EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + conn->tickets_sent = UINT16_MAX; + + /* Set up output stuffer */ + struct s2n_stuffer output = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_server_nst_write(conn, &output), S2N_ERR_INTEGER_OVERFLOW); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&output)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /** ticket_age_add values do not repeat after sending multiple new session tickets + *= https://tools.ietf.org/rfc/rfc8446#section-4.6.1 + *= type=test + *# The server MUST generate a fresh value + *# for each ticket it sends. + **/ + { + struct s2n_config *config; + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(config = s2n_config_new()); + + EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + + /* Set up output stuffer */ + struct s2n_stuffer output = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + + EXPECT_OK(s2n_tls13_server_nst_write(conn, &output)); + + uint32_t original_ticket_age_add = 0; + EXPECT_SUCCESS(s2n_stuffer_skip_read(&output, TICKET_AGE_ADD_MARKER)); + EXPECT_SUCCESS(s2n_stuffer_read_uint32(&output, &original_ticket_age_add)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&output)); + EXPECT_OK(s2n_tls13_server_nst_write(conn, &output)); + + uint32_t new_ticket_age_add = 0; + EXPECT_SUCCESS(s2n_stuffer_skip_read(&output, TICKET_AGE_ADD_MARKER)); + EXPECT_SUCCESS(s2n_stuffer_read_uint32(&output, &new_ticket_age_add)); + + EXPECT_NOT_EQUAL(original_ticket_age_add, new_ticket_age_add); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&output)); + EXPECT_SUCCESS(s2n_config_free(config)); + } + + /* Test that the message written by the server includes extensions. + * Specifically, check for the early_data_indication extension. */ + { + const uint32_t expected_max_early_data_size = 10; + + /* Calculate extension list offset. Extension list should be last. */ + const uint32_t extension_list_offset = sizeof(uint32_t) /* max_early_data_size */ + + sizeof(uint16_t) /* size of extension */ + + sizeof(uint16_t); /* type of extension */ + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(conn, expected_max_early_data_size)); + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + + struct s2n_stuffer output = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + + EXPECT_OK(s2n_tls13_server_nst_write(conn, &output)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&output, + s2n_stuffer_data_available(&output) - extension_list_offset)); + + uint16_t extension_type = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&output, &extension_type)); + EXPECT_EQUAL(extension_type, TLS_EXTENSION_EARLY_DATA); + + uint16_t extension_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&output, &extension_size)); + EXPECT_EQUAL(extension_size, sizeof(uint32_t)); + + uint32_t actual_max_early_data_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint32(&output, &actual_max_early_data_size)); + EXPECT_EQUAL(actual_max_early_data_size, expected_max_early_data_size); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&output)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Can't write ticket larger than allowed size of a PSK identity */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_cb, NULL)); + EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(server_conn, 10)); + + /* Set context to be UINT16_MAX */ + uint8_t early_data_context[UINT16_MAX] = { 0 }; + EXPECT_SUCCESS(s2n_connection_set_server_early_data_context(server_conn, + early_data_context, sizeof(early_data_context))); + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_server_nst_write(server_conn, &stuffer), S2N_ERR_SIZE_MISMATCH); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + }; + + /* s2n_generate_ticket_lifetime */ + { + uint32_t min_lifetime = 0; + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + /* Test: encrypt + decrypt key has shortest lifetime */ + conn->config->encrypt_decrypt_key_lifetime_in_nanos = ONE_HOUR_IN_NANOS; + conn->config->decrypt_key_lifetime_in_nanos = ONE_HOUR_IN_NANOS; + conn->config->session_state_lifetime_in_nanos = ONE_HOUR_IN_NANOS * 3; + + EXPECT_OK(s2n_generate_ticket_lifetime(conn, &min_lifetime)); + EXPECT_EQUAL(min_lifetime, (ONE_HOUR_IN_NANOS * 2) / ONE_SEC_IN_NANOS); + + /* Test: Session state has shortest lifetime */ + conn->config->encrypt_decrypt_key_lifetime_in_nanos = ONE_HOUR_IN_NANOS; + conn->config->decrypt_key_lifetime_in_nanos = ONE_HOUR_IN_NANOS; + conn->config->session_state_lifetime_in_nanos = ONE_HOUR_IN_NANOS; + + EXPECT_OK(s2n_generate_ticket_lifetime(conn, &min_lifetime)); + EXPECT_EQUAL(min_lifetime, ONE_HOUR_IN_NANOS / ONE_SEC_IN_NANOS); + + /** Test: Both session state and decrypt key have longer lifetimes than a week + *= https://tools.ietf.org/rfc/rfc8446#section-4.6.1 + *= type=test + *# Servers MUST NOT use any value greater than + *# 604800 seconds (7 days). + **/ + /* Note: We turn these integer literals into uint64_t values because otherwise + * they will be interpreted as uint32_t values and an faulty integer overflow error + * will be thrown. */ + uint64_t one_week_in_sec = ONE_WEEK_IN_SEC; + uint64_t one_sec_in_nanos = ONE_SEC_IN_NANOS; + uint64_t one_week_in_nanos = one_week_in_sec * one_sec_in_nanos; + conn->config->encrypt_decrypt_key_lifetime_in_nanos = one_week_in_nanos; + conn->config->decrypt_key_lifetime_in_nanos = one_week_in_nanos; + conn->config->session_state_lifetime_in_nanos = one_week_in_nanos + 1; + + EXPECT_OK(s2n_generate_ticket_lifetime(conn, &min_lifetime)); + EXPECT_EQUAL(min_lifetime, ONE_WEEK_IN_SEC); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* s2n_generate_ticket_nonce */ + { + struct { + uint16_t value; + uint8_t expected_output[2]; + } test_cases[] = { + { .value = 0, .expected_output = { 0, 0 } }, + { .value = 1, .expected_output = { 0, 1 } }, + { .value = 20, .expected_output = { 0, 20 } }, + { .value = UINT8_MAX, .expected_output = { 0, UINT8_MAX } }, + { .value = UINT8_MAX + 1, .expected_output = { 1, 0 } }, + { .value = UINT16_MAX, .expected_output = { UINT8_MAX, UINT8_MAX } }, + { .value = UINT16_MAX - 1, .expected_output = { UINT8_MAX, UINT8_MAX - 1 } }, + }; + + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + uint8_t data[sizeof(uint16_t)] = { 0 }; + struct s2n_blob blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&blob, data, sizeof(data))); + + EXPECT_OK(s2n_generate_ticket_nonce(test_cases[i].value, &blob)); + + EXPECT_EQUAL(test_cases[i].expected_output[0], data[0]); + EXPECT_EQUAL(test_cases[i].expected_output[1], data[1]); + } + }; + + /* s2n_generate_ticket_age_add */ + { + struct { + uint8_t value[4]; + uint32_t expected_output; + } test_cases[] = { + { .value = { 0, 0, 0, 0 }, .expected_output = 0 }, + { .value = { 0, 0, 0, 1 }, .expected_output = 1 }, + { .value = { 0, 0, 0, 20 }, .expected_output = 20 }, + { .value = { 0, 0, 1, 0 }, .expected_output = UINT8_MAX + 1 }, + { .value = { 0, 1, 0, 0 }, .expected_output = UINT16_MAX + 1 }, + { .value = { 0, 0, UINT8_MAX, UINT8_MAX }, .expected_output = UINT16_MAX }, + { .value = { UINT8_MAX, UINT8_MAX, UINT8_MAX, UINT8_MAX }, .expected_output = UINT32_MAX }, + { .value = { UINT8_MAX, UINT8_MAX, UINT8_MAX, UINT8_MAX - 1 }, .expected_output = UINT32_MAX - 1 }, + }; + + for (size_t i = 0; i < s2n_array_len(test_cases); i++) { + uint32_t output = 0; + struct s2n_blob blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&blob, test_cases[i].value, sizeof(test_cases[i].value))); + EXPECT_OK(s2n_generate_ticket_age_add(&blob, &output)); + + EXPECT_EQUAL(output, test_cases[i].expected_output); + } + }; + + /* s2n_generate_session_secret */ + { + /** + *= https://tools.ietf.org/rfc/rfc8448#section-3 + *# expanded (32 octets): 4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c + *# a4 c5 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3 + **/ + /* clang-format off */ + S2N_BLOB_FROM_HEX(expected_session_secret, + "4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c \ + a4 c5 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3"); + /* clang-format on */ + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + EXPECT_SUCCESS(s2n_setup_test_resumption_secret(conn)); + + uint8_t nonce_data[sizeof(uint16_t)] = { 0 }; + struct s2n_blob nonce = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&nonce, nonce_data, sizeof(nonce_data))); + + struct s2n_blob *output = &conn->tls13_ticket_fields.session_secret; + EXPECT_SUCCESS(s2n_generate_session_secret(conn, &nonce, output)); + EXPECT_EQUAL(output->size, expected_session_secret.size); + EXPECT_BYTEARRAY_EQUAL(output->data, expected_session_secret.data, expected_session_secret.size); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* s2n_tls13_server_nst_recv */ + { + uint8_t test_ticket[] = { TEST_TICKET }; + uint8_t nst_data[] = { + TEST_LIFETIME, /* ticket lifetime */ + TEST_TICKET_AGE_ADD, /* ticket age add */ + 0x02, /* nonce len */ + 0x00, 0x00, /* nonce */ + 0x00, 0x03, /* ticket len */ + TEST_TICKET, /* ticket */ + 0x00, 0x00, /* extensions len */ + }; + + /* Does not read ticket message if config->use_tickets is not set */ + { + struct s2n_config *config = s2n_config_new(); + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_NOT_NULL(config); + + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Set up input stuffer */ + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + + struct s2n_blob nst_message = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&nst_message, nst_data, sizeof(nst_data))); + EXPECT_SUCCESS(s2n_stuffer_write(&input, &nst_message)); + + EXPECT_OK(s2n_tls13_server_nst_recv(conn, &input)); + + EXPECT_EQUAL(conn->client_ticket.size, 0); + EXPECT_TRUE(s2n_stuffer_data_available(&input) > 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Not allowed in TLS1.2 */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS12; + + /* Set up input stuffer */ + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + + struct s2n_blob nst_message = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&nst_message, nst_data, sizeof(nst_data))); + EXPECT_SUCCESS(s2n_stuffer_write(&input, &nst_message)); + + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_server_nst_recv(conn, &input), S2N_ERR_BAD_MESSAGE); + + EXPECT_EQUAL(conn->client_ticket.size, 0); + EXPECT_TRUE(s2n_stuffer_data_available(&input) > 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Tests session_ticket_cb correctly serializes session data from an arbitrary new session ticket message */ + { + struct s2n_config *config = s2n_config_new(); + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_NOT_NULL(config); + + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_cb, NULL)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + /* Set up input stuffer */ + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + + struct s2n_blob nst_message = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&nst_message, nst_data, sizeof(nst_data))); + EXPECT_SUCCESS(s2n_stuffer_write(&input, &nst_message)); + + EXPECT_OK(s2n_tls13_server_nst_recv(conn, &input)); + EXPECT_BYTEARRAY_EQUAL(conn->client_ticket.data, test_ticket, sizeof(test_ticket)); + EXPECT_EQUAL(s2n_stuffer_data_available(&input), 0); + + /* Initialize a stuffer to examine the serialized data returned in the session ticket callback */ + struct s2n_blob session_blob = { 0 }; + struct s2n_stuffer session_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&session_blob, cb_session_data, cb_session_data_len)); + EXPECT_SUCCESS(s2n_stuffer_init(&session_stuffer, &session_blob)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&session_stuffer, cb_session_data_len)); + + /* Check the serialized ticket is what was in the arbitrary nst message */ + { + /* Skip to encrypted ticket size */ + EXPECT_SUCCESS((s2n_stuffer_skip_read(&session_stuffer, sizeof(uint8_t)))); + + uint16_t ticket_size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&session_stuffer, &ticket_size)); + EXPECT_EQUAL(ticket_size, sizeof(test_ticket)); + + uint8_t *ticket = s2n_stuffer_raw_read(&session_stuffer, ticket_size); + EXPECT_NOT_NULL(ticket); + EXPECT_BYTEARRAY_EQUAL(ticket, test_ticket, ticket_size); + }; + + /* Check the serialized ticket_age_add is what was in the arbitrary nst message */ + { + uint8_t test_ticket_age_add[] = { TEST_TICKET_AGE_ADD }; + uint8_t ticket_age_add_marker = sizeof(uint8_t) + /* client state format */ + sizeof(uint8_t) + /* protocol version */ + sizeof(uint16_t) + /* cipher suite */ + sizeof(uint64_t); /* time */ + /* Skip to ticket_age_add */ + EXPECT_SUCCESS((s2n_stuffer_skip_read(&session_stuffer, ticket_age_add_marker))); + + uint8_t ticket_age_add[sizeof(uint32_t)] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&session_stuffer, ticket_age_add, sizeof(uint32_t))); + EXPECT_BYTEARRAY_EQUAL(ticket_age_add, test_ticket_age_add, sizeof(uint32_t)); + }; + + /* Check ticket lifetime is what was in the arbitrary nst message */ + { + uint8_t test_lifetime[] = { TEST_LIFETIME }; + uint32_t expected_lifetime = test_lifetime[3] | (test_lifetime[2] << 8) | (test_lifetime[1] << 16) | (test_lifetime[0] << 24); + EXPECT_EQUAL(expected_lifetime, cb_session_lifetime); + }; + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Known values test */ + { + /** + * NewSessionTicket handshake message + * + *= https://tools.ietf.org/rfc/rfc8448#section-3 + *# NewSessionTicket (205 octets): 04 00 00 c9 00 00 00 1e fa d6 aa + *# c5 02 00 00 00 b2 2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00 00 00 + *# 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70 ad 3c + *# 49 88 83 c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9 82 11 + *# 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6 1d 28 + *# 27 db 27 9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0 37 25 + *# a6 a4 da fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5 90 6c + *# 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5 ae a6 + *# 17 64 6f ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d e6 50 + *# 5e 5b fb c3 88 e9 33 43 69 40 93 93 4a e4 d3 57 00 08 00 2a 00 + *# 04 00 00 04 00 + **/ + /* clang-format off */ + S2N_BLOB_FROM_HEX(nst_message, + "04 00 00 c9 00 00 00 1e fa d6 aa \ + c5 02 00 00 00 b2 2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00 00 00 \ + 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70 ad 3c \ + 49 88 83 c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9 82 11 \ + 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6 1d 28 \ + 27 db 27 9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0 37 25 \ + a6 a4 da fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5 90 6c \ + 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5 ae a6 \ + 17 64 6f ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d e6 50 \ + 5e 5b fb c3 88 e9 33 43 69 40 93 93 4a e4 d3 57 00 08 00 2a 00 \ + 04 00 00 04 00"); + /* clang-format on */ + + struct s2n_config *config = s2n_config_new(); + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_NOT_NULL(config); + + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_cb, NULL)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + /* Set up input stuffer */ + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + + EXPECT_SUCCESS(s2n_stuffer_write(&input, &nst_message)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&input, sizeof(uint8_t) + SIZEOF_UINT24)); + EXPECT_OK(s2n_tls13_server_nst_recv(conn, &input)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Self-talk test */ + { + struct s2n_config *config = s2n_config_new(); + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_NOT_NULL(config); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_cb, NULL)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + client_conn->actual_protocol_version = S2N_TLS13; + server_conn->actual_protocol_version = S2N_TLS13; + client_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + server_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_OK(s2n_tls13_server_nst_write(server_conn, &stuffer)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&stuffer, sizeof(uint8_t) + SIZEOF_UINT24)); + EXPECT_OK(s2n_tls13_server_nst_recv(client_conn, &stuffer)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test that the client processes extensions. + * Specifically, check for the early_data_indication extension. */ + { + const uint32_t expected_max_early_data_size = 17; + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_cb, NULL)); + EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + client_conn->actual_protocol_version = S2N_TLS13; + client_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(server_conn, expected_max_early_data_size)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_EQUAL(client_conn->server_max_early_data_size, 0); + EXPECT_OK(s2n_tls13_server_nst_write(server_conn, &stuffer)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&stuffer, sizeof(uint8_t) + SIZEOF_UINT24)); + EXPECT_OK(s2n_tls13_server_nst_recv(client_conn, &stuffer)); + EXPECT_EQUAL(client_conn->server_max_early_data_size, expected_max_early_data_size); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test that the client can handle different max_early_data_size values. */ + { + const uint32_t expected_max_early_data_sizes[] = { 17, 0, UINT16_MAX, 0, 20, UINT32_MAX, 5, 0 }; + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_cb, NULL)); + EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + client_conn->actual_protocol_version = S2N_TLS13; + client_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + for (size_t i = 0; i < s2n_array_len(expected_max_early_data_sizes); i++) { + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(server_conn, expected_max_early_data_sizes[i])); + EXPECT_OK(s2n_tls13_server_nst_write(server_conn, &stuffer)); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&stuffer, sizeof(uint8_t) + SIZEOF_UINT24)); + EXPECT_OK(s2n_tls13_server_nst_recv(client_conn, &stuffer)); + EXPECT_EQUAL(client_conn->server_max_early_data_size, expected_max_early_data_sizes[i]); + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + } + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test that the client rejects tickets with invalid ticket_lifetime */ + { + const size_t lifetime_size = sizeof(uint32_t); + const uint8_t *nst_data_without_lifetime = nst_data + lifetime_size; + const size_t nst_data_without_lifetime_size = sizeof(nst_data) - lifetime_size; + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_cb, NULL)); + + /** + *= https://tools.ietf.org/rfc/rfc8446#section-4.6.1 + *= type=test + *# The value of zero indicates that the + *# ticket should be discarded immediately. + */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->actual_protocol_version = S2N_TLS13; + + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, sizeof(nst_data))); + EXPECT_SUCCESS(s2n_stuffer_write_uint32(&input, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&input, nst_data_without_lifetime, nst_data_without_lifetime_size)); + + EXPECT_OK(s2n_tls13_server_nst_recv(conn, &input)); + /* Verify that the client only got as far as the ticket_lifetime when parsing */ + EXPECT_EQUAL(s2n_stuffer_data_available(&input), nst_data_without_lifetime_size); + /* Verify that the client did not accept + store the ticket */ + EXPECT_EQUAL(s2n_connection_get_session_length(conn), 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /** + *= https://tools.ietf.org/rfc/rfc8446#section-4.6.1 + *= type=test + *# Servers MUST NOT use any value greater than + *# 604800 seconds (7 days). + */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->actual_protocol_version = S2N_TLS13; + + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, sizeof(nst_data))); + EXPECT_SUCCESS(s2n_stuffer_write_uint32(&input, UINT32_MAX)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&input, nst_data_without_lifetime, nst_data_without_lifetime_size)); + + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_server_nst_recv(conn, &input), S2N_ERR_BAD_MESSAGE); + /* Verify that the client only got as far as the ticket_lifetime when parsing */ + EXPECT_EQUAL(s2n_stuffer_data_available(&input), nst_data_without_lifetime_size); + /* Verify that the client did not accept + store the ticket */ + EXPECT_EQUAL(s2n_connection_get_session_length(conn), 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + EXPECT_SUCCESS(s2n_config_free(config)); + }; + }; + + /* s2n_tls13_server_nst_send */ + { + /* Mode is not server */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + conn->actual_protocol_version = S2N_TLS13; + conn->tickets_to_send = 1; + + /* Setup io */ + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_tls13_server_nst_send(conn, &blocked)); + + EXPECT_EQUAL(0, s2n_stuffer_data_available(&stuffer)); + EXPECT_ERROR_WITH_ERRNO(s2n_assert_tickets_sent(conn, 0), S2N_ERR_CLIENT_MODE); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Protocol is less than TLS13 */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + conn->tickets_to_send = 1; + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + s2n_blocked_status blocked = 0; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_ERROR(s2n_tls13_server_nst_send(conn, &blocked)); + EXPECT_TICKETS_SENT(conn, 0); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_tls13_server_nst_send(conn, &blocked)); + EXPECT_TICKETS_SENT(conn, 1); + }; + + /* 0 tickets are requested */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->actual_protocol_version = S2N_TLS13; + conn->tickets_to_send = 0; + EXPECT_NOT_EQUAL(0, s2n_stuffer_space_remaining(&conn->handshake.io)); + + /* Setup io */ + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_tls13_server_nst_send(conn, &blocked)); + + /* Check no tickets are written */ + EXPECT_EQUAL(0, s2n_stuffer_data_available(&stuffer)); + EXPECT_TICKETS_SENT(conn, 0); + + /* Check handshake.io is cleaned up */ + EXPECT_EQUAL(0, s2n_stuffer_space_remaining(&conn->handshake.io)); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* QUIC mode is enabled */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), + s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + conn->tickets_to_send = 1; + conn->actual_protocol_version = S2N_TLS13; + + DEFER_CLEANUP(struct s2n_stuffer stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + s2n_blocked_status blocked = 0; + + conn->quic_enabled = true; + /* No mutually-supported psk mode agreed upon */ + EXPECT_OK(s2n_tls13_server_nst_send(conn, &blocked)); + EXPECT_TICKETS_SENT(conn, 0); + + /* Client has indicated that it supports both resumption and psk_dhe_ke mode */ + conn->psk_params.psk_ke_mode = S2N_PSK_DHE_KE; + EXPECT_OK(s2n_tls13_server_nst_send(conn, &blocked)); + EXPECT_TICKETS_SENT(conn, 1); + }; + + /* Sends one new session ticket */ + { + struct s2n_config *config; + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + conn->tickets_to_send = 1; + EXPECT_NOT_EQUAL(s2n_stuffer_space_remaining(&conn->handshake.io), 0); + + /* Setup io */ + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_tls13_server_nst_send(conn, &blocked)); + EXPECT_TICKETS_SENT(conn, 1); + + /* Check only one record was written */ + uint16_t record_len = 0; + EXPECT_SUCCESS(s2n_stuffer_skip_read(&stuffer, RECORD_LEN_MARKER)); + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &record_len)); + EXPECT_TRUE(record_len > 0); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&stuffer, record_len)); + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Send no more tickets if keying material is expired + * + *= https://tools.ietf.org/rfc/rfc8446#section-4.6.1 + *= type=test + *# Note that in principle it is possible to continue issuing new tickets + *# which indefinitely extend the lifetime of the keying material + *# originally derived from an initial non-PSK handshake (which was most + *# likely tied to the peer's certificate). It is RECOMMENDED that + *# implementations place limits on the total lifetime of such keying + *# material; these limits should take into account the lifetime of the + *# peer's certificate, the likelihood of intervening revocation, and the + *# time since the peer's online CertificateVerify signature. + */ + { + const uint8_t current_tickets = 10; + const uint8_t new_tickets = 5; + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->actual_protocol_version = S2N_TLS13; + conn->tickets_sent = current_tickets; + conn->tickets_to_send = current_tickets; + EXPECT_TICKETS_SENT(conn, current_tickets); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + /* Can request new tickets */ + EXPECT_SUCCESS(s2n_connection_add_new_tickets_to_send(conn, new_tickets)); + EXPECT_EQUAL(conn->tickets_sent, current_tickets); + EXPECT_EQUAL(conn->tickets_to_send, current_tickets + new_tickets); + EXPECT_TICKETS_SENT(conn, current_tickets); + + /* Add expired keying material */ + DEFER_CLEANUP(struct s2n_psk *chosen_psk = s2n_test_psk_new(conn), s2n_psk_free); + EXPECT_NOT_NULL(chosen_psk); + chosen_psk->type = S2N_PSK_TYPE_RESUMPTION; + chosen_psk->keying_material_expiration = 0; + conn->psk_params.chosen_psk = chosen_psk; + + /* Despite tickets requested, no tickets sent */ + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_tls13_server_nst_send(conn, &blocked)); + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + EXPECT_EQUAL(conn->tickets_sent, current_tickets); + EXPECT_EQUAL(conn->tickets_to_send, current_tickets); + EXPECT_TICKETS_SENT(conn, current_tickets); + + /* Can't request more tickets */ + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_add_new_tickets_to_send(conn, new_tickets), + S2N_ERR_KEYING_MATERIAL_EXPIRED); + EXPECT_EQUAL(conn->tickets_sent, current_tickets); + EXPECT_EQUAL(conn->tickets_to_send, current_tickets); + EXPECT_TICKETS_SENT(conn, current_tickets); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* s2n_config_set_session_tickets_onoff used to enable tickets */ + { + uint8_t test_data[S2N_TICKET_KEY_NAME_LEN] = "data"; + uint64_t current_time = 0; + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, true)); + EXPECT_SUCCESS(config->wall_clock(config->sys_clock_ctx, ¤t_time)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, test_data, sizeof(test_data), + test_data, sizeof(test_data), current_time / ONE_SEC_IN_NANOS)); + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + /* Setup io */ + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_tls13_server_nst_send(conn, &blocked)); + EXPECT_TICKETS_SENT(conn, 1); + EXPECT_NOT_EQUAL(0, s2n_stuffer_data_available(&stuffer)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + + /* Request more tickets */ + EXPECT_SUCCESS(s2n_connection_add_new_tickets_to_send(conn, 1)); + EXPECT_OK(s2n_tls13_server_nst_send(conn, &blocked)); + EXPECT_TICKETS_SENT(conn, 2); + EXPECT_NOT_EQUAL(0, s2n_stuffer_data_available(&stuffer)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + + /* Turn tickets off */ + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, false)); + + /* Request more tickets */ + EXPECT_SUCCESS(s2n_connection_add_new_tickets_to_send(conn, 1)); + EXPECT_OK(s2n_tls13_server_nst_send(conn, &blocked)); + EXPECT_TICKETS_SENT(conn, 2); + EXPECT_EQUAL(0, s2n_stuffer_data_available(&stuffer)); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* s2n_config_set_initial_ticket_count used to enable tickets */ + { + uint8_t test_data[S2N_TICKET_KEY_NAME_LEN] = "data"; + uint64_t current_time = 0; + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_initial_ticket_count(config, 1)); + EXPECT_SUCCESS(config->wall_clock(config->sys_clock_ctx, ¤t_time)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, test_data, sizeof(test_data), + test_data, sizeof(test_data), current_time / ONE_SEC_IN_NANOS)); + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + /* Setup io */ + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_tls13_server_nst_send(conn, &blocked)); + EXPECT_TICKETS_SENT(conn, 1); + EXPECT_NOT_EQUAL(0, s2n_stuffer_data_available(&stuffer)); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Sends multiple new session tickets */ + { + struct s2n_config *config; + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(config = s2n_config_new()); + + EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + conn->actual_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + uint16_t tickets_to_send = 5; + conn->tickets_to_send = tickets_to_send; + + /* Setup io */ + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&stuffer, &stuffer, conn)); + + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_tls13_server_nst_send(conn, &blocked)); + EXPECT_TICKETS_SENT(conn, tickets_to_send); + + /* Check five records were written */ + uint16_t record_len = 0; + for (size_t i = 0; i < tickets_to_send; i++) { + EXPECT_SUCCESS(s2n_stuffer_skip_read(&stuffer, RECORD_LEN_MARKER)); + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &record_len)); + EXPECT_TRUE(record_len > 0); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&stuffer, record_len)); + } + EXPECT_TRUE(s2n_stuffer_data_available(&stuffer) == 0); + + /* No more tickets to send */ + EXPECT_SUCCESS(s2n_stuffer_rewrite(&stuffer)); + EXPECT_OK(s2n_tls13_server_nst_send(conn, &blocked)); + EXPECT_EQUAL(0, s2n_stuffer_data_available(&stuffer)); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test S2N_TLS13_MAX_FIXED_NEW_SESSION_TICKET_SIZE */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* TLS1.3 tickets contain extra fields */ + conn->actual_protocol_version = S2N_TLS13; + /* Largest possible TLS1.3 secret size */ + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + /* Necessary for extensions, which contribute to size */ + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(conn, 10)); + + /* Setup io */ + struct s2n_stuffer output = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&output, &output, conn)); + + /* Test with no variable fields */ + { + size_t session_state_size = 0; + EXPECT_OK(s2n_connection_get_session_state_size(conn, &session_state_size)); + EXPECT_NOT_EQUAL(session_state_size, 0); + + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_tls13_server_nst_send(conn, &blocked)); + EXPECT_NOT_EQUAL(s2n_stuffer_data_available(&output), 0); + + uint32_t expected_max_size = s2n_stuffer_data_available(&output) - S2N_TLS_RECORD_HEADER_LENGTH; + uint32_t expected_max_fixed_size = expected_max_size - session_state_size; + if (S2N_TLS13_MAX_FIXED_NEW_SESSION_TICKET_SIZE != expected_max_fixed_size) { + fprintf(stdout, "\nS2N_TLS13_MAX_FIXED_NEW_SESSION_TICKET_SIZE (%i) should be %u\n", + (int) S2N_TLS13_MAX_FIXED_NEW_SESSION_TICKET_SIZE, expected_max_fixed_size); + } + EXPECT_EQUAL(S2N_TLS13_MAX_FIXED_NEW_SESSION_TICKET_SIZE, expected_max_fixed_size); + }; + + EXPECT_SUCCESS(s2n_stuffer_wipe(&output)); + conn->tickets_to_send++; + + /* Test with some variable fields */ + { + const uint8_t early_data_context[] = "early data context"; + EXPECT_SUCCESS(s2n_connection_set_server_early_data_context(conn, + early_data_context, sizeof(early_data_context))); + + size_t session_state_size = 0; + EXPECT_OK(s2n_connection_get_session_state_size(conn, &session_state_size)); + EXPECT_NOT_EQUAL(session_state_size, 0); + + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_tls13_server_nst_send(conn, &blocked)); + EXPECT_NOT_EQUAL(s2n_stuffer_data_available(&output), 0); + + uint32_t expected_max_size = s2n_stuffer_data_available(&output) - S2N_TLS_RECORD_HEADER_LENGTH; + uint32_t expected_max_fixed_size = expected_max_size - session_state_size; + EXPECT_EQUAL(S2N_TLS13_MAX_FIXED_NEW_SESSION_TICKET_SIZE, expected_max_fixed_size); + }; + + EXPECT_SUCCESS(s2n_stuffer_wipe(&output)); + conn->tickets_to_send++; + + /* Test with all variable fields */ + { + const uint8_t early_data_context[] = "different early data context"; + EXPECT_SUCCESS(s2n_connection_set_server_early_data_context(conn, + early_data_context, sizeof(early_data_context))); + + const uint8_t app_protocol[] = "https"; + EXPECT_MEMCPY_SUCCESS(conn->application_protocol, app_protocol, sizeof(app_protocol)); + + size_t session_state_size = 0; + EXPECT_OK(s2n_connection_get_session_state_size(conn, &session_state_size)); + EXPECT_NOT_EQUAL(session_state_size, 0); + + s2n_blocked_status blocked = 0; + EXPECT_OK(s2n_tls13_server_nst_send(conn, &blocked)); + EXPECT_NOT_EQUAL(s2n_stuffer_data_available(&output), 0); + + uint32_t expected_max_size = s2n_stuffer_data_available(&output) - S2N_TLS_RECORD_HEADER_LENGTH; + uint32_t expected_max_fixed_size = expected_max_size - session_state_size; + EXPECT_EQUAL(S2N_TLS13_MAX_FIXED_NEW_SESSION_TICKET_SIZE, expected_max_fixed_size); + }; + + EXPECT_SUCCESS(s2n_stuffer_free(&output)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + }; + + /* Functional test: s2n_negotiate sends new session tickets after the handshake is complete */ + if (s2n_is_tls13_fully_supported()) { + /* Setup connections */ + struct s2n_connection *client_conn, *server_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + /* Setup config */ + struct s2n_cert_chain_and_key *chain_and_key; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + EXPECT_SUCCESS(s2n_setup_test_ticket_key(config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + uint16_t tickets_to_send = 5; + server_conn->tickets_to_send = tickets_to_send; + + struct s2n_stuffer client_to_server = { 0 }; + struct s2n_stuffer server_to_client = { 0 }; + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); + + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + + /* Do handshake */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TICKETS_SENT(server_conn, tickets_to_send); + + /* Check handshake.io was cleaned up. + * If a ticket was written, this happens afterwards. */ + EXPECT_EQUAL(s2n_stuffer_space_remaining(&server_conn->handshake.io), 0); + EXPECT_EQUAL(s2n_stuffer_space_remaining(&client_conn->handshake.io), 0); + + /* Check there are five records corresponding to five new session tickets + * not read as part of the handshake */ + uint16_t record_len = 0; + for (size_t i = 0; i < tickets_to_send; i++) { + EXPECT_SUCCESS(s2n_stuffer_skip_read(&server_to_client, RECORD_LEN_MARKER)); + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&server_to_client, &record_len)); + EXPECT_TRUE(record_len > 0); + EXPECT_SUCCESS(s2n_stuffer_skip_read(&server_to_client, record_len)); + } + EXPECT_TRUE(s2n_stuffer_data_available(&server_to_client) == 0); + + /* Call s2n_negotiate again to ensure no more tickets are sent */ + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(0, s2n_stuffer_data_available(&server_to_client)); + EXPECT_TICKETS_SENT(server_conn, tickets_to_send); + + EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); + EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_config_free(config)); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_server_psk_extension_test.c b/tests/unit/s2n_server_psk_extension_test.c new file mode 100644 index 00000000000..be655545631 --- /dev/null +++ b/tests/unit/s2n_server_psk_extension_test.c @@ -0,0 +1,303 @@ +/* + * 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 "crypto/s2n_hmac.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_server_psk.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_bitmap.h" + +#define TEST_PSK_WIRE_INDEX 1 +#define TEST_PSK_HMAC S2N_HMAC_SHA384 + +uint8_t test_identity[] = "test identity"; +uint8_t test_secret[] = "test secret"; + +static s2n_result setup_client_psks(struct s2n_connection *client_conn) +{ + RESULT_ENSURE_REF(client_conn); + + /* Setup other client PSK */ + uint8_t other_client_data[] = "other client data"; + struct s2n_psk *other_client_psk = NULL; + RESULT_GUARD(s2n_array_pushback(&client_conn->psk_params.psk_list, (void **) &other_client_psk)); + RESULT_GUARD(s2n_psk_init(other_client_psk, S2N_PSK_TYPE_EXTERNAL)); + RESULT_GUARD_POSIX(s2n_psk_set_identity(other_client_psk, other_client_data, sizeof(other_client_data))); + RESULT_GUARD_POSIX(s2n_psk_set_secret(other_client_psk, other_client_data, sizeof(other_client_data))); + other_client_psk->hmac_alg = S2N_HMAC_SHA256; + + /* Setup shared PSK for client */ + struct s2n_psk *shared_psk = NULL; + RESULT_GUARD(s2n_array_pushback(&client_conn->psk_params.psk_list, (void **) &shared_psk)); + RESULT_GUARD(s2n_psk_init(shared_psk, S2N_PSK_TYPE_EXTERNAL)); + RESULT_GUARD_POSIX(s2n_psk_set_identity(shared_psk, test_identity, sizeof(test_identity))); + RESULT_GUARD_POSIX(s2n_psk_set_secret(shared_psk, test_secret, sizeof(test_secret))); + shared_psk->hmac_alg = TEST_PSK_HMAC; + + return S2N_RESULT_OK; +} + +static s2n_result setup_server_psks(struct s2n_connection *server_conn) +{ + RESULT_ENSURE_REF(server_conn); + + EXPECT_OK(s2n_connection_set_psk_type(server_conn, S2N_PSK_TYPE_EXTERNAL)); + + /* Setup shared PSK for server */ + struct s2n_psk *shared_psk = NULL; + RESULT_GUARD(s2n_array_pushback(&server_conn->psk_params.psk_list, (void **) &shared_psk)); + RESULT_GUARD(s2n_psk_init(shared_psk, S2N_PSK_TYPE_EXTERNAL)); + RESULT_GUARD_POSIX(s2n_psk_set_identity(shared_psk, test_identity, sizeof(test_identity))); + RESULT_GUARD_POSIX(s2n_psk_set_secret(shared_psk, test_secret, sizeof(test_secret))); + shared_psk->hmac_alg = TEST_PSK_HMAC; + + /* Setup other server PSK */ + uint8_t other_server_data[] = "other server data"; + struct s2n_psk *other_server_psk = NULL; + RESULT_GUARD(s2n_array_pushback(&server_conn->psk_params.psk_list, (void **) &other_server_psk)); + RESULT_GUARD(s2n_psk_init(other_server_psk, S2N_PSK_TYPE_EXTERNAL)); + RESULT_GUARD_POSIX(s2n_psk_set_identity(other_server_psk, other_server_data, sizeof(other_server_data))); + RESULT_GUARD_POSIX(s2n_psk_set_secret(other_server_psk, other_server_data, sizeof(other_server_data))); + other_server_psk->hmac_alg = S2N_HMAC_SHA224; + + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Test: s2n_server_psk_should_send */ + { + struct s2n_psk *psk = NULL; + + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_FALSE(s2n_server_psk_extension.should_send(NULL)); + + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); + EXPECT_FALSE(s2n_server_psk_extension.should_send(conn)); + + conn->psk_params.chosen_psk_wire_index = 0; + EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, conn->psk_params.chosen_psk_wire_index, + (void **) &conn->psk_params.chosen_psk)); + EXPECT_TRUE(s2n_server_psk_extension.should_send(conn)); + + /* If send is called with a NULL stuffer, it will fail. + * So a failure indicates that send was called. + */ + EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(conn)); + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_extension_send(&s2n_server_psk_extension, conn, NULL)); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FAILURE(s2n_extension_send(&s2n_server_psk_extension, conn, NULL)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: s2n_server_psk_send */ + { + /* Send the index of the chosen PSK that is stored on the connection. */ + { + struct s2n_stuffer out = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_OK(setup_server_psks(server_conn)); + + server_conn->psk_params.chosen_psk_wire_index = TEST_PSK_WIRE_INDEX; + EXPECT_SUCCESS(s2n_server_psk_extension.send(server_conn, &out)); + + uint16_t chosen_psk_wire_index = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&out, &chosen_psk_wire_index)); + EXPECT_EQUAL(chosen_psk_wire_index, server_conn->psk_params.chosen_psk_wire_index); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + }; + }; + + /* Test: s2n_server_psk_recv */ + { + s2n_extension_type_id key_share_ext_id; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_KEY_SHARE, &key_share_ext_id)); + + /* Test s2n_server_psk_recv for invalid TLS versions <= TLS1.2 */ + { + struct s2n_stuffer out = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_OK(setup_client_psks(conn)); + + /* The keyshare extension needs to be present, as s2n currently only + * supports pre-shared keys in (EC)DHE key exchange mode. + */ + S2N_CBIT_SET(conn->extension_requests_received, key_share_ext_id); + + uint16_t chosen_psk_wire_index = 1; + /* Incorrect protocol version */ + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&out, chosen_psk_wire_index)); + + EXPECT_NULL(conn->psk_params.chosen_psk); + EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(conn)); + EXPECT_SUCCESS(s2n_extension_recv(&s2n_server_psk_extension, conn, &out)); + EXPECT_NULL(conn->psk_params.chosen_psk); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + }; + + /* Test s2n_server_psk_recv when server key_share extension is not present */ + { + struct s2n_stuffer out = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_OK(setup_client_psks(conn)); + + uint16_t chosen_psk_wire_index = TEST_PSK_WIRE_INDEX; + conn->actual_protocol_version = S2N_TLS13; + + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&out, chosen_psk_wire_index)); + + EXPECT_NULL(conn->psk_params.chosen_psk); + EXPECT_FAILURE_WITH_ERRNO(s2n_server_psk_extension.recv(conn, &out), S2N_ERR_MISSING_EXTENSION); + EXPECT_NULL(conn->psk_params.chosen_psk); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + }; + + /* Receive invalid chosen psk wire index */ + { + struct s2n_stuffer out = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_OK(setup_client_psks(conn)); + + /* The keyshare extension needs to be present, as s2n currently only + * supports pre-shared keys in (EC)DHE key exchange mode. + */ + S2N_CBIT_SET(conn->extension_responses_received, key_share_ext_id); + + /* Invalid chosen psk wire index */ + uint16_t chosen_psk_wire_index = 10; + conn->actual_protocol_version = S2N_TLS13; + + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&out, chosen_psk_wire_index)); + + EXPECT_NULL(conn->psk_params.chosen_psk); + EXPECT_FAILURE_WITH_ERRNO(s2n_server_psk_extension.recv(conn, &out), S2N_ERR_INVALID_ARGUMENT); + EXPECT_NULL(conn->psk_params.chosen_psk); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + }; + + /* Receive valid server preshared extension recv */ + { + struct s2n_stuffer out = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&out, 0)); + + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_OK(setup_client_psks(conn)); + + /* The keyshare extension needs to be present, as s2n currently only + * supports pre-shared keys in (EC)DHE key exchange mode. + */ + S2N_CBIT_SET(conn->extension_responses_received, key_share_ext_id); + + uint16_t chosen_psk_wire_index = TEST_PSK_WIRE_INDEX; + conn->actual_protocol_version = S2N_TLS13; + + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&out, chosen_psk_wire_index)); + + EXPECT_NULL(conn->psk_params.chosen_psk); + EXPECT_SUCCESS(s2n_server_psk_extension.recv(conn, &out)); + + /* Verify chosen PSK */ + EXPECT_EQUAL(conn->psk_params.psk_ke_mode, S2N_PSK_DHE_KE); + EXPECT_EQUAL(conn->psk_params.chosen_psk_wire_index, TEST_PSK_WIRE_INDEX); + EXPECT_EQUAL(conn->psk_params.chosen_psk->identity.size, sizeof(test_identity)); + EXPECT_BYTEARRAY_EQUAL(conn->psk_params.chosen_psk->identity.data, test_identity, sizeof(test_identity)); + EXPECT_EQUAL(conn->psk_params.chosen_psk->secret.size, sizeof(test_secret)); + EXPECT_BYTEARRAY_EQUAL(conn->psk_params.chosen_psk->secret.data, test_secret, sizeof(test_secret)); + EXPECT_EQUAL(conn->psk_params.chosen_psk->hmac_alg, TEST_PSK_HMAC); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&out)); + }; + }; + + /* Functional test */ + if (s2n_is_tls13_fully_supported()) { + /* Setup connections */ + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + struct s2n_connection *client_conn, *server_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + + EXPECT_OK(setup_client_psks(client_conn)); + EXPECT_OK(setup_server_psks(server_conn)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + /* Verify shared PSK chosen */ + EXPECT_EQUAL(server_conn->psk_params.chosen_psk_wire_index, TEST_PSK_WIRE_INDEX); + + EXPECT_SUCCESS(s2n_server_hello_send(server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + + EXPECT_NULL(client_conn->psk_params.chosen_psk); + EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); + + /* Verify chosen PSK received */ + EXPECT_EQUAL(client_conn->psk_params.psk_ke_mode, S2N_PSK_DHE_KE); + EXPECT_EQUAL(client_conn->psk_params.chosen_psk_wire_index, TEST_PSK_WIRE_INDEX); + EXPECT_EQUAL(client_conn->psk_params.chosen_psk->identity.size, sizeof(test_identity)); + EXPECT_BYTEARRAY_EQUAL(client_conn->psk_params.chosen_psk->identity.data, test_identity, sizeof(test_identity)); + EXPECT_EQUAL(client_conn->psk_params.chosen_psk->secret.size, sizeof(test_secret)); + EXPECT_BYTEARRAY_EQUAL(client_conn->psk_params.chosen_psk->secret.data, test_secret, sizeof(test_secret)); + EXPECT_EQUAL(client_conn->psk_params.chosen_psk->hmac_alg, TEST_PSK_HMAC); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_server_renegotiation_info_test.c b/tests/unit/s2n_server_renegotiation_info_test.c new file mode 100644 index 00000000000..ace9a73582e --- /dev/null +++ b/tests/unit/s2n_server_renegotiation_info_test.c @@ -0,0 +1,498 @@ +/* + * 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 "tls/extensions/s2n_server_renegotiation_info.h" + +#include + +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +S2N_RESULT s2n_server_renegotiation_info_extension_write(struct s2n_stuffer *out, + const uint8_t *client_verify_data, const uint8_t *server_verify_data, uint8_t verify_data_len) +{ + RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(out, verify_data_len * 3)); + + struct s2n_stuffer_reservation len = { 0 }; + RESULT_GUARD_POSIX(s2n_stuffer_reserve_uint8(out, &len)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(out, client_verify_data, verify_data_len)); + RESULT_GUARD_POSIX(s2n_stuffer_write_bytes(out, server_verify_data, verify_data_len)); + RESULT_GUARD_POSIX(s2n_stuffer_write_vector_size(&len)); + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + + const uint8_t client_verify_data[] = "client verify data"; + const uint8_t server_verify_data[] = "server verify data"; + EXPECT_EQUAL(sizeof(client_verify_data), sizeof(server_verify_data)); + const uint8_t verify_data_len = sizeof(server_verify_data); + + /* Test should_send + * + *= https://tools.ietf.org/rfc/rfc5746#3.6 + *= type=test + *# o If the secure_renegotiation flag is set to TRUE, the server MUST + *# include an empty "renegotiation_info" extension in the ServerHello + *# message. + */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + /* TLS1.2 and secure renegotiation not enabled -> DON'T send */ + conn->actual_protocol_version = S2N_TLS12; + conn->secure_renegotiation = false; + EXPECT_FALSE(s2n_server_renegotiation_info_extension.should_send(conn)); + + /* TLS1.3 and secure renegotiation not enabled -> DON'T send */ + conn->actual_protocol_version = S2N_TLS13; + conn->secure_renegotiation = false; + EXPECT_FALSE(s2n_server_renegotiation_info_extension.should_send(conn)); + + /* TLS1.3 and secure renegotiation enabled -> DON'T send */ + conn->actual_protocol_version = S2N_TLS13; + conn->secure_renegotiation = true; + EXPECT_FALSE(s2n_server_renegotiation_info_extension.should_send(conn)); + + /* TLS1.2 and secure renegotiation enabled -> send */ + conn->actual_protocol_version = S2N_TLS12; + conn->secure_renegotiation = true; + EXPECT_TRUE(s2n_server_renegotiation_info_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test server_renegotiation_info send and recv during initial handshake + * + *= https://tools.ietf.org/rfc/rfc5746#4.3 + *= type=test + *# In order to enable clients to probe, even servers that do not support + *# renegotiation MUST implement the minimal version of the extension + *# described in this document for initial handshakes, thus signaling + *# that they have been upgraded. + */ + { + struct s2n_connection *server_conn, *client_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension, 0)); + + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->secure_renegotiation = 1; + + EXPECT_SUCCESS(s2n_server_renegotiation_info_extension.send(server_conn, &extension)); + EXPECT_NOT_EQUAL(s2n_stuffer_data_available(&extension), 0); + + EXPECT_SUCCESS(s2n_server_renegotiation_info_extension.recv(client_conn, &extension)); + EXPECT_EQUAL(client_conn->secure_renegotiation, 1); + EXPECT_EQUAL(s2n_stuffer_data_available(&extension), 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + } + + /* Test server_renegotiation_info recv when using SSLv3 + * + *= https://tools.ietf.org/rfc/rfc5746#4.5 + *= type=test + *# Clients that support SSLv3 and offer secure renegotiation (either via SCSV or + *# "renegotiation_info") MUST accept the "renegotiation_info" extension + *# from the server, even if the server version is {0x03, 0x00}, and + *# behave as described in this specification. + */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + + DEFER_CLEANUP(struct s2n_stuffer extension = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension, 0)); + + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->secure_renegotiation = true; + EXPECT_SUCCESS(s2n_server_renegotiation_info_extension.send(server_conn, &extension)); + EXPECT_NOT_EQUAL(s2n_stuffer_data_available(&extension), 0); + + client_conn->client_protocol_version = S2N_SSLv3; + client_conn->actual_protocol_version = S2N_SSLv3; + EXPECT_SUCCESS(s2n_server_renegotiation_info_extension.recv(client_conn, &extension)); + EXPECT_EQUAL(client_conn->secure_renegotiation, 1); + EXPECT_EQUAL(s2n_stuffer_data_available(&extension), 0); + }; + + /* Test server_renegotiation_info recv during initial handshake - extension too long */ + { + struct s2n_connection *server_conn, *client_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension, 0)); + + server_conn->actual_protocol_version = S2N_TLS12; + server_conn->secure_renegotiation = 1; + + EXPECT_SUCCESS(s2n_server_renegotiation_info_extension.send(server_conn, &extension)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension, 0)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_renegotiation_info_extension.recv(client_conn, &extension), + S2N_ERR_NON_EMPTY_RENEGOTIATION_INFO); + EXPECT_EQUAL(client_conn->secure_renegotiation, 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test server_renegotiation_info recv during initial handshake - extension length wrong + * + *= https://tools.ietf.org/rfc/rfc5746#3.4 + *= type=test + *# * The client MUST then verify that the length of the + *# "renegotiated_connection" field is zero, and if it is not, MUST + *# abort the handshake (by sending a fatal handshake_failure alert). + */ + { + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + struct s2n_stuffer extension = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension, 0)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension, 5)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_renegotiation_info_extension.recv(client_conn, &extension), + S2N_ERR_NON_EMPTY_RENEGOTIATION_INFO); + EXPECT_EQUAL(client_conn->secure_renegotiation, 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* Test: if_missing during initial handshake is a no-op + * + *= https://tools.ietf.org/rfc/rfc5746#3.4 + *= type=test + *# * If the extension is not present, the server does not support + *# secure renegotiation; set secure_renegotiation flag to FALSE. + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_server_renegotiation_info_extension.if_missing(conn)); + EXPECT_FALSE(conn->secure_renegotiation); + }; + + /* Test: if_missing during renegotiation handshake is an error + * + *= https://tools.ietf.org/rfc/rfc5746#3.5 + *= type=test + *# o When a ServerHello is received, the client MUST verify that the + *# "renegotiation_info" extension is present; if it is not, the + *# client MUST abort the handshake. + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->handshake.renegotiation = true; + EXPECT_FAILURE_WITH_ERRNO(s2n_server_renegotiation_info_extension.if_missing(conn), + S2N_ERR_NO_RENEGOTIATION); + }; + + /* Test: recv during renegotiation handshake + * + *= https://tools.ietf.org/rfc/rfc5746#3.5 + *= type=test + *# o The client MUST then verify that the first half of the + *# "renegotiated_connection" field is equal to the saved + *# client_verify_data value, and the second half is equal to the + *# saved server_verify_data value. If they are not, the client MUST + *# abort the handshake. + */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->handshake.renegotiation = true; + conn->secure_renegotiation = true; + + /* Setup verify_data */ + EXPECT_MEMCPY_SUCCESS(conn->handshake.client_finished, + client_verify_data, sizeof(client_verify_data)); + EXPECT_MEMCPY_SUCCESS(conn->handshake.server_finished, + server_verify_data, sizeof(server_verify_data)); + conn->handshake.finished_len = verify_data_len; + uint8_t renegotiation_info_len = verify_data_len * 2; + + /* Secure renegotiation not supported */ + { + /* Write valid verify_data */ + DEFER_CLEANUP(struct s2n_stuffer extension = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_server_renegotiation_info_extension_write(&extension, + client_verify_data, server_verify_data, verify_data_len)); + + conn->secure_renegotiation = false; + EXPECT_FAILURE_WITH_ERRNO(s2n_server_renegotiation_info_extension.recv(conn, &extension), + S2N_ERR_NO_RENEGOTIATION); + }; + + /* Turn on secure renegotiation for the rest of the tests */ + conn->secure_renegotiation = true; + + /* Receive valid client and server verify_data */ + { + /* Write valid verify_data */ + DEFER_CLEANUP(struct s2n_stuffer extension = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_server_renegotiation_info_extension_write(&extension, + client_verify_data, server_verify_data, verify_data_len)); + + EXPECT_SUCCESS(s2n_server_renegotiation_info_extension.recv(conn, &extension)); + }; + + /* Receive incorrect length: too small */ + { + /* Write valid verify_data */ + DEFER_CLEANUP(struct s2n_stuffer extension = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_server_renegotiation_info_extension_write(&extension, + client_verify_data, server_verify_data, verify_data_len)); + + /* Modify length */ + EXPECT_SUCCESS(s2n_stuffer_rewrite(&extension)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension, renegotiation_info_len - 1)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&extension, renegotiation_info_len)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_renegotiation_info_extension.recv(conn, &extension), + S2N_ERR_BAD_MESSAGE); + }; + + /* Receive incorrect length: too large */ + { + /* Write valid verify_data */ + DEFER_CLEANUP(struct s2n_stuffer extension = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_server_renegotiation_info_extension_write(&extension, + client_verify_data, server_verify_data, verify_data_len)); + + /* Modify length */ + EXPECT_SUCCESS(s2n_stuffer_rewrite(&extension)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension, renegotiation_info_len + 1)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&extension, renegotiation_info_len)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_renegotiation_info_extension.recv(conn, &extension), + S2N_ERR_BAD_MESSAGE); + }; + + /* Receive incorrect client_verify_data */ + { + DEFER_CLEANUP(struct s2n_stuffer extension = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_server_renegotiation_info_extension_write(&extension, + server_verify_data, server_verify_data, verify_data_len)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_renegotiation_info_extension.recv(conn, &extension), + S2N_ERR_BAD_MESSAGE); + }; + + /* Receive incorrect server_verify_data */ + { + DEFER_CLEANUP(struct s2n_stuffer extension = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_server_renegotiation_info_extension_write(&extension, + client_verify_data, client_verify_data, verify_data_len)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_renegotiation_info_extension.recv(conn, &extension), + S2N_ERR_BAD_MESSAGE); + }; + + /* Receive initial handshake extension */ + { + DEFER_CLEANUP(struct s2n_stuffer extension = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension, 0)); + + conn->handshake.renegotiation = false; + EXPECT_SUCCESS(s2n_server_renegotiation_info_extension.send(conn, &extension)); + conn->handshake.renegotiation = true; + EXPECT_FAILURE_WITH_ERRNO(s2n_server_renegotiation_info_extension.recv(conn, &extension), + S2N_ERR_BAD_MESSAGE); + }; + }; + + /* Test send during renegotiation handshake + * + *= https://tools.ietf.org/rfc/rfc5746#3.7 + *= type=test + *# o The server MUST include a "renegotiation_info" extension + *# containing the saved client_verify_data and server_verify_data in + *# the ServerHello. + */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + client_conn->handshake.renegotiation = true; + client_conn->secure_renegotiation = true; + EXPECT_MEMCPY_SUCCESS(client_conn->handshake.client_finished, + client_verify_data, sizeof(client_verify_data)); + EXPECT_MEMCPY_SUCCESS(client_conn->handshake.server_finished, + server_verify_data, sizeof(server_verify_data)); + client_conn->handshake.finished_len = verify_data_len; + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + server_conn->handshake.renegotiation = true; + server_conn->secure_renegotiation = true; + EXPECT_MEMCPY_SUCCESS(server_conn->handshake.client_finished, + client_verify_data, sizeof(client_verify_data)); + EXPECT_MEMCPY_SUCCESS(server_conn->handshake.server_finished, + server_verify_data, sizeof(server_verify_data)); + server_conn->handshake.finished_len = verify_data_len; + + DEFER_CLEANUP(struct s2n_stuffer sent_extension = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&sent_extension, 0)); + EXPECT_TRUE(s2n_server_renegotiation_info_extension.should_send(client_conn)); + EXPECT_SUCCESS(s2n_server_renegotiation_info_extension.send(client_conn, &sent_extension)); + + /* Verify matches test method */ + DEFER_CLEANUP(struct s2n_stuffer test_extension = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_server_renegotiation_info_extension_write(&test_extension, + client_verify_data, server_verify_data, verify_data_len)); + EXPECT_EQUAL(s2n_stuffer_data_available(&sent_extension), s2n_stuffer_data_available(&test_extension)); + EXPECT_BYTEARRAY_EQUAL(sent_extension.blob.data, test_extension.blob.data, + s2n_stuffer_data_available(&sent_extension)); + + /* Verify we can recv what we send */ + EXPECT_SUCCESS(s2n_server_renegotiation_info_extension.recv(server_conn, &sent_extension)); + }; + + /* Functional Test + * + *= https://tools.ietf.org/rfc/rfc5746#3.4 + *= type=test + *# o When a ServerHello is received, the client MUST check if it + *# includes the "renegotiation_info" extension: + */ + { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + /* Send and receive the ClientHello */ + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + /* Test "renegotiation_info" extension NOT included during initial handshake */ + { + EXPECT_FALSE(client_conn->secure_renegotiation); + + server_conn->secure_renegotiation = false; + EXPECT_SUCCESS(s2n_server_hello_send(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); + + EXPECT_FALSE(client_conn->secure_renegotiation); + }; + + /* Test "renegotiation_info" extension included during initial handshake */ + { + EXPECT_FALSE(client_conn->secure_renegotiation); + + server_conn->secure_renegotiation = true; + EXPECT_SUCCESS(s2n_server_hello_send(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); + + EXPECT_TRUE(client_conn->secure_renegotiation); + }; + }; + + /* Functional Test: SSLv3 + * + *= https://tools.ietf.org/rfc/rfc5746#4.5 + *= type=test + *# Clients that support SSLv3 and offer secure renegotiation (either via SCSV or + *# "renegotiation_info") MUST accept the "renegotiation_info" extension + *# from the server, even if the server version is {0x03, 0x00}, and + *# behave as described in this specification. TLS servers that support + *# secure renegotiation and support SSLv3 MUST accept SCSV or the + *# "renegotiation_info" extension and respond as described in this + *# specification even if the offered client version is {0x03, 0x00}. + **/ + if (s2n_hash_is_available(S2N_HASH_MD5)) { + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "test_all")); + client_conn->client_protocol_version = S2N_SSLv3; + client_conn->actual_protocol_version = S2N_SSLv3; + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "test_all")); + server_conn->server_protocol_version = S2N_SSLv3; + server_conn->actual_protocol_version = S2N_SSLv3; + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_TRUE(client_conn->secure_renegotiation); + EXPECT_TRUE(server_conn->secure_renegotiation); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_SSLv3); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_SSLv3); + } + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_server_sct_list_extension_test.c b/tests/unit/s2n_server_sct_list_extension_test.c new file mode 100644 index 00000000000..137710292d7 --- /dev/null +++ b/tests/unit/s2n_server_sct_list_extension_test.c @@ -0,0 +1,120 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_server_sct_list.h" + +const uint8_t sct_list_data[] = "SCT LIST DATA"; + +int s2n_test_enable_sending_extension(struct s2n_connection *conn, struct s2n_cert_chain_and_key *chain_and_key) +{ + conn->mode = S2N_SERVER; + conn->ct_level_requested = S2N_CT_SUPPORT_REQUEST; + conn->handshake_params.our_chain_and_key = chain_and_key; + EXPECT_SUCCESS(s2n_cert_chain_and_key_set_sct_list(chain_and_key, sct_list_data, s2n_array_len(sct_list_data))); + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + /* should_send */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* Don't send by default */ + EXPECT_FALSE(s2n_server_sct_list_extension.should_send(conn)); + + /* Send if all prerequisites met */ + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + EXPECT_TRUE(s2n_server_sct_list_extension.should_send(conn)); + + /* Don't send if client */ + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + conn->mode = S2N_CLIENT; + EXPECT_FALSE(s2n_server_sct_list_extension.should_send(conn)); + + /* Don't send if certificate transparency not requested */ + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + conn->ct_level_requested = S2N_CT_SUPPORT_NONE; + EXPECT_FALSE(s2n_server_sct_list_extension.should_send(conn)); + + /* Don't send if no certificate set */ + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + conn->handshake_params.our_chain_and_key = NULL; + EXPECT_FALSE(s2n_server_sct_list_extension.should_send(conn)); + + /* Don't send if no ocsp data */ + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + EXPECT_SUCCESS(s2n_free(&conn->handshake_params.our_chain_and_key->sct_list)); + EXPECT_FALSE(s2n_server_sct_list_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test send */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_server_sct_list_extension.send(conn, &stuffer)); + + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), s2n_array_len(sct_list_data)); + EXPECT_BYTEARRAY_EQUAL(stuffer.blob.data, sct_list_data, s2n_array_len(sct_list_data)); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test recv */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_test_enable_sending_extension(conn, chain_and_key)); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + EXPECT_SUCCESS(s2n_server_sct_list_extension.send(conn, &stuffer)); + + EXPECT_EQUAL(conn->ct_response.size, 0); + EXPECT_SUCCESS(s2n_server_sct_list_extension.recv(conn, &stuffer)); + EXPECT_EQUAL(conn->ct_response.size, s2n_array_len(sct_list_data)); + EXPECT_BYTEARRAY_EQUAL(conn->ct_response.data, sct_list_data, s2n_array_len(sct_list_data)); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_server_server_name_extension_test.c b/tests/unit/s2n_server_server_name_extension_test.c new file mode 100644 index 00000000000..e94e5e174cb --- /dev/null +++ b/tests/unit/s2n_server_server_name_extension_test.c @@ -0,0 +1,81 @@ +/* + * 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 "s2n_test.h" +#include "tls/extensions/s2n_server_server_name.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" + +#define S2N_TEST_RESUMPTION_HANDSHAKE (NEGOTIATED) +#define S2N_TEST_NOT_RESUMPTION_HANDSHAKE (NEGOTIATED | FULL_HANDSHAKE) + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* should_send */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + /* By default, do not send */ + EXPECT_FALSE(s2n_server_server_name_extension.should_send(conn)); + + /* server_name not used and resumption handshake -> do not send */ + conn->server_name_used = false; + conn->handshake.handshake_type = S2N_TEST_RESUMPTION_HANDSHAKE; + EXPECT_FALSE(s2n_server_server_name_extension.should_send(conn)); + + /* server_name used and resumption handshake -> do not send */ + conn->server_name_used = true; + conn->handshake.handshake_type = S2N_TEST_RESUMPTION_HANDSHAKE; + EXPECT_FALSE(s2n_server_server_name_extension.should_send(conn)); + + /* server_name not used and not resumption handshake -> do not send */ + conn->server_name_used = false; + conn->handshake.handshake_type = S2N_TEST_NOT_RESUMPTION_HANDSHAKE; + EXPECT_FALSE(s2n_server_server_name_extension.should_send(conn)); + + /* server_name used and not resumption handshake -> send */ + conn->server_name_used = true; + conn->handshake.handshake_type = S2N_TEST_NOT_RESUMPTION_HANDSHAKE; + EXPECT_TRUE(s2n_server_server_name_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* send */ + { + /* Send writes nothing and always succeeds. */ + EXPECT_SUCCESS(s2n_server_server_name_extension.send(NULL, NULL)); + }; + + /* recv */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + /* Recv reads nothing and always succeeds */ + EXPECT_FALSE(conn->server_name_used); + EXPECT_SUCCESS(s2n_server_server_name_extension.recv(conn, NULL)); + EXPECT_TRUE(conn->server_name_used); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_server_session_ticket_extension_test.c b/tests/unit/s2n_server_session_ticket_extension_test.c new file mode 100644 index 00000000000..fc1394feac9 --- /dev/null +++ b/tests/unit/s2n_server_session_ticket_extension_test.c @@ -0,0 +1,96 @@ +/* + * 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 "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/extensions/s2n_server_session_ticket.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +static int s2n_test_enable_extension(struct s2n_connection *conn) +{ + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(conn->config, true)); + conn->session_ticket_status = S2N_NEW_TICKET; + conn->actual_protocol_version = S2N_TLS12; + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Test should_send */ + { + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + /* By default, do not send */ + EXPECT_FALSE(s2n_server_session_ticket_extension.should_send(conn)); + + /* If all prerequisites met, send */ + EXPECT_SUCCESS(s2n_test_enable_extension(conn)); + EXPECT_TRUE(s2n_server_session_ticket_extension.should_send(conn)); + + /* If tickets not enabled, do not send */ + EXPECT_SUCCESS(s2n_test_enable_extension(conn)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, false)); + EXPECT_FALSE(s2n_server_session_ticket_extension.should_send(conn)); + + /* If ticket not new, do not send */ + EXPECT_SUCCESS(s2n_test_enable_extension(conn)); + conn->session_ticket_status = S2N_DECRYPT_TICKET; + EXPECT_FALSE(s2n_server_session_ticket_extension.should_send(conn)); + + /* If no ticket, do not send */ + EXPECT_SUCCESS(s2n_test_enable_extension(conn)); + conn->session_ticket_status = S2N_NO_TICKET; + EXPECT_FALSE(s2n_server_session_ticket_extension.should_send(conn)); + + /* If protocol version too high, do not send */ + EXPECT_SUCCESS(s2n_test_enable_extension(conn)); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FALSE(s2n_server_session_ticket_extension.should_send(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + }; + + /* Test server_session_ticket send and recv */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_SUCCESS(s2n_server_session_ticket_extension.send(conn, NULL)); + + EXPECT_EQUAL(conn->session_ticket_status, S2N_NO_TICKET); + EXPECT_SUCCESS(s2n_server_session_ticket_extension.recv(conn, NULL)); + EXPECT_EQUAL(conn->session_ticket_status, S2N_NEW_TICKET); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_server_signature_algorithms_extension_test.c b/tests/unit/s2n_server_signature_algorithms_extension_test.c new file mode 100644 index 00000000000..7ecfd7786ad --- /dev/null +++ b/tests/unit/s2n_server_signature_algorithms_extension_test.c @@ -0,0 +1,59 @@ +/* + * 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 "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_server_signature_algorithms.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + s2n_enable_tls13_in_test(); + + { + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + struct s2n_stuffer io = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&io, 0)); + + EXPECT_SUCCESS(s2n_server_signature_algorithms_extension.send(server_conn, &io)); + EXPECT_SUCCESS(s2n_server_signature_algorithms_extension.recv(client_conn, &io)); + EXPECT_EQUAL(s2n_stuffer_data_available(&io), 0); + + EXPECT_TRUE(client_conn->handshake_params.server_sig_hash_algs.len > 0); + + s2n_stuffer_free(&io); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + s2n_disable_tls13_in_test(); + + END_TEST(); + + return 0; +} diff --git a/tests/unit/s2n_server_supported_versions_extension_test.c b/tests/unit/s2n_server_supported_versions_extension_test.c new file mode 100644 index 00000000000..1e089b39619 --- /dev/null +++ b/tests/unit/s2n_server_supported_versions_extension_test.c @@ -0,0 +1,158 @@ +/* + * 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 "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/extensions/s2n_server_supported_versions.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +int write_test_supported_version(struct s2n_stuffer *list, uint8_t supported_version) +{ + POSIX_GUARD(s2n_stuffer_write_uint8(list, S2N_TLS_PROTOCOL_VERSION_LEN)); + + POSIX_GUARD(s2n_stuffer_write_uint8(list, supported_version / 10)); + POSIX_GUARD(s2n_stuffer_write_uint8(list, supported_version % 10)); + + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + uint8_t latest_version = S2N_TLS13; + + struct s2n_config *config; + EXPECT_NOT_NULL(config = s2n_config_new()); + + /* Server sends a supported_version the client can parse */ + { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + uint16_t expected_length = 6; + + struct s2n_stuffer extension = { 0 }; + s2n_stuffer_alloc(&extension, expected_length); + + EXPECT_SUCCESS(s2n_server_supported_versions_extension.send(server_conn, &extension)); + + /* Check that size is correct */ + uint16_t extension_length = s2n_extensions_server_supported_versions_size(server_conn) + - S2N_EXTENSION_TYPE_FIELD_LENGTH - S2N_EXTENSION_LENGTH_FIELD_LENGTH; + EXPECT_EQUAL(extension_length, s2n_stuffer_data_available(&extension)); + + /* Check that the client can process the version */ + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + EXPECT_SUCCESS(s2n_server_supported_versions_extension.recv(client_conn, &extension)); + EXPECT_EQUAL(client_conn->client_protocol_version, latest_version); + EXPECT_EQUAL(client_conn->server_protocol_version, latest_version); + EXPECT_EQUAL(client_conn->actual_protocol_version, latest_version); + + /* Clean up */ + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + }; + + /* Client alerts if supported_version less than min supported by client */ + { + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + uint8_t unsupported_version_unknown = S2N_UNKNOWN_PROTOCOL_VERSION; + + uint16_t supported_version_length = 6; + + struct s2n_stuffer extension = { 0 }; + s2n_stuffer_alloc(&extension, supported_version_length); + + EXPECT_SUCCESS(write_test_supported_version(&extension, unsupported_version_unknown)); + EXPECT_FAILURE_WITH_ERRNO(s2n_server_supported_versions_extension.recv(client_conn, &extension), S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + }; + + /* Client alerts if supported_version greater than max supported by client */ + { + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + uint8_t unsupported_version_gt_tls13 = 255; + + uint16_t supported_version_length = 6; + + struct s2n_stuffer extension = { 0 }; + s2n_stuffer_alloc(&extension, supported_version_length); + + EXPECT_SUCCESS(write_test_supported_version(&extension, unsupported_version_gt_tls13)); + EXPECT_FAILURE_WITH_ERRNO(s2n_server_supported_versions_extension.recv(client_conn, &extension), S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + }; + + /* Client alerts if supported_version is empty */ + { + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_stuffer extension = { 0 }; + s2n_stuffer_alloc(&extension, 1); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension, 0)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_supported_versions_extension.recv(client_conn, &extension), S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + }; + + /* Client alerts if supported_version is malformed */ + { + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_stuffer extension = { 0 }; + s2n_stuffer_alloc(&extension, 1); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&extension, 13)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_server_supported_versions_extension.recv(client_conn, &extension), S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&extension)); + }; + + EXPECT_SUCCESS(s2n_config_free(config)); + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_session_ticket_test.c b/tests/unit/s2n_session_ticket_test.c new file mode 100644 index 00000000000..4dbd7669996 --- /dev/null +++ b/tests/unit/s2n_session_ticket_test.c @@ -0,0 +1,1358 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_bitmap.h" +#include "utils/s2n_safety.h" + +#define S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS (S2N_TICKET_ENCRYPT_DECRYPT_KEY_LIFETIME_IN_NANOS + S2N_TICKET_DECRYPT_KEY_LIFETIME_IN_NANOS) / ONE_SEC_IN_NANOS +#define S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES S2N_STATE_FORMAT_LEN + S2N_SESSION_TICKET_SIZE_LEN +#define ONE_SEC_DELAY 1 + +#define S2N_CLOCK_SYS CLOCK_REALTIME + +/** + * This function is used to "skip" time in unit tests. It will mock the system + * time to be current_time (ns) + data (ns). The "data" parameter is a uint64_t + * passed in as a void*. + */ +int mock_nanoseconds_since_epoch(void *data, uint64_t *nanoseconds) +{ + struct timespec current_time; + + clock_gettime(S2N_CLOCK_SYS, ¤t_time); + + /** + * current_time fields are represented as time_t, and time_t has a platform + * dependent size. On 32 bit platforms, attempting to convert the current + * system time to nanoseconds will overflow, causing odd failures in unit + * tests. We upcast current_time fields to uint64_t before multiplying to + * avoid this. + */ + *nanoseconds = 0; + *nanoseconds += (uint64_t) current_time.tv_sec * ONE_SEC_IN_NANOS; + *nanoseconds += (uint64_t) current_time.tv_nsec; + *nanoseconds += *(uint64_t *) data; + + return 0; +} + +static int mock_time(void *data, uint64_t *nanoseconds) +{ + *nanoseconds = 1000000000; + return S2N_SUCCESS; +} + +uint8_t cb_session_data[S2N_TLS12_SESSION_SIZE * 2] = { 0 }; +size_t cb_session_data_len = 0; +uint32_t cb_session_lifetime = 0; +static int s2n_test_session_ticket_callback(struct s2n_connection *conn, void *ctx, struct s2n_session_ticket *ticket) +{ + EXPECT_NOT_NULL(conn); + EXPECT_NOT_NULL(ticket); + + /* Store the callback data for comparison at the end of the connection. */ + EXPECT_SUCCESS(s2n_session_ticket_get_data_len(ticket, &cb_session_data_len)); + EXPECT_SUCCESS(s2n_session_ticket_get_data(ticket, sizeof(cb_session_data), cb_session_data)); + EXPECT_SUCCESS(s2n_session_ticket_get_lifetime(ticket, &cb_session_lifetime)); + + return S2N_SUCCESS; +} + +/* make a struct with a ticket name and key adjacent in memory */ +struct small_name_ticket { + uint8_t name[1]; + uint8_t key[32]; +}; + +int main(int argc, char **argv) +{ + char *cert_chain; + char *private_key; + struct s2n_cert_chain_and_key *chain_and_key; + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + struct s2n_config *client_config; + struct s2n_config *server_config; + uint64_t now; + struct s2n_ticket_key *ticket_key; + uint32_t ticket_keys_len = 0; + + size_t serialized_session_state_length = 0; + uint8_t s2n_state_with_session_id = S2N_STATE_WITH_SESSION_ID; + uint8_t serialized_session_state[S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES + S2N_TLS12_STATE_SIZE_IN_BYTES] = { 0 }; + + /* Session ticket keys. Taken from test vectors in https://tools.ietf.org/html/rfc5869 */ + uint8_t ticket_key_name1[1] = "A"; + uint8_t ticket_key_name2[4] = "BBBB"; + uint8_t ticket_key_name3[16] = "CCCCCCCCCCCCCCCC"; + uint8_t ticket_key1[32] = { 0x07, 0x77, 0x09, 0x36, 0x2c, 0x2e, 0x32, 0xdf, 0x0d, 0xdc, + 0x3f, 0x0d, 0xc4, 0x7b, 0xba, 0x63, 0x90, 0xb6, 0xc7, 0x3b, + 0xb5, 0x0f, 0x9c, 0x31, 0x22, 0xec, 0x84, 0x4a, 0xd7, 0xc2, + 0xb3, 0xe5 }; + uint8_t ticket_key2[32] = { 0x06, 0xa6, 0xb8, 0x8c, 0x58, 0x53, 0x36, 0x1a, 0x06, 0x10, + 0x4c, 0x9c, 0xeb, 0x35, 0xb4, 0x5c, 0xef, 0x76, 0x00, 0x14, + 0x90, 0x46, 0x71, 0x01, 0x4a, 0x19, 0x3f, 0x40, 0xc1, 0x5f, + 0xc2, 0x44 }; + uint8_t ticket_key3[32] = { 0x19, 0xef, 0x24, 0xa3, 0x2c, 0x71, 0x7b, 0x16, 0x7f, 0x33, + 0xa9, 0x1d, 0x6f, 0x64, 0x8b, 0xdf, 0x96, 0x59, 0x67, 0x76, + 0xaf, 0xdb, 0x63, 0x77, 0xac, 0x43, 0x4c, 0x1c, 0x29, 0x3c, + 0xcb, 0x04 }; + + /* Testcases: + * 1) Client sends empty ST extension. Server issues NST. + * 2) Client sends empty ST extension. Server does a full handshake, but is unable to issue NST due to absence of an encrypt-decrypt key. + * 3) Client sends non-empty ST extension. Server does an abbreviated handshake without issuing NST. + * 4) Client sends non-empty ST extension. Server does an abbreviated handshake and issues a NST because the key is in decrypt-only state. + * 5) Client sends non-empty ST extension. Server does an abbreviated handshake, but does not issue a NST even though the key is in + * decrypt-only state due to absence of encrypt-decrypt key. + * 6) Client sends non-empty ST extension. Server does a full handshake and issues a NST because the key is not found. + * 7) Client sends non-empty ST extension. Server does a full handshake and issues a NST because the key has expired. + * 8) Client sends non-empty ST extension. Server does a full handshake and issues a NST because the ticket has non-standard size. + * 9) Client sends non-empty ST extension, but server cannot or does not want to honor the ticket. + * 10) Client sets corrupted ST extension. + * 11) User tries adding a duplicate key to the server. + * 12) Testing expired keys are removed from the server config while adding new keys. + * 13) Scenario 1: Client sends empty ST and server has multiple encrypt-decrypt keys to choose from for encrypting NST. + * 14) Scenario 2: Client sends empty ST and server has multiple encrypt-decrypt keys to choose from for encrypting NST. + * 15) Testing s2n_config_set_ticket_encrypt_decrypt_key_lifetime and s2n_config_set_ticket_decrypt_key_lifetime calls. + * 16) Add keys out of order and pre-emptively add a key. + * 17) Handshake with client auth and session ticket enabled. + * 18) Session resumption APIs and session_ticket_cb return the same values when receiving a new ticket in TLS1.2 + * 19) Session resumption APIs and session_ticket_cb return sane values when receiving a new ticket in TLS1.3 + * 20) Client has TLS1.3 ticket but negotiates TLS1.2, so does full handshake + */ + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + DEFER_CLEANUP(struct s2n_stuffer tls13_serialized_session_state = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&tls13_serialized_session_state, 0)); + + struct s2n_test_io_pair io_pair; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_PRIVATE_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(chain_and_key = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + + struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + + EXPECT_SUCCESS(setenv("S2N_DONT_MLOCK", "1", 0)); + + /* Test ticket name handling */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + + /* Enable session tickets */ + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + + /* The name should be greater than 0 and less than or equal to 16 */ + uint8_t too_large_name[17] = { 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_ticket_crypto_key(config, too_large_name, 0, ticket_key1, s2n_array_len(ticket_key1), 0), S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); + EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_ticket_crypto_key(config, too_large_name, 17, ticket_key1, s2n_array_len(ticket_key1), 0), S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); + + /* Add a ticket with a single-byte name */ + struct small_name_ticket small = { .name = { 0xAA }, .key = { 0xBB, 0xBB, 0xBB, 0xBB } }; + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, small.name, s2n_array_len(small.name), small.key, s2n_array_len(small.key), 0)); + + /* Ensure a ticket with the same name is not added */ + struct small_name_ticket small2 = { .name = { 0xAA }, .key = { 0xCC, 0xCC, 0xCC, 0xCC } }; + EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_ticket_crypto_key(config, small2.name, s2n_array_len(small2.name), small2.key, s2n_array_len(small2.key), 0), S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); + + /* Ensure a ticket with a zero-padded name is not added */ + uint8_t padded_name[16] = { 0xAA, 0 }; + EXPECT_FAILURE_WITH_ERRNO(s2n_config_add_ticket_crypto_key(config, padded_name, s2n_array_len(padded_name), ticket_key1, s2n_array_len(ticket_key1), 0), S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); + }; + + /* Client sends empty ST extension. Server issues NST. */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + /* Add one session ticket key with an intro time in the past so that the key is immediately valid */ + POSIX_GUARD(server_config->wall_clock(server_config->sys_clock_ctx, &now)); + uint64_t key_intro_time = (now / ONE_SEC_IN_NANOS) - ONE_SEC_DELAY; + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name1, s2n_array_len(ticket_key_name1), + ticket_key1, s2n_array_len(ticket_key1), key_intro_time)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* A newly created connection should not be considered resumed */ + EXPECT_FALSE(s2n_connection_is_session_resumed(server_conn)); + EXPECT_FALSE(s2n_connection_is_session_resumed(client_conn)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and issued NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* Verify that the client received NST */ + serialized_session_state_length = s2n_connection_get_session_length(client_conn); + EXPECT_EQUAL(s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length), serialized_session_state_length); + EXPECT_BYTEARRAY_EQUAL(serialized_session_state + S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES, ticket_key_name1, s2n_array_len(ticket_key_name1)); + + /* Verify the lifetime hint from the server */ + EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends empty ST extension. Server does a full handshake, but is unable + * to issue NST due to absence of an encrypt-decrypt key. */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and did not issue NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends non-empty ST extension. Server does an abbreviated handshake without issuing NST. */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + /* Set client ST and session state */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + /* Add one session ticket key with an intro time in the past so that the key is immediately valid */ + POSIX_GUARD(server_config->wall_clock(server_config->sys_clock_ctx, &now)); + uint64_t key_intro_time = (now / ONE_SEC_IN_NANOS) - ONE_SEC_DELAY; + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name1, s2n_array_len(ticket_key_name1), + ticket_key1, s2n_array_len(ticket_key1), key_intro_time)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did an abbreviated handshake and not issue NST */ + EXPECT_TRUE(IS_RESUMPTION_HANDSHAKE(server_conn)); + EXPECT_TRUE(s2n_connection_is_session_resumed(server_conn)); + EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* Verify that client_ticket is same as before because server didn't issue a NST */ + uint8_t old_session_ticket[S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES]; + EXPECT_MEMCPY_SUCCESS(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES); + EXPECT_TRUE(s2n_connection_is_session_resumed(client_conn)); + s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length); + EXPECT_BYTEARRAY_EQUAL(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES); + + /* Verify that the server lifetime hint is 0 because server didn't issue a NST */ + EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), 0); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends non-empty ST extension. Server does an abbreviated handshake and issues a NST + * because the key is in decrypt-only state. + */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + /* Set client ST and session state */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + /* Add one ST key */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name1, s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); + + /* Add a mock delay such that key 1 moves to decrypt-only state */ + uint64_t mock_delay = server_config->encrypt_decrypt_key_lifetime_in_nanos; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_nanoseconds_since_epoch, &mock_delay)); + + /* Add one session ticket key with an intro time in the past so that the key is immediately valid */ + POSIX_GUARD(server_config->wall_clock(server_config->sys_clock_ctx, &now)); + uint64_t key_intro_time = (now / ONE_SEC_IN_NANOS) - ONE_SEC_DELAY; + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), + ticket_key2, s2n_array_len(ticket_key2), key_intro_time)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did an abbreviated handshake and issued NST */ + EXPECT_TRUE(IS_RESUMPTION_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* Verify that client_ticket is not same as before because server issued a NST */ + uint8_t old_session_ticket[S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES]; + EXPECT_MEMCPY_SUCCESS(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES); + + s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length); + EXPECT_TRUE(memcmp(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES)); + + /* Verify the lifetime hint from the server */ + EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS); + + /* Verify that the new NST is encrypted using second ST */ + EXPECT_BYTEARRAY_EQUAL(serialized_session_state + S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES, ticket_key_name2, s2n_array_len(ticket_key_name2)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends non-empty ST extension. Server does an abbreviated handshake, + * but does not issue a NST even though the key is in decrypt-only state due to + * the absence of encrypt-decrypt key. + */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + /* Set client ST and session state */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + /* Add one ST key */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); + + /* Add a mock delay such that key 1 moves to decrypt-only state */ + uint64_t mock_delay = server_config->encrypt_decrypt_key_lifetime_in_nanos; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_nanoseconds_since_epoch, &mock_delay)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did an abbreviated handshake and did not issue a NST */ + EXPECT_TRUE(IS_RESUMPTION_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* Verify that client_ticket is same as before because server did not issue a NST */ + uint8_t old_session_ticket[S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES]; + EXPECT_MEMCPY_SUCCESS(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES); + + s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length); + EXPECT_FALSE(memcmp(old_session_ticket, serialized_session_state, S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES + S2N_TLS12_TICKET_SIZE_IN_BYTES)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends non-empty ST extension. Server does a full handshake and issues + * a NST because the key used to encrypt the session ticket is not found. + */ + { + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + /* Set client ST and session state */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + /* Add one session ticket key with an intro time in the past so that the key is immediately valid */ + POSIX_GUARD(server_config->wall_clock(server_config->sys_clock_ctx, &now)); + uint64_t key_intro_time = (now / ONE_SEC_IN_NANOS) - ONE_SEC_DELAY; + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name1, s2n_array_len(ticket_key_name1), + ticket_key1, s2n_array_len(ticket_key1), key_intro_time)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and issued NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* Verify that the client received NST */ + serialized_session_state_length = s2n_connection_get_session_length(client_conn); + EXPECT_EQUAL(s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length), serialized_session_state_length); + EXPECT_BYTEARRAY_EQUAL(serialized_session_state + S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES, ticket_key_name1, s2n_array_len(ticket_key_name1)); + + /* Verify the lifetime hint from the server */ + EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends non-empty ST extension. Server does a full handshake and issues a NST + * because the key has expired. + */ + { + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + /* Set client ST and session state */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + /* Add one ST key */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name1, s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); + + /* Add a mock delay such that the key used to encrypt ST expires */ + uint64_t mock_delay = server_config->decrypt_key_lifetime_in_nanos + server_config->encrypt_decrypt_key_lifetime_in_nanos; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_nanoseconds_since_epoch, &mock_delay)); + + /* Add a second session ticket key with an intro time in the past so that the key is immediately valid */ + POSIX_GUARD(server_config->wall_clock(server_config->sys_clock_ctx, &now)); + uint64_t key_intro_time = (now / ONE_SEC_IN_NANOS) - ONE_SEC_DELAY; + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), + ticket_key2, s2n_array_len(ticket_key2), key_intro_time)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and issued NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* Verify that the server has only the unexpired key */ + EXPECT_OK(s2n_set_get(server_config->ticket_keys, 0, (void **) &ticket_key)); + EXPECT_BYTEARRAY_EQUAL(ticket_key->key_name, ticket_key_name2, s2n_array_len(ticket_key_name2)); + EXPECT_OK(s2n_set_len(server_config->ticket_keys, &ticket_keys_len)); + EXPECT_EQUAL(ticket_keys_len, 1); + + /* Verify that the client received NST */ + serialized_session_state_length = s2n_connection_get_session_length(client_conn); + EXPECT_EQUAL(s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length), serialized_session_state_length); + EXPECT_BYTEARRAY_EQUAL(serialized_session_state + S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES, ticket_key_name2, s2n_array_len(ticket_key_name2)); + + /* Verify the lifetime hint from the server */ + EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends non-empty ST extension. Server does a full handshake and issues a NST because the ticket has non-standard size. */ + { + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + /* Tamper session state to make session ticket size smaller than what we expect */ + /* Verify that client_ticket is same as before because server did not issue a NST */ + uint8_t tampered_session_state[sizeof(serialized_session_state) - 1]; + /* Copy session format */ + tampered_session_state[0] = serialized_session_state[0]; + /* Copy and reduce by 1 the session ticket length */ + tampered_session_state[1] = serialized_session_state[1]; + tampered_session_state[2] = serialized_session_state[2] - 1; + /* Skip 1 byte of the session ticket and copy the rest */ + EXPECT_MEMCPY_SUCCESS(tampered_session_state + 3, serialized_session_state + 4, sizeof(tampered_session_state) - 4); + + /* Set client tampered ST and session state */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tampered_session_state, serialized_session_state_length - 1)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + /* Add a session ticket key with an intro time in the past so that the key is immediately valid */ + POSIX_GUARD(server_config->wall_clock(server_config->sys_clock_ctx, &now)); + uint64_t key_intro_time = (now / ONE_SEC_IN_NANOS) - ONE_SEC_DELAY; + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name1, s2n_array_len(ticket_key_name1), + ticket_key1, s2n_array_len(ticket_key1), key_intro_time)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and issued NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* Verify that the client received NST */ + serialized_session_state_length = s2n_connection_get_session_length(client_conn); + EXPECT_EQUAL(s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length), serialized_session_state_length); + EXPECT_BYTEARRAY_EQUAL(serialized_session_state + S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES, ticket_key_name1, s2n_array_len(ticket_key_name1)); + + /* Verify the lifetime hint from the server */ + EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sends non-empty ST extension, but server cannot or does not want to honor the ticket. */ + { + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + /* Set client ST and session state */ + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Not enabling resumption using ST */ + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and did not issue NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* Verify that client_ticket is empty */ + EXPECT_EQUAL(s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length), 1 + 1 + client_conn->session_id_len + S2N_TLS12_STATE_SIZE_IN_BYTES); + EXPECT_EQUAL(memcmp(serialized_session_state, &s2n_state_with_session_id, 1), 0); + EXPECT_NOT_EQUAL(memcmp(serialized_session_state + S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES, ticket_key_name2, s2n_array_len(ticket_key_name2)), 0); + + /* Verify the lifetime hint from the server */ + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_session_ticket_lifetime_hint(client_conn), S2N_ERR_SESSION_TICKET_NOT_SUPPORTED); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Client sets corrupted ST extension. */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + memset(serialized_session_state, 0, serialized_session_state_length); + + /* Set client ST and session state */ + EXPECT_FAILURE(s2n_connection_set_session(client_conn, serialized_session_state, serialized_session_state_length)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* User tries adding a duplicate key to the server */ + { + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name1, s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); + + /* Try adding the same key, but with a different name */ + EXPECT_EQUAL(-1, s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key1, s2n_array_len(ticket_key1), 0)); + + /* Try adding a different key, but with the same name */ + EXPECT_EQUAL(-1, s2n_config_add_ticket_crypto_key(server_config, ticket_key_name1, s2n_array_len(ticket_key_name1), ticket_key2, s2n_array_len(ticket_key2), 0)); + EXPECT_EQUAL(s2n_errno, S2N_ERR_INVALID_TICKET_KEY_NAME_OR_NAME_LENGTH); + + /* Try adding a key with invalid key length */ + EXPECT_EQUAL(-1, s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, 0, 0)); + EXPECT_EQUAL(s2n_errno, S2N_ERR_INVALID_TICKET_KEY_LENGTH); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Testing expired keys are removed from the server config while adding new keys. */ + { + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + + /* Add 2 ST keys */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name1, s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); + + /* Add a mock delay such that the first two keys expire */ + uint64_t mock_delay = server_config->decrypt_key_lifetime_in_nanos + server_config->encrypt_decrypt_key_lifetime_in_nanos; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_nanoseconds_since_epoch, &mock_delay)); + + /* Add a third ST key */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name3, s2n_array_len(ticket_key_name3), ticket_key3, s2n_array_len(ticket_key3), 0)); + + /* Try adding the expired keys */ + EXPECT_EQUAL(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0), -1); + EXPECT_EQUAL(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name1, s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0), -1); + + /* Verify that the config has only one unexpired key */ + EXPECT_OK(s2n_set_get(server_config->ticket_keys, 0, (void **) &ticket_key)); + EXPECT_BYTEARRAY_EQUAL(ticket_key->key_name, ticket_key_name3, s2n_array_len(ticket_key_name3)); + EXPECT_OK(s2n_set_len(server_config->ticket_keys, &ticket_keys_len)); + EXPECT_EQUAL(ticket_keys_len, 1); + + /* Verify that the total number of key hashes is three */ + EXPECT_OK(s2n_set_len(server_config->ticket_key_hashes, &ticket_keys_len)); + EXPECT_EQUAL(ticket_keys_len, 3); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Scenario 1: Client sends empty ST and server has multiple encrypt-decrypt keys to choose from for encrypting NST. */ + { + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + /* Add one ST key */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name1, s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); + + /* Add a mock delay such that the first key is close to it's encryption peak */ + uint64_t mock_delay = (server_config->encrypt_decrypt_key_lifetime_in_nanos / 2) - ONE_SEC_IN_NANOS; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_nanoseconds_since_epoch, &mock_delay)); + + /* Add two more ST keys */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name3, s2n_array_len(ticket_key_name3), ticket_key3, s2n_array_len(ticket_key3), 0)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and issued NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* Verify that the client received NST which is encrypted using a key which is at it's peak encryption */ + serialized_session_state_length = s2n_connection_get_session_length(client_conn); + EXPECT_EQUAL(s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length), serialized_session_state_length); + EXPECT_BYTEARRAY_EQUAL(serialized_session_state + S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES, ticket_key_name1, s2n_array_len(ticket_key_name1)); + + /* Verify the lifetime hint from the server */ + EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Scenario 2: Client sends empty ST and server has multiple encrypt-decrypt keys to choose from for encrypting NST */ + { + const size_t allowed_failures = 1; + size_t failures = 0; + bool expected_key_chosen = false; + + /* This test sets up three different ticket encryption keys at various times in their encryption lifetime. The test + * is meant to check that the weighted random selection algorithm correctly selects the key that is at its + * encryption peak. However the test will sometimes pick a key that is not at its encryption peak because the + * selection function uses a weighted random selection algorithm. Here we retry the test once if the key chosen + * is not the expected key. + * + * The wrong key will be chosen 0.02% of the time. This value is drawn from the weight of the expected key, + * which does not change per test run. Therefore, the probability that the test chooses the wrong key + * more than allowed_failures times is 0.0002 ^ 2 = 0.00000004, which is extremely unlikely to occur. If + * the logic changes to chose the wrong key at a higher rate, say 50% of the time, this test would fail at a + * 0.5 ^ 2 = 0.25 or 25% of the time. This rate is high enough for us to notice and investigate. + */ + while (expected_key_chosen == false) { + EXPECT_TRUE(failures <= allowed_failures); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + /* Add one ST key */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name1, s2n_array_len(ticket_key_name1), + ticket_key1, s2n_array_len(ticket_key1), 0)); + + /* Add second key when the first key is very close to it's encryption peak */ + uint64_t mock_delay = (server_config->encrypt_decrypt_key_lifetime_in_nanos / 2) - ONE_SEC_IN_NANOS; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_nanoseconds_since_epoch, &mock_delay)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), + ticket_key2, s2n_array_len(ticket_key2), 0)); + + /* Add third key when the second key is very close to it's encryption peak and + * the first key is about to transition from encrypt-decrypt state to decrypt-only state + */ + mock_delay = server_config->encrypt_decrypt_key_lifetime_in_nanos - ONE_SEC_IN_NANOS; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_nanoseconds_since_epoch, &mock_delay)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name3, s2n_array_len(ticket_key_name3), + ticket_key3, s2n_array_len(ticket_key3), 0)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and issued NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* Verify that the client received NST which is encrypted using a key which is at it's peak encryption */ + serialized_session_state_length = s2n_connection_get_session_length(client_conn); + EXPECT_EQUAL(s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length), + serialized_session_state_length); + int result = memcmp(serialized_session_state + S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES, ticket_key_name2, + s2n_array_len(ticket_key_name2)); + if (result == 0) { + expected_key_chosen = true; + } else { + failures += 1; + } + /* Verify the lifetime hint from the server */ + EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + } + }; + + /* Testing s2n_config_set_ticket_encrypt_decrypt_key_lifetime and + * s2n_config_set_ticket_decrypt_key_lifetime calls */ + { + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + /* Set encrypt-decrypt key expire time to 24 hours */ + EXPECT_SUCCESS(s2n_config_set_ticket_encrypt_decrypt_key_lifetime(server_config, 86400)); + + /* Set decrypt-only key expire time to 5 hours */ + EXPECT_SUCCESS(s2n_config_set_ticket_decrypt_key_lifetime(server_config, 18000)); + + /* Add one ST key */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name1, s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); + + /* Add second key when the first key is very close to it's encryption peak */ + uint64_t mock_delay = (server_config->encrypt_decrypt_key_lifetime_in_nanos / 2) - ONE_SEC_IN_NANOS; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_nanoseconds_since_epoch, &mock_delay)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), 0)); + + /* Add third key when the second key is very close to it's encryption peak and + * the first key is about to transition from encrypt-decrypt state to decrypt-only state + */ + mock_delay = server_config->encrypt_decrypt_key_lifetime_in_nanos - ONE_SEC_IN_NANOS; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_nanoseconds_since_epoch, &mock_delay)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name3, s2n_array_len(ticket_key_name3), ticket_key3, s2n_array_len(ticket_key3), 0)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and issued NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* Verify that the client received NST which is encrypted using a key which is at it's peak encryption */ + serialized_session_state_length = s2n_connection_get_session_length(client_conn); + EXPECT_EQUAL(s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length), serialized_session_state_length); + EXPECT_BYTEARRAY_EQUAL(serialized_session_state + S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES, ticket_key_name2, s2n_array_len(ticket_key_name2)); + + /* Verify the lifetime hint from the server */ + EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), 86400 + 18000); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Add keys out of order and pre-emptively add a key */ + { + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + /* Add a key. After 1 hour it will be considered an encrypt-decrypt key. */ + POSIX_GUARD(server_config->wall_clock(server_config->sys_clock_ctx, &now)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name1, s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), (now / ONE_SEC_IN_NANOS) + 3600)); + + /* Add a key. After 1 hour it will reach it's peak */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name2, s2n_array_len(ticket_key_name2), ticket_key2, s2n_array_len(ticket_key2), now / ONE_SEC_IN_NANOS)); + + /* Add a key pre-emptively. It can be used only after 10 hours */ + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name3, s2n_array_len(ticket_key_name3), ticket_key3, s2n_array_len(ticket_key3), now / ONE_SEC_IN_NANOS + 36000)); + + /* Add a mock delay such that negotiation happens after 1 hour */ + uint64_t mock_delay = (server_config->encrypt_decrypt_key_lifetime_in_nanos / 2) - ONE_SEC_IN_NANOS; + EXPECT_SUCCESS(s2n_config_set_wall_clock(server_config, mock_nanoseconds_since_epoch, &mock_delay)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and issued NST */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_TRUE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + /* Verify that the client received NST which is encrypted using a key which is at it's peak encryption */ + serialized_session_state_length = s2n_connection_get_session_length(client_conn); + EXPECT_EQUAL(s2n_connection_get_session(client_conn, serialized_session_state, serialized_session_state_length), serialized_session_state_length); + EXPECT_BYTEARRAY_EQUAL(serialized_session_state + S2N_PARTIAL_SESSION_STATE_INFO_IN_BYTES, ticket_key_name2, s2n_array_len(ticket_key_name2)); + + /* Verify that the keys are stored from oldest to newest */ + EXPECT_OK(s2n_set_get(server_config->ticket_keys, 0, (void **) &ticket_key)); + EXPECT_BYTEARRAY_EQUAL(ticket_key->key_name, ticket_key_name2, s2n_array_len(ticket_key_name2)); + EXPECT_OK(s2n_set_get(server_config->ticket_keys, 1, (void **) &ticket_key)); + EXPECT_BYTEARRAY_EQUAL(ticket_key->key_name, ticket_key_name1, s2n_array_len(ticket_key_name1)); + EXPECT_OK(s2n_set_get(server_config->ticket_keys, 2, (void **) &ticket_key)); + EXPECT_BYTEARRAY_EQUAL(ticket_key->key_name, ticket_key_name3, s2n_array_len(ticket_key_name3)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* Handshake with client auth and session ticket enabled */ + { + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + + /* Client has session ticket and mutual auth enabled */ + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + /* Server has session ticket and mutual auth enabled */ + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_OPTIONAL)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that the server did a full handshake and did not issue NST since client + * auth is enabled in server mode */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + EXPECT_FALSE(IS_ISSUING_NEW_SESSION_TICKET(server_conn)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + /* s2n_decrypt_session_ticket fails to decrypt when presented with a valid ticket_key, valid iv and invalid encrypted blob */ + { + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + + /* Add Session Ticket key on the server config */ + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name1, s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Setup stuffers value containing the valid key name, valid iv and invalid encrypted blob */ + POSIX_GUARD(s2n_stuffer_write_bytes(&server_conn->client_ticket_to_decrypt, ticket_key_name1, s2n_array_len(ticket_key_name1))); + + uint8_t valid_iv[S2N_TLS_GCM_IV_LEN] = { 0 }; + POSIX_GUARD(s2n_stuffer_write_bytes(&server_conn->client_ticket_to_decrypt, valid_iv, sizeof(valid_iv))); + + uint8_t invalid_en_data[S2N_TLS12_STATE_SIZE_IN_BYTES + S2N_TLS_GCM_TAG_LEN] = { 0 }; + POSIX_GUARD(s2n_stuffer_write_bytes(&server_conn->client_ticket_to_decrypt, invalid_en_data, sizeof(invalid_en_data))); + + server_conn->session_ticket_status = S2N_DECRYPT_TICKET; + EXPECT_FAILURE_WITH_ERRNO(s2n_decrypt_session_ticket(server_conn, &server_conn->client_ticket_to_decrypt), S2N_ERR_DECRYPT); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* s2n_decrypt_session_ticket fails with a key not found error when presented with an invalid ticket_key, valid iv and invalid encrypted blob */ + { + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + + /* Add Session Ticket key on the server config */ + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name1, s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), 0)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + /* Setup stuffers value containing the invalid key name, valid iv and invalid encrypted blob */ + POSIX_GUARD(s2n_stuffer_write_bytes(&server_conn->client_ticket_to_decrypt, ticket_key_name2, s2n_array_len(ticket_key_name2))); + + uint8_t valid_iv[S2N_TLS_GCM_IV_LEN] = { 0 }; + POSIX_GUARD(s2n_stuffer_write_bytes(&server_conn->client_ticket_to_decrypt, valid_iv, sizeof(valid_iv))); + + uint8_t invalid_en_data[S2N_TLS12_STATE_SIZE_IN_BYTES + S2N_TLS_GCM_TAG_LEN] = { 0 }; + POSIX_GUARD(s2n_stuffer_write_bytes(&server_conn->client_ticket_to_decrypt, invalid_en_data, sizeof(invalid_en_data))); + + server_conn->session_ticket_status = S2N_DECRYPT_TICKET; + EXPECT_FAILURE_WITH_ERRNO(s2n_decrypt_session_ticket(server_conn, &server_conn->client_ticket_to_decrypt), S2N_ERR_KEY_USED_IN_SESSION_TICKET_NOT_FOUND); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + }; + + /* Test s2n_connection_is_session_resumed */ + { + /* TLS1.2 */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS12; + + conn->handshake.handshake_type = INITIAL; + EXPECT_FALSE(s2n_connection_is_session_resumed(conn)); + + conn->handshake.handshake_type = NEGOTIATED | WITH_SESSION_TICKET; + EXPECT_TRUE(s2n_connection_is_session_resumed(conn)); + + /* Ignores PSK mode */ + conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; + EXPECT_TRUE(s2n_connection_is_session_resumed(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS1.3 */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = S2N_TLS13; + + conn->handshake.handshake_type = INITIAL; + conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; + EXPECT_FALSE(s2n_connection_is_session_resumed(conn)); + + conn->handshake.handshake_type = NEGOTIATED; + conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; + EXPECT_FALSE(s2n_connection_is_session_resumed(conn)); + + conn->handshake.handshake_type = NEGOTIATED; + conn->psk_params.type = S2N_PSK_TYPE_RESUMPTION; + EXPECT_TRUE(s2n_connection_is_session_resumed(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Session resumption APIs and session_ticket_cb return the same values + * when receiving a new ticket in TLS1.2 + */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_wall_clock(client_config, mock_time, NULL)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + + /* Client will use callback when server nst is received */ + EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(client_config, s2n_test_session_ticket_callback, NULL)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + /* Create nonblocking pipes */ + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Set session state lifetime for 15 hours which is equal to the default lifetime of a ticket key */ + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(server_config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + + /* Add one ST key */ + POSIX_GUARD(server_config->wall_clock(server_config->sys_clock_ctx, &now)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(server_config, ticket_key_name1, s2n_array_len(ticket_key_name1), ticket_key1, s2n_array_len(ticket_key1), now / ONE_SEC_IN_NANOS)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Expect values from the session_ticket_cb are equivalent to values from the APIs */ + EXPECT_EQUAL(cb_session_data_len, s2n_connection_get_session_length(client_conn)); + uint8_t session_data[S2N_TLS12_SESSION_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_connection_get_session(client_conn, session_data, cb_session_data_len)); + EXPECT_BYTEARRAY_EQUAL(cb_session_data, session_data, cb_session_data_len); + + EXPECT_EQUAL(cb_session_lifetime, s2n_connection_get_session_ticket_lifetime_hint(client_conn)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + }; + + EXPECT_SUCCESS(s2n_reset_tls13_in_test()); + + /* Session resumption APIs and session_ticket_cb return the same values + * when receiving a new ticket in TLS1.3 + */ + if (s2n_is_tls13_fully_supported()) { + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name1, s2n_array_len(ticket_key_name1), + ticket_key1, s2n_array_len(ticket_key1), 0)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + + /* Freeze time */ + POSIX_GUARD(config->wall_clock(config->sys_clock_ctx, &now)); + EXPECT_OK(s2n_config_mock_wall_clock(config, &now)); + + /* Send one NewSessionTicket */ + cb_session_data_len = 0; + EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(config, s2n_test_session_ticket_callback, NULL)); + EXPECT_SUCCESS(s2n_config_set_initial_ticket_count(config, 1)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that TLS1.3 was negotiated */ + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); + + /* Old TLS1.2 customer code will likely attempt to read the ticket here -- ensure we indicate no ticket yet */ + EXPECT_EQUAL(s2n_connection_get_session_length(client_conn), 0); + + /* Receive and save the issued session ticket for the next test */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + uint8_t out = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(client_conn, &out, 1, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_NOT_EQUAL(cb_session_data_len, 0); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&tls13_serialized_session_state, cb_session_data, cb_session_data_len)); + + /* Verify correct session ticket lifetime "hint" */ + EXPECT_EQUAL(s2n_connection_get_session_ticket_lifetime_hint(client_conn), cb_session_lifetime); + + /* Verify the session ticket APIs produce the same results as the callback */ + DEFER_CLEANUP(struct s2n_blob legacy_api_ticket = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_realloc(&legacy_api_ticket, cb_session_data_len)); + EXPECT_EQUAL(s2n_connection_get_session_length(client_conn), cb_session_data_len); + EXPECT_SUCCESS(s2n_connection_get_session(client_conn, legacy_api_ticket.data, legacy_api_ticket.size)); + EXPECT_BYTEARRAY_EQUAL(cb_session_data, legacy_api_ticket.data, legacy_api_ticket.size); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + } + + /* Client has TLS1.3 ticket but negotiates TLS1.2 */ + if (s2n_is_tls13_fully_supported()) { + s2n_extension_type_id client_session_ticket_ext_id = 0, psk_ext_id = 0; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_PRE_SHARED_KEY, &psk_ext_id)); + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(TLS_EXTENSION_SESSION_TICKET, &client_session_ticket_ext_id)); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(config, 1)); + EXPECT_SUCCESS(s2n_config_set_session_state_lifetime(config, S2N_SESSION_STATE_CONFIGURABLE_LIFETIME_IN_SECS)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name1, s2n_array_len(ticket_key_name1), + ticket_key1, s2n_array_len(ticket_key1), 0)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "test_all")); + EXPECT_SUCCESS(s2n_connection_set_session(client_conn, tls13_serialized_session_state.blob.data, + s2n_stuffer_data_available(&tls13_serialized_session_state))); + + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default")); + + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Verify that TLS1.2 was negotiated */ + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + + /* Verify that the client did NOT try to use TLS1.2 tickets */ + EXPECT_FALSE(S2N_CBIT_TEST(client_conn->extension_requests_sent, client_session_ticket_ext_id)); + EXPECT_FALSE(S2N_CBIT_TEST(client_conn->extension_requests_sent, client_session_ticket_ext_id)); + + /* Verify that the client tried to use TLS1.3 tickets, but the server ignored them */ + EXPECT_TRUE(S2N_CBIT_TEST(client_conn->extension_requests_sent, psk_ext_id)); + EXPECT_FALSE(S2N_CBIT_TEST(server_conn->extension_requests_sent, psk_ext_id)); + + /* Verify that a full handshake occurred instead */ + EXPECT_TRUE(IS_FULL_HANDSHAKE(client_conn)); + EXPECT_TRUE(IS_FULL_HANDSHAKE(server_conn)); + + EXPECT_SUCCESS(s2n_shutdown_test_server_and_client(server_conn, client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_config_free(config)); + } + + EXPECT_SUCCESS(s2n_io_pair_close(&io_pair)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_chain_and_key)); + free(cert_chain); + free(private_key); + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_set_test.c b/tests/unit/s2n_set_test.c new file mode 100644 index 00000000000..1435ea09b0c --- /dev/null +++ b/tests/unit/s2n_set_test.c @@ -0,0 +1,173 @@ +/* + * 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 "utils/s2n_set.h" + +#include "s2n_test.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +struct array_element { + int first; + char second; +}; + +static int s2n_binary_search_comparator(const void *pa, const void *pb) +{ + const struct array_element *a = (const struct array_element *) pa; + const struct array_element *b = (const struct array_element *) pb; + + if (a->first > b->first) { + return 1; + } else if (a->first < b->first) { + return -1; + } else { + return 0; + } +} + +int main(int argc, char **argv) +{ + const int element_size = sizeof(struct array_element); + uint32_t set_len = 0; + struct array_element *ep = NULL; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + EXPECT_NULL(s2n_set_new(element_size, NULL)); + + struct s2n_set *set = NULL; + EXPECT_NOT_NULL(set = s2n_set_new(element_size, s2n_binary_search_comparator)); + EXPECT_OK(s2n_set_len(set, &set_len)); + EXPECT_EQUAL(set_len, 0); + EXPECT_ERROR(s2n_set_remove(set, 0)); + + struct array_element e1 = { .first = 1, .second = 'a' }; + EXPECT_OK(s2n_set_add(set, &e1)); + EXPECT_OK(s2n_set_len(set, &set_len)); + EXPECT_EQUAL(set_len, 1); + EXPECT_OK(s2n_set_get(set, 0, (void **) &ep)); + EXPECT_NOT_NULL(ep); + EXPECT_EQUAL(ep->first, 1); + EXPECT_EQUAL(ep->second, 'a'); + EXPECT_ERROR(s2n_set_get(set, 1, (void **) &ep)); + + /* Insert an element that will go after */ + struct array_element e2 = { .first = 10, .second = 'b' }; + EXPECT_OK(s2n_set_add(set, &e2)); + EXPECT_OK(s2n_set_len(set, &set_len)); + EXPECT_EQUAL(set_len, 2); + EXPECT_OK(s2n_set_get(set, 0, (void **) &ep)); + EXPECT_EQUAL(ep->first, 1); + EXPECT_EQUAL(ep->second, 'a'); + EXPECT_OK(s2n_set_get(set, 1, (void **) &ep)); + EXPECT_EQUAL(ep->first, 10); + EXPECT_EQUAL(ep->second, 'b'); + EXPECT_ERROR(s2n_set_get(set, 2, (void **) &ep)); + + /* insert an element to the middle */ + struct array_element e3 = { .first = 5, .second = 'c' }; + EXPECT_OK(s2n_set_add(set, &e3)); + EXPECT_OK(s2n_set_len(set, &set_len)); + EXPECT_EQUAL(set_len, 3); + EXPECT_OK(s2n_set_get(set, 0, (void **) &ep)); + EXPECT_EQUAL(ep->first, 1); + EXPECT_EQUAL(ep->second, 'a'); + EXPECT_OK(s2n_set_get(set, 1, (void **) &ep)); + EXPECT_EQUAL(ep->first, 5); + EXPECT_EQUAL(ep->second, 'c'); + EXPECT_OK(s2n_set_get(set, 2, (void **) &ep)); + EXPECT_EQUAL(ep->first, 10); + EXPECT_EQUAL(ep->second, 'b'); + EXPECT_ERROR(s2n_set_get(set, 3, (void **) &ep)); + + /* insert an element at the front */ + struct array_element e4 = { .first = 0, .second = 'd' }; + EXPECT_OK(s2n_set_add(set, &e4)); + EXPECT_OK(s2n_set_len(set, &set_len)); + EXPECT_EQUAL(set_len, 4); + EXPECT_OK(s2n_set_get(set, 0, (void **) &ep)); + EXPECT_EQUAL(ep->first, 0); + EXPECT_EQUAL(ep->second, 'd'); + EXPECT_OK(s2n_set_get(set, 1, (void **) &ep)); + EXPECT_EQUAL(ep->first, 1); + EXPECT_EQUAL(ep->second, 'a'); + EXPECT_OK(s2n_set_get(set, 2, (void **) &ep)); + EXPECT_EQUAL(ep->first, 5); + EXPECT_EQUAL(ep->second, 'c'); + EXPECT_OK(s2n_set_get(set, 3, (void **) &ep)); + EXPECT_EQUAL(ep->first, 10); + EXPECT_EQUAL(ep->second, 'b'); + EXPECT_ERROR(s2n_set_get(set, 4, (void **) &ep)); + + /* Try removing non-existent elements */ + EXPECT_ERROR(s2n_set_remove(set, 4)); + EXPECT_OK(s2n_set_len(set, &set_len)); + EXPECT_EQUAL(set_len, 4); + EXPECT_OK(s2n_set_get(set, 0, (void **) &ep)); + EXPECT_EQUAL(ep->first, 0); + EXPECT_EQUAL(ep->second, 'd'); + EXPECT_OK(s2n_set_get(set, 1, (void **) &ep)); + EXPECT_EQUAL(ep->first, 1); + EXPECT_EQUAL(ep->second, 'a'); + EXPECT_OK(s2n_set_get(set, 2, (void **) &ep)); + EXPECT_EQUAL(ep->first, 5); + EXPECT_EQUAL(ep->second, 'c'); + EXPECT_OK(s2n_set_get(set, 3, (void **) &ep)); + EXPECT_EQUAL(ep->first, 10); + EXPECT_EQUAL(ep->second, 'b'); + EXPECT_ERROR(s2n_set_get(set, 4, (void **) &ep)); + + /* Successfully remove an element */ + EXPECT_OK(s2n_set_remove(set, 1)); + EXPECT_OK(s2n_set_len(set, &set_len)); + EXPECT_EQUAL(set_len, 3); + EXPECT_OK(s2n_set_get(set, 0, (void **) &ep)); + EXPECT_EQUAL(ep->first, 0); + EXPECT_EQUAL(ep->second, 'd'); + EXPECT_OK(s2n_set_get(set, 1, (void **) &ep)); + EXPECT_EQUAL(ep->first, 5); + EXPECT_EQUAL(ep->second, 'c'); + EXPECT_OK(s2n_set_get(set, 2, (void **) &ep)); + EXPECT_EQUAL(ep->first, 10); + EXPECT_EQUAL(ep->second, 'b'); + EXPECT_ERROR(s2n_set_get(set, 3, (void **) &ep)); + + /* insert an element that already exists */ + struct array_element e5 = { .first = 5, .second = 'e' }; + EXPECT_ERROR(s2n_set_add(set, &e5)); + EXPECT_OK(s2n_set_len(set, &set_len)); + EXPECT_EQUAL(set_len, 3); + EXPECT_OK(s2n_set_get(set, 0, (void **) &ep)); + EXPECT_EQUAL(ep->first, 0); + EXPECT_EQUAL(ep->second, 'd'); + EXPECT_OK(s2n_set_get(set, 1, (void **) &ep)); + EXPECT_EQUAL(ep->first, 5); + EXPECT_EQUAL(ep->second, 'c'); + EXPECT_OK(s2n_set_get(set, 2, (void **) &ep)); + EXPECT_EQUAL(ep->first, 10); + EXPECT_EQUAL(ep->second, 'b'); + EXPECT_ERROR(s2n_set_get(set, 3, (void **) &ep)); + + /* Free the set to avoid memory leak */ + EXPECT_OK(s2n_set_free(set)); + + /* Check what happens if there is an integer overflow */ + /* 0xF00000F0 * 16 = 3840 (in 32 bit arithmatic) */ + EXPECT_NULL(s2n_set_new(0xF00000F0, s2n_binary_search_comparator)); + EXPECT_NOT_NULL(set = s2n_set_new(240, s2n_binary_search_comparator)); + EXPECT_OK(s2n_set_free(set)); + END_TEST(); +} diff --git a/tests/unit/s2n_shutdown_test.c b/tests/unit/s2n_shutdown_test.c new file mode 100644 index 00000000000..61fddacfc9a --- /dev/null +++ b/tests/unit/s2n_shutdown_test.c @@ -0,0 +1,843 @@ +/* + * 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 "tls/s2n_shutdown.c" + +#include "s2n_test.h" +#include "testlib/s2n_ktls_test_utils.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_alerts.h" +#include "utils/s2n_socket.h" + +#define ALERT_LEN (sizeof(uint16_t)) + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + const uint8_t close_notify_alert[] = { + S2N_TLS_ALERT_LEVEL_WARNING, + S2N_TLS_ALERT_CLOSE_NOTIFY + }; + + const uint8_t alert_record_header[] = { + /* record type */ + TLS_ALERT, + /* protocol version */ + S2N_TLS12 / 10, + S2N_TLS12 % 10, + /* length */ + 0, + S2N_ALERT_LENGTH, + }; + + const uint8_t alert_record_size = sizeof(alert_record_header) + S2N_ALERT_LENGTH; + + /* Test: Do not send or await close_notify if reader alert already queued */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_skip_handshake(conn)); + + /* Setup output, but no input. We expect no reads. */ + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + EXPECT_SUCCESS(s2n_connection_set_send_io_stuffer(&output, conn)); + + /* Verify state prior to alert */ + EXPECT_TRUE(s2n_handshake_is_complete(conn)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->close_notify_received)); + EXPECT_FALSE(conn->alert_sent); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_FULL_DUPLEX)); + + /* Queue reader alert */ + EXPECT_SUCCESS(s2n_queue_reader_handshake_failure_alert(conn)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); + + /* Verify state after shutdown attempt */ + EXPECT_TRUE(s2n_handshake_is_complete(conn)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->close_notify_received)); + EXPECT_TRUE(conn->alert_sent); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + + /* Verify only one alert sent */ + EXPECT_EQUAL(s2n_stuffer_data_available(&output), alert_record_size); + + /* Verify that the single alert is a fatal error, not a close_notify */ + uint8_t level = 0, code = 0; + EXPECT_SUCCESS(s2n_stuffer_skip_read(&output, sizeof(alert_record_header))); + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&output, &level)); + EXPECT_EQUAL(level, S2N_TLS_ALERT_LEVEL_FATAL); + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&output, &code)); + EXPECT_EQUAL(code, S2N_TLS_ALERT_HANDSHAKE_FAILURE); + }; + + /* Test: Send and await close_notify if a warning alert was sent */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_skip_handshake(conn)); + + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, conn)); + + /* Verify state prior to alert */ + EXPECT_TRUE(s2n_handshake_is_complete(conn)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->close_notify_received)); + EXPECT_FALSE(conn->alert_sent); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_FULL_DUPLEX)); + + /* Queue reader warning */ + EXPECT_OK(s2n_queue_reader_no_renegotiation_alert(conn)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_FAILURE_WITH_ERRNO(s2n_shutdown(conn, &blocked), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + /* Verify state after shutdown attempt */ + EXPECT_TRUE(s2n_handshake_is_complete(conn)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->close_notify_received)); + EXPECT_TRUE(conn->alert_sent); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_WRITABLE)); + + /* Verify two alerts sent: the warning + the close_notify */ + EXPECT_EQUAL(s2n_stuffer_data_available(&output), alert_record_size * 2); + }; + + /* Test: Do not send or await close_notify if error alert was already received */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_skip_handshake(conn)); + + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, conn)); + + /* Verify state prior to alert */ + EXPECT_TRUE(s2n_handshake_is_complete(conn)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->close_notify_received)); + EXPECT_FALSE(conn->alert_sent); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_FULL_DUPLEX)); + + /* Queue input alert */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&input, alert_record_header, + sizeof(alert_record_header))); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&input, S2N_TLS_ALERT_LEVEL_FATAL)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&input, S2N_TLS_ALERT_INTERNAL_ERROR)); + + /* Receive alert */ + uint8_t buffer[1] = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(conn, buffer, sizeof(buffer), &blocked), + S2N_ERR_ALERT); + + /* Call s2n_connection_get_alert(), to make sure that + * https://github.com/aws/s2n-tls/issues/3933 doesn't affect shutdown. + */ + EXPECT_EQUAL(s2n_connection_get_alert(conn), S2N_TLS_ALERT_INTERNAL_ERROR); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_get_alert(conn), S2N_ERR_NO_ALERT); + + /* Shutdown should succeed, since it's a no-op */ + EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); + + /* Verify state after shutdown attempt */ + EXPECT_TRUE(s2n_handshake_is_complete(conn)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->close_notify_received)); + EXPECT_FALSE(conn->alert_sent); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + + /* Verify no alerts sent */ + EXPECT_EQUAL(s2n_stuffer_data_available(&output), 0); + }; + + /* Test: Do not wait for response close_notify if handshake not complete */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, conn)); + + /* Verify state prior to alert */ + EXPECT_FALSE(s2n_handshake_is_complete(conn)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->close_notify_received)); + EXPECT_FALSE(conn->alert_sent); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_FULL_DUPLEX)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); + + /* Verify state after shutdown */ + EXPECT_FALSE(s2n_handshake_is_complete(conn)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->close_notify_received)); + EXPECT_TRUE(conn->alert_sent); + + /* Fully closed: we don't worry about truncating data */ + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + }; + + /* Test: Await close_notify if no close_notify received yet */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_skip_handshake(conn)); + + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, conn)); + + /* Verify state prior to alert */ + EXPECT_FALSE(s2n_atomic_flag_test(&conn->close_notify_received)); + EXPECT_FALSE(conn->alert_sent); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_FULL_DUPLEX)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_FAILURE_WITH_ERRNO(s2n_shutdown(conn, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + /* Verify state after shutdown attempt */ + EXPECT_FALSE(s2n_atomic_flag_test(&conn->close_notify_received)); + EXPECT_TRUE(conn->alert_sent); + + /* Half-close: only write closed */ + EXPECT_EQUAL(s2n_connection_get_protocol_version(conn), S2N_TLS13); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_WRITABLE)); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_READABLE)); + }; + + /* Test: Do not await close_notify if close_notify already received */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_skip_handshake(conn)); + + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, conn)); + + /* Verify state prior to alert */ + EXPECT_FALSE(s2n_atomic_flag_test(&conn->close_notify_received)); + EXPECT_FALSE(conn->alert_sent); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_FULL_DUPLEX)); + + /* Write and process the alert */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->in, close_notify_alert, sizeof(close_notify_alert))); + EXPECT_SUCCESS(s2n_process_alert_fragment(conn)); + + /* Verify state after alert */ + EXPECT_TRUE(s2n_atomic_flag_test(&conn->close_notify_received)); + EXPECT_FALSE(conn->alert_sent); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_WRITABLE)); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_READABLE)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + /* Verify state after shutdown attempt */ + EXPECT_TRUE(s2n_atomic_flag_test(&conn->close_notify_received)); + EXPECT_TRUE(conn->alert_sent); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + }; + + /* Test: s2n_shutdown reports alerts received after a close_notify is sent */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_skip_handshake(conn)); + + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, conn)); + + /* Verify s2n_shutdown is waiting for a close_notify */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_FAILURE_WITH_ERRNO(s2n_shutdown(conn, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + EXPECT_TRUE(conn->alert_sent); + + /* Queue an input error alert */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&input, alert_record_header, sizeof(alert_record_header))); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&input, 2)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&input, S2N_TLS_ALERT_INTERNAL_ERROR)); + + /* Receive and report the error alert */ + EXPECT_FAILURE_WITH_ERRNO(s2n_shutdown(conn, &blocked), S2N_ERR_ALERT); + + /* Verify state after shutdown attempt */ + EXPECT_FALSE(s2n_atomic_flag_test(&conn->close_notify_received)); + EXPECT_TRUE(conn->alert_sent); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + EXPECT_EQUAL(s2n_stuffer_data_available(&output), alert_record_size); + + /* Future calls are no-ops */ + for (size_t i = 0; i < 5; i++) { + EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->close_notify_received)); + EXPECT_TRUE(conn->alert_sent); + } + }; + + /* Test: s2n_shutdown ignores data received after a close_notify is sent */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_skip_handshake(conn)); + + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, conn)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_FAILURE_WITH_ERRNO(s2n_shutdown(conn, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + EXPECT_TRUE(conn->alert_sent); + + /* Receive a non-alert record */ + uint8_t record_bytes[] = { + /* record type */ + TLS_HANDSHAKE, + /* protocol version */ + S2N_TLS12 / 10, + S2N_TLS12 % 10, + /* length */ + 0, + 1, + /* data */ + 'x' + }; + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&input, record_bytes, sizeof(record_bytes))); + EXPECT_FAILURE_WITH_ERRNO(s2n_shutdown(conn, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + /* Receive the response close_notify */ + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&input, alert_record_header, sizeof(alert_record_header))); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&input, close_notify_alert, sizeof(close_notify_alert))); + EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); + }; + + /* Test: s2n_shutdown with aggressive socket close */ + { + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_OK(s2n_skip_handshake(server_conn)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_OK(s2n_skip_handshake(client_conn)); + + struct s2n_test_io_pair io_pair = { 0 }; + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* The client's first shutdown attempt blocks on the server's close_notify */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_FAILURE_WITH_ERRNO(s2n_shutdown(client_conn, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + + /* The server's next shutdown succeeds. + * From the server's perspective the connection is now gracefully shutdown and + * the socket can be closed. + */ + EXPECT_SUCCESS(s2n_shutdown(server_conn, &blocked)); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_SERVER)); + + /* Even though the socket is now closed, we should be able to finish + * shutting down the client connection too. + */ + EXPECT_SUCCESS(s2n_shutdown(client_conn, &blocked)); + EXPECT_SUCCESS(s2n_io_pair_close_one_end(&io_pair, S2N_CLIENT)); + }; + + /* Test: Do not send or await close_notify if supporting QUIC */ + if (s2n_is_tls13_fully_supported()) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_enable_quic(conn)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + + /* Verify state after shutdown attempt */ + EXPECT_FALSE(s2n_atomic_flag_test(&conn->close_notify_received)); + EXPECT_FALSE(conn->alert_sent); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + }; + + /* Test: s2n_shutdown_send */ + { + /* Test: Safety */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + EXPECT_FAILURE_WITH_ERRNO(s2n_shutdown_send(NULL, &blocked), S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_shutdown_send(conn, NULL), S2N_ERR_NULL); + } + + /* Test: Basic successful call */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + /* Only setup write IO. + * By not setting up read IO, we test that s2n_shutdown_send never + * attempts to read. + */ + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_connection_set_send_io_stuffer(&output, conn)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + + /* s2n_shutdown also doesn't attempt to read unless we skip the + * handshake. s2n_shutdown_send doesn't care about the state of the + * handshake, but skip anyway to prove that. + */ + EXPECT_OK(s2n_skip_handshake(conn)); + + /* Successful half-close */ + EXPECT_SUCCESS(s2n_shutdown_send(conn, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_WRITABLE)); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_READABLE)); + EXPECT_EQUAL(s2n_stuffer_data_available(&output), alert_record_size); + EXPECT_TRUE(conn->alert_sent); + }; + + /* Test: Handles blocking IO */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + /* Do not initially allocate any memory for the output stuffer. + * That will cause writes to block. + */ + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_connection_set_send_io_stuffer(&output, conn)); + + /* All attempts to shutdown should block */ + for (size_t i = 0; i < 5; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_shutdown_send(conn, &blocked), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_EQUAL(s2n_stuffer_data_available(&output), 0); + } + + /* Once we allocate memory for the output stuffer (by marking it + * growable here), writes should start succeeding. + */ + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + + /* Successful half-close */ + EXPECT_SUCCESS(s2n_shutdown_send(conn, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_WRITABLE)); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_READABLE)); + EXPECT_EQUAL(s2n_stuffer_data_available(&output), alert_record_size); + EXPECT_TRUE(conn->alert_sent); + }; + + /* Test: No-op on wipe */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_SUCCESS(s2n_shutdown_send(conn, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_FULL_DUPLEX)); + EXPECT_FALSE(conn->alert_sent); + }; + + /* Test: Full close after half close */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_skip_handshake(conn)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, conn)); + + /* Successful half-close. + * Subsequent calls are no-ops. + */ + for (size_t i = 0; i < 5; i++) { + EXPECT_SUCCESS(s2n_shutdown_send(conn, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_WRITABLE)); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_READABLE)); + EXPECT_EQUAL(s2n_stuffer_data_available(&output), alert_record_size); + EXPECT_TRUE(conn->alert_sent); + } + + /* Full close blocks on input */ + for (size_t i = 0; i < 5; i++) { + EXPECT_FAILURE_WITH_ERRNO(s2n_shutdown(conn, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + EXPECT_FALSE(s2n_connection_check_io_status(conn, S2N_IO_WRITABLE)); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_READABLE)); + } + + /* Copy alert from output to input */ + EXPECT_SUCCESS(s2n_stuffer_copy(&output, &input, s2n_stuffer_data_available(&output))); + + /* Full close succeeds. + * Subsequent calls are no-ops. + */ + for (size_t i = 0; i < 5; i++) { + EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + EXPECT_EQUAL(s2n_stuffer_data_available(&input), 0); + EXPECT_EQUAL(s2n_stuffer_data_available(&output), 0); + EXPECT_TRUE(conn->alert_sent); + } + }; + + /* Test: Half close, local alert, then full close */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_skip_handshake(conn)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, conn)); + + /* Successful half-close */ + EXPECT_SUCCESS(s2n_shutdown_send(conn, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_TRUE(conn->alert_sent); + + /* Queue a local fatal alert */ + EXPECT_SUCCESS(s2n_queue_reader_handshake_failure_alert(conn)); + + /* Full close is no-op */ + EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->close_notify_received)); + }; + + /* Test: Half close, peer alert, then full close */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_OK(s2n_skip_handshake(conn)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, conn)); + + /* Successful half-close */ + EXPECT_SUCCESS(s2n_shutdown_send(conn, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_TRUE(conn->alert_sent); + + /* Receive alert */ + uint8_t buffer[1] = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&input, alert_record_header, + sizeof(alert_record_header))); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&input, S2N_TLS_ALERT_LEVEL_FATAL)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&input, S2N_TLS_ALERT_INTERNAL_ERROR)); + EXPECT_FAILURE_WITH_ERRNO(s2n_recv(conn, buffer, sizeof(buffer), &blocked), + S2N_ERR_ALERT); + + /* Full close is no-op */ + EXPECT_SUCCESS(s2n_shutdown(conn, &blocked)); + EXPECT_TRUE(s2n_connection_check_io_status(conn, S2N_IO_CLOSED)); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->close_notify_received)); + }; + + /* Test: kTLS enabled */ + { + /* Test: Successfully send alert */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_SEND); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer out = { 0 }, + s2n_ktls_io_stuffer_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer_send(conn, &out)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_SUCCESS(s2n_shutdown_send(conn, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_TRUE(conn->alert_sent); + EXPECT_EQUAL(out.sendmsg_invoked_count, 1); + EXPECT_OK(s2n_test_validate_ancillary(&out, TLS_ALERT, S2N_ALERT_LENGTH)); + EXPECT_OK(s2n_test_validate_data(&out, + close_notify_alert, sizeof(close_notify_alert))); + + /* Repeating the shutdown does not resend the alert */ + for (size_t i = 0; i < 5; i++) { + EXPECT_SUCCESS(s2n_shutdown_send(conn, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_TRUE(conn->alert_sent); + EXPECT_EQUAL(out.sendmsg_invoked_count, 1); + } + }; + + /* Test: Successfully send alert after blocking */ + { + /* One call does the partial write, the second blocks */ + const size_t partial_write = 1; + const size_t second_write = sizeof(close_notify_alert) - partial_write; + EXPECT_TRUE(second_write > 0); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + s2n_ktls_configure_connection(conn, S2N_KTLS_MODE_SEND); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer out = { 0 }, + s2n_ktls_io_stuffer_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer_send(conn, &out)); + EXPECT_SUCCESS(s2n_stuffer_free(&out.data_buffer)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&out.data_buffer, partial_write)); + + /* One call does the partial write, the second blocks */ + size_t expected_calls = 2; + + /* Initial shutdown blocks */ + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_FAILURE_WITH_ERRNO(s2n_shutdown_send(conn, &blocked), + S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_TRUE(conn->alert_sent); + EXPECT_EQUAL(out.sendmsg_invoked_count, expected_calls); + EXPECT_OK(s2n_test_validate_ancillary(&out, TLS_ALERT, partial_write)); + EXPECT_OK(s2n_test_validate_data(&out, close_notify_alert, partial_write)); + + /* Unblock the output stuffer */ + out.data_buffer.growable = true; + expected_calls++; + EXPECT_SUCCESS(s2n_stuffer_wipe(&out.ancillary_buffer)); + + /* Second shutdown succeeds */ + EXPECT_SUCCESS(s2n_shutdown_send(conn, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_TRUE(conn->alert_sent); + EXPECT_EQUAL(out.sendmsg_invoked_count, expected_calls); + EXPECT_OK(s2n_test_validate_ancillary(&out, TLS_ALERT, second_write)); + EXPECT_OK(s2n_test_validate_data(&out, close_notify_alert, + sizeof(close_notify_alert))); + + /* Repeating the shutdown does not resend the alert */ + for (size_t i = 0; i < 5; i++) { + EXPECT_SUCCESS(s2n_shutdown_send(conn, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_TRUE(conn->alert_sent); + EXPECT_EQUAL(out.sendmsg_invoked_count, expected_calls); + } + }; + }; + }; + + /* Test: ktls enabled */ + { + /* Test: Successfully shutdown */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + s2n_ktls_configure_connection(client, S2N_KTLS_MODE_SEND); + s2n_ktls_configure_connection(client, S2N_KTLS_MODE_RECV); + EXPECT_OK(s2n_skip_handshake(client)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + s2n_ktls_configure_connection(server, S2N_KTLS_MODE_SEND); + s2n_ktls_configure_connection(server, S2N_KTLS_MODE_RECV); + EXPECT_OK(s2n_skip_handshake(server)); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair io_pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(server, client, &io_pair)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_SUCCESS(s2n_shutdown_send(client, &blocked)); + EXPECT_TRUE(client->alert_sent); + + EXPECT_SUCCESS(s2n_shutdown(server, &blocked)); + EXPECT_TRUE(server->alert_sent); + EXPECT_TRUE(s2n_connection_check_io_status(server, S2N_IO_CLOSED)); + }; + + /* Test: Successfully shutdown after blocking */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + s2n_ktls_configure_connection(client, S2N_KTLS_MODE_SEND); + s2n_ktls_configure_connection(client, S2N_KTLS_MODE_RECV); + EXPECT_OK(s2n_skip_handshake(client)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + s2n_ktls_configure_connection(server, S2N_KTLS_MODE_SEND); + s2n_ktls_configure_connection(server, S2N_KTLS_MODE_RECV); + EXPECT_OK(s2n_skip_handshake(server)); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair io_pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(server, client, &io_pair)); + + /* Setup the client->server stuffer to not fit the entire close_notify */ + EXPECT_SUCCESS(s2n_stuffer_free(&io_pair.server_in.data_buffer)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&io_pair.server_in.data_buffer, 1)); + + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + EXPECT_FAILURE_WITH_ERRNO(s2n_shutdown(client, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_WRITE); + EXPECT_FALSE(s2n_connection_check_io_status(client, S2N_IO_WRITABLE)); + EXPECT_TRUE(s2n_connection_check_io_status(client, S2N_IO_READABLE)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_shutdown(server, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + EXPECT_FALSE(s2n_connection_check_io_status(server, S2N_IO_WRITABLE)); + EXPECT_TRUE(s2n_connection_check_io_status(server, S2N_IO_READABLE)); + + /* Reuse the client->server stuffer for the remaining close_notify */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&io_pair.server_in.data_buffer)); + + EXPECT_SUCCESS(s2n_shutdown(client, &blocked)); + EXPECT_SUCCESS(s2n_shutdown(server, &blocked)); + }; + + /* Test: Skip application data when waiting for close_notify */ + { + DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(client); + s2n_ktls_configure_connection(client, S2N_KTLS_MODE_SEND); + s2n_ktls_configure_connection(client, S2N_KTLS_MODE_RECV); + EXPECT_OK(s2n_skip_handshake(client)); + + DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(server); + s2n_ktls_configure_connection(server, S2N_KTLS_MODE_SEND); + s2n_ktls_configure_connection(server, S2N_KTLS_MODE_RECV); + EXPECT_OK(s2n_skip_handshake(server)); + + DEFER_CLEANUP(struct s2n_test_ktls_io_stuffer_pair io_pair = { 0 }, + s2n_ktls_io_stuffer_pair_free); + EXPECT_OK(s2n_test_init_ktls_io_stuffer(server, client, &io_pair)); + + /* Send some application data for shutdown to skip */ + uint8_t app_data[] = "hello world"; + size_t app_data_size = sizeof(app_data); + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + size_t app_data_count = 5; + for (size_t i = 0; i < app_data_count; i++) { + EXPECT_SUCCESS(s2n_send(client, app_data, app_data_size, &blocked)); + EXPECT_SUCCESS(s2n_send(server, app_data, app_data_size, &blocked)); + } + EXPECT_OK(s2n_test_validate_ancillary(&io_pair.client_in, TLS_APPLICATION_DATA, app_data_size)); + EXPECT_OK(s2n_test_validate_ancillary(&io_pair.server_in, TLS_APPLICATION_DATA, app_data_size)); + EXPECT_OK(s2n_test_records_in_ancillary(&io_pair.client_in, app_data_count)); + EXPECT_OK(s2n_test_records_in_ancillary(&io_pair.server_in, app_data_count)); + + /* Client's first shutdown blocks on reading the close_notify, + * but successfully writes the close_notify and skips all the app data.*/ + EXPECT_FAILURE_WITH_ERRNO(s2n_shutdown(client, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + EXPECT_FALSE(s2n_connection_check_io_status(client, S2N_IO_WRITABLE)); + EXPECT_TRUE(s2n_connection_check_io_status(client, S2N_IO_READABLE)); + EXPECT_TRUE(client->alert_sent); + EXPECT_OK(s2n_test_records_in_ancillary(&io_pair.client_in, 0)); + EXPECT_OK(s2n_test_records_in_ancillary(&io_pair.server_in, app_data_count + 1)); + + /* Server's first shutdown successfully skips all the app data + * and receives the close_notify */ + EXPECT_SUCCESS(s2n_shutdown(server, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_TRUE(s2n_connection_check_io_status(server, S2N_IO_CLOSED)); + EXPECT_TRUE(server->alert_sent); + EXPECT_OK(s2n_test_records_in_ancillary(&io_pair.client_in, 1)); + EXPECT_OK(s2n_test_records_in_ancillary(&io_pair.server_in, 0)); + + /* Client's second shutdown successfully receives the close_notify */ + EXPECT_SUCCESS(s2n_shutdown(client, &blocked)); + EXPECT_EQUAL(blocked, S2N_NOT_BLOCKED); + EXPECT_TRUE(s2n_connection_check_io_status(client, S2N_IO_CLOSED)); + EXPECT_OK(s2n_test_records_in_ancillary(&io_pair.client_in, 0)); + EXPECT_OK(s2n_test_records_in_ancillary(&io_pair.server_in, 0)); + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_signature_algorithms_test.c b/tests/unit/s2n_signature_algorithms_test.c new file mode 100644 index 00000000000..a17bc97d76c --- /dev/null +++ b/tests/unit/s2n_signature_algorithms_test.c @@ -0,0 +1,1001 @@ +/* + * 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 "tls/s2n_signature_algorithms.h" + +#include "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "crypto/s2n_rsa_pss.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_signature_scheme.h" + +#define LENGTH (s2n_array_len(test_signature_schemes)) +#define STUFFER_SIZE (LENGTH * TLS_SIGNATURE_SCHEME_LEN + 10) + +#define RSA_CIPHER_SUITE &s2n_ecdhe_rsa_with_aes_128_cbc_sha +#define ECDSA_CIPHER_SUITE &s2n_ecdhe_ecdsa_with_aes_128_cbc_sha +#define TLS13_CIPHER_SUITE &s2n_tls13_aes_128_gcm_sha256 + +const struct s2n_signature_scheme *const test_signature_schemes[] = { + &s2n_ecdsa_secp384r1_sha384, + &s2n_rsa_pkcs1_sha256, + &s2n_rsa_pkcs1_sha224, + &s2n_rsa_pkcs1_sha1, + &s2n_ecdsa_sha1, +}; + +const struct s2n_signature_preferences test_preferences = { + .count = LENGTH, + .signature_schemes = test_signature_schemes, +}; + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + struct s2n_cert_chain_and_key *rsa_cert_chain; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&rsa_cert_chain, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + struct s2n_cert_chain_and_key *ecdsa_cert_chain; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_cert_chain, + S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); + + struct s2n_cert_chain_and_key *certs[] = { ecdsa_cert_chain, rsa_cert_chain }; + + /* s2n_signature_algorithms_supported_list_send */ + { + struct s2n_security_policy test_security_policy = *s2n_fetch_default_config()->security_policy; + test_security_policy.signature_preferences = &test_preferences; + + /* Test: if all signatures supported, send all signatures */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->security_policy_override = &test_security_policy; + conn->actual_protocol_version = S2N_TLS13; + + DEFER_CLEANUP(struct s2n_stuffer result = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&result, 0)); + EXPECT_OK(s2n_signature_algorithms_supported_list_send(conn, &result)); + + uint16_t size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&result, &size)); + EXPECT_EQUAL(size, s2n_stuffer_data_available(&result)); + + for (size_t i = 0; i < s2n_array_len(test_signature_schemes); i++) { + uint16_t iana_value = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&result, &iana_value)); + EXPECT_EQUAL(iana_value, test_signature_schemes[i]->iana_value); + } + EXPECT_EQUAL(s2n_stuffer_data_available(&result), 0); + }; + + /* Test: do not send unsupported signatures */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->security_policy_override = &test_security_policy; + conn->actual_protocol_version = S2N_TLS12; + + DEFER_CLEANUP(struct s2n_stuffer result = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&result, 0)); + EXPECT_OK(s2n_signature_algorithms_supported_list_send(conn, &result)); + + uint16_t size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&result, &size)); + EXPECT_EQUAL(size, s2n_stuffer_data_available(&result)); + + for (size_t i = 0; i < s2n_array_len(test_signature_schemes); i++) { + if (test_signature_schemes[i] != &s2n_ecdsa_secp384r1_sha384) { + uint16_t iana_value = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&result, &iana_value)); + EXPECT_EQUAL(iana_value, test_signature_schemes[i]->iana_value); + } + } + EXPECT_EQUAL(s2n_stuffer_data_available(&result), 0); + }; + + /* Test: written signatures readable */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->security_policy_override = &test_security_policy; + conn->actual_protocol_version = S2N_TLS13; + + DEFER_CLEANUP(struct s2n_stuffer result = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&result, 0)); + EXPECT_OK(s2n_signature_algorithms_supported_list_send(conn, &result)); + + struct s2n_sig_scheme_list signatures = { 0 }; + EXPECT_SUCCESS(s2n_recv_supported_sig_scheme_list(&result, &signatures)); + EXPECT_EQUAL(s2n_stuffer_data_available(&result), 0); + + EXPECT_EQUAL(signatures.len, s2n_array_len(test_signature_schemes)); + for (size_t i = 0; i < s2n_array_len(test_signature_schemes); i++) { + EXPECT_EQUAL(signatures.iana_list[i], test_signature_schemes[i]->iana_value); + } + }; + + /* Test: do not send TLS1.2 signature schemes if QUIC enabled */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->security_policy_override = &test_security_policy; + conn->actual_protocol_version = S2N_TLS13; + conn->quic_enabled = true; + + DEFER_CLEANUP(struct s2n_stuffer result = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&result, 0)); + EXPECT_OK(s2n_signature_algorithms_supported_list_send(conn, &result)); + + uint16_t size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&result, &size)); + EXPECT_EQUAL(size, s2n_stuffer_data_available(&result)); + + uint16_t iana_value = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&result, &iana_value)); + EXPECT_EQUAL(iana_value, s2n_ecdsa_secp384r1_sha384.iana_value); + EXPECT_EQUAL(s2n_stuffer_data_available(&result), 0); + }; + }; + + /* s2n_get_and_validate_negotiated_signature_scheme */ + { + struct s2n_config *config = s2n_config_new(); + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + s2n_connection_set_config(conn, config); + + const struct s2n_security_policy *security_policy = NULL; + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_NOT_NULL(security_policy); + + struct s2n_security_policy test_security_policy = { + .minimum_protocol_version = security_policy->minimum_protocol_version, + .cipher_preferences = security_policy->cipher_preferences, + .kem_preferences = security_policy->kem_preferences, + .signature_preferences = &test_preferences, + .ecc_preferences = security_policy->ecc_preferences, + }; + + config->security_policy = &test_security_policy; + + struct s2n_stuffer choice = { 0 }; + s2n_stuffer_growable_alloc(&choice, STUFFER_SIZE); + + /* Test: successfully choose valid signature */ + { + const struct s2n_signature_scheme *result = NULL; + + s2n_stuffer_wipe(&choice); + s2n_stuffer_write_uint16(&choice, s2n_rsa_pkcs1_sha256.iana_value); + + EXPECT_SUCCESS(s2n_get_and_validate_negotiated_signature_scheme(conn, &choice, &result)); + EXPECT_EQUAL(result, &s2n_rsa_pkcs1_sha256); + }; + + /* Test: don't negotiate invalid signatures (protocol not high enough) */ + { + const struct s2n_signature_scheme *result = NULL; + + s2n_stuffer_wipe(&choice); + s2n_stuffer_write_uint16(&choice, s2n_ecdsa_secp384r1_sha384.iana_value); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_get_and_validate_negotiated_signature_scheme(conn, &choice, &result)); + EXPECT_EQUAL(result, &s2n_ecdsa_secp384r1_sha384); + + s2n_stuffer_reread(&choice); + conn->actual_protocol_version = S2N_TLS12; + EXPECT_FAILURE_WITH_ERRNO(s2n_get_and_validate_negotiated_signature_scheme(conn, &choice, &result), + S2N_ERR_INVALID_SIGNATURE_SCHEME); + }; + + /* Test: don't negotiate invalid signatures (protocol too high) */ + { + const struct s2n_signature_scheme *result = NULL; + + s2n_stuffer_wipe(&choice); + s2n_stuffer_write_uint16(&choice, s2n_rsa_pkcs1_sha224.iana_value); + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_get_and_validate_negotiated_signature_scheme(conn, &choice, &result)); + EXPECT_EQUAL(result, &s2n_rsa_pkcs1_sha224); + + s2n_stuffer_reread(&choice); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FAILURE_WITH_ERRNO(s2n_get_and_validate_negotiated_signature_scheme(conn, &choice, &result), + S2N_ERR_INVALID_SIGNATURE_SCHEME); + }; + + s2n_connection_free(conn); + s2n_config_free(config); + s2n_stuffer_free(&choice); + }; + + /* Test: choose correct signature for duplicate iana values. + * Some signature schemes have the same iana, but are different for + * different protocol versions. */ + { + const struct s2n_signature_scheme *const dup_test_signature_schemes[] = { + &s2n_ecdsa_secp384r1_sha384, + &s2n_ecdsa_sha384, + }; + + const struct s2n_signature_preferences dup_test_preferences = { + .count = 2, + .signature_schemes = dup_test_signature_schemes, + }; + + struct s2n_config *config = s2n_config_new(); + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + s2n_connection_set_config(conn, config); + + const struct s2n_security_policy *security_policy = NULL; + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_NOT_NULL(security_policy); + + struct s2n_security_policy test_security_policy = { + .minimum_protocol_version = security_policy->minimum_protocol_version, + .cipher_preferences = security_policy->cipher_preferences, + .kem_preferences = security_policy->kem_preferences, + .signature_preferences = &dup_test_preferences, + .ecc_preferences = security_policy->ecc_preferences, + }; + + config->security_policy = &test_security_policy; + + struct s2n_stuffer choice = { 0 }; + s2n_stuffer_growable_alloc(&choice, STUFFER_SIZE); + + const struct s2n_signature_scheme *result = NULL; + + conn->actual_protocol_version = S2N_TLS13; + s2n_stuffer_write_uint16(&choice, s2n_ecdsa_sha384.iana_value); + EXPECT_SUCCESS(s2n_get_and_validate_negotiated_signature_scheme(conn, &choice, &result)); + EXPECT_EQUAL(result, &s2n_ecdsa_secp384r1_sha384); + + conn->actual_protocol_version = S2N_TLS12; + s2n_stuffer_write_uint16(&choice, s2n_ecdsa_sha384.iana_value); + EXPECT_SUCCESS(s2n_get_and_validate_negotiated_signature_scheme(conn, &choice, &result)); + EXPECT_EQUAL(result, &s2n_ecdsa_sha384); + + s2n_connection_free(conn); + s2n_config_free(config); + s2n_stuffer_free(&choice); + }; + + /* s2n_choose_default_sig_scheme */ + { + /* This method is used by both the client and server to choose default schemes + * for both themselves and for their peers, so it needs to behave the same regardless + * of the connection mode. + */ + s2n_mode modes[] = { S2N_CLIENT, S2N_SERVER }; + for (size_t i = 0; i < s2n_array_len(modes); i++) { + struct s2n_config *config = s2n_config_new(); + struct s2n_connection *conn = s2n_connection_new(modes[i]); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + const struct s2n_security_policy *security_policy = NULL; + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_NOT_NULL(security_policy); + + struct s2n_security_policy test_security_policy = { + .minimum_protocol_version = security_policy->minimum_protocol_version, + .cipher_preferences = security_policy->cipher_preferences, + .kem_preferences = security_policy->kem_preferences, + .signature_preferences = &test_preferences, + .ecc_preferences = security_policy->ecc_preferences, + }; + + config->security_policy = &test_security_policy; + + /* + * For pre-TLS1.2, always choose either RSA or ECDSA depending on the auth method. + * Only use RSA-SHA1 if forced to by FIPS. + */ + { + conn->actual_protocol_version = S2N_TLS10; + + /* For the server signature, the auth method must match the cipher suite. */ + { + /* Choose RSA for an RSA cipher suite. */ + { + const struct s2n_signature_scheme *result = NULL; + conn->secure->cipher_suite = RSA_CIPHER_SUITE; + EXPECT_SUCCESS(s2n_choose_default_sig_scheme(conn, &result, S2N_SERVER)); + EXPECT_EQUAL(result, &s2n_rsa_pkcs1_md5_sha1); + }; + + /* Choose ECDSA for a ECDSA cipher suite. */ + { + const struct s2n_signature_scheme *result = NULL; + conn->secure->cipher_suite = ECDSA_CIPHER_SUITE; + EXPECT_SUCCESS(s2n_choose_default_sig_scheme(conn, &result, S2N_SERVER)); + EXPECT_EQUAL(result, &s2n_ecdsa_sha1); + }; + + /* Ignore the type of the client certificate. */ + { + const struct s2n_signature_scheme *result = NULL; + conn->secure->cipher_suite = ECDSA_CIPHER_SUITE; + conn->handshake_params.client_cert_pkey_type = S2N_PKEY_TYPE_RSA; + EXPECT_SUCCESS(s2n_choose_default_sig_scheme(conn, &result, S2N_SERVER)); + EXPECT_EQUAL(result, &s2n_ecdsa_sha1); + }; + + /* When in doubt, choose RSA. */ + { + const struct s2n_signature_scheme *result = NULL; + conn->secure->cipher_suite = TLS13_CIPHER_SUITE; + EXPECT_SUCCESS(s2n_choose_default_sig_scheme(conn, &result, S2N_SERVER)); + EXPECT_EQUAL(result, &s2n_rsa_pkcs1_md5_sha1); + }; + }; + + /* For the client signature, the auth type must match the type of the client certificate */ + { + /* Choose RSA for an RSA certificate */ + { + const struct s2n_signature_scheme *result = NULL; + conn->handshake_params.client_cert_pkey_type = S2N_PKEY_TYPE_RSA; + EXPECT_SUCCESS(s2n_choose_default_sig_scheme(conn, &result, S2N_CLIENT)); + EXPECT_EQUAL(result, &s2n_rsa_pkcs1_md5_sha1); + }; + + /* Choose ECDSA for a ECDSA certificate */ + { + const struct s2n_signature_scheme *result = NULL; + conn->handshake_params.client_cert_pkey_type = S2N_PKEY_TYPE_ECDSA; + EXPECT_SUCCESS(s2n_choose_default_sig_scheme(conn, &result, S2N_CLIENT)); + EXPECT_EQUAL(result, &s2n_ecdsa_sha1); + }; + + /* Ignore the auth type of the cipher suite */ + { + const struct s2n_signature_scheme *result = NULL; + conn->secure->cipher_suite = RSA_CIPHER_SUITE; + conn->handshake_params.client_cert_pkey_type = S2N_PKEY_TYPE_ECDSA; + EXPECT_SUCCESS(s2n_choose_default_sig_scheme(conn, &result, S2N_CLIENT)); + EXPECT_EQUAL(result, &s2n_ecdsa_sha1); + }; + }; + }; + + /* + * For TLS1.2, always choose either RSA-SHA1 or ECDSA depending on the auth method. + */ + { + conn->actual_protocol_version = S2N_TLS12; + + /* For the server signature, the auth method must match the cipher suite. */ + { + /* Choose RSA for an RSA cipher suite. */ + { + const struct s2n_signature_scheme *result = NULL; + conn->secure->cipher_suite = RSA_CIPHER_SUITE; + EXPECT_SUCCESS(s2n_choose_default_sig_scheme(conn, &result, S2N_SERVER)); + EXPECT_EQUAL(result, &s2n_rsa_pkcs1_sha1); + }; + + /* Choose ECDSA for a ECDSA cipher suite. */ + { + const struct s2n_signature_scheme *result = NULL; + conn->secure->cipher_suite = ECDSA_CIPHER_SUITE; + EXPECT_SUCCESS(s2n_choose_default_sig_scheme(conn, &result, S2N_SERVER)); + EXPECT_EQUAL(result, &s2n_ecdsa_sha1); + }; + + /* Ignore the type of the client certificate. */ + { + const struct s2n_signature_scheme *result = NULL; + conn->secure->cipher_suite = ECDSA_CIPHER_SUITE; + conn->handshake_params.client_cert_pkey_type = S2N_PKEY_TYPE_RSA; + EXPECT_SUCCESS(s2n_choose_default_sig_scheme(conn, &result, S2N_SERVER)); + EXPECT_EQUAL(result, &s2n_ecdsa_sha1); + }; + + /* When in doubt, choose RSA. */ + { + const struct s2n_signature_scheme *result = NULL; + conn->secure->cipher_suite = TLS13_CIPHER_SUITE; + EXPECT_SUCCESS(s2n_choose_default_sig_scheme(conn, &result, S2N_SERVER)); + EXPECT_EQUAL(result, &s2n_rsa_pkcs1_sha1); + }; + }; + + /* For the client signature, the auth type must match the type of the client certificate */ + { + /* Choose RSA for an RSA certificate */ + { + const struct s2n_signature_scheme *result = NULL; + conn->handshake_params.client_cert_pkey_type = S2N_PKEY_TYPE_RSA; + EXPECT_SUCCESS(s2n_choose_default_sig_scheme(conn, &result, S2N_CLIENT)); + EXPECT_EQUAL(result, &s2n_rsa_pkcs1_sha1); + }; + + /* Choose ECDSA for a ECDSA certificate */ + { + const struct s2n_signature_scheme *result = NULL; + conn->handshake_params.client_cert_pkey_type = S2N_PKEY_TYPE_ECDSA; + EXPECT_SUCCESS(s2n_choose_default_sig_scheme(conn, &result, S2N_CLIENT)); + EXPECT_EQUAL(result, &s2n_ecdsa_sha1); + }; + + /* Ignore the auth type of the cipher suite */ + { + const struct s2n_signature_scheme *result = NULL; + conn->secure->cipher_suite = RSA_CIPHER_SUITE; + conn->handshake_params.client_cert_pkey_type = S2N_PKEY_TYPE_ECDSA; + EXPECT_SUCCESS(s2n_choose_default_sig_scheme(conn, &result, S2N_CLIENT)); + EXPECT_EQUAL(result, &s2n_ecdsa_sha1); + }; + }; + + /* Do not fall back to a default if not allowed by security policy */ + { + const struct s2n_signature_scheme *const no_defaults[] = { + &s2n_ecdsa_secp384r1_sha384, + &s2n_rsa_pkcs1_sha256, + &s2n_rsa_pkcs1_sha224, + }; + + const struct s2n_signature_preferences no_defaults_preferences = { + .count = s2n_array_len(no_defaults), + .signature_schemes = test_signature_schemes, + }; + + struct s2n_security_policy no_defaults_security_policy = { + .minimum_protocol_version = security_policy->minimum_protocol_version, + .cipher_preferences = security_policy->cipher_preferences, + .kem_preferences = security_policy->kem_preferences, + .signature_preferences = &no_defaults_preferences, + .ecc_preferences = security_policy->ecc_preferences, + }; + conn->security_policy_override = &no_defaults_security_policy; + + /* Client / RSA */ + { + const struct s2n_signature_scheme *actual = NULL; + conn->secure->cipher_suite = RSA_CIPHER_SUITE; + EXPECT_SUCCESS(s2n_choose_default_sig_scheme(conn, &actual, S2N_SERVER)); + EXPECT_EQUAL(actual, &s2n_null_sig_scheme); + }; + + /* Server / ECDSA */ + { + const struct s2n_signature_scheme *actual = NULL; + conn->handshake_params.client_cert_pkey_type = S2N_PKEY_TYPE_ECDSA; + EXPECT_SUCCESS(s2n_choose_default_sig_scheme(conn, &actual, S2N_CLIENT)); + EXPECT_EQUAL(actual, &s2n_null_sig_scheme); + }; + }; + }; + + s2n_connection_free(conn); + s2n_config_free(config); + } + }; + + /* s2n_choose_sig_scheme_from_peer_preference_list */ + { + struct s2n_config *config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, rsa_cert_chain)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_cert_chain)); + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + const struct s2n_security_policy *security_policy = NULL; + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_NOT_NULL(security_policy); + + struct s2n_security_policy test_security_policy = { + .minimum_protocol_version = security_policy->minimum_protocol_version, + .cipher_preferences = security_policy->cipher_preferences, + .kem_preferences = security_policy->kem_preferences, + .signature_preferences = &test_preferences, + .ecc_preferences = security_policy->ecc_preferences, + }; + + config->security_policy = &test_security_policy; + + /* Test: no peer list */ + { + const struct s2n_signature_scheme *result = NULL; + + conn->secure->cipher_suite = ECDSA_CIPHER_SUITE; + conn->actual_protocol_version = S2N_TLS10; + const struct s2n_signature_scheme *default_scheme = &s2n_ecdsa_sha1; + + /* Choose default if NULL peer list */ + EXPECT_SUCCESS(s2n_choose_sig_scheme_from_peer_preference_list(conn, NULL, &result)); + EXPECT_EQUAL(result, default_scheme); + + /* Choose default if empty peer list */ + struct s2n_sig_scheme_list peer_list = { + .len = 0, + .iana_list = { 0 }, + }; + EXPECT_SUCCESS(s2n_choose_sig_scheme_from_peer_preference_list(conn, &peer_list, &result)); + EXPECT_EQUAL(result, default_scheme); + + /* If we cannot find a match in TLS1.3, allow defaults for success */ + conn->secure->cipher_suite = TLS13_CIPHER_SUITE; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_choose_sig_scheme_from_peer_preference_list(conn, &peer_list, &result)); + }; + + /* Test: no shared valid signature schemes, using TLS1.3. Server picks preferred */ + { + const struct s2n_signature_scheme *result = NULL; + + conn->secure->cipher_suite = TLS13_CIPHER_SUITE; + conn->actual_protocol_version = S2N_TLS13; + + struct s2n_sig_scheme_list peer_list = { + .len = 2, + .iana_list = { + s2n_rsa_pkcs1_sha224.iana_value, /* Invalid (wrong protocol version) */ + s2n_rsa_pkcs1_sha1.iana_value, /* Not in preference list */ + }, + }; + + /* behavior is that we fallback to a preferred signature algorithm */ + EXPECT_SUCCESS(s2n_choose_sig_scheme_from_peer_preference_list(conn, &peer_list, &result)); + EXPECT_EQUAL(result, &s2n_ecdsa_secp384r1_sha384); + }; + + /* Test: no shared valid signature schemes, using TLS1.2 */ + { + const struct s2n_signature_scheme *result = NULL; + + conn->secure->cipher_suite = TLS13_CIPHER_SUITE; + conn->actual_protocol_version = S2N_TLS12; + + /* Peer list contains no signature schemes that we support */ + struct s2n_sig_scheme_list peer_list = { + .len = 1, + .iana_list = { 1 }, + }; + + EXPECT_SUCCESS(s2n_choose_sig_scheme_from_peer_preference_list(conn, &peer_list, &result)); + + /* Verify that we did not choose the peer's offered signature scheme */ + EXPECT_NOT_NULL(result); + EXPECT_NOT_EQUAL(result->iana_value, peer_list.iana_list[0]); + + /* Verify that we chose the default signature scheme, even though it wasn't in + * the peer's offered list. This proves that when we share no signature schemes + * with the peer, then calling s2n_choose_sig_scheme_from_peer_preference_list + * is equivalent to calling s2n_choose_default_sig_scheme. */ + const struct s2n_signature_scheme *default_scheme = NULL; + EXPECT_SUCCESS(s2n_choose_default_sig_scheme(conn, &default_scheme, S2N_SERVER)); + EXPECT_EQUAL(result, default_scheme); + }; + + /* Test: choose valid signature from peer list */ + { + const struct s2n_signature_scheme *result = NULL; + + conn->secure->cipher_suite = RSA_CIPHER_SUITE; + conn->actual_protocol_version = S2N_TLS12; + + struct s2n_sig_scheme_list peer_list = { + .len = 4, + .iana_list = { + s2n_ecdsa_secp384r1_sha384.iana_value, /* Invalid: wrong protocol, wrong auth method */ + s2n_rsa_pkcs1_sha1.iana_value, /* Invalid: not in preference list */ + s2n_rsa_pkcs1_sha256.iana_value, /* Valid -- should be chosen */ + s2n_rsa_pkcs1_sha224.iana_value, /* Valid, but lower priority -- should not be chosen */ + }, + }; + + EXPECT_SUCCESS(s2n_choose_sig_scheme_from_peer_preference_list(conn, &peer_list, &result)); + EXPECT_EQUAL(result, &s2n_rsa_pkcs1_sha256); + }; + + /* Test: invalid scheme, because wrong protocol version */ + { + const struct s2n_signature_scheme *result = NULL; + + conn->secure->cipher_suite = RSA_CIPHER_SUITE; + + struct s2n_sig_scheme_list peer_list = { + .len = 1, + .iana_list = { s2n_rsa_pkcs1_sha224.iana_value }, + }; + + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_choose_sig_scheme_from_peer_preference_list(conn, &peer_list, &result)); + EXPECT_EQUAL(result, &s2n_rsa_pkcs1_sha224); + + conn->actual_protocol_version = S2N_TLS13; + EXPECT_FAILURE_WITH_ERRNO(s2n_choose_sig_scheme_from_peer_preference_list(conn, &peer_list, &result), + S2N_ERR_INVALID_SIGNATURE_SCHEME); + }; + + s2n_connection_free(conn); + s2n_config_free(config); + }; + + /* Test: send and receive default signature preferences */ + for (size_t i = S2N_TLS10; i < S2N_TLS13; i++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + conn->actual_protocol_version = i; + + DEFER_CLEANUP(struct s2n_stuffer result = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&result, 0)); + EXPECT_OK(s2n_signature_algorithms_supported_list_send(conn, &result)); + + struct s2n_sig_scheme_list signatures = { 0 }; + EXPECT_SUCCESS(s2n_recv_supported_sig_scheme_list(&result, &signatures)); + EXPECT_EQUAL(s2n_stuffer_data_available(&result), 0); + + /* Verify no duplicates - some preferences contain duplicates, but only + * one should be valid at a time. */ + uint16_t iana = 0, other_iana = 0; + for (size_t a = 0; a < signatures.len; a++) { + iana = signatures.iana_list[a]; + for (int b = 0; b < signatures.len; b++) { + if (a == b) { + continue; + } + other_iana = signatures.iana_list[b]; + EXPECT_NOT_EQUAL(iana, other_iana); + } + } + }; + + /* Test: libcrypto may not support PSS signatures */ + { + const struct s2n_signature_scheme *const pss_test_signature_schemes[] = { + &s2n_rsa_pss_rsae_sha256, + &s2n_rsa_pss_pss_sha256, + }; + + const struct s2n_signature_preferences pss_test_preferences = { + .count = 2, + .signature_schemes = pss_test_signature_schemes, + }; + + struct s2n_config *config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, rsa_cert_chain)); + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->secure->cipher_suite = TLS13_CIPHER_SUITE; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + const struct s2n_security_policy *security_policy = NULL; + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_NOT_NULL(security_policy); + + struct s2n_security_policy test_security_policy = { + .minimum_protocol_version = security_policy->minimum_protocol_version, + .cipher_preferences = security_policy->cipher_preferences, + .kem_preferences = security_policy->kem_preferences, + .signature_preferences = &pss_test_preferences, + .ecc_preferences = security_policy->ecc_preferences, + }; + + config->security_policy = &test_security_policy; + + /* Do not offer PSS signatures schemes if unsupported: + * s2n_signature_algorithms_supported_list_send + PSS */ + { + DEFER_CLEANUP(struct s2n_stuffer result = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&result, 0)); + EXPECT_OK(s2n_signature_algorithms_supported_list_send(conn, &result)); + + uint16_t size = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&result, &size)); + EXPECT_EQUAL(size, s2n_stuffer_data_available(&result)); + if (s2n_is_rsa_pss_certs_supported()) { + EXPECT_EQUAL(size, 2 * sizeof(uint16_t)); + } else if (s2n_is_rsa_pss_signing_supported()) { + EXPECT_EQUAL(size, 1 * sizeof(uint16_t)); + } else { + EXPECT_EQUAL(size, 0); + } + }; + + /* Do not accept a PSS signature scheme if unsupported: + * s2n_get_and_validate_negotiated_signature_scheme + PSS */ + { + struct s2n_stuffer choice = { 0 }; + s2n_stuffer_growable_alloc(&choice, STUFFER_SIZE); + s2n_stuffer_write_uint16(&choice, s2n_rsa_pss_rsae_sha256.iana_value); + + const struct s2n_signature_scheme *result = NULL; + + if (s2n_is_rsa_pss_signing_supported()) { + EXPECT_SUCCESS(s2n_get_and_validate_negotiated_signature_scheme(conn, &choice, &result)); + EXPECT_EQUAL(result, &s2n_rsa_pss_rsae_sha256); + } else { + EXPECT_FAILURE_WITH_ERRNO(s2n_get_and_validate_negotiated_signature_scheme(conn, &choice, &result), + S2N_ERR_INVALID_SIGNATURE_SCHEME); + } + + s2n_stuffer_free(&choice); + }; + + /* Do not choose a PSS signature scheme if unsupported: + * s2n_choose_sig_scheme_from_peer_preference_list + PSS */ + { + struct s2n_sig_scheme_list peer_list = { + .len = 1, + .iana_list = { s2n_rsa_pss_rsae_sha256.iana_value }, + }; + + const struct s2n_signature_scheme *result = NULL; + + if (s2n_is_rsa_pss_signing_supported()) { + EXPECT_SUCCESS(s2n_choose_sig_scheme_from_peer_preference_list(conn, &peer_list, &result)); + EXPECT_EQUAL(result, &s2n_rsa_pss_rsae_sha256); + } else { + EXPECT_FAILURE_WITH_ERRNO(s2n_choose_sig_scheme_from_peer_preference_list(conn, &peer_list, &result), + S2N_ERR_INVALID_SIGNATURE_SCHEME); + } + }; + + s2n_connection_free(conn); + s2n_config_free(config); + }; + + /* Test fallback of TLS 1.3 signature algorithms */ + if (s2n_is_rsa_pss_signing_supported()) { + struct s2n_config *config = s2n_config_new(); + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + const struct s2n_security_policy *security_policy = NULL; + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_NOT_NULL(security_policy); + + const struct s2n_signature_scheme *const test_rsae_signature_schemes[] = { + &s2n_rsa_pss_rsae_sha256, + }; + + const struct s2n_signature_preferences test_rsae_preferences = { + .count = 1, + .signature_schemes = test_rsae_signature_schemes, + }; + + struct s2n_security_policy test_security_policy = { + .minimum_protocol_version = security_policy->minimum_protocol_version, + .cipher_preferences = security_policy->cipher_preferences, + .kem_preferences = security_policy->kem_preferences, + .signature_preferences = &test_rsae_preferences, + .ecc_preferences = security_policy->ecc_preferences, + }; + + config->security_policy = &test_security_policy; + + /* Test: no shared valid signature schemes, using TLS1.3. Server cant pick preferred */ + { + const struct s2n_signature_scheme *result = NULL; + conn->secure->cipher_suite = TLS13_CIPHER_SUITE; + conn->actual_protocol_version = S2N_TLS13; + + struct s2n_sig_scheme_list peer_list = { + .len = 1, + .iana_list = { + s2n_rsa_pkcs1_sha224.iana_value, /* Invalid (wrong protocol version) */ + }, + }; + + EXPECT_FAILURE_WITH_ERRNO(s2n_choose_sig_scheme_from_peer_preference_list(conn, &peer_list, &result), + S2N_ERR_INVALID_SIGNATURE_SCHEME); + }; + + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, rsa_cert_chain)); + + /* Test: no shared valid signature schemes, using TLS1.3. Server picks a preferred */ + { + const struct s2n_signature_scheme *result = NULL; + conn->secure->cipher_suite = TLS13_CIPHER_SUITE; + conn->actual_protocol_version = S2N_TLS13; + + struct s2n_sig_scheme_list peer_list = { + .len = 1, + .iana_list = { + s2n_rsa_pkcs1_sha224.iana_value, /* Invalid (wrong protocol version) */ + }, + }; + + /* behavior is that we fallback to a preferred signature algorithm */ + EXPECT_SUCCESS(s2n_choose_sig_scheme_from_peer_preference_list(conn, &peer_list, &result)); + EXPECT_EQUAL(result, &s2n_rsa_pss_rsae_sha256); + }; + + s2n_connection_free(conn); + s2n_config_free(config); + } + + /* Self-Talk tests: default signature schemes */ + { + const struct s2n_signature_scheme *const default_schemes[] = { + &s2n_rsa_pkcs1_sha1, + &s2n_ecdsa_sha1 + }; + + const struct s2n_signature_scheme *const sha256_schemes[] = { + &s2n_rsa_pkcs1_sha256, + &s2n_ecdsa_sha256 + }; + + const struct s2n_signature_scheme *const sha384_schemes[] = { + &s2n_rsa_pkcs1_sha384, + &s2n_ecdsa_sha384 + }; + + const struct s2n_signature_preferences defaults_preferences = { + .count = s2n_array_len(default_schemes), + .signature_schemes = default_schemes, + }; + + const struct s2n_signature_preferences sha256_preferences = { + .count = s2n_array_len(sha256_schemes), + .signature_schemes = sha256_schemes, + }; + for (size_t i = 0; i < sha256_preferences.count; i++) { + for (size_t j = 0; j < defaults_preferences.count; j++) { + EXPECT_NOT_EQUAL(sha256_preferences.signature_schemes[i]->iana_value, + defaults_preferences.signature_schemes[j]->iana_value); + } + } + + const struct s2n_signature_preferences sha384_preferences = { + .count = s2n_array_len(sha384_schemes), + .signature_schemes = sha384_schemes, + }; + for (size_t i = 0; i < sha384_preferences.count; i++) { + for (size_t j = 0; j < defaults_preferences.count; j++) { + EXPECT_NOT_EQUAL(sha384_preferences.signature_schemes[i]->iana_value, + defaults_preferences.signature_schemes[j]->iana_value); + } + } + + /* The policy needs to negotiate TLS1.2 and forward secret kex */ + struct s2n_security_policy defaults_policy = security_policy_20190214; + defaults_policy.signature_preferences = &defaults_preferences; + struct s2n_security_policy sha256_policy = security_policy_20190214; + sha256_policy.signature_preferences = &sha256_preferences; + struct s2n_security_policy sha384_policy = security_policy_20190214; + sha384_policy.signature_preferences = &sha384_preferences; + + /* Self-Talk test: client and server can negotiate without any defaults */ + for (size_t cert_i = 0; cert_i < s2n_array_len(certs); cert_i++) { + /* Setup config */ + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, certs[cert_i])); + + /* Setup connections */ + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + /* Create nonblocking pipes */ + DEFER_CLEANUP(struct s2n_test_io_pair io_pair, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Client and server security policies should match but include no defaults */ + client_conn->security_policy_override = &sha256_policy; + server_conn->security_policy_override = &sha256_policy; + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + }; + + /* Self-Talk test: server does not fallback to unsupported defaults */ + for (size_t cert_i = 0; cert_i < s2n_array_len(certs); cert_i++) { + /* Setup config */ + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, certs[cert_i])); + + /* Setup connections */ + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + /* Create nonblocking pipes */ + DEFER_CLEANUP(struct s2n_test_io_pair io_pair, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Client and server security policies should have no signature schemes + * in common, and not include the default signature schemes. + */ + client_conn->security_policy_override = &sha256_policy; + server_conn->security_policy_override = &sha384_policy; + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_INVALID_SIGNATURE_ALGORITHM); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + }; + + /* Self-Talk test: client does not accept unsupported defaults */ + for (size_t cert_i = 0; cert_i < s2n_array_len(certs); cert_i++) { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, certs[cert_i])); + + /* Setup connections */ + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), + s2n_connection_ptr_free); + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + + /* Create nonblocking pipes */ + DEFER_CLEANUP(struct s2n_test_io_pair io_pair, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connections_set_io_pair(client_conn, server_conn, &io_pair)); + + /* Client and server security policies should have no signature schemes in common. + * Server should include the default policies. + */ + client_conn->security_policy_override = &sha256_policy; + server_conn->security_policy_override = &defaults_policy; + + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, client_conn), + S2N_ERR_INVALID_SIGNATURE_SCHEME); + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS12); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS12); + }; + }; + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(rsa_cert_chain)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(ecdsa_cert_chain)); + + END_TEST(); + + return 0; +} diff --git a/tests/unit/s2n_signature_scheme_test.c b/tests/unit/s2n_signature_scheme_test.c new file mode 100644 index 00000000000..83ff401e175 --- /dev/null +++ b/tests/unit/s2n_signature_scheme_test.c @@ -0,0 +1,48 @@ +/* + * 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 "tls/s2n_signature_scheme.c" + +#include "s2n_test.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* Test all signature schemes */ + size_t policy_i = 0; + while (security_policy_selection[policy_i].version != NULL) { + const struct s2n_signature_preferences *sig_prefs = + security_policy_selection[policy_i].security_policy->signature_preferences; + for (size_t sig_i = 0; sig_i < sig_prefs->count; sig_i++) { + const struct s2n_signature_scheme *const sig_scheme = sig_prefs->signature_schemes[sig_i]; + + EXPECT_NOT_EQUAL(sig_scheme->iana_value, 0); + EXPECT_NOT_EQUAL(sig_scheme->hash_alg, S2N_HASH_NONE); + EXPECT_NOT_EQUAL(sig_scheme->sig_alg, S2N_SIGNATURE_ANONYMOUS); + EXPECT_NOT_EQUAL(sig_scheme->libcrypto_nid, 0); + + if (sig_scheme->sig_alg == S2N_SIGNATURE_ECDSA + && sig_scheme->minimum_protocol_version == S2N_TLS13) { + EXPECT_NOT_NULL(sig_scheme->signature_curve); + } else { + EXPECT_NULL(sig_scheme->signature_curve); + } + } + policy_i++; + } + + END_TEST(); +} diff --git a/tests/unit/s2n_ssl_prf_test.c b/tests/unit/s2n_ssl_prf_test.c new file mode 100644 index 00000000000..c4cdfa8356c --- /dev/null +++ b/tests/unit/s2n_ssl_prf_test.c @@ -0,0 +1,104 @@ +/* + * 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 "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_prf.h" + +/* + + * Grabbed from gnutls-cli --insecure -d 9 www.example.com --ciphers AES --macs SHA --protocols SSLv3 + * + * |<9>| INT: PREMASTER SECRET[48]: 03009e8e006a7f1451d32164088a8cba5077d1b819160662a97e90a765cec244b5f8f98fd50cfe8e4fba97994a7a4843 + * |<9>| INT: CLIENT RANDOM[32]: 537fb7fdddc05090774e55f8ef8564c2b5b238819703409bfdabe14e4cf1897d + * |<9>| INT: SERVER RANDOM[32]: 537fb7fe649225c9f37904b24916452d51794b3b5735fc7e628b6090db52209f + * |<9>| INT: MASTER SECRET: 02b811717e3aa29e6b0526d7e9ae2b74496d461564401f47498e9cdbdf54c8afa69c25a648b360de2004c74850e8f7db + */ +int main(int argc, char **argv) +{ + uint8_t master_secret_hex_pad[96]; + char premaster_secret_hex_in[] = "03009e8e006a7f1451d32164088a8cba5077d1b819160662a97e90a765cec244b5f8f98fd50cfe8e4fba97994a7a4843"; + char client_random_hex_in[] = "537fb7fdddc05090774e55f8ef8564c2b5b238819703409bfdabe14e4cf1897d"; + char server_random_hex_in[] = "537fb7fe649225c9f37904b24916452d51794b3b5735fc7e628b6090db52209f"; + char master_secret_hex_in[] = "02b811717e3aa29e6b0526d7e9ae2b74496d461564401f47498e9cdbdf54c8afa69c25a648b360de2004c74850e8f7db"; + + struct s2n_stuffer client_random_in = { 0 }; + struct s2n_stuffer server_random_in = { 0 }; + struct s2n_stuffer premaster_secret_in = { 0 }; + struct s2n_stuffer master_secret_hex_out = { 0 }; + struct s2n_blob master_secret = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&master_secret, master_secret_hex_pad, sizeof(master_secret_hex_pad))); + struct s2n_blob pms = { 0 }; + + struct s2n_connection *conn = NULL; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + if (s2n_is_in_fips_mode()) { + /* Skip when FIPS mode is set as FIPS mode does not support SSLv3 */ + END_TEST(); + } + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_SUCCESS(s2n_stuffer_alloc_ro_from_string(&client_random_in, client_random_hex_in)); + EXPECT_SUCCESS(s2n_stuffer_alloc_ro_from_string(&server_random_in, server_random_hex_in)); + EXPECT_SUCCESS(s2n_stuffer_alloc_ro_from_string(&premaster_secret_in, premaster_secret_hex_in)); + + EXPECT_SUCCESS(s2n_stuffer_init(&master_secret_hex_out, &master_secret)); + + /* Parse the hex */ + for (int i = 0; i < 48; i++) { + uint8_t c = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8_hex(&premaster_secret_in, &c)); + conn->secrets.version.tls12.rsa_premaster_secret[i] = c; + } + for (int i = 0; i < 32; i++) { + uint8_t c = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8_hex(&client_random_in, &c)); + conn->handshake_params.client_random[i] = c; + } + for (int i = 0; i < 32; i++) { + uint8_t c = 0; + EXPECT_SUCCESS(s2n_stuffer_read_uint8_hex(&server_random_in, &c)); + conn->handshake_params.server_random[i] = c; + } + + /* Set the protocol version to sslv3 */ + conn->actual_protocol_version = S2N_SSLv3; + pms.data = conn->secrets.version.tls12.rsa_premaster_secret; + pms.size = sizeof(conn->secrets.version.tls12.rsa_premaster_secret); + EXPECT_SUCCESS(s2n_tls_prf_master_secret(conn, &pms)); + + /* Convert the master secret to hex */ + for (int i = 0; i < 48; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&master_secret_hex_out, conn->secrets.version.tls12.master_secret[i])); + } + + EXPECT_EQUAL(memcmp(master_secret_hex_pad, master_secret_hex_in, sizeof(master_secret_hex_pad)), 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&client_random_in)); + EXPECT_SUCCESS(s2n_stuffer_free(&server_random_in)); + EXPECT_SUCCESS(s2n_stuffer_free(&premaster_secret_in)); + + END_TEST(); +} diff --git a/tests/unit/s2n_stacktrace_test.c b/tests/unit/s2n_stacktrace_test.c new file mode 100644 index 00000000000..163cfe933f6 --- /dev/null +++ b/tests/unit/s2n_stacktrace_test.c @@ -0,0 +1,54 @@ +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "utils/s2n_blob.h" + +int raises_error() +{ + POSIX_BAIL(S2N_ERR_INVALID_ARGUMENT); +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); +#ifdef S2N_STACKTRACE + EXPECT_SUCCESS(s2n_stack_traces_enabled_set(true)); + struct s2n_stacktrace trace; + /* If nothing has errored yet, we have no stacktrace */ + EXPECT_SUCCESS(s2n_get_stacktrace(&trace)); + EXPECT_NULL(trace.trace); + EXPECT_EQUAL(trace.trace_size, 0); + + /* Raise an error, and see that it generates a stacktrace */ + EXPECT_FAILURE(raises_error()); + EXPECT_SUCCESS(s2n_get_stacktrace(&trace)); + EXPECT_NOT_NULL(trace.trace); + EXPECT_NOT_EQUAL(trace.trace_size, 0); + + /* Test printing the stacktrace. */ + FILE *stream = fopen("/dev/null", "w"); + EXPECT_SUCCESS(s2n_print_stacktrace(stream)); + fclose(stream); + + /* Free the stacktrace to avoid memory leaks */ + EXPECT_SUCCESS(s2n_free_stacktrace()); + END_TEST(); +#else + END_TEST(); +#endif +} diff --git a/tests/unit/s2n_stream_cipher_null_test.c b/tests/unit/s2n_stream_cipher_null_test.c new file mode 100644 index 00000000000..9b557d1b0c3 --- /dev/null +++ b/tests/unit/s2n_stream_cipher_null_test.c @@ -0,0 +1,60 @@ +/* + * 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 "crypto/s2n_stream_cipher_null.c" + +#include "api/s2n.h" +#include "s2n_test.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Test that in and out being the same size succeeds */ + { + uint8_t array[9] = { 0 }; + struct s2n_blob in = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&in, array, 9)); + struct s2n_blob out = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&out, array, 9)); + EXPECT_SUCCESS(s2n_stream_cipher_null_endecrypt(NULL, &in, &out)); + }; + + /* Test that in size > out size fails */ + { + uint8_t array[9] = { 0 }; + struct s2n_blob in = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&in, array, 9)); + struct s2n_blob out = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&out, array, 8)); + EXPECT_FAILURE(s2n_stream_cipher_null_endecrypt(NULL, &in, &out)); + }; + + /* Test that in is copied to out when they are different */ + { + uint8_t in_array[9] = { 0, 1, 2, 3, 4, 5, 6, 7, 8 }; + uint8_t out_array[9] = { 0 }; + struct s2n_blob in = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&in, in_array, 9)); + struct s2n_blob out = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&out, out_array, 9)); + EXPECT_BYTEARRAY_NOT_EQUAL(in_array, out_array, out.size); + EXPECT_SUCCESS(s2n_stream_cipher_null_endecrypt(NULL, &in, &out)); + EXPECT_BYTEARRAY_EQUAL(in_array, out_array, out.size); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_stuffer_base64_test.c b/tests/unit/s2n_stuffer_base64_test.c new file mode 100644 index 00000000000..65104f8c88c --- /dev/null +++ b/tests/unit/s2n_stuffer_base64_test.c @@ -0,0 +1,87 @@ +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "utils/s2n_random.h" + +int main(int argc, char **argv) +{ + char hello_world[] = "Hello world!"; + uint8_t hello_world_base64[] = "SGVsbG8gd29ybGQhAA=="; + struct s2n_stuffer stuffer = { 0 }, known_data = { 0 }, scratch = { 0 }, entropy = { 0 }, mirror = { 0 }; + uint8_t pad[50]; + struct s2n_blob r = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&r, pad, sizeof(pad))); + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Create a 100 byte stuffer */ + EXPECT_SUCCESS(s2n_stuffer_alloc(&stuffer, 1000)); + + /* Write our known data */ + EXPECT_SUCCESS(s2n_stuffer_alloc_ro_from_string(&known_data, hello_world)); + EXPECT_SUCCESS(s2n_stuffer_write_base64(&stuffer, &known_data)); + EXPECT_SUCCESS(s2n_stuffer_free(&known_data)); + + /* Check it against the known output */ + EXPECT_EQUAL(memcmp(stuffer.blob.data, hello_world_base64, strlen((char *) hello_world)), 0); + + /* Check that we can read it again */ + EXPECT_SUCCESS(s2n_stuffer_alloc(&scratch, 50)); + EXPECT_SUCCESS(s2n_stuffer_read_base64(&stuffer, &scratch)); + EXPECT_SUCCESS(memcmp(scratch.blob.data, hello_world, strlen(hello_world))); + + /* Now try with some randomly generated data. Make sure we try each boundary case, + * where size % 3 == 0, 1, 2 + */ + EXPECT_SUCCESS(s2n_stuffer_alloc(&entropy, 50)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&mirror, 50)); + + for (size_t i = entropy.blob.size; i > 0; i--) { + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&entropy)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&mirror)); + + /* Get i bytes of random data */ + r.size = i; + EXPECT_OK(s2n_get_public_random_data(&r)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&entropy, pad, i)); + + /* Write i bytes it, base64 encoded */ + /* Read it back, decoded */ + EXPECT_SUCCESS(s2n_stuffer_write_base64(&stuffer, &entropy)); + + /* Should be (i / 3) * 4 + a carry */ + EXPECT_EQUAL((i / 3) * 4 + ((i % 3) ? 4 : 0), s2n_stuffer_data_available(&stuffer)); + + /* Read it back, decoded */ + EXPECT_SUCCESS(s2n_stuffer_read_base64(&stuffer, &mirror)); + + /* Verify it's the same */ + EXPECT_EQUAL(memcmp(mirror.blob.data, entropy.blob.data, i), 0); + } + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + EXPECT_SUCCESS(s2n_stuffer_free(&scratch)); + EXPECT_SUCCESS(s2n_stuffer_free(&mirror)); + EXPECT_SUCCESS(s2n_stuffer_free(&entropy)); + + END_TEST(); +} diff --git a/tests/unit/s2n_stuffer_hex_test.c b/tests/unit/s2n_stuffer_hex_test.c new file mode 100644 index 00000000000..57758f36d70 --- /dev/null +++ b/tests/unit/s2n_stuffer_hex_test.c @@ -0,0 +1,115 @@ +/* + * 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 "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_random.h" + +int main(int argc, char **argv) +{ + uint8_t pad[100]; + struct s2n_blob b = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&b, pad, sizeof(pad))); + struct s2n_stuffer stuffer = { 0 }; + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Create a 100 byte stuffer */ + EXPECT_SUCCESS(s2n_stuffer_init(&stuffer, &b)); + + /* Try to write 51 1-byte ints bytes */ + for (uint8_t i = 0; i < 50; i++) { + uint8_t value = i * (0xff / 50); + EXPECT_SUCCESS(s2n_stuffer_write_uint8_hex(&stuffer, value)); + } + EXPECT_FAILURE(s2n_stuffer_write_uint8_hex(&stuffer, 1)); + + /* Read those back, and expect the same results */ + for (int8_t i = 0; i < 50; i++) { + uint8_t value = i * (0xff / 50); + EXPECT_SUCCESS(s2n_stuffer_read_uint8_hex(&stuffer, &u8)); + EXPECT_EQUAL(u8, value); + } + EXPECT_FAILURE(s2n_stuffer_read_uint8_hex(&stuffer, &u8)); + + /* Try to write 26 2-byte ints bytes */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + for (uint16_t i = 0; i < 25; i++) { + uint16_t value = i * (0xffff / 25); + EXPECT_SUCCESS(s2n_stuffer_write_uint16_hex(&stuffer, value)); + } + EXPECT_FAILURE(s2n_stuffer_write_uint16_hex(&stuffer, 1)); + + /* Read those back, and expect the same results */ + for (uint16_t i = 0; i < 25; i++) { + uint16_t value = i * (0xffff / 25); + EXPECT_SUCCESS(s2n_stuffer_read_uint16_hex(&stuffer, &u16)); + EXPECT_EQUAL(value, u16); + } + EXPECT_FAILURE(s2n_stuffer_read_uint16_hex(&stuffer, &u16)); + + /* Try to write 13 4-byte ints bytes */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + EXPECT_SUCCESS(s2n_stuffer_init(&stuffer, &b)); + for (uint32_t i = 0; i < 12; i++) { + uint32_t value = i * (0xffffffff / 12); + EXPECT_SUCCESS(s2n_stuffer_write_uint32_hex(&stuffer, value)); + } + EXPECT_FAILURE(s2n_stuffer_write_uint32_hex(&stuffer, 1)); + + /* Read those back, and expect the same results */ + for (uint32_t i = 0; i < 12; i++) { + uint32_t value = i * (0xffffffff / 12); + EXPECT_SUCCESS(s2n_stuffer_read_uint32_hex(&stuffer, &u32)); + EXPECT_EQUAL(value, u32); + } + EXPECT_FAILURE(s2n_stuffer_read_uint32_hex(&stuffer, &u32)); + + /* Try to write 7 8-byte ints bytes */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + for (uint64_t i = 0; i < 6; i++) { + uint64_t value = i * (0xffffffffffffffff / 6); + EXPECT_SUCCESS(s2n_stuffer_write_uint64_hex(&stuffer, value)); + } + EXPECT_FAILURE(s2n_stuffer_write_uint64_hex(&stuffer, 1)); + + /* Read those back, and expect the same results */ + for (uint64_t i = 0; i < 6; i++) { + uint64_t value = i * (0xffffffffffffffff / 6); + EXPECT_SUCCESS(s2n_stuffer_read_uint64_hex(&stuffer, &u64)); + EXPECT_EQUAL(value, u64); + } + EXPECT_FAILURE(s2n_stuffer_read_uint64_hex(&stuffer, &u64)); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + uint8_t hex[] = "f0F0Zz"; + struct s2n_blob text = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&text, hex, strlen((char *) hex))); + EXPECT_SUCCESS(s2n_stuffer_write(&stuffer, &text)); + + EXPECT_SUCCESS(s2n_stuffer_read_uint8_hex(&stuffer, &u8)); + EXPECT_EQUAL(u8, 240); + EXPECT_SUCCESS(s2n_stuffer_read_uint8_hex(&stuffer, &u8)); + EXPECT_EQUAL(u8, 240); + EXPECT_FAILURE(s2n_stuffer_read_uint8_hex(&stuffer, &u8)); + + END_TEST(); +} diff --git a/tests/unit/s2n_stuffer_network_order_test.c b/tests/unit/s2n_stuffer_network_order_test.c new file mode 100644 index 00000000000..fc696f08cbb --- /dev/null +++ b/tests/unit/s2n_stuffer_network_order_test.c @@ -0,0 +1,297 @@ +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "utils/s2n_mem.h" + +#define SIZEOF_UINT24 3 + +int s2n_stuffer_write_network_order(struct s2n_stuffer *stuffer, uint64_t input, uint8_t length); +int s2n_stuffer_write_reservation(struct s2n_stuffer_reservation *reservation, const uint32_t u); + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + struct s2n_stuffer stuffer = { 0 }; + + /* s2n_stuffer_write_network_order */ + { + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + /* Null checks */ + EXPECT_FAILURE(s2n_stuffer_write_network_order(NULL, 0, 1)); + + /* No-op for zero length */ + EXPECT_SUCCESS(s2n_stuffer_write_network_order(&stuffer, 0x00, 0)); + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + uint8_t byte_length; + + /* uint8_t */ + { + byte_length = sizeof(uint8_t); + uint8_t actual_value; + + for (int i = 0; i <= UINT8_MAX; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_network_order(&stuffer, i, byte_length)); + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&stuffer, &actual_value)); + EXPECT_EQUAL(i, actual_value); + } + }; + + /* uint16_t */ + { + byte_length = sizeof(uint16_t); + uint16_t actual_value; + + for (int i = 0; i < UINT16_MAX; i++) { + EXPECT_SUCCESS(s2n_stuffer_write_network_order(&stuffer, i, byte_length)); + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &actual_value)); + EXPECT_EQUAL(i, actual_value); + } + }; + + /* uint24 */ + { + byte_length = 3; + uint32_t actual_value; + uint32_t test_values[] = { 0x000001, 0x0000FF, 0xABCDEF, 0xFFFFFF }; + + for (size_t i = 0; i < s2n_array_len(test_values); i++) { + EXPECT_SUCCESS(s2n_stuffer_write_network_order(&stuffer, test_values[i], byte_length)); + EXPECT_SUCCESS(s2n_stuffer_read_uint24(&stuffer, &actual_value)); + EXPECT_EQUAL(test_values[i], actual_value); + } + + uint16_t prime = 257; + for (uint32_t i = 0; i < (uint32_t) 0xFFFFFF - prime; i += prime) { + EXPECT_SUCCESS(s2n_stuffer_write_network_order(&stuffer, i, byte_length)); + EXPECT_SUCCESS(s2n_stuffer_read_uint24(&stuffer, &actual_value)); + EXPECT_EQUAL(i, actual_value); + } + }; + + /* uint32_t */ + { + byte_length = sizeof(uint32_t); + uint32_t actual_value; + uint32_t test_values[] = { 0x00000001, 0x000000FF, 0xABCDEF12, UINT32_MAX }; + + for (size_t i = 0; i < s2n_array_len(test_values); i++) { + EXPECT_SUCCESS(s2n_stuffer_write_network_order(&stuffer, test_values[i], byte_length)); + EXPECT_SUCCESS(s2n_stuffer_read_uint32(&stuffer, &actual_value)); + EXPECT_EQUAL(test_values[i], actual_value); + } + + uint32_t prime = 65537; + for (uint32_t i = 0; i < UINT32_MAX - prime; i += prime) { + EXPECT_SUCCESS(s2n_stuffer_write_network_order(&stuffer, i, byte_length)); + EXPECT_SUCCESS(s2n_stuffer_read_uint32(&stuffer, &actual_value)); + EXPECT_EQUAL(i, actual_value); + } + }; + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + /* s2n_stuffer_reserve_uint16 */ + { + uint16_t actual_value; + struct s2n_stuffer_reservation reservation = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + /* Null checks */ + EXPECT_FAILURE(s2n_stuffer_reserve_uint16(NULL, &reservation)); + EXPECT_FAILURE(s2n_stuffer_reserve_uint16(&stuffer, NULL)); + + /* Happy case: successfully reserves space for a uint16_t */ + { + /* Write some data. We want to verify it isn't overwritten. */ + uint16_t data_before = 5; + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, data_before)); + + /* Reserve uint16 */ + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&stuffer, &reservation)); + EXPECT_EQUAL(reservation.stuffer, &stuffer); + EXPECT_EQUAL(reservation.write_cursor, sizeof(uint16_t)); + EXPECT_EQUAL(reservation.length, sizeof(uint16_t)); + + /* Reserve uint16 again */ + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&stuffer, &reservation)); + EXPECT_EQUAL(reservation.stuffer, &stuffer); + EXPECT_EQUAL(reservation.write_cursor, sizeof(uint16_t) * 2); + EXPECT_EQUAL(reservation.length, sizeof(uint16_t)); + + /* Write some more data. We want to verify it isn't overwritten. */ + uint16_t data_after = -1; + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, data_after)); + + /* Make sure expected values read back */ + uint8_t actual_bytes[sizeof(uint16_t)], expected_bytes[] = { S2N_WIPE_PATTERN, S2N_WIPE_PATTERN }; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &actual_value)); + EXPECT_EQUAL(actual_value, data_before); + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, actual_bytes, sizeof(uint16_t))); + EXPECT_BYTEARRAY_EQUAL(actual_bytes, expected_bytes, sizeof(uint16_t)); + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, actual_bytes, sizeof(uint16_t))); + EXPECT_BYTEARRAY_EQUAL(actual_bytes, expected_bytes, sizeof(uint16_t)); + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &actual_value)); + EXPECT_EQUAL(actual_value, data_after); + }; + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + /* s2n_stuffer_reserve_uint24 */ + { + uint16_t actual_value; + struct s2n_stuffer_reservation reservation = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + /* Null checks */ + EXPECT_FAILURE(s2n_stuffer_reserve_uint24(NULL, &reservation)); + EXPECT_FAILURE(s2n_stuffer_reserve_uint24(&stuffer, NULL)); + + /* Happy case: successfully reserves space for a uint24_t */ + { + /* Write some data. We want to verify it isn't overwritten. */ + uint16_t data_before = 5; + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, data_before)); + + /* Reserve uint24 */ + EXPECT_SUCCESS(s2n_stuffer_reserve_uint24(&stuffer, &reservation)); + EXPECT_EQUAL(reservation.stuffer, &stuffer); + EXPECT_EQUAL(reservation.write_cursor, sizeof(uint16_t)); + EXPECT_EQUAL(reservation.length, SIZEOF_UINT24); + + /* Reserve uint24 again */ + EXPECT_SUCCESS(s2n_stuffer_reserve_uint24(&stuffer, &reservation)); + EXPECT_EQUAL(reservation.stuffer, &stuffer); + EXPECT_EQUAL(reservation.write_cursor, sizeof(uint16_t) + SIZEOF_UINT24); + EXPECT_EQUAL(reservation.length, SIZEOF_UINT24); + + /* Write some more data. We want to verify it isn't overwritten. */ + uint16_t data_after = -1; + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, data_after)); + + /* Make sure expected values read back */ + uint8_t actual_bytes[SIZEOF_UINT24], expected_bytes[] = { S2N_WIPE_PATTERN, S2N_WIPE_PATTERN, S2N_WIPE_PATTERN }; + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &actual_value)); + EXPECT_EQUAL(actual_value, data_before); + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, actual_bytes, SIZEOF_UINT24)); + EXPECT_BYTEARRAY_EQUAL(actual_bytes, expected_bytes, SIZEOF_UINT24); + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&stuffer, actual_bytes, SIZEOF_UINT24)); + EXPECT_BYTEARRAY_EQUAL(actual_bytes, expected_bytes, SIZEOF_UINT24); + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &actual_value)); + EXPECT_EQUAL(actual_value, data_after); + }; + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + /* s2n_stuffer_write_reservation */ + { + uint16_t actual_value; + struct s2n_stuffer_reservation reservation = { 0 }; + struct s2n_stuffer_reservation other_reservation = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + uint32_t expected_write_cursor = stuffer.write_cursor; + + /* Null checks */ + reservation.stuffer = NULL; + EXPECT_FAILURE(s2n_stuffer_write_reservation(&reservation, 0)); + EXPECT_EQUAL(stuffer.write_cursor, expected_write_cursor); + reservation.stuffer = &stuffer; + + /* Should throw an error if reservation has wrong size */ + reservation.length = sizeof(uint64_t); + EXPECT_FAILURE_WITH_ERRNO(s2n_stuffer_write_reservation(&reservation, 0), S2N_ERR_SAFETY); + EXPECT_EQUAL(stuffer.write_cursor, expected_write_cursor); + reservation.length = sizeof(uint16_t); + + /* Should throw an error if value length does not match reservation length */ + EXPECT_FAILURE_WITH_ERRNO(s2n_stuffer_write_reservation(&reservation, UINT32_MAX), S2N_ERR_SAFETY); + EXPECT_EQUAL(stuffer.write_cursor, expected_write_cursor); + + /* Should throw an error if rewriting would require an invalid stuffer state. + * ( A write cursor being greater than the high water mark is an invalid stuffer state.) */ + reservation.write_cursor = stuffer.high_water_mark + 1; + EXPECT_FAILURE_WITH_ERRNO(s2n_stuffer_write_reservation(&reservation, 0), S2N_ERR_SAFETY); + EXPECT_EQUAL(stuffer.write_cursor, expected_write_cursor); + + /* Happy case: successfully rewrites a uint16_t */ + { + /* Write some data. We want to verify it isn't overwritten. */ + uint16_t data_before = 5; + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, data_before)); + + /* Reserve uint16s */ + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&stuffer, &reservation)); + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&stuffer, &other_reservation)); + + /* Write some more data. We want to verify it isn't overwritten. */ + uint16_t data_after = -1; + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, data_after)); + + /* Rewrite reserved uint16s */ + uint16_t expected_value = 0xabcd; + expected_write_cursor = stuffer.write_cursor; + EXPECT_SUCCESS(s2n_stuffer_write_reservation(&reservation, expected_value)); + EXPECT_EQUAL(stuffer.write_cursor, expected_write_cursor); + EXPECT_SUCCESS(s2n_stuffer_write_reservation(&other_reservation, expected_value)); + EXPECT_EQUAL(stuffer.write_cursor, expected_write_cursor); + + /* Make sure expected values read back */ + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &actual_value)); + EXPECT_EQUAL(actual_value, data_before); + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &actual_value)); + EXPECT_EQUAL(actual_value, expected_value); + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &actual_value)); + EXPECT_EQUAL(actual_value, expected_value); + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &actual_value)); + EXPECT_EQUAL(actual_value, data_after); + }; + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + /* s2n_stuffer_write_vector_size */ + { + uint16_t actual_value; + struct s2n_stuffer_reservation reservation = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 0)); + + /* Happy cases */ + uint16_t test_sizes[] = { 0, 1, 5, 0x88, 0xF0, 0xFF }; + for (size_t i = 0; i < s2n_array_len(test_sizes); i++) { + EXPECT_SUCCESS(s2n_stuffer_reserve_uint16(&stuffer, &reservation)); + + EXPECT_SUCCESS(s2n_stuffer_skip_write(&stuffer, test_sizes[i])); + EXPECT_SUCCESS(s2n_stuffer_write_vector_size(&reservation)); + + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &actual_value)); + EXPECT_EQUAL(actual_value, test_sizes[i]); + + EXPECT_SUCCESS(s2n_stuffer_skip_read(&stuffer, test_sizes[i])); + } + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_stuffer_test.c b/tests/unit/s2n_stuffer_test.c new file mode 100644 index 00000000000..88e10acf54c --- /dev/null +++ b/tests/unit/s2n_stuffer_test.c @@ -0,0 +1,244 @@ +/* + * 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 "stuffer/s2n_stuffer.h" + +#include "api/s2n.h" +#include "s2n_test.h" +#include "utils/s2n_mem.h" + +int main(int argc, char **argv) +{ + uint8_t entropy[2048] = { 0 }; + struct s2n_stuffer stuffer = { 0 }; + uint8_t u8; + uint16_t u16; + uint32_t u32; + uint64_t u64; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Create a 100 byte stuffer */ + EXPECT_SUCCESS(s2n_stuffer_alloc(&stuffer, 100)); + + /* Try to write 101 bytes */ + struct s2n_blob in = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&in, entropy, 101)); + EXPECT_FAILURE(s2n_stuffer_write(&stuffer, &in)); + + /* Try to write 101 1-byte ints bytes */ + for (uint64_t i = 0; i < 100; i++) { + uint64_t value = i * (0xff / 100); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, value)); + } + EXPECT_FAILURE(s2n_stuffer_write_uint8(&stuffer, 1)); + + struct s2n_blob copy_of_bytes = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_extract_blob(&stuffer, ©_of_bytes)); + + /* Read those back, and expect the same results */ + for (uint64_t i = 0; i < 100; i++) { + uint64_t value = i * (0xff / 100); + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&stuffer, &u8)); + EXPECT_EQUAL(value, u8); + EXPECT_EQUAL(copy_of_bytes.data[i], u8); + } + + /* The copy_of_bytes should have the same values */ + for (uint64_t i = 0; i < 100; i++) { + uint64_t value = i * (0xff / 100); + EXPECT_EQUAL(copy_of_bytes.data[i], value); + } + + EXPECT_FAILURE(s2n_stuffer_read_uint8(&stuffer, &u8)); + + /* Try to write 51 2-byte ints bytes */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + for (uint64_t i = 0; i < 50; i++) { + uint64_t value = i * (0xffff / 50); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&stuffer, value)); + } + EXPECT_FAILURE(s2n_stuffer_write_uint16(&stuffer, 1)); + + /* Read those back, and expect the same results */ + for (uint64_t i = 0; i < 50; i++) { + uint64_t value = i * (0xffff / 50); + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&stuffer, &u16)); + EXPECT_EQUAL(value, u16); + } + EXPECT_FAILURE(s2n_stuffer_read_uint16(&stuffer, &u16)); + + /* Try to write 34 3-byte ints bytes */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + for (uint64_t i = 0; i < 33; i++) { + uint64_t value = i * (0xffffff / 33); + EXPECT_SUCCESS(s2n_stuffer_write_uint24(&stuffer, value)); + } + EXPECT_FAILURE(s2n_stuffer_write_uint24(&stuffer, 1)); + + /* Read those back, and expect the same results */ + for (uint64_t i = 0; i < 33; i++) { + uint64_t value = i * (0xffffff / 33); + EXPECT_SUCCESS(s2n_stuffer_read_uint24(&stuffer, &u32)); + EXPECT_EQUAL(value, u32); + } + EXPECT_FAILURE(s2n_stuffer_read_uint16(&stuffer, &u16)); + + /* Try to write 26 4-byte ints bytes */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + for (uint64_t i = 0; i < 25; i++) { + uint64_t value = i * (0xffffffff / 25); + EXPECT_SUCCESS(s2n_stuffer_write_uint32(&stuffer, value)); + } + EXPECT_FAILURE(s2n_stuffer_write_uint32(&stuffer, 1)); + + /* Read those back, and expect the same results */ + for (uint64_t i = 0; i < 25; i++) { + uint64_t value = i * (0xffffffff / 25); + EXPECT_SUCCESS(s2n_stuffer_read_uint32(&stuffer, &u32)); + EXPECT_EQUAL(value, u32); + } + EXPECT_FAILURE(s2n_stuffer_read_uint32(&stuffer, &u32)); + + /* Try to write 13 8-byte ints bytes */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + for (int i = 0; i < 12; i++) { + uint64_t value = i * (0xffffffffffffffff / 12); + EXPECT_SUCCESS(s2n_stuffer_write_uint64(&stuffer, value)); + } + EXPECT_FAILURE(s2n_stuffer_write_uint64(&stuffer, 1)); + + /* Read those back, and expect the same results */ + for (int i = 0; i < 12; i++) { + uint64_t value = i * (0xffffffffffffffff / 12); + EXPECT_SUCCESS(s2n_stuffer_read_uint64(&stuffer, &u64)); + EXPECT_EQUAL(value, u64); + } + EXPECT_FAILURE(s2n_stuffer_read_uint64(&stuffer, &u64)); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + + /* Can still read the copy_of_bytes even once the stuffer has been overwritten and freed */ + for (uint64_t i = 0; i < 100; i++) { + uint64_t value = i * (0xff / 100); + EXPECT_EQUAL(copy_of_bytes.data[i], value); + } + EXPECT_SUCCESS(s2n_free(©_of_bytes)); + +#ifndef NDEBUG + /* Invalid blob should fail init */ + struct s2n_stuffer s1 = { 0 }; + struct s2n_blob b1 = { .data = 0, .size = 101 }; + EXPECT_FAILURE(s2n_stuffer_init(&s1, &b1)); +#endif + + /* Valid empty blob should succeed init */ + struct s2n_stuffer s2 = { 0 }; + struct s2n_blob b2 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&b2, 0, 0)); + EXPECT_SUCCESS(s2n_stuffer_init(&s2, &b2)); + + /* Valid blob should succeed init */ + struct s2n_stuffer s3 = { 0 }; + uint8_t a3[12]; + struct s2n_blob b3 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&b3, a3, sizeof(a3))); + EXPECT_SUCCESS(s2n_stuffer_init(&s3, &b3)); + + /* Null blob should fail init */ + struct s2n_stuffer s4 = { 0 }; + EXPECT_FAILURE(s2n_stuffer_init(&s4, NULL)); + + /* Null stuffer should fail init */ + struct s2n_blob b5 = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&b5, 0, 0)); + EXPECT_FAILURE(s2n_stuffer_init(NULL, &b5)); + + /* Check s2n_stuffer_validate() function */ + EXPECT_ERROR(s2n_stuffer_validate(NULL)); + uint8_t valid_blob_array[12]; + struct s2n_blob blob_valid = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&blob_valid, valid_blob_array, sizeof(valid_blob_array))); + + struct s2n_stuffer stuffer_valid = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&stuffer_valid, &blob_valid)); + EXPECT_OK(s2n_stuffer_validate(&stuffer)); + +#ifndef NDEBUG + struct s2n_blob blob_invalid = { .data = 0, .size = sizeof(valid_blob_array) }; + struct s2n_stuffer stuffer_invalid1 = { .blob = blob_invalid }; + EXPECT_ERROR(s2n_stuffer_validate(&stuffer_invalid1)); + + struct s2n_stuffer stuffer_invalid2 = { .blob = blob_valid, .write_cursor = 13 }; + EXPECT_ERROR(s2n_stuffer_validate(&stuffer_invalid2)); + + struct s2n_stuffer stuffer_invalid3 = { .blob = blob_valid, .read_cursor = 13 }; + EXPECT_ERROR(s2n_stuffer_validate(&stuffer_invalid3)); + + struct s2n_stuffer stuffer_invalid4 = { .blob = blob_valid, .read_cursor = 12, .write_cursor = 1 }; + EXPECT_ERROR(s2n_stuffer_validate(&stuffer_invalid4)); +#endif + + struct s2n_stuffer reserve_test_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_alloc(&reserve_test_stuffer, 1024)); + EXPECT_EQUAL(s2n_stuffer_space_remaining(&reserve_test_stuffer), 1024); + EXPECT_EQUAL(s2n_stuffer_data_available(&reserve_test_stuffer), 0); + EXPECT_FAILURE(s2n_stuffer_reserve_space(&reserve_test_stuffer, 2048)); + EXPECT_EQUAL(s2n_stuffer_space_remaining(&reserve_test_stuffer), 1024); + EXPECT_EQUAL(s2n_stuffer_data_available(&reserve_test_stuffer), 0); + EXPECT_SUCCESS(s2n_stuffer_free(&reserve_test_stuffer)); + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&reserve_test_stuffer, 1024)); + EXPECT_EQUAL(s2n_stuffer_space_remaining(&reserve_test_stuffer), 1024); + EXPECT_EQUAL(s2n_stuffer_data_available(&reserve_test_stuffer), 0); + EXPECT_SUCCESS(s2n_stuffer_reserve_space(&reserve_test_stuffer, 2048)); + EXPECT_EQUAL(s2n_stuffer_space_remaining(&reserve_test_stuffer), 2048); + EXPECT_EQUAL(s2n_stuffer_data_available(&reserve_test_stuffer), 0); + EXPECT_SUCCESS(s2n_stuffer_free(&reserve_test_stuffer)); + + /* Test: s2n_stuffer_init_written */ + { + uint8_t data[] = "hello world"; + uint8_t input[sizeof(data)] = { 0 }; + uint8_t output[sizeof(data)] = { 0 }; + + struct s2n_blob blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&blob, input, sizeof(input))); + + /* Repeat control to show behavior is consistent */ + for (size_t i = 0; i < 3; i++) { + struct s2n_stuffer unwritten_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&unwritten_stuffer, &blob)); + EXPECT_EQUAL(s2n_stuffer_data_available(&unwritten_stuffer), 0); + EXPECT_FAILURE_WITH_ERRNO(s2n_stuffer_read_bytes(&unwritten_stuffer, output, sizeof(output)), + S2N_ERR_STUFFER_OUT_OF_DATA); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&unwritten_stuffer, data, sizeof(data))); + } + + /* Repeat test to show behavior is consistent */ + for (size_t i = 0; i < 3; i++) { + struct s2n_stuffer written_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init_written(&written_stuffer, &blob)); + EXPECT_EQUAL(s2n_stuffer_data_available(&written_stuffer), sizeof(data)); + EXPECT_SUCCESS(s2n_stuffer_read_bytes(&written_stuffer, output, sizeof(output))); + EXPECT_BYTEARRAY_EQUAL(data, output, sizeof(output)); + EXPECT_FAILURE_WITH_ERRNO(s2n_stuffer_write_bytes(&written_stuffer, data, sizeof(data)), + S2N_ERR_STUFFER_IS_FULL); + } + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_stuffer_text_test.c b/tests/unit/s2n_stuffer_text_test.c new file mode 100644 index 00000000000..b8e066f317b --- /dev/null +++ b/tests/unit/s2n_stuffer_text_test.c @@ -0,0 +1,167 @@ +/* + * 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 "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_random.h" + +int main(int argc, char **argv) +{ + char c; + uint32_t skipped = 0; + struct s2n_stuffer stuffer, token; + struct s2n_blob pad_blob, token_blob; + char text[] = " This is some text\r\n\tmore text"; + char fields[] = "one,two,three"; + uint8_t pad[1024]; + char out[1024]; + char tokenpad[6]; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Check whitespace reading */ + { + /* Create a stuffer */ + EXPECT_SUCCESS(s2n_blob_init(&token_blob, (uint8_t *) tokenpad, sizeof(tokenpad))); + EXPECT_SUCCESS(s2n_stuffer_init(&token, &token_blob)); + EXPECT_SUCCESS(s2n_blob_init(&pad_blob, (uint8_t *) pad, sizeof(pad))); + EXPECT_SUCCESS(s2n_stuffer_init(&stuffer, &pad_blob)); + EXPECT_SUCCESS(s2n_stuffer_write_text(&stuffer, text, sizeof(text))); + + /* Skip 4 bytes of whitespace */ + EXPECT_SUCCESS(s2n_stuffer_skip_whitespace(&stuffer, &skipped)); + EXPECT_EQUAL(skipped, 4); + EXPECT_SUCCESS(s2n_stuffer_peek_char(&stuffer, &c)); + EXPECT_EQUAL(c, 'T'); + + /* Read the next 17 chars */ + EXPECT_SUCCESS(s2n_stuffer_read_text(&stuffer, out, 17)); + EXPECT_EQUAL(memcmp(out, "This is some text", 17), 0); + + /* Skip 3 bytes of whitespace */ + EXPECT_SUCCESS(s2n_stuffer_skip_whitespace(&stuffer, &skipped)); + EXPECT_EQUAL(skipped, 3); + + /* Read the next 10 chars (including the terminating zero) */ + EXPECT_SUCCESS(s2n_stuffer_read_text(&stuffer, out, 10)); + EXPECT_EQUAL(memcmp(out, "more text", 10), 0); + + /* Test end of stream behaviour */ + EXPECT_SUCCESS(s2n_stuffer_skip_whitespace(&stuffer, NULL)); + EXPECT_FAILURE(s2n_stuffer_peek_char(&stuffer, &c)); + EXPECT_FAILURE(s2n_stuffer_read_char(&stuffer, &c)); + }; + + /* Check read_until, rewinding, and expecting */ + { + /* Create a stuffer */ + EXPECT_SUCCESS(s2n_blob_init(&token_blob, (uint8_t *) tokenpad, sizeof(tokenpad))); + EXPECT_SUCCESS(s2n_stuffer_init(&token, &token_blob)); + EXPECT_SUCCESS(s2n_blob_init(&pad_blob, (uint8_t *) pad, sizeof(pad))); + EXPECT_SUCCESS(s2n_stuffer_init(&stuffer, &pad_blob)); + EXPECT_SUCCESS(s2n_stuffer_write_text(&stuffer, text, sizeof(text))); + + char target[] = "text"; + char non_target[] = "someStringNotInStuffer"; + EXPECT_SUCCESS(s2n_stuffer_skip_read_until(&stuffer, target)); + EXPECT_EQUAL(stuffer.read_cursor, 21); + EXPECT_SUCCESS(s2n_stuffer_rewind_read(&stuffer, strlen(target))); + EXPECT_EQUAL(stuffer.read_cursor, 17); + EXPECT_SUCCESS(s2n_stuffer_read_expected_str(&stuffer, target)); + EXPECT_EQUAL(stuffer.read_cursor, 21); + EXPECT_SUCCESS(s2n_stuffer_skip_read_until(&stuffer, target)); + EXPECT_EQUAL(stuffer.read_cursor, 33); + EXPECT_FAILURE(s2n_stuffer_rewind_read(&stuffer, 99)); + EXPECT_SUCCESS(s2n_stuffer_reread(&stuffer)); + EXPECT_SUCCESS(s2n_stuffer_skip_read_until(&stuffer, non_target)); + EXPECT_EQUAL(stuffer.read_cursor, stuffer.write_cursor - strlen(non_target) + 1); + }; + + /* Check token reading */ + { + /* Start a new buffer */ + EXPECT_SUCCESS(s2n_stuffer_init(&stuffer, &pad_blob)); + EXPECT_SUCCESS(s2n_stuffer_write_text(&stuffer, fields, strlen(fields))); + + EXPECT_SUCCESS(s2n_stuffer_read_token(&stuffer, &token, ',')); + EXPECT_EQUAL(memcmp("one", token.blob.data, 3), 0); + + EXPECT_SUCCESS(s2n_stuffer_init(&token, &token_blob)); + EXPECT_SUCCESS(s2n_stuffer_read_token(&stuffer, &token, ',')); + EXPECT_EQUAL(memcmp("two", token.blob.data, 3), 0); + + /* Check for end-of-stream termination */ + EXPECT_SUCCESS(s2n_stuffer_init(&token, &token_blob)); + EXPECT_SUCCESS(s2n_stuffer_read_token(&stuffer, &token, ',')); + EXPECT_EQUAL(memcmp("three", token.blob.data, 5), 0); + }; + + /* Check line reading */ + { + struct s2n_blob line_blob = { 0 }; + struct s2n_stuffer lstuffer = { 0 }; + char lf_line[] = "a LF terminated line\n"; + char crlf_line[] = "a CRLF terminated line\r\n"; + char lf_line_trailing_cr[] = "a LF terminated line with trailing CR\n\r\r\r\r\r\r"; + char not_a_line[] = "not a line"; + + EXPECT_SUCCESS(s2n_blob_init(&line_blob, (uint8_t *) lf_line, sizeof(lf_line))); + EXPECT_SUCCESS(s2n_stuffer_init(&lstuffer, &line_blob)); + EXPECT_SUCCESS(s2n_stuffer_write(&lstuffer, &line_blob)); + memset(pad, 0, sizeof(pad)); + EXPECT_SUCCESS(s2n_blob_init(&pad_blob, pad, sizeof(pad))); + EXPECT_SUCCESS(s2n_stuffer_init(&token, &pad_blob)); + EXPECT_SUCCESS(s2n_stuffer_read_line(&lstuffer, &token)); + EXPECT_EQUAL(strlen("a LF terminated line"), s2n_stuffer_data_available(&token)); + EXPECT_SUCCESS(memcmp("a LF terminated line", token.blob.data, s2n_stuffer_data_available(&token))); + + EXPECT_SUCCESS(s2n_blob_init(&line_blob, (uint8_t *) crlf_line, sizeof(crlf_line))); + EXPECT_SUCCESS(s2n_stuffer_init(&lstuffer, &line_blob)); + EXPECT_SUCCESS(s2n_stuffer_write(&lstuffer, &line_blob)); + memset(pad, 0, sizeof(pad)); + EXPECT_SUCCESS(s2n_blob_init(&pad_blob, pad, sizeof(pad))); + EXPECT_SUCCESS(s2n_stuffer_init(&token, &pad_blob)); + EXPECT_SUCCESS(s2n_stuffer_read_line(&lstuffer, &token)); + EXPECT_EQUAL(strlen("a CRLF terminated line"), s2n_stuffer_data_available(&token)); + EXPECT_SUCCESS(memcmp("a CRLF terminated line", token.blob.data, s2n_stuffer_data_available(&token))); + + EXPECT_SUCCESS(s2n_blob_init(&line_blob, (uint8_t *) lf_line_trailing_cr, sizeof(lf_line_trailing_cr))); + EXPECT_SUCCESS(s2n_stuffer_init(&lstuffer, &line_blob)); + EXPECT_SUCCESS(s2n_stuffer_write(&lstuffer, &line_blob)); + memset(pad, 0, sizeof(pad)); + EXPECT_SUCCESS(s2n_blob_init(&pad_blob, pad, sizeof(pad))); + EXPECT_SUCCESS(s2n_stuffer_init(&token, &pad_blob)); + EXPECT_SUCCESS(s2n_stuffer_read_line(&lstuffer, &token)); + EXPECT_EQUAL(strlen("a LF terminated line with trailing CR"), s2n_stuffer_data_available(&token)); + EXPECT_SUCCESS(memcmp("a LF terminated line with trailing CR", token.blob.data, s2n_stuffer_data_available(&token))); + + EXPECT_SUCCESS(s2n_blob_init(&line_blob, (uint8_t *) not_a_line, sizeof(not_a_line))); + EXPECT_SUCCESS(s2n_stuffer_init(&lstuffer, &line_blob)); + EXPECT_SUCCESS(s2n_stuffer_write(&lstuffer, &line_blob)); + memset(pad, 0, sizeof(pad)); + EXPECT_SUCCESS(s2n_blob_init(&pad_blob, pad, sizeof(pad))); + EXPECT_SUCCESS(s2n_stuffer_init(&token, &pad_blob)); + EXPECT_SUCCESS(s2n_stuffer_read_line(&lstuffer, &token)); + EXPECT_EQUAL(sizeof(not_a_line), s2n_stuffer_data_available(&token)); + EXPECT_SUCCESS(memcmp("not a line", token.blob.data, s2n_stuffer_data_available(&token))); + }; + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_testlib_test.c b/tests/unit/s2n_testlib_test.c new file mode 100644 index 00000000000..649ee081242 --- /dev/null +++ b/tests/unit/s2n_testlib_test.c @@ -0,0 +1,47 @@ +/* + * 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 "testlib/s2n_testlib.h" + +#include "s2n_test.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Test that s2n_negotiate_test_server_and_client produces useful errors. + * In the past, we failed to surface errors and instead reported io errors when + * the failed connection's peer couldn't read the next expected message. + * + * We should always report the actual error to allow better debugging of tests. + */ + { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + /* Create nonblocking pipes */ + struct s2n_test_io_pair io_pair; + POSIX_GUARD(s2n_io_pair_init_non_blocking(&io_pair)); + POSIX_GUARD(s2n_connection_set_io_pair(server_conn, &io_pair)); + + /* This should NEVER fail with an error related to blocked IO. */ + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate_test_server_and_client(server_conn, NULL), S2N_ERR_NULL); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_timer_test.c b/tests/unit/s2n_timer_test.c new file mode 100644 index 00000000000..66a174dac66 --- /dev/null +++ b/tests/unit/s2n_timer_test.c @@ -0,0 +1,63 @@ +/* + * 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 "utils/s2n_timer.h" + +#include "s2n_test.h" +#include "tls/s2n_config.h" + +int mock_clock(void *in, uint64_t *out) +{ + *out = *(uint64_t *) in; + + return 0; +} + +int main(int argc, char **argv) +{ + struct s2n_config *config; + struct s2n_timer timer; + uint64_t nanoseconds; + uint64_t mock_time; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_monotonic_clock(config, mock_clock, &mock_time)); + + mock_time = 0; + EXPECT_OK(s2n_timer_start(config, &timer)); + + mock_time = 10; + EXPECT_OK(s2n_timer_reset(config, &timer, &nanoseconds)); + EXPECT_EQUAL(nanoseconds, 10); + + mock_time = 20; + EXPECT_OK(s2n_timer_elapsed(config, &timer, &nanoseconds)); + EXPECT_EQUAL(nanoseconds, 10); + + mock_time = 30; + EXPECT_OK(s2n_timer_reset(config, &timer, &nanoseconds)); + EXPECT_EQUAL(nanoseconds, 20); + + mock_time = 40; + EXPECT_OK(s2n_timer_elapsed(config, &timer, &nanoseconds)); + EXPECT_EQUAL(nanoseconds, 10); + EXPECT_EQUAL(mock_time, 40); /* Work-around for cppcheck complaining that mock_time is never read after being set */ + + EXPECT_SUCCESS(s2n_config_free(config)); + + END_TEST(); +} diff --git a/tests/unit/s2n_tls12_handshake_test.c b/tests/unit/s2n_tls12_handshake_test.c new file mode 100644 index 00000000000..2ba2984d58a --- /dev/null +++ b/tests/unit/s2n_tls12_handshake_test.c @@ -0,0 +1,528 @@ +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "utils/s2n_safety.h" + +/* Just to get access to the static functions / variables we need to test */ +#include "tls/s2n_handshake_io.c" + +static message_type_t invalid_handshake[S2N_MAX_HANDSHAKE_LENGTH] = { 0 }; + +static int expected_handler_called; +static int unexpected_handler_called; + +static int s2n_test_handler(struct s2n_connection *conn) +{ + unexpected_handler_called = 1; + return 0; +} + +static int s2n_test_expected_handler(struct s2n_connection *conn) +{ + expected_handler_called = 1; + return 0; +} + +static int s2n_setup_handler_to_expect(message_type_t expected, uint8_t direction) +{ + for (int i = 0; i < s2n_array_len(state_machine); i++) { + state_machine[i].handler[0] = s2n_test_handler; + state_machine[i].handler[1] = s2n_test_handler; + } + + state_machine[expected].handler[direction] = s2n_test_expected_handler; + + expected_handler_called = 0; + unexpected_handler_called = 0; + + return 0; +} + +static int s2n_test_write_header(struct s2n_stuffer *output, uint8_t record_type, uint8_t message_type) +{ + POSIX_GUARD(s2n_stuffer_write_uint8(output, record_type)); + + /* TLS1.2 protocol version */ + POSIX_GUARD(s2n_stuffer_write_uint8(output, 3)); + POSIX_GUARD(s2n_stuffer_write_uint8(output, 3)); + + if (record_type == TLS_HANDSHAKE) { + /* Total message size */ + POSIX_GUARD(s2n_stuffer_write_uint16(output, 4)); + + POSIX_GUARD(s2n_stuffer_write_uint8(output, message_type)); + + /* Handshake message data size */ + POSIX_GUARD(s2n_stuffer_write_uint24(output, 0)); + return 0; + } + + if (record_type == TLS_CHANGE_CIPHER_SPEC) { + /* Total message size */ + POSIX_GUARD(s2n_stuffer_write_uint16(output, 1)); + + /* change spec is always just 0x01 */ + POSIX_GUARD(s2n_stuffer_write_uint8(output, 1)); + return 0; + } + + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Construct an array of all valid tls1.2 handshake_types */ + uint16_t valid_tls12_handshakes[S2N_HANDSHAKES_COUNT]; + int valid_tls12_handshakes_size = 0; + for (int i = 0; i < S2N_HANDSHAKES_COUNT; i++) { + if (memcmp(handshakes[i], invalid_handshake, S2N_MAX_HANDSHAKE_LENGTH) != 0) { + valid_tls12_handshakes[valid_tls12_handshakes_size] = i; + valid_tls12_handshakes_size++; + } + } + EXPECT_TRUE(valid_tls12_handshakes_size > 0); + EXPECT_TRUE(valid_tls12_handshakes_size < S2N_HANDSHAKES_COUNT); + + /* Test: When using TLS 1.2, use the existing state machine and handshakes */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS12; + EXPECT_EQUAL(ACTIVE_STATE_MACHINE(conn), state_machine); + EXPECT_EQUAL(ACTIVE_HANDSHAKES(conn), handshakes); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.2 server waits for expected CCS messages */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS12; + + for (int i = 0; i < valid_tls12_handshakes_size; i++) { + int handshake = valid_tls12_handshakes[i]; + + conn->handshake.handshake_type = handshake; + + for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + if (handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { + conn->handshake.message_number = j - 1; + + EXPECT_SUCCESS(s2n_advance_message(conn)); + + EXPECT_EQUAL(conn->handshake.message_number, j); + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_CHANGE_CIPHER_SPEC); + + break; + } + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: Client CCS messages always come before Client Finished messages */ + { + for (int i = 0; i < valid_tls12_handshakes_size; i++) { + int handshake = valid_tls12_handshakes[i]; + /* Initial handshake doesn't contain a CCS message */ + if (handshake == INITIAL) { + continue; + } + + bool ccs_encountered = false; + + for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + if (handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { + ccs_encountered = true; + } + + if (handshakes[handshake][j] == CLIENT_FINISHED) { + EXPECT_TRUE(ccs_encountered); + } + } + /* Every valid handshake includes a CCS message */ + EXPECT_TRUE(ccs_encountered); + } + }; + + /* Test: TLS1.2 client waits for expected CCS messages */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS12; + + for (int i = 0; i < valid_tls12_handshakes_size; i++) { + int handshake = valid_tls12_handshakes[i]; + + conn->handshake.handshake_type = handshake; + + for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + if (handshakes[handshake][j] == SERVER_CHANGE_CIPHER_SPEC) { + conn->handshake.message_number = j - 1; + + EXPECT_SUCCESS(s2n_advance_message(conn)); + + EXPECT_EQUAL(conn->handshake.message_number, j); + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), SERVER_CHANGE_CIPHER_SPEC); + + break; + } + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.2 client handles expected server CCS messages + * but errors on unexpected CCS messages */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS12; + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + for (int i = 0; i < valid_tls12_handshakes_size; i++) { + int handshake = valid_tls12_handshakes[i]; + + conn->handshake.handshake_type = handshake; + + for (int j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + EXPECT_SUCCESS(s2n_setup_handler_to_expect(SERVER_CHANGE_CIPHER_SPEC, S2N_CLIENT)); + conn->handshake.message_number = j; + conn->in_status = ENCRYPTED; + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_CHANGE_CIPHER_SPEC, 0)); + + if (handshakes[handshake][j] == SERVER_CHANGE_CIPHER_SPEC) { + EXPECT_SUCCESS(s2n_handshake_read_io(conn)); + EXPECT_TRUE(expected_handler_called); + EXPECT_FALSE(unexpected_handler_called); + } else { + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + EXPECT_FALSE(expected_handler_called); + EXPECT_FALSE(unexpected_handler_called); + } + + EXPECT_SUCCESS(s2n_stuffer_wipe(&input)); + } + } + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.2 server handles expected client CCS messages + * but errors on unexpected CCS messages */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS12; + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + for (int i = 0; i < valid_tls12_handshakes_size; i++) { + int handshake = valid_tls12_handshakes[i]; + + conn->handshake.handshake_type = handshake; + + for (int j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_CHANGE_CIPHER_SPEC, S2N_SERVER)); + conn->handshake.message_number = j; + conn->in_status = ENCRYPTED; + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_CHANGE_CIPHER_SPEC, 0)); + + if (handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { + EXPECT_SUCCESS(s2n_handshake_read_io(conn)); + EXPECT_TRUE(expected_handler_called); + EXPECT_FALSE(unexpected_handler_called); + } else { + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + EXPECT_FALSE(expected_handler_called); + EXPECT_FALSE(unexpected_handler_called); + } + + EXPECT_SUCCESS(s2n_stuffer_wipe(&input)); + } + } + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.2 client can receive a hello request message at any time. */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS12; + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + for (size_t i = 0; i < valid_tls12_handshakes_size; i++) { + uint16_t handshake = valid_tls12_handshakes[i]; + + for (size_t j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + if (handshakes[handshake][j] == APPLICATION_DATA) { + break; + } + + conn->handshake.message_number = j; + conn->in_status = ENCRYPTED; + conn->handshake.handshake_type = handshake; + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_HELLO_REQUEST)); + EXPECT_SUCCESS(s2n_handshake_read_io(conn)); + EXPECT_EQUAL(conn->handshake.message_number, j); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&input)); + } + } + + EXPECT_FALSE(unexpected_handler_called); + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.2 s2n_handshake_read_io should accept only the expected message */ + { + /* TLS1.2 should accept the expected message */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS12; + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + conn->handshake.handshake_type = 0; + conn->handshake.message_number = 0; + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CLIENT_HELLO)); + EXPECT_SUCCESS(s2n_handshake_read_io(conn)); + + EXPECT_EQUAL(conn->handshake.message_number, 1); + EXPECT_FALSE(unexpected_handler_called); + EXPECT_TRUE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS1.2 should error for an unexpected message */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS12; + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + conn->handshake.handshake_type = 0; + conn->handshake.message_number = 0; + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CERTIFICATE)); + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_EQUAL(conn->handshake.message_number, 0); + EXPECT_FALSE(unexpected_handler_called); + EXPECT_FALSE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS1.2 should error for an expected message from the wrong writer */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS12; + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + conn->handshake.handshake_type = 0; + conn->handshake.message_number = 0; + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CLIENT_HELLO)); + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_EQUAL(conn->handshake.message_number, 0); + EXPECT_FALSE(unexpected_handler_called); + EXPECT_FALSE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS1.2 should error for an expected message from the wrong record type */ + { + /* Unfortunately, all our non-handshake record types have a message type of 0, + * and the combination of TLS_HANDSHAKE + "0" is actually a message (TLS_HELLO_REQUEST) + * which can appear at any point in a TLS1.2 handshake. + * + * To test, temporarily modify the actions table. + * We MUST restore this after this test. + */ + uint8_t old_message_type = state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type; + state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type = 1; + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS12; + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + uint8_t server_css_message_number = 2; + conn->handshake.handshake_type = NEGOTIATED; + conn->handshake.message_number = server_css_message_number; + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), SERVER_CHANGE_CIPHER_SPEC); + EXPECT_SUCCESS(s2n_setup_handler_to_expect(SERVER_CHANGE_CIPHER_SPEC, S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, ACTIVE_STATE(conn).message_type)); + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_EQUAL(conn->handshake.message_number, server_css_message_number); + EXPECT_FALSE(unexpected_handler_called); + EXPECT_FALSE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type = old_message_type; + }; + }; + + /* Test: TLS1.2 handshake type name maximum size is set correctly. + * The maximum size is the size of a name with all flags set. */ + { + size_t correct_size = 0; + for (size_t i = 0; i < s2n_array_len(tls12_handshake_type_names); i++) { + correct_size += strlen(tls12_handshake_type_names[i]); + } + if (correct_size > MAX_HANDSHAKE_TYPE_LEN) { + fprintf(stderr, "\nMAX_HANDSHAKE_TYPE_LEN should be at least %lu\n", (unsigned long) correct_size); + FAIL_MSG("MAX_HANDSHAKE_TYPE_LEN wrong for TLS1.2 handshakes"); + } + }; + + /* Test: TLS 1.2 handshake types are all properly printed */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS12; + + conn->handshake.handshake_type = INITIAL; + EXPECT_STRING_EQUAL("INITIAL", s2n_connection_get_handshake_type_name(conn)); + + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; + EXPECT_STRING_EQUAL("NEGOTIATED|FULL_HANDSHAKE", s2n_connection_get_handshake_type_name(conn)); + + const char *all_flags_handshake_type_name = "NEGOTIATED|FULL_HANDSHAKE|CLIENT_AUTH|NO_CLIENT_CERT|" + "TLS12_PERFECT_FORWARD_SECRECY|OCSP_STATUS|WITH_SESSION_TICKET|WITH_NPN"; + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | WITH_SESSION_TICKET | WITH_NPN; + EXPECT_STRING_EQUAL(all_flags_handshake_type_name, s2n_connection_get_handshake_type_name(conn)); + + const char *handshake_type_name; + for (int i = 0; i < valid_tls12_handshakes_size; i++) { + conn->handshake.handshake_type = valid_tls12_handshakes[i]; + + handshake_type_name = s2n_connection_get_handshake_type_name(conn); + + /* The handshake type names must be unique */ + for (int j = 0; j < valid_tls12_handshakes_size; j++) { + conn->handshake.handshake_type = valid_tls12_handshakes[j]; + if (i == j) { + EXPECT_STRING_EQUAL(handshake_type_name, s2n_connection_get_handshake_type_name(conn)); + } else { + EXPECT_STRING_NOT_EQUAL(handshake_type_name, s2n_connection_get_handshake_type_name(conn)); + } + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS 1.2 message types are all properly printed */ + { + uint32_t test_handshake_type = NEGOTIATED | FULL_HANDSHAKE | TLS12_PERFECT_FORWARD_SECRECY | OCSP_STATUS | CLIENT_AUTH | WITH_SESSION_TICKET | WITH_NPN; + const char *expected[] = { "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" }; + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + + conn->handshake.handshake_type = test_handshake_type; + + for (size_t i = 0; i < sizeof(expected) / sizeof(char *); i++) { + conn->handshake.message_number = i; + EXPECT_STRING_EQUAL(expected[i], s2n_connection_get_last_message_name(conn)); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: A WITH_NPN form of every valid, negotiated handshake exists */ + { + uint32_t handshake_type_original, handshake_type_npn; + message_type_t *messages_original, *messages_npn; + + for (size_t i = 0; i < valid_tls12_handshakes_size; i++) { + handshake_type_original = valid_tls12_handshakes[i]; + messages_original = handshakes[handshake_type_original]; + + /* Ignore INITIAL and WITH_NPN handshakes */ + if (!(handshake_type_original & NEGOTIATED) || (handshake_type_original & WITH_NPN)) { + continue; + } + + /* Get the WITH_NPN form of the handshake */ + handshake_type_npn = handshake_type_original | WITH_NPN; + messages_npn = handshakes[handshake_type_npn]; + + for (size_t j = 0, j_npn = 0; j < S2N_MAX_HANDSHAKE_LENGTH && j_npn < S2N_MAX_HANDSHAKE_LENGTH; j++, j_npn++) { + /* The original handshake cannot contain the Next Protocol message */ + EXPECT_NOT_EQUAL(messages_original[j], CLIENT_NPN); + + /* Skip the Next Protocol message in WITH_NPN handshake */ + if (messages_npn[j_npn] == CLIENT_NPN) { + j_npn++; + } + + /* Otherwise the handshakes must be equivalent */ + EXPECT_EQUAL(messages_original[j], messages_npn[j_npn]); + } + } + }; + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_tls13_cert_request_extensions_test.c b/tests/unit/s2n_tls13_cert_request_extensions_test.c new file mode 100644 index 00000000000..755d3065891 --- /dev/null +++ b/tests/unit/s2n_tls13_cert_request_extensions_test.c @@ -0,0 +1,67 @@ +/* + * 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 "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_server_signature_algorithms.h" +#include "tls/extensions/s2n_server_supported_versions.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* Test correct required extension (sig_alg) sent and received */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + conn->actual_protocol_version = S2N_TLS13; + + EXPECT_EQUAL(conn->handshake_params.server_sig_hash_algs.len, 0); + EXPECT_SUCCESS(s2n_tls13_cert_req_send(conn)); + EXPECT_SUCCESS(s2n_tls13_cert_req_recv(conn)); + EXPECT_NOT_EQUAL(conn->handshake_params.server_sig_hash_algs.len, 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Test client fails to parse certificate request with no extensions */ + { + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->actual_protocol_version = S2N_TLS13; + + /* Write 0 length request context https://tools.ietf.org/html/rfc8446#section-4.3.2 */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&client_conn->handshake.io, 0)); + EXPECT_SUCCESS(s2n_extension_list_send(S2N_EXTENSION_LIST_EMPTY, client_conn, &client_conn->handshake.io)); + + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_cert_req_recv(client_conn), S2N_ERR_MISSING_EXTENSION); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + END_TEST(); + + return 0; +} diff --git a/tests/unit/s2n_tls13_cert_request_test.c b/tests/unit/s2n_tls13_cert_request_test.c new file mode 100644 index 00000000000..6d51b8df75c --- /dev/null +++ b/tests/unit/s2n_tls13_cert_request_test.c @@ -0,0 +1,99 @@ +/* + * 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 "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_server_signature_algorithms.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* Test the output of s2n_tls13_cert_req_send() */ + { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS13; + + EXPECT_SUCCESS(s2n_tls13_cert_req_send(server_conn)); + + /* verify output */ + uint8_t request_context_length; + uint16_t extensions_length, extension_size, extension_type; + EXPECT_TRUE(s2n_stuffer_data_available(&server_conn->handshake.io) > 7); + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&server_conn->handshake.io, &request_context_length)); + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&server_conn->handshake.io, &extensions_length)); + EXPECT_EQUAL(s2n_stuffer_data_available(&server_conn->handshake.io), extensions_length); + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&server_conn->handshake.io, &extension_type)); + EXPECT_SUCCESS(s2n_stuffer_read_uint16(&server_conn->handshake.io, &extension_size)); + EXPECT_EQUAL(request_context_length, 0); + EXPECT_EQUAL(extension_type, TLS_EXTENSION_SIGNATURE_ALGORITHMS); + EXPECT_TRUE(extension_size > 0); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + } + + /* Test client can receive and parse certificate request */ + { + struct s2n_connection *server_conn; + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + server_conn->actual_protocol_version = S2N_TLS13; + client_conn->actual_protocol_version = S2N_TLS13; + + EXPECT_SUCCESS(s2n_tls13_cert_req_send(server_conn)); + EXPECT_TRUE(s2n_stuffer_data_available(&server_conn->handshake.io) > 0); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, s2n_stuffer_data_available(&server_conn->handshake.io))); + EXPECT_TRUE(s2n_stuffer_data_available(&client_conn->handshake.io) > 0); + EXPECT_SUCCESS(s2n_tls13_cert_req_recv(client_conn)); + + EXPECT_TRUE(client_conn->handshake_params.server_sig_hash_algs.len > 0); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test request context length other than 0 fails */ + { + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->actual_protocol_version = S2N_TLS13; + + /* Request context correct */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&client_conn->handshake.io, 0)); + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_cert_req_recv(client_conn), S2N_ERR_MISSING_EXTENSION); + + /* Request context incorrect */ + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&client_conn->handshake.io, 2)); + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_cert_req_recv(client_conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + END_TEST(); + + return 0; +} diff --git a/tests/unit/s2n_tls13_cert_verify_test.c b/tests/unit/s2n_tls13_cert_verify_test.c new file mode 100644 index 00000000000..0afbdf67091 --- /dev/null +++ b/tests/unit/s2n_tls13_cert_verify_test.c @@ -0,0 +1,389 @@ +/* + * 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 "crypto/s2n_ecdsa.h" +#include "error/s2n_errno.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "tls/s2n_tls13_certificate_verify.c" + +uint8_t hello[] = "Hello, World!\n"; +uint8_t goodbye[] = "Goodbye, World!\n"; + +struct s2n_tls13_cert_verify_test { + const char *const cert_file; + const char *const key_file; + const struct s2n_signature_scheme *sig_scheme; + const s2n_mode sender_mode; + const s2n_mode verifier_mode; +}; + +const struct s2n_tls13_cert_verify_test test_cases[] = { + { .cert_file = S2N_ECDSA_P384_PKCS1_CERT_CHAIN, .key_file = S2N_ECDSA_P384_PKCS1_KEY, .sig_scheme = &s2n_ecdsa_secp256r1_sha256 }, +#if RSA_PSS_CERTS_SUPPORTED + { .cert_file = S2N_RSA_PSS_2048_SHA256_LEAF_CERT, .key_file = S2N_RSA_PSS_2048_SHA256_LEAF_KEY, .sig_scheme = &s2n_rsa_pss_pss_sha256 }, +#endif +}; + +int run_tests(const struct s2n_tls13_cert_verify_test *test_case, s2n_mode verifier_mode) +{ + const char *cert_file = test_case->cert_file; + const char *key_file = test_case->key_file; + struct s2n_signature_scheme sig_scheme = *test_case->sig_scheme; + + struct s2n_config *config = NULL; + EXPECT_NOT_NULL(config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20200207")); + + /* Successfully send and receive certificate verify */ + { + /* Derive private/public keys and set connection variables */ + struct s2n_stuffer certificate_in = { 0 }, certificate_out = { 0 }; + struct s2n_blob b = { 0 }; + struct s2n_cert_chain_and_key *cert_chain = NULL; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + s2n_pkey_type pkey_type = { 0 }; + + struct s2n_connection *verifying_conn = NULL, *sending_conn = NULL; + EXPECT_NOT_NULL(verifying_conn = s2n_connection_new(verifier_mode)); + verifying_conn->actual_protocol_version = S2N_TLS13; + EXPECT_NOT_NULL(sending_conn = s2n_connection_new(verifier_mode == S2N_CLIENT ? S2N_SERVER : S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_in, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_out, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(cert_chain = s2n_cert_chain_and_key_new()); + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(cert_file, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(key_file, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(cert_chain, cert_chain_pem, private_key_pem)); + + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, cert_chain)); + EXPECT_SUCCESS(s2n_connection_set_config(sending_conn, config)); + sending_conn->handshake_params.our_chain_and_key = cert_chain; + sending_conn->handshake_params.server_cert_sig_scheme = &sig_scheme; + sending_conn->handshake_params.client_cert_sig_scheme = &sig_scheme; + sending_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + sending_conn->actual_protocol_version = S2N_TLS13; + + EXPECT_SUCCESS(s2n_connection_set_config(verifying_conn, config)); + verifying_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + EXPECT_SUCCESS(s2n_blob_init(&b, (uint8_t *) cert_chain_pem, strlen(cert_chain_pem) + 1)); + EXPECT_SUCCESS(s2n_stuffer_write(&certificate_in, &b)); + EXPECT_SUCCESS(s2n_stuffer_certificate_from_pem(&certificate_in, &certificate_out)); + + /* Extract public key from certificate and set it for verifying connection */ + uint32_t available_size = s2n_stuffer_data_available(&certificate_out); + EXPECT_SUCCESS(s2n_blob_init(&b, s2n_stuffer_raw_read(&certificate_out, available_size), available_size)); + if (verifying_conn->mode == S2N_CLIENT) { + EXPECT_SUCCESS(s2n_asn1der_to_public_key_and_type(&verifying_conn->handshake_params.server_public_key, &pkey_type, &b)); + EXPECT_SUCCESS(s2n_pkey_match(&verifying_conn->handshake_params.server_public_key, sending_conn->handshake_params.our_chain_and_key->private_key)); + } else { + EXPECT_SUCCESS(s2n_asn1der_to_public_key_and_type(&verifying_conn->handshake_params.client_public_key, &pkey_type, &b)); + EXPECT_SUCCESS(s2n_pkey_match(&verifying_conn->handshake_params.client_public_key, sending_conn->handshake_params.our_chain_and_key->private_key)); + } + + /* Hash initialization */ + EXPECT_SUCCESS(s2n_hash_init(&sending_conn->handshake.hashes->sha256, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&sending_conn->handshake.hashes->sha256, hello, strlen((char *) hello))); + EXPECT_SUCCESS(s2n_hash_init(&verifying_conn->handshake.hashes->sha256, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&verifying_conn->handshake.hashes->sha256, hello, strlen((char *) hello))); + + /* Send cert verify */ + EXPECT_SUCCESS(s2n_tls13_cert_verify_send(sending_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&sending_conn->handshake.io, &verifying_conn->handshake.io, s2n_stuffer_data_available(&sending_conn->handshake.io))); + + /* Receive and verify cert */ + EXPECT_SUCCESS(s2n_tls13_cert_verify_recv(verifying_conn)); + + /* Repeat the above test successfully */ + EXPECT_SUCCESS(s2n_tls13_cert_verify_send(sending_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&sending_conn->handshake.io, &verifying_conn->handshake.io, s2n_stuffer_data_available(&sending_conn->handshake.io))); + EXPECT_SUCCESS(s2n_tls13_cert_verify_recv(verifying_conn)); + + /* Test fails if cipher suites hash is configured incorrectly */ + verifying_conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + EXPECT_SUCCESS(s2n_tls13_cert_verify_send(sending_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&sending_conn->handshake.io, &verifying_conn->handshake.io, s2n_stuffer_data_available(&sending_conn->handshake.io))); + EXPECT_FAILURE(s2n_tls13_cert_verify_recv(verifying_conn)); + + /* Clean up */ + free(cert_chain_pem); + free(private_key_pem); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(cert_chain)); + EXPECT_SUCCESS(s2n_stuffer_free(&certificate_in)); + EXPECT_SUCCESS(s2n_stuffer_free(&certificate_out)); + EXPECT_SUCCESS(s2n_connection_free(sending_conn)); + EXPECT_SUCCESS(s2n_connection_free(verifying_conn)); + }; + + /* Verifying connection errors with incorrect signed content */ + { + /* Derive private/public keys and set connection variables */ + struct s2n_stuffer certificate_in = { 0 }, certificate_out = { 0 }; + struct s2n_blob b = { 0 }; + struct s2n_cert_chain_and_key *cert_chain = NULL; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + uint64_t bytes_in_hash = 0; + s2n_pkey_type pkey_type = { 0 }; + + EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_in, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_out, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(cert_chain = s2n_cert_chain_and_key_new()); + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(cert_file, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(key_file, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(cert_chain, cert_chain_pem, private_key_pem)); + + struct s2n_connection *verifying_conn = NULL; + EXPECT_NOT_NULL(verifying_conn = s2n_connection_new(verifier_mode)); + verifying_conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, cert_chain)); + EXPECT_SUCCESS(s2n_connection_set_config(verifying_conn, config)); + verifying_conn->handshake_params.our_chain_and_key = cert_chain; + verifying_conn->handshake_params.server_cert_sig_scheme = &sig_scheme; + verifying_conn->handshake_params.client_cert_sig_scheme = &sig_scheme; + verifying_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + EXPECT_SUCCESS(s2n_blob_init(&b, (uint8_t *) cert_chain_pem, strlen(cert_chain_pem) + 1)); + EXPECT_SUCCESS(s2n_stuffer_write(&certificate_in, &b)); + EXPECT_SUCCESS(s2n_stuffer_certificate_from_pem(&certificate_in, &certificate_out)); + + uint32_t available_size = s2n_stuffer_data_available(&certificate_out); + EXPECT_SUCCESS(s2n_blob_init(&b, s2n_stuffer_raw_read(&certificate_out, available_size), available_size)); + if (verifying_conn->mode == S2N_CLIENT) { + EXPECT_SUCCESS(s2n_asn1der_to_public_key_and_type(&verifying_conn->handshake_params.server_public_key, &pkey_type, &b)); + EXPECT_SUCCESS(s2n_pkey_match(&verifying_conn->handshake_params.server_public_key, verifying_conn->handshake_params.our_chain_and_key->private_key)); + } else { + EXPECT_SUCCESS(s2n_asn1der_to_public_key_and_type(&verifying_conn->handshake_params.client_public_key, &pkey_type, &b)); + EXPECT_SUCCESS(s2n_pkey_match(&verifying_conn->handshake_params.client_public_key, verifying_conn->handshake_params.our_chain_and_key->private_key)); + } + /* Initialize send hash with hello */ + EXPECT_SUCCESS(s2n_hash_init(&verifying_conn->handshake.hashes->sha256, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&verifying_conn->handshake.hashes->sha256, hello, strlen((char *) hello))); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&verifying_conn->handshake.hashes->sha256, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 14); + + /* Send and receive cert verify */ + EXPECT_SUCCESS(s2n_tls13_cert_verify_send(verifying_conn)); + + /* Initialize receive hash with goodbye */ + EXPECT_SUCCESS(s2n_hash_init(&verifying_conn->handshake.hashes->sha256, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&verifying_conn->handshake.hashes->sha256, goodbye, strlen((char *) goodbye))); + EXPECT_SUCCESS(s2n_hash_get_currently_in_hash_total(&verifying_conn->handshake.hashes->sha256, &bytes_in_hash)); + EXPECT_EQUAL(bytes_in_hash, 16); + + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_cert_verify_recv(verifying_conn), S2N_ERR_VERIFY_SIGNATURE); + + EXPECT_SUCCESS(s2n_pkey_free(&verifying_conn->handshake_params.client_public_key)); + + /* Clean up */ + free(cert_chain_pem); + free(private_key_pem); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(cert_chain)); + EXPECT_SUCCESS(s2n_stuffer_free(&certificate_in)); + EXPECT_SUCCESS(s2n_stuffer_free(&certificate_out)); + EXPECT_SUCCESS(s2n_connection_free(verifying_conn)); + }; + + /* Verifying connection errors with even 1 bit incorrect */ + { + struct s2n_stuffer certificate_in = { 0 }, certificate_out = { 0 }; + struct s2n_blob b = { 0 }; + struct s2n_cert_chain_and_key *cert_chain = NULL; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + s2n_pkey_type pkey_type = { 0 }; + + EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_in, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_out, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(cert_chain = s2n_cert_chain_and_key_new()); + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(cert_file, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(key_file, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(cert_chain, cert_chain_pem, private_key_pem)); + + struct s2n_connection *verifying_conn = NULL; + EXPECT_NOT_NULL(verifying_conn = s2n_connection_new(verifier_mode)); + verifying_conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, cert_chain)); + EXPECT_SUCCESS(s2n_connection_set_config(verifying_conn, config)); + verifying_conn->handshake_params.our_chain_and_key = cert_chain; + verifying_conn->handshake_params.server_cert_sig_scheme = &sig_scheme; + verifying_conn->handshake_params.client_cert_sig_scheme = &sig_scheme; + verifying_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + EXPECT_SUCCESS(s2n_blob_init(&b, (uint8_t *) cert_chain_pem, strlen(cert_chain_pem) + 1)); + EXPECT_SUCCESS(s2n_stuffer_write(&certificate_in, &b)); + EXPECT_SUCCESS(s2n_stuffer_certificate_from_pem(&certificate_in, &certificate_out)); + + uint32_t available_size = s2n_stuffer_data_available(&certificate_out); + EXPECT_SUCCESS(s2n_blob_init(&b, s2n_stuffer_raw_read(&certificate_out, available_size), available_size)); + if (verifying_conn->mode == S2N_CLIENT) { + EXPECT_SUCCESS(s2n_asn1der_to_public_key_and_type(&verifying_conn->handshake_params.server_public_key, &pkey_type, &b)); + } else { + EXPECT_SUCCESS(s2n_asn1der_to_public_key_and_type(&verifying_conn->handshake_params.client_public_key, &pkey_type, &b)); + } + + /* Initialize send hash with hello */ + EXPECT_SUCCESS(s2n_hash_init(&verifying_conn->handshake.hashes->sha256, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&verifying_conn->handshake.hashes->sha256, hello, strlen((char *) hello))); + + /* Send and receive cert verify */ + EXPECT_SUCCESS(s2n_tls13_cert_verify_send(verifying_conn)); + + /* Initialize receive hash with hello and flip one bit in verifying_conn io buffer */ + EXPECT_SUCCESS(s2n_hash_init(&verifying_conn->handshake.hashes->sha256, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&verifying_conn->handshake.hashes->sha256, hello, strlen((char *) hello))); + EXPECT_TRUE(10 < s2n_stuffer_data_available(&verifying_conn->handshake.io)); + verifying_conn->handshake.io.blob.data[10] ^= 1; + + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_cert_verify_recv(verifying_conn), S2N_ERR_VERIFY_SIGNATURE); + + /* Clean up */ + if (verifying_conn->mode == S2N_CLIENT) { + EXPECT_SUCCESS(s2n_pkey_free(&verifying_conn->handshake_params.server_public_key)); + } else { + EXPECT_SUCCESS(s2n_pkey_free(&verifying_conn->handshake_params.client_public_key)); + } + + free(cert_chain_pem); + free(private_key_pem); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(cert_chain)); + EXPECT_SUCCESS(s2n_stuffer_free(&certificate_in)); + EXPECT_SUCCESS(s2n_stuffer_free(&certificate_out)); + EXPECT_SUCCESS(s2n_connection_free(verifying_conn)); + }; + + /* Verifying connection errors with wrong hash/signature algorithms */ + { + /* Derive private/public keys and set connection variables */ + struct s2n_stuffer certificate_in = { 0 }, certificate_out = { 0 }; + struct s2n_blob b = { 0 }; + struct s2n_cert_chain_and_key *cert_chain = NULL; + char *cert_chain_pem = NULL; + char *private_key_pem = NULL; + s2n_pkey_type pkey_type = { 0 }; + + EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_in, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_stuffer_alloc(&certificate_out, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(cert_chain = s2n_cert_chain_and_key_new()); + EXPECT_NOT_NULL(cert_chain_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key_pem = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(cert_file, cert_chain_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(key_file, private_key_pem, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem(cert_chain, cert_chain_pem, private_key_pem)); + + struct s2n_connection *verifying_conn = NULL; + EXPECT_NOT_NULL(verifying_conn = s2n_connection_new(verifier_mode)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, cert_chain)); + EXPECT_SUCCESS(s2n_connection_set_config(verifying_conn, config)); + verifying_conn->handshake_params.our_chain_and_key = cert_chain; + verifying_conn->handshake_params.server_cert_sig_scheme = &sig_scheme; + verifying_conn->handshake_params.client_cert_sig_scheme = &sig_scheme; + verifying_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + verifying_conn->actual_protocol_version = S2N_TLS13; + + EXPECT_SUCCESS(s2n_blob_init(&b, (uint8_t *) cert_chain_pem, strlen(cert_chain_pem) + 1)); + EXPECT_SUCCESS(s2n_stuffer_write(&certificate_in, &b)); + + EXPECT_SUCCESS(s2n_stuffer_certificate_from_pem(&certificate_in, &certificate_out)); + + uint32_t available_size = s2n_stuffer_data_available(&certificate_out); + EXPECT_SUCCESS(s2n_blob_init(&b, s2n_stuffer_raw_read(&certificate_out, available_size), available_size)); + if (verifying_conn->mode == S2N_CLIENT) { + EXPECT_SUCCESS(s2n_asn1der_to_public_key_and_type(&verifying_conn->handshake_params.server_public_key, &pkey_type, &b)); + } else { + EXPECT_SUCCESS(s2n_asn1der_to_public_key_and_type(&verifying_conn->handshake_params.client_public_key, &pkey_type, &b)); + } + + /* Hash initialization */ + EXPECT_SUCCESS(s2n_hash_init(&verifying_conn->handshake.hashes->sha256, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&verifying_conn->handshake.hashes->sha256, hello, strlen((char *) hello))); + + /* Send and receive with mismatched hash algs */ + EXPECT_SUCCESS(s2n_tls13_cert_verify_send(verifying_conn)); + + /* Reinitialize hash */ + EXPECT_SUCCESS(s2n_hash_init(&verifying_conn->handshake.hashes->sha256, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&verifying_conn->handshake.hashes->sha256, hello, strlen((char *) hello))); + + /* In this case it doesn't matter if we use server_cert_sig_scheme or client_cert_sig_scheme as they are currently equal */ + struct s2n_signature_scheme test_scheme = *verifying_conn->handshake_params.server_cert_sig_scheme; + verifying_conn->handshake_params.server_cert_sig_scheme = &test_scheme; + test_scheme.hash_alg = S2N_HASH_SHA1; + EXPECT_FAILURE(s2n_tls13_cert_read_and_verify_signature(verifying_conn, + verifying_conn->handshake_params.server_cert_sig_scheme)); + + /* send and receive with mismatched signature algs */ + verifying_conn->handshake_params.client_cert_sig_scheme = &test_scheme; + test_scheme.hash_alg = S2N_HASH_SHA256; + test_scheme.sig_alg = S2N_SIGNATURE_ECDSA; + test_scheme.iana_value = 0xFFFF; + + EXPECT_SUCCESS(s2n_hash_init(&verifying_conn->handshake.hashes->sha256, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&verifying_conn->handshake.hashes->sha256, hello, strlen((char *) hello))); + + EXPECT_SUCCESS(s2n_tls13_cert_verify_send(verifying_conn)); + + EXPECT_SUCCESS(s2n_hash_init(&verifying_conn->handshake.hashes->sha256, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_update(&verifying_conn->handshake.hashes->sha256, hello, strlen((char *) hello))); + + EXPECT_FAILURE(s2n_tls13_cert_verify_recv(verifying_conn)); + + /* Clean up */ + if (verifying_conn->mode == S2N_CLIENT) { + EXPECT_SUCCESS(s2n_pkey_free(&verifying_conn->handshake_params.server_public_key)); + } else { + EXPECT_SUCCESS(s2n_pkey_free(&verifying_conn->handshake_params.client_public_key)); + } + + free(cert_chain_pem); + free(private_key_pem); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(cert_chain)); + EXPECT_SUCCESS(s2n_stuffer_free(&certificate_in)); + EXPECT_SUCCESS(s2n_stuffer_free(&certificate_out)); + EXPECT_SUCCESS(s2n_connection_free(verifying_conn)); + }; + + EXPECT_SUCCESS(s2n_config_free(config)); + + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + for (size_t i = 0; i < sizeof(test_cases) / sizeof(struct s2n_tls13_cert_verify_test); i++) { + /* Run all tests for server sending and client receiving/verifying cert_verify message */ + run_tests(&test_cases[i], S2N_CLIENT); + + /* Run all tests for client sending and server receiving/verifying cert_verify message */ + run_tests(&test_cases[i], S2N_SERVER); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_client_finished_test.c b/tests/unit/s2n_tls13_client_finished_test.c new file mode 100644 index 00000000000..dfdba69dde6 --- /dev/null +++ b/tests/unit/s2n_tls13_client_finished_test.c @@ -0,0 +1,178 @@ +/* + * 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 "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_safety.h" + +static int reset_stuffers(struct s2n_stuffer *reread, struct s2n_stuffer *wipe) +{ + POSIX_GUARD(s2n_stuffer_reread(reread)); + POSIX_GUARD(s2n_stuffer_wipe(wipe)); + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Test s2n_tls13_client_finished_send and s2n_tls13_client_finished_recv */ + { + struct s2n_cipher_suite cipher_suites[] = { + s2n_tls13_aes_128_gcm_sha256, + s2n_tls13_aes_256_gcm_sha384, + s2n_tls13_chacha20_poly1305_sha256 + }; + + int hash_sizes[] = { + 32, 48, 32 + }; + + for (int i = 0; i < 3; i++) { + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + client_conn->actual_protocol_version = S2N_TLS13; + client_conn->secure->cipher_suite = &cipher_suites[i]; + + int hash_size = hash_sizes[i]; + + EXPECT_SUCCESS(s2n_tls13_client_finished_send(client_conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(&client_conn->handshake.io), hash_size); + + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_CLIENT)); + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->secure->cipher_suite = &cipher_suites[i]; + + EXPECT_SUCCESS(reset_stuffers(&client_conn->handshake.io, &server_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, hash_size)); + EXPECT_SUCCESS(s2n_tls13_client_finished_recv(server_conn)); + + /* Expect failure if verify has a missing byte */ + EXPECT_SUCCESS(reset_stuffers(&client_conn->handshake.io, &server_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, hash_size - 1)); + EXPECT_FAILURE(s2n_tls13_client_finished_recv(server_conn)); + + /* Expect failure if verify have additional byte */ + EXPECT_SUCCESS(reset_stuffers(&client_conn->handshake.io, &server_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, hash_size)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&server_conn->handshake.io, 0)); + EXPECT_FAILURE(s2n_tls13_client_finished_recv(server_conn)); + + /* Expect failure if verify on wire is modified by 1 bit */ + EXPECT_SUCCESS(reset_stuffers(&client_conn->handshake.io, &server_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, hash_size)); + server_conn->handshake.io.blob.data[0] ^= 1; + EXPECT_FAILURE(s2n_tls13_client_finished_recv(server_conn)); + + /* Expect failure if finished key differs */ + EXPECT_SUCCESS(reset_stuffers(&client_conn->handshake.io, &server_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, hash_size)); + server_conn->handshake.client_finished[0] ^= 1; + EXPECT_FAILURE(s2n_tls13_client_finished_recv(server_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + } + }; + + /* Test that they can only run in TLS 1.3 mode */ + { + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + + EXPECT_FAILURE(s2n_tls13_client_finished_send(client_conn)); + + /* now with TLS 1.3, server finished send can run */ + client_conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_tls13_client_finished_send(client_conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(&client_conn->handshake.io), 48); + + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_CLIENT)); + + server_conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, 48)); + EXPECT_FAILURE(s2n_tls13_client_finished_recv(server_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* Test for failure cases if cipher suites are incompatible */ + { + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + client_conn->actual_protocol_version = S2N_TLS13; + client_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + EXPECT_SUCCESS(s2n_tls13_client_finished_send(client_conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(&client_conn->handshake.io), 32); + + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_CLIENT)); + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + + EXPECT_SUCCESS(reset_stuffers(&client_conn->handshake.io, &server_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, 32)); + EXPECT_FAILURE(s2n_tls13_client_finished_recv(server_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* Test for failure cases when finished secret key differs */ + { + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + client_conn->actual_protocol_version = S2N_TLS13; + client_conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + + EXPECT_SUCCESS(s2n_tls13_client_finished_send(client_conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(&client_conn->handshake.io), 48); + + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_CLIENT)); + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + + for (int i = 0; i < 48; i++) { + EXPECT_SUCCESS(reset_stuffers(&client_conn->handshake.io, &server_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, 48)); + + /* flip a bit to test failure */ + server_conn->handshake.client_finished[i] ^= 1; + EXPECT_FAILURE(s2n_tls13_client_finished_recv(server_conn)); + + /* flip the bit back */ + server_conn->handshake.client_finished[i] ^= 1; + } + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_tls13_compute_shared_secret_test.c b/tests/unit/s2n_tls13_compute_shared_secret_test.c new file mode 100644 index 00000000000..69e4e97d13c --- /dev/null +++ b/tests/unit/s2n_tls13_compute_shared_secret_test.c @@ -0,0 +1,161 @@ +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13_handshake.c" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + struct s2n_cert_chain_and_key *cert_chain = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&cert_chain, + S2N_ECDSA_P384_PKCS1_CERT_CHAIN, S2N_ECDSA_P384_PKCS1_KEY)); + EXPECT_NOT_NULL(cert_chain); + + struct s2n_config *config = s2n_config_new(); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, cert_chain)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + + struct s2n_connection *client_conn; + + /* This test ensures that if the server did not send a keyshare extension in the server hello function, + * a null pointer error is correctly thrown. + */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + client_conn->actual_protocol_version = S2N_TLS13; + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(client_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + /* Select curve and generate key for client */ + client_conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_conn->kex_params.client_ecc_evp_params)); + /* Recreating conditions where negotiated curve was not set */ + struct s2n_ecc_evp_params missing_params = { NULL, NULL }; + client_conn->kex_params.server_ecc_evp_params = missing_params; + DEFER_CLEANUP(struct s2n_blob client_shared_secret = { 0 }, s2n_free); + /* Compute fails because server's curve and public key are missing. */ + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_compute_shared_secret(client_conn, &client_shared_secret), S2N_ERR_NULL); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* This test ensures that if a server sent a keyshare extension without a public key, a null pointer + * error is correctly thrown. + */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + client_conn->actual_protocol_version = S2N_TLS13; + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(client_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + /* Select curve and generate key for client */ + client_conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_conn->kex_params.client_ecc_evp_params)); + + /* Set curve server sent in server hello */ + client_conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + + DEFER_CLEANUP(struct s2n_blob client_shared_secret = { 0 }, s2n_free); + /* Compute fails because server's public key is missing */ + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_compute_shared_secret(client_conn, &client_shared_secret), S2N_ERR_NULL); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* This test ensures that if a server sent a keyshare extension with a public key and curve, a client can + * generate a shared secret from it. + */ + { + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + const struct s2n_ecc_preferences *ecc_pref = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(client_conn, &ecc_pref)); + EXPECT_NOT_NULL(ecc_pref); + + client_conn->actual_protocol_version = S2N_TLS13; + + /* Select curve and generate key for client */ + client_conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_conn->kex_params.client_ecc_evp_params)); + + /* Set curve server sent in server hello */ + client_conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_pref->ecc_curves[0]; + + /* Generate public key server sent in server hello */ + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&client_conn->kex_params.server_ecc_evp_params)); + DEFER_CLEANUP(struct s2n_blob client_shared_secret = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_tls13_compute_shared_secret(client_conn, &client_shared_secret)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* This test ensures that the shared secrets computed by a client and server after negotiation match */ + { + client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + EXPECT_SUCCESS(s2n_client_hello_send(client_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&client_conn->handshake.io, &server_conn->handshake.io, + s2n_stuffer_data_available(&client_conn->handshake.io))); + EXPECT_SUCCESS(s2n_client_hello_recv(server_conn)); + + EXPECT_SUCCESS(s2n_server_hello_send(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, + s2n_stuffer_data_available(&server_conn->handshake.io))); + EXPECT_SUCCESS(s2n_server_hello_recv(client_conn)); + + DEFER_CLEANUP(struct s2n_blob client_shared_secret = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_tls13_compute_shared_secret(client_conn, &client_shared_secret)); + EXPECT_TRUE(client_shared_secret.size > 0); + + DEFER_CLEANUP(struct s2n_blob server_shared_secret = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_tls13_compute_shared_secret(server_conn, &server_shared_secret)); + EXPECT_TRUE(server_shared_secret.size > 0); + + S2N_BLOB_EXPECT_EQUAL(server_shared_secret, client_shared_secret); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(cert_chain)); + EXPECT_SUCCESS(s2n_config_free(config)); + + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_handshake_early_data_test.c b/tests/unit/s2n_tls13_handshake_early_data_test.c new file mode 100644 index 00000000000..e0203553d3a --- /dev/null +++ b/tests/unit/s2n_tls13_handshake_early_data_test.c @@ -0,0 +1,356 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_record.h" +#include "tls/s2n_tls13_handshake.h" +#include "utils/s2n_array.h" +#include "utils/s2n_mem.h" + +/* Just to get access to the static functions / variables we need to test */ +#include "tls/s2n_handshake_io.c" +#include "tls/s2n_handshake_transcript.c" +#include "tls/s2n_tls13_handshake.c" + +#define S2N_SECRET_TYPE_COUNT 5 + +const uint8_t empty_secret[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; +message_type_t empty_handshake[S2N_MAX_HANDSHAKE_LENGTH] = { 0 }; + +int main() +{ + BEGIN_TEST(); + + /* Test early data encryption */ + { + /** + *= https://tools.ietf.org/rfc/rfc8448#section-3 + *= type=test + *# {server} generate resumption secret "tls13 resumption": + *# + *# PRK (32 octets): 7d f2 35 f2 03 1d 2a 05 12 87 d0 2b 02 41 b0 bf + *# da f8 6c c8 56 23 1f 2d 5a ba 46 c4 34 ec 19 6c + *# + *# hash (2 octets): 00 00 + *# + *# info (22 octets): 00 20 10 74 6c 73 31 33 20 72 65 73 75 6d 70 74 + *# 69 6f 6e 02 00 00 + *# + *# expanded (32 octets): 4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c + *# a4 c5 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3 + */ + S2N_BLOB_FROM_HEX(psk_secret, "4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c \ + a4 c5 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3"); + + /** + *= https://tools.ietf.org/rfc/rfc8448#section-3 + *= type=test + *# {server} construct a NewSessionTicket handshake message: + *# + *# NewSessionTicket (205 octets): 04 00 00 c9 00 00 00 1e fa d6 aa + *# c5 02 00 00 00 b2 2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00 00 00 + *# 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70 ad 3c + *# 49 88 83 c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9 82 11 + *# 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6 1d 28 + *# 27 db 27 9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0 37 25 + *# a6 a4 da fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5 90 6c + *# 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5 ae a6 + *# 17 64 6f ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d e6 50 + *# 5e 5b fb c3 88 e9 33 43 69 40 93 93 4a e4 d3 57 00 08 00 2a 00 + *# 04 00 00 04 00 + */ + /* Skip past the message type, message size, ticket lifetime, + * ticket age add, nonce, and ticket size: + * 04 00 00 c9 00 00 00 1e fa d6 aa + * c5 02 00 00 00 b2 + */ + S2N_BLOB_FROM_HEX(psk_identity, + "2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00 00 00 \ + 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70 ad 3c \ + 49 88 83 c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9 82 11 \ + 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6 1d 28 \ + 27 db 27 9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0 37 25 \ + a6 a4 da fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5 90 6c \ + 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5 ae a6 \ + 17 64 6f ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d e6 50 \ + 5e 5b fb c3 88 e9 33 43 69 40 93 93 4a e4 d3 57"); + /* Skip past the total extensions size, early data extension type, + * and early data extension size: 00 08 00 2a 00 + * 04 + */ + const uint32_t max_early_data = 0x00000400; + + /** + *= https://tools.ietf.org/rfc/rfc8448#section-4 + *= type=test + *# {client} send handshake record: + *# + *# payload (512 octets): 01 00 01 fc 03 03 1b c3 ce b6 bb e3 9c ff + *# 93 83 55 b5 a5 0a db 6d b2 1b 7a 6a f6 49 d7 b4 bc 41 9d 78 76 + *# 48 7d 95 00 00 06 13 01 13 03 13 02 01 00 01 cd 00 00 00 0b 00 + *# 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00 12 + *# 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 33 00 + *# 26 00 24 00 1d 00 20 e4 ff b6 8a c0 5f 8d 96 c9 9d a2 66 98 34 + *# 6c 6b e1 64 82 ba dd da fe 05 1a 66 b4 f1 8d 66 8f 0b 00 2a 00 + *# 00 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03 02 + *# 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06 02 + *# 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 00 15 00 57 00 00 00 + *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 29 00 dd 00 b8 00 b2 2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00 + *# 00 00 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70 + *# ad 3c 49 88 83 c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9 + *# 82 11 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6 + *# 1d 28 27 db 27 9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0 + *# 37 25 a6 a4 da fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5 + *# 90 6c 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5 + *# ae a6 17 64 6f ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d + *# e6 50 5e 5b fb c3 88 e9 33 43 69 40 93 93 4a e4 d3 57 fa d6 aa + *# cb 00 21 20 3a dd 4f b2 d8 fd f8 22 a0 ca 3c f7 67 8e f5 e8 8d + *# ae 99 01 41 c5 92 4d 57 bb 6f a3 1b 9e 5f 9d + */ + S2N_BLOB_FROM_HEX(client_hello_msg, + "01 00 01 fc 03 03 1b c3 ce b6 bb e3 9c ff \ + 93 83 55 b5 a5 0a db 6d b2 1b 7a 6a f6 49 d7 b4 bc 41 9d 78 76 \ + 48 7d 95 00 00 06 13 01 13 03 13 02 01 00 01 cd 00 00 00 0b 00 \ + 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 00 00 0a 00 14 00 12 \ + 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 01 03 01 04 00 33 00 \ + 26 00 24 00 1d 00 20 e4 ff b6 8a c0 5f 8d 96 c9 9d a2 66 98 34 \ + 6c 6b e1 64 82 ba dd da fe 05 1a 66 b4 f1 8d 66 8f 0b 00 2a 00 \ + 00 00 2b 00 03 02 03 04 00 0d 00 20 00 1e 04 03 05 03 06 03 02 \ + 03 08 04 08 05 08 06 04 01 05 01 06 01 02 01 04 02 05 02 06 02 \ + 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 00 15 00 57 00 00 00 \ + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 29 00 dd 00 b8 00 b2 2c 03 5d 82 93 59 ee 5f f7 af 4e c9 00 \ + 00 00 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb 33 fa 90 bf 1b 00 70 \ + ad 3c 49 88 83 c9 36 7c 09 a2 be 78 5a bc 55 cd 22 60 97 a3 a9 \ + 82 11 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 6d 64 e8 61 be 7f d6 \ + 1d 28 27 db 27 9c ce 14 50 77 d4 54 a3 66 4d 4e 6d a4 d2 9e e0 \ + 37 25 a6 a4 da fc d0 fc 67 d2 ae a7 05 29 51 3e 3d a2 67 7f a5 \ + 90 6c 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 14 70 f9 fb f2 97 b5 \ + ae a6 17 64 6f ac 5c 03 27 2e 97 07 27 c6 21 a7 91 41 ef 5f 7d \ + e6 50 5e 5b fb c3 88 e9 33 43 69 40 93 93 4a e4 d3 57 fa d6 aa \ + cb 00 21 20 3a dd 4f b2 d8 fd f8 22 a0 ca 3c f7 67 8e f5 e8 8d \ + ae 99 01 41 c5 92 4d 57 bb 6f a3 1b 9e 5f 9d") + /** + *= https://tools.ietf.org/rfc/rfc8448#section-4 + *= type=test + *# + *# complete record (517 octets): 16 03 01 02 00 01 00 01 fc 03 03 1b + *# c3 ce b6 bb e3 9c ff 93 83 55 b5 a5 0a db 6d b2 1b 7a 6a f6 49 + *# d7 b4 bc 41 9d 78 76 48 7d 95 00 00 06 13 01 13 03 13 02 01 00 + *# 01 cd 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 + *# 00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 + *# 01 03 01 04 00 33 00 26 00 24 00 1d 00 20 e4 ff b6 8a c0 5f 8d + *# 96 c9 9d a2 66 98 34 6c 6b e1 64 82 ba dd da fe 05 1a 66 b4 f1 + *# 8d 66 8f 0b 00 2a 00 00 00 2b 00 03 02 03 04 00 0d 00 20 00 1e + *# 04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 + *# 01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 + *# 00 15 00 57 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 29 00 dd 00 b8 00 b2 2c 03 5d 82 93 59 + *# ee 5f f7 af 4e c9 00 00 00 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb + *# 33 fa 90 bf 1b 00 70 ad 3c 49 88 83 c9 36 7c 09 a2 be 78 5a bc + *# 55 cd 22 60 97 a3 a9 82 11 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 + *# 6d 64 e8 61 be 7f d6 1d 28 27 db 27 9c ce 14 50 77 d4 54 a3 66 + *# 4d 4e 6d a4 d2 9e e0 37 25 a6 a4 da fc d0 fc 67 d2 ae a7 05 29 + *# 51 3e 3d a2 67 7f a5 90 6c 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 + *# 14 70 f9 fb f2 97 b5 ae a6 17 64 6f ac 5c 03 27 2e 97 07 27 c6 + *# 21 a7 91 41 ef 5f 7d e6 50 5e 5b fb c3 88 e9 33 43 69 40 93 93 + *# 4a e4 d3 57 fa d6 aa cb 00 21 20 3a dd 4f b2 d8 fd f8 22 a0 ca + *# 3c f7 67 8e f5 e8 8d ae 99 01 41 c5 92 4d 57 bb 6f a3 1b 9e 5f + *# 9d + */ + S2N_BLOB_FROM_HEX(ch_record, "16 03 01 02 00 01 00 01 fc 03 03 1b \ + c3 ce b6 bb e3 9c ff 93 83 55 b5 a5 0a db 6d b2 1b 7a 6a f6 49 \ + d7 b4 bc 41 9d 78 76 48 7d 95 00 00 06 13 01 13 03 13 02 01 00 \ + 01 cd 00 00 00 0b 00 09 00 00 06 73 65 72 76 65 72 ff 01 00 01 \ + 00 00 0a 00 14 00 12 00 1d 00 17 00 18 00 19 01 00 01 01 01 02 \ + 01 03 01 04 00 33 00 26 00 24 00 1d 00 20 e4 ff b6 8a c0 5f 8d \ + 96 c9 9d a2 66 98 34 6c 6b e1 64 82 ba dd da fe 05 1a 66 b4 f1 \ + 8d 66 8f 0b 00 2a 00 00 00 2b 00 03 02 03 04 00 0d 00 20 00 1e \ + 04 03 05 03 06 03 02 03 08 04 08 05 08 06 04 01 05 01 06 01 02 \ + 01 04 02 05 02 06 02 02 02 00 2d 00 02 01 01 00 1c 00 02 40 01 \ + 00 15 00 57 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 \ + 00 00 00 00 00 00 00 00 29 00 dd 00 b8 00 b2 2c 03 5d 82 93 59 \ + ee 5f f7 af 4e c9 00 00 00 00 26 2a 64 94 dc 48 6d 2c 8a 34 cb \ + 33 fa 90 bf 1b 00 70 ad 3c 49 88 83 c9 36 7c 09 a2 be 78 5a bc \ + 55 cd 22 60 97 a3 a9 82 11 72 83 f8 2a 03 a1 43 ef d3 ff 5d d3 \ + 6d 64 e8 61 be 7f d6 1d 28 27 db 27 9c ce 14 50 77 d4 54 a3 66 \ + 4d 4e 6d a4 d2 9e e0 37 25 a6 a4 da fc d0 fc 67 d2 ae a7 05 29 \ + 51 3e 3d a2 67 7f a5 90 6c 5b 3f 7d 8f 92 f2 28 bd a4 0d da 72 \ + 14 70 f9 fb f2 97 b5 ae a6 17 64 6f ac 5c 03 27 2e 97 07 27 c6 \ + 21 a7 91 41 ef 5f 7d e6 50 5e 5b fb c3 88 e9 33 43 69 40 93 93 \ + 4a e4 d3 57 fa d6 aa cb 00 21 20 3a dd 4f b2 d8 fd f8 22 a0 ca \ + 3c f7 67 8e f5 e8 8d ae 99 01 41 c5 92 4d 57 bb 6f a3 1b 9e 5f \ + 9d"); + + /** + *= https://tools.ietf.org/rfc/rfc8448#section-4 + *= type=test + *# {client} extract secret "early": + *# + *# salt: 0 (all zero octets) + *# + *# IKM (32 octets): 4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c a4 c5 + *# 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3 + *# + *# secret (32 octets): 9b 21 88 e9 b2 fc 6d 64 d7 1d c3 29 90 0e 20 + *# bb 41 91 50 00 f6 78 aa 83 9c bb 79 7c b7 d8 33 2c + */ + S2N_BLOB_FROM_HEX(early_secret, + "9b 21 88 e9 b2 fc 6d 64 d7 1d c3 29 90 0e 20 \ + bb 41 91 50 00 f6 78 aa 83 9c bb 79 7c b7 d8 33 2c"); + + /** + *= https://tools.ietf.org/rfc/rfc8448#section-4 + *= type=test + *# {client} derive write traffic keys for early application data: + *# + *# PRK (32 octets): 3f bb e6 a6 0d eb 66 c3 0a 32 79 5a ba 0e ff 7e + *# aa 10 10 55 86 e7 be 5c 09 67 8d 63 b6 ca ab 62 + *# + *# key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00 + *# + *# key expanded (16 octets): 92 02 05 a5 b7 bf 21 15 e6 fc 5c 29 42 + *# 83 4f 54 + *# + *# iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00 + *# + *# iv expanded (12 octets): 6d 47 5f 09 93 c8 e5 64 61 0d b2 b9 + */ + S2N_BLOB_FROM_HEX(iv, "6d 47 5f 09 93 c8 e5 64 61 0d b2 b9"); + + /** + *= https://tools.ietf.org/rfc/rfc8448#section-4 + *= type=test + *# {client} send application_data record: + *# + *# payload (6 octets): 41 42 43 44 45 46 + */ + S2N_BLOB_FROM_HEX(payload, "41 42 43 44 45 46"); + /** + *= https://tools.ietf.org/rfc/rfc8448#section-4 + *= type=test + *# + *# complete record (28 octets): 17 03 03 00 17 ab 1d f4 20 e7 5c 45 + *# 7a 7c c5 d2 84 4f 76 d5 ae e4 b4 ed bf 04 9b e0 + */ + S2N_BLOB_FROM_HEX(complete_record, "17 03 03 00 17 ab 1d f4 20 e7 5c 45 \ + 7a 7c c5 d2 84 4f 76 d5 ae e4 b4 ed bf 04 9b e0"); + + /* Test client early data encryption against known client outputs */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + client_conn->actual_protocol_version = S2N_TLS13; + client_conn->server_protocol_version = S2N_TLS13; + client_conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_pushback(&client_conn->psk_params.psk_list, (void **) &psk)); + psk->hmac_alg = S2N_HMAC_SHA256; + EXPECT_SUCCESS(s2n_psk_configure_early_data(psk, max_early_data, 0x13, 0x01)); + client_conn->secure->cipher_suite = psk->early_data_config.cipher_suite; + + /* Rewrite early secret with known early secret. */ + EXPECT_SUCCESS(s2n_dup(&early_secret, &psk->early_secret)); + + /* Rewrite hashes with known ClientHello */ + EXPECT_SUCCESS(s2n_conn_update_handshake_hashes(client_conn, &client_hello_msg)); + + EXPECT_OK(s2n_tls13_secrets_update(client_conn)); + EXPECT_OK(s2n_tls13_key_schedule_update(client_conn)); + + /* Check early secret secret set correctly */ + EXPECT_EQUAL(client_conn->secrets.extract_secret_type, S2N_EARLY_SECRET); + EXPECT_BYTEARRAY_EQUAL(client_conn->secrets.version.tls13.extract_secret, early_secret.data, early_secret.size); + + /* Check IV calculated correctly */ + EXPECT_BYTEARRAY_EQUAL(client_conn->secure->client_implicit_iv, iv.data, iv.size); + + /* Check payload encrypted correctly */ + EXPECT_OK(s2n_record_write(client_conn, TLS_APPLICATION_DATA, &payload)); + EXPECT_EQUAL(s2n_stuffer_data_available(&client_conn->out), complete_record.size); + EXPECT_BYTEARRAY_EQUAL(client_conn->out.blob.data, complete_record.data, complete_record.size); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + +/* The known ClientHello uses the x25519 curve, + * which the S2N server won't accept if the EVP APIs are not supported */ +#if EVP_APIS_SUPPORTED + /* Test server early data encryption with known client inputs */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_early_data_expected(server_conn)); + EXPECT_SUCCESS(s2n_connection_set_server_max_early_data_size(server_conn, max_early_data)); + + DEFER_CLEANUP(struct s2n_psk *psk = s2n_external_psk_new(), s2n_psk_free); + psk->type = S2N_PSK_TYPE_RESUMPTION; + EXPECT_SUCCESS(s2n_psk_set_identity(psk, psk_identity.data, psk_identity.size)); + EXPECT_SUCCESS(s2n_psk_set_secret(psk, psk_secret.data, psk_secret.size)); + EXPECT_SUCCESS(s2n_psk_configure_early_data(psk, max_early_data, 0x13, 0x01)); + EXPECT_SUCCESS(s2n_connection_append_psk(server_conn, psk)); + /* We need to explicitly set the psk_params type to skip our stateless session resumption recv + * code because the handshake traces we're using are meant for stateful session resumption. + * TODO: https://github.com/aws/s2n-tls/issues/2742 */ + server_conn->psk_params.type = S2N_PSK_TYPE_EXTERNAL; + + DEFER_CLEANUP(struct s2n_stuffer input = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer output = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, S2N_DEFAULT_RECORD_LENGTH)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, S2N_DEFAULT_RECORD_LENGTH)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, server_conn)); + + EXPECT_SUCCESS(s2n_stuffer_write(&input, &ch_record)); + + s2n_blocked_status blocked = 0; + EXPECT_FAILURE_WITH_ERRNO(s2n_negotiate(server_conn, &blocked), S2N_ERR_IO_BLOCKED); + EXPECT_EQUAL(blocked, S2N_BLOCKED_ON_READ); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), END_OF_EARLY_DATA); + EXPECT_EQUAL(s2n_stuffer_data_available(&input), 0); + + EXPECT_SUCCESS(s2n_stuffer_write(&input, &complete_record)); + + DEFER_CLEANUP(struct s2n_blob actual_payload = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&actual_payload, payload.size)); + int r = s2n_recv(server_conn, actual_payload.data, actual_payload.size, &blocked); + EXPECT_EQUAL(r, payload.size); + EXPECT_BYTEARRAY_EQUAL(actual_payload.data, payload.data, payload.size); + EXPECT_EQUAL(s2n_stuffer_data_available(&input), 0); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; +#endif + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_handshake_state_machine_test.c b/tests/unit/s2n_tls13_handshake_state_machine_test.c new file mode 100644 index 00000000000..963d195e11f --- /dev/null +++ b/tests/unit/s2n_tls13_handshake_state_machine_test.c @@ -0,0 +1,1012 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_quic_support.h" +#include "tls/s2n_tls13.h" + +/* Just to get access to the static functions / variables we need to test */ +#include "tls/s2n_handshake_io.c" + +static message_type_t invalid_handshake[S2N_MAX_HANDSHAKE_LENGTH] = { 0 }; + +static int expected_handler_called; +static int unexpected_handler_called; + +static int s2n_test_handler(struct s2n_connection *conn) +{ + unexpected_handler_called = 1; + return 0; +} + +static int s2n_test_expected_handler(struct s2n_connection *conn) +{ + expected_handler_called = 1; + return 0; +} + +static int s2n_setup_handler_to_expect(message_type_t expected, uint8_t direction) +{ + for (int i = 0; i < s2n_array_len(tls13_state_machine); i++) { + tls13_state_machine[i].handler[0] = s2n_test_handler; + tls13_state_machine[i].handler[1] = s2n_test_handler; + } + + tls13_state_machine[expected].handler[direction] = s2n_test_expected_handler; + + expected_handler_called = 0; + unexpected_handler_called = 0; + + return 0; +} + +static int s2n_test_write_header(struct s2n_stuffer *output, uint8_t record_type, uint8_t message_type) +{ + POSIX_GUARD(s2n_stuffer_write_uint8(output, record_type)); + + /* TLS1.2 protocol version */ + POSIX_GUARD(s2n_stuffer_write_uint8(output, 3)); + POSIX_GUARD(s2n_stuffer_write_uint8(output, 3)); + + if (record_type == TLS_HANDSHAKE) { + /* Total message size */ + POSIX_GUARD(s2n_stuffer_write_uint16(output, 4)); + + POSIX_GUARD(s2n_stuffer_write_uint8(output, message_type)); + + /* Handshake message data size */ + POSIX_GUARD(s2n_stuffer_write_uint24(output, 0)); + return 0; + } + + if (record_type == TLS_CHANGE_CIPHER_SPEC) { + /* Total message size */ + POSIX_GUARD(s2n_stuffer_write_uint16(output, 1)); + + /* change spec is always just 0x01 */ + POSIX_GUARD(s2n_stuffer_write_uint8(output, 1)); + return 0; + } + + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /* Construct an array of all valid tls1.3 handshake_types */ + uint16_t valid_tls13_handshakes[S2N_HANDSHAKES_COUNT]; + int valid_tls13_handshakes_size = 0; + for (int i = 0; i < S2N_HANDSHAKES_COUNT; i++) { + if (memcmp(tls13_handshakes[i], invalid_handshake, S2N_MAX_HANDSHAKE_LENGTH) != 0) { + valid_tls13_handshakes[valid_tls13_handshakes_size] = i; + valid_tls13_handshakes_size++; + } + } + EXPECT_TRUE(valid_tls13_handshakes_size > 0); + EXPECT_TRUE(valid_tls13_handshakes_size < S2N_HANDSHAKES_COUNT); + + /* Use handler stubs to avoid errors in handler implementation */ + for (int i = 0; i < s2n_array_len(tls13_state_machine); i++) { + tls13_state_machine[i].handler[0] = s2n_test_handler; + tls13_state_machine[i].handler[1] = s2n_test_handler; + } + + /* Test: A WITH_EARLY_DATA form of every non-full, non-retry handshake exists + * and matches the non-WITH_EARLY_DATA form EXCEPT for the END_OF_EARLY_DATA + * message and client CCS messages. + */ + { + uint32_t original_handshake_type, early_data_handshake_type; + message_type_t *original_messages, *early_data_messages; + + for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { + original_handshake_type = valid_tls13_handshakes[i]; + original_messages = tls13_handshakes[original_handshake_type]; + + /* WITH_EARLY_DATA form of the handshake */ + early_data_handshake_type = original_handshake_type | WITH_EARLY_DATA; + early_data_messages = tls13_handshakes[early_data_handshake_type]; + + /* No handshake exists for INITIAL, FULL_HANDSHAKE, or HELLO_RETRY_REQUEST handshakes */ + if (!(original_handshake_type & NEGOTIATED) || (original_handshake_type & FULL_HANDSHAKE) + || (original_handshake_type & HELLO_RETRY_REQUEST)) { + EXPECT_BYTEARRAY_EQUAL(early_data_messages, invalid_handshake, sizeof(invalid_handshake)); + continue; + } + + /* Ignore identical handshakes */ + if (original_handshake_type == early_data_handshake_type) { + continue; + } + + size_t end_of_early_data_messages = 0; + size_t j = 0, j_ed = 0; + while (j < S2N_MAX_HANDSHAKE_LENGTH && j_ed < S2N_MAX_HANDSHAKE_LENGTH) { + /* The original handshake must NOT contain END_OF_EARLY_DATA messages */ + EXPECT_NOT_EQUAL(original_messages[j], END_OF_EARLY_DATA); + + /* Count END_OF_EARLY_DATA messages in the WITH_EARLY_DATA handshake */ + if (early_data_messages[j_ed] == END_OF_EARLY_DATA) { + end_of_early_data_messages++; + j_ed++; + continue; + } + + /* Skip client CCS message in both handshakes - they won't appear at the same point */ + if (early_data_messages[j_ed] == CLIENT_CHANGE_CIPHER_SPEC) { + j_ed++; + continue; + } + if (original_messages[j] == CLIENT_CHANGE_CIPHER_SPEC) { + j++; + continue; + } + + /* The handshakes must be otherwise equivalent */ + EXPECT_EQUAL(original_messages[j], early_data_messages[j_ed]); + j++; + j_ed++; + } + if (original_handshake_type & NEGOTIATED) { + EXPECT_EQUAL(end_of_early_data_messages, 1); + } else { + EXPECT_EQUAL(end_of_early_data_messages, 0); + } + } + }; + + /* Test: A MIDDLEBOX_COMPAT form of every valid, negotiated handshake exists + * and matches the non-MIDDLEBOX_COMPAT form EXCEPT for CCS messages */ + { + uint32_t handshake_type_original, handshake_type_mc; + message_type_t *messages_original, *messages_mc; + + for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { + handshake_type_original = valid_tls13_handshakes[i]; + messages_original = tls13_handshakes[handshake_type_original]; + + /* Ignore INITIAL and MIDDLEBOX_COMPAT handshakes */ + if (!(handshake_type_original & NEGOTIATED) || (handshake_type_original & MIDDLEBOX_COMPAT)) { + continue; + } + + /* MIDDLEBOX_COMPAT form of the handshake */ + handshake_type_mc = handshake_type_original | MIDDLEBOX_COMPAT; + messages_mc = tls13_handshakes[handshake_type_mc]; + + for (size_t j = 0, j_mc = 0; j < S2N_MAX_HANDSHAKE_LENGTH && j_mc < S2N_MAX_HANDSHAKE_LENGTH; j++, j_mc++) { + /* The original handshake cannot contain CCS messages */ + EXPECT_NOT_EQUAL(messages_original[j], SERVER_CHANGE_CIPHER_SPEC); + EXPECT_NOT_EQUAL(messages_original[j], CLIENT_CHANGE_CIPHER_SPEC); + + /* Skip CCS messages in the MIDDLEBOX_COMPAT handshake */ + while (messages_mc[j_mc] == SERVER_CHANGE_CIPHER_SPEC + || messages_mc[j_mc] == CLIENT_CHANGE_CIPHER_SPEC) { + j_mc++; + } + + /* The handshakes must be otherwise equivalent */ + EXPECT_EQUAL(messages_original[j], messages_mc[j_mc]); + } + } + }; + + /* Test: A non-FULL_HANDSHAKE form of every valid, negotiated handshake exists */ + { + uint32_t handshake_type_original, handshake_type_fh; + message_type_t *messages_original, *messages_fh; + + for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { + handshake_type_original = valid_tls13_handshakes[i]; + messages_original = tls13_handshakes[handshake_type_original]; + + /* Ignore INITIAL and FULL_HANDSHAKE handshakes */ + if (!(handshake_type_original & NEGOTIATED) || (handshake_type_original & FULL_HANDSHAKE)) { + continue; + } + + /* FULL_HANDSHAKE form of the handshake */ + handshake_type_fh = handshake_type_original | FULL_HANDSHAKE; + messages_fh = tls13_handshakes[handshake_type_fh]; + + /* No FULL handshake exists for INITIAL or WITH_EARLY_DATA handshakes */ + if (!(handshake_type_original & NEGOTIATED) || (handshake_type_original & WITH_EARLY_DATA)) { + EXPECT_BYTEARRAY_EQUAL(messages_fh, invalid_handshake, sizeof(invalid_handshake)); + continue; + } + + /* Ignore identical handshakes */ + if (handshake_type_original == handshake_type_fh) { + continue; + } + + for (size_t j = 0, j_fh = 0; j < S2N_MAX_HANDSHAKE_LENGTH && j_fh < S2N_MAX_HANDSHAKE_LENGTH; j++, j_fh++) { + /* The original handshake cannot contain authentication messages */ + EXPECT_NOT_EQUAL(messages_original[j], SERVER_CERT); + EXPECT_NOT_EQUAL(messages_original[j], SERVER_CERT_VERIFY); + + /* Skip authentication messages in the FULL_HANDSHAKE handshake */ + while (messages_fh[j_fh] == SERVER_CERT || messages_fh[j_fh] == SERVER_CERT_VERIFY) { + j_fh++; + } + + /* The handshakes must be otherwise equivalent */ + EXPECT_EQUAL(messages_original[j], messages_fh[j_fh]); + } + } + }; + + /* Test: A EARLY_CLIENT_CCS form of every middlebox compatible handshake exists. + * Any handshake could start with early data, even if that early data is later rejected. */ + { + uint32_t handshake_type_original, handshake_type_test; + message_type_t *messages_original, *messages_test; + + for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { + handshake_type_original = valid_tls13_handshakes[i]; + messages_original = tls13_handshakes[handshake_type_original]; + + /* Ignore non-MIDDLEBOX_COMPAT handshakes */ + if (!(handshake_type_original & MIDDLEBOX_COMPAT)) { + continue; + } + + /* EARLY_CLIENT_CCS form of the handshake */ + handshake_type_test = handshake_type_original | EARLY_CLIENT_CCS; + messages_test = tls13_handshakes[handshake_type_test]; + + /* Ignore identical handshakes */ + if (handshake_type_original == handshake_type_test) { + continue; + } + + for (size_t j = 0, j_test = 0; j < S2N_MAX_HANDSHAKE_LENGTH && j_test < S2N_MAX_HANDSHAKE_LENGTH; j++, j_test++) { + /* Skip Client CCS messages */ + while (messages_original[j] == CLIENT_CHANGE_CIPHER_SPEC) { + j++; + } + while (messages_test[j_test] == CLIENT_CHANGE_CIPHER_SPEC) { + j_test++; + } + + /* The handshakes must be otherwise equivalent */ + EXPECT_EQUAL(messages_original[j], messages_test[j_test]); + } + } + }; + + /* Test: When using TLS 1.3, use the new state machine and handshakes */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + EXPECT_EQUAL(ACTIVE_STATE_MACHINE(conn), tls13_state_machine); + EXPECT_EQUAL(ACTIVE_HANDSHAKES(conn), tls13_handshakes); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.3 server does not wait for client cipher change requests */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + for (int i = 0; i < valid_tls13_handshakes_size; i++) { + int handshake = valid_tls13_handshakes[i]; + + conn->handshake.handshake_type = handshake; + + for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + if (tls13_handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { + conn->handshake.message_number = j - 1; + + EXPECT_SUCCESS(s2n_advance_message(conn)); + + EXPECT_EQUAL(conn->handshake.message_number, j + 1); + EXPECT_NOT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_CHANGE_CIPHER_SPEC); + + break; + } + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.3 server does not skip server cipher change requests */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + for (int i = 0; i < valid_tls13_handshakes_size; i++) { + int handshake = valid_tls13_handshakes[i]; + + conn->handshake.handshake_type = handshake; + + for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + if (tls13_handshakes[handshake][j] == SERVER_CHANGE_CIPHER_SPEC) { + conn->handshake.message_number = j - 1; + + EXPECT_SUCCESS(s2n_advance_message(conn)); + + EXPECT_EQUAL(conn->handshake.message_number, j); + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), SERVER_CHANGE_CIPHER_SPEC); + + break; + } + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.3 client does not wait for server cipher change requests */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + for (int i = 0; i < valid_tls13_handshakes_size; i++) { + int handshake = valid_tls13_handshakes[i]; + + conn->handshake.handshake_type = handshake; + + for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + if (tls13_handshakes[handshake][j] == SERVER_CHANGE_CIPHER_SPEC) { + conn->handshake.message_number = j - 1; + + EXPECT_SUCCESS(s2n_advance_message(conn)); + + EXPECT_EQUAL(conn->handshake.message_number, j + 1); + EXPECT_NOT_EQUAL(ACTIVE_MESSAGE(conn), SERVER_CHANGE_CIPHER_SPEC); + + break; + } + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.3 client does not skip client cipher change requests */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + for (int i = 0; i < valid_tls13_handshakes_size; i++) { + int handshake = valid_tls13_handshakes[i]; + + conn->handshake.handshake_type = handshake; + + for (int j = 0; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + if (tls13_handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { + conn->handshake.message_number = j - 1; + + EXPECT_SUCCESS(s2n_advance_message(conn)); + + EXPECT_EQUAL(conn->handshake.message_number, j); + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_CHANGE_CIPHER_SPEC); + + break; + } + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.3 client can receive a server cipher change spec at any time. */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + EXPECT_SUCCESS(s2n_setup_handler_to_expect(SERVER_CHANGE_CIPHER_SPEC, S2N_CLIENT)); + + for (int i = 0; i < valid_tls13_handshakes_size; i++) { + int handshake = valid_tls13_handshakes[i]; + + conn->handshake.handshake_type = handshake; + conn->in_status = ENCRYPTED; + + for (int j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + conn->handshake.message_number = j; + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_CHANGE_CIPHER_SPEC, 0)); + + EXPECT_SUCCESS(s2n_handshake_read_io(conn)); + + if (tls13_handshakes[handshake][j] == SERVER_CHANGE_CIPHER_SPEC) { + EXPECT_EQUAL(conn->handshake.message_number, j + 1); + } else { + EXPECT_EQUAL(conn->handshake.message_number, j); + } + + EXPECT_FALSE(unexpected_handler_called); + EXPECT_TRUE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&input)); + } + } + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.3 server can receive a client cipher change request at any time. */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_CHANGE_CIPHER_SPEC, S2N_SERVER)); + + for (int i = 0; i < valid_tls13_handshakes_size; i++) { + int handshake = valid_tls13_handshakes[i]; + + conn->handshake.handshake_type = handshake; + conn->in_status = ENCRYPTED; + + for (int j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + conn->handshake.message_number = j; + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_CHANGE_CIPHER_SPEC, 0)); + + EXPECT_SUCCESS(s2n_handshake_read_io(conn)); + + if (tls13_handshakes[handshake][j] == CLIENT_CHANGE_CIPHER_SPEC) { + EXPECT_EQUAL(conn->handshake.message_number, j + 1); + } else { + EXPECT_EQUAL(conn->handshake.message_number, j); + } + + EXPECT_FALSE(unexpected_handler_called); + EXPECT_TRUE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&input)); + } + } + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS1.3 s2n_handshake_read_io should accept only the expected message */ + { + /* TLS1.3 should accept the expected message */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + conn->handshake.handshake_type = 0; + conn->handshake.message_number = 0; + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CLIENT_HELLO)); + EXPECT_SUCCESS(s2n_handshake_read_io(conn)); + + EXPECT_EQUAL(conn->handshake.message_number, 1); + EXPECT_FALSE(unexpected_handler_called); + EXPECT_TRUE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS1.3 should error for an unexpected message */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + conn->handshake.handshake_type = 0; + conn->handshake.message_number = 0; + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CERTIFICATE)); + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_EQUAL(conn->handshake.message_number, 0); + EXPECT_FALSE(unexpected_handler_called); + EXPECT_FALSE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS1.3 should error for an expected message from the wrong writer */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + conn->handshake.handshake_type = 0; + conn->handshake.message_number = 0; + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_setup_handler_to_expect(CLIENT_HELLO, S2N_SERVER)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CLIENT_HELLO)); + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_EQUAL(conn->handshake.message_number, 0); + EXPECT_FALSE(unexpected_handler_called); + EXPECT_FALSE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* TLS1.3 should error for an expected message from the wrong record type */ + { + /* Unfortunately, all our non-handshake record types have a message type of 0, + * and the combination of TLS_HANDSHAKE + "0" is actually a message (TLS_HELLO_REQUEST) + * which can appear at any point in a TLS1.2 handshake. + * + * To test, temporarily modify the actions table. + * We MUST restore this after this test. + */ + uint8_t old_message_type = state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type; + state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type = 1; + + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + uint8_t server_css_message_number = 2; + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | MIDDLEBOX_COMPAT; + conn->handshake.message_number = server_css_message_number; + EXPECT_EQUAL(ACTIVE_MESSAGE(conn), SERVER_CHANGE_CIPHER_SPEC); + EXPECT_SUCCESS(s2n_setup_handler_to_expect(SERVER_CHANGE_CIPHER_SPEC, S2N_CLIENT)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, ACTIVE_STATE(conn).message_type)); + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_EQUAL(conn->handshake.message_number, server_css_message_number); + EXPECT_FALSE(unexpected_handler_called); + EXPECT_FALSE(expected_handler_called); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + state_machine[SERVER_CHANGE_CIPHER_SPEC].message_type = old_message_type; + }; + + /* Error if a client receives a client cert request in non-FULL_HANDSHAKE mode */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + POSIX_GUARD(s2n_connection_set_client_auth_type(conn, S2N_CERT_AUTH_OPTIONAL)); + + struct s2n_stuffer input = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, NULL, conn)); + + EXPECT_SUCCESS(s2n_test_write_header(&input, TLS_HANDSHAKE, TLS_CERT_REQ)); + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(conn), S2N_ERR_HANDSHAKE_STATE); + + EXPECT_SUCCESS(s2n_stuffer_free(&input)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test: TLS 1.3 MIDDLEBOX_COMPAT handshakes all follow CCS middlebox compatibility rules. + * + *= https://tools.ietf.org/rfc/rfc8446#appendix-D.4 + *= type=test + *# Field measurements [Ben17a] [Ben17b] [Res17a] [Res17b] have found + *# that a significant number of middleboxes misbehave when a TLS + *# client/server pair negotiates TLS 1.3. Implementations can increase + *# the chance of making connections through those middleboxes by making + *# the TLS 1.3 handshake look more like a TLS 1.2 handshake: + */ + { + bool change_cipher_spec_found; + uint32_t handshake_type; + message_type_t *messages; + + /* + *= https://tools.ietf.org/rfc/rfc8446#appendix-D.4 + *= type=test + *# If not offering early data, the client sends a dummy + *# change_cipher_spec record (see the third paragraph of Section 5) + *# immediately before its second flight. This may either be before + *# its second ClientHello or before its encrypted handshake flight. + **/ + for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { + change_cipher_spec_found = false; + handshake_type = valid_tls13_handshakes[i]; + messages = tls13_handshakes[handshake_type]; + + /* Ignore INITIAL and non-MIDDLEBOX_COMPAT handshakes */ + if (!(handshake_type & NEGOTIATED) + || !(handshake_type & MIDDLEBOX_COMPAT) + || (handshake_type & EARLY_CLIENT_CCS)) { + continue; + } + + for (size_t j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + /* Is it the second client flight? + * Have we switched from the server sending to the client sending? */ + if (tls13_state_machine[messages[j]].writer != 'C' + || tls13_state_machine[messages[j - 1]].writer != 'S') { + continue; + } + + EXPECT_EQUAL(messages[j], CLIENT_CHANGE_CIPHER_SPEC); + EXPECT_EQUAL(tls13_state_machine[messages[j + 1]].writer, 'C'); + + /* CCS message found. We are done with this handshake. */ + change_cipher_spec_found = true; + break; + } + + EXPECT_TRUE(change_cipher_spec_found); + } + + /** + *= https://tools.ietf.org/rfc/rfc8446#appendix-D.4 + *= type=test + *# If offering early data, the record is placed immediately after the + *# first ClientHello. + */ + for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { + handshake_type = valid_tls13_handshakes[i]; + messages = tls13_handshakes[handshake_type]; + + /* Ignore handshakes where early data did not trigger the change in CCS behavior */ + if (!(handshake_type & EARLY_CLIENT_CCS)) { + continue; + } + + EXPECT_EQUAL(messages[0], CLIENT_HELLO); + EXPECT_EQUAL(messages[1], CLIENT_CHANGE_CIPHER_SPEC); + for (size_t j = 2; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + EXPECT_NOT_EQUAL(messages[j], CLIENT_CHANGE_CIPHER_SPEC); + } + } + + /** + *= https://tools.ietf.org/rfc/rfc8446#appendix-D.4 + *= type=test + *# The server sends a dummy change_cipher_spec record immediately + *# after its first handshake message. This may either be after a + *# ServerHello or a HelloRetryRequest. + **/ + for (size_t i = 0; i < valid_tls13_handshakes_size; i++) { + change_cipher_spec_found = false; + handshake_type = valid_tls13_handshakes[i]; + messages = tls13_handshakes[handshake_type]; + + /* Ignore INITIAL and non-MIDDLEBOX_COMPAT handshakes */ + if (!(handshake_type & NEGOTIATED) || !(handshake_type & MIDDLEBOX_COMPAT)) { + continue; + } + + for (size_t j = 1; j < S2N_MAX_HANDSHAKE_LENGTH; j++) { + /* Is it the first server flight? + * Have we switched from the client sending to the server sending? */ + if (tls13_state_machine[messages[j]].writer != 'S' + || tls13_state_machine[messages[j - 1]].writer != 'C') { + continue; + } + + EXPECT_EQUAL(messages[j + 1], SERVER_CHANGE_CIPHER_SPEC); + EXPECT_TRUE(messages[j] == SERVER_HELLO || messages[j] == HELLO_RETRY_MSG); + + /* CCS message found. We are done with this handshake. */ + change_cipher_spec_found = true; + break; + } + + EXPECT_TRUE(change_cipher_spec_found); + } + }; + + /* Test: TLS1.3 s2n_conn_set_handshake_type sets only handshake flags allowed by TLS1.3 */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + + /* Ensure WITH_SESSION_TICKETS is set */ + conn->config->use_tickets = 1; + conn->session_ticket_status = S2N_NEW_TICKET; + + /* Ensure CLIENT_AUTH is set */ + conn->config->client_cert_auth_type = S2N_CERT_AUTH_REQUIRED; + + /* Ensure TLS12_PERFECT_FORWARD_SECRECY is set by choosing a cipher suite with is_ephemeral=1 on the kex */ + conn->secure->cipher_suite = &s2n_dhe_rsa_with_chacha20_poly1305_sha256; + + /* Ensure OCSP_STATUS is set by setting the connection status_type */ + conn->status_type = S2N_STATUS_REQUEST_OCSP; + + /* Verify that tls1.2 DOES set the flags allowed by tls1.2 */ + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); + EXPECT_TRUE(conn->handshake.handshake_type & TLS12_PERFECT_FORWARD_SECRECY); + EXPECT_TRUE(conn->handshake.handshake_type & OCSP_STATUS); + EXPECT_TRUE(conn->handshake.handshake_type & WITH_SESSION_TICKET); + EXPECT_TRUE(conn->handshake.handshake_type & CLIENT_AUTH); + + /* Reset which state machine we're on */ + EXPECT_OK(s2n_handshake_type_reset(conn)); + conn->handshake.state_machine = S2N_STATE_MACHINE_INITIAL; + + /* Verify that tls1.3 ONLY sets the flags allowed by tls1.3 */ + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); + EXPECT_EQUAL(conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | MIDDLEBOX_COMPAT); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: s2n_conn_set_handshake_type only allows HELLO_RETRY_REQUEST with TLS1.3 */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + + /* HELLO_RETRY_REQUEST allowed with tls1.3 */ + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + conn->handshake.handshake_type = INITIAL | HELLO_RETRY_REQUEST; + EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); + EXPECT_TRUE(conn->handshake.handshake_type & HELLO_RETRY_REQUEST); + + /* Reset state machine */ + conn->handshake.state_machine = S2N_STATE_MACHINE_INITIAL; + + /* HELLO_RETRY_REQUEST not allowed with tls1.2 */ + conn->actual_protocol_version = S2N_TLS12; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS12)); + conn->handshake.handshake_type = INITIAL | HELLO_RETRY_REQUEST; + EXPECT_SUCCESS(s2n_conn_set_handshake_type(conn)); + EXPECT_FALSE(conn->handshake.handshake_type & HELLO_RETRY_REQUEST); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: s2n_conn_set_tls13_handshake_type does not set FULL_HANDSHAKE if + * a pre-shared key has been chosen. */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT); + struct s2n_psk *psk = NULL; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); + + conn->psk_params.chosen_psk = psk; + EXPECT_NOT_NULL(conn->psk_params.chosen_psk); + EXPECT_OK(s2n_conn_set_tls13_handshake_type(conn)); + EXPECT_FALSE(conn->handshake.handshake_type & FULL_HANDSHAKE); + + conn->psk_params.chosen_psk = NULL; + EXPECT_OK(s2n_conn_set_tls13_handshake_type(conn)); + EXPECT_TRUE(conn->handshake.handshake_type & FULL_HANDSHAKE); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: s2n_conn_set_tls13_handshake_type ignores client auth type if a pre-shared key is + * chosen and s2n is a client. */ + { + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_pushback(&client_conn->psk_params.psk_list, (void **) &psk)); + client_conn->psk_params.chosen_psk = psk; + EXPECT_NOT_NULL(client_conn->psk_params.chosen_psk); + + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(client_conn, S2N_CERT_AUTH_REQUIRED)); + + EXPECT_OK(s2n_conn_set_tls13_handshake_type(client_conn)); + EXPECT_FALSE(client_conn->handshake.handshake_type & CLIENT_AUTH); + EXPECT_FALSE(client_conn->handshake.handshake_type & FULL_HANDSHAKE); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /* Test: s2n_conn_set_tls13_handshake_type ignores client auth type if a pre-shared key is + * chosen and s2n is a server. */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + struct s2n_psk *psk = NULL; + + EXPECT_OK(s2n_array_pushback(&server_conn->psk_params.psk_list, (void **) &psk)); + server_conn->psk_params.chosen_psk = psk; + EXPECT_NOT_NULL(server_conn->psk_params.chosen_psk); + + EXPECT_SUCCESS(s2n_connection_set_client_auth_type(server_conn, S2N_CERT_AUTH_REQUIRED)); + + EXPECT_OK(s2n_conn_set_tls13_handshake_type(server_conn)); + EXPECT_FALSE(server_conn->handshake.handshake_type & CLIENT_AUTH); + EXPECT_FALSE(server_conn->handshake.handshake_type & FULL_HANDSHAKE); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test: s2n_conn_set_tls13_handshake_type sets WITH_EARLY_DATA */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + server_conn->actual_protocol_version = S2N_TLS13; + + server_conn->early_data_state = S2N_EARLY_DATA_ACCEPTED; + EXPECT_OK(s2n_conn_set_tls13_handshake_type(server_conn)); + EXPECT_TRUE(server_conn->handshake.handshake_type & WITH_EARLY_DATA); + EXPECT_TRUE(server_conn->handshake.handshake_type & NEGOTIATED); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test: s2n_conn_set_tls13_handshake_type does not set WITH_EARLY_DATA if wrong state */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + server_conn->actual_protocol_version = S2N_TLS13; + + server_conn->early_data_state = S2N_EARLY_DATA_REJECTED; + EXPECT_OK(s2n_conn_set_tls13_handshake_type(server_conn)); + EXPECT_FALSE(server_conn->handshake.handshake_type & WITH_EARLY_DATA); + EXPECT_TRUE(server_conn->handshake.handshake_type & NEGOTIATED); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test: TLS1.3 handshake type name maximum size is set correctly. + * The maximum size is the size of a name with all flags set. */ + { + size_t correct_size = 0; + for (size_t i = 0; i < s2n_array_len(tls13_handshake_type_names); i++) { + correct_size += strlen(tls13_handshake_type_names[i]); + } + if (correct_size > MAX_HANDSHAKE_TYPE_LEN) { + fprintf(stderr, "\nMAX_HANDSHAKE_TYPE_LEN should be at least %lu\n", (unsigned long) correct_size); + FAIL_MSG("MAX_HANDSHAKE_TYPE_LEN wrong for TLS1.3 handshakes"); + } + }; + + /* Test: TLS 1.3 handshake types are all properly printed */ + { + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS13; + + conn->handshake.handshake_type = INITIAL; + EXPECT_STRING_EQUAL("INITIAL", s2n_connection_get_handshake_type_name(conn)); + + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; + EXPECT_STRING_EQUAL("NEGOTIATED|FULL_HANDSHAKE", s2n_connection_get_handshake_type_name(conn)); + + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | HELLO_RETRY_REQUEST; + EXPECT_STRING_EQUAL("NEGOTIATED|FULL_HANDSHAKE|HELLO_RETRY_REQUEST", s2n_connection_get_handshake_type_name(conn)); + + const char *all_flags_handshake_type_name = "NEGOTIATED|FULL_HANDSHAKE|CLIENT_AUTH|NO_CLIENT_CERT" + "|MIDDLEBOX_COMPAT|WITH_EARLY_DATA|EARLY_CLIENT_CCS"; + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH | NO_CLIENT_CERT + | MIDDLEBOX_COMPAT | WITH_EARLY_DATA | EARLY_CLIENT_CCS; + EXPECT_STRING_EQUAL(all_flags_handshake_type_name, s2n_connection_get_handshake_type_name(conn)); + + const char *handshake_type_name; + for (int i = 0; i < valid_tls13_handshakes_size; i++) { + conn->handshake.handshake_type = valid_tls13_handshakes[i]; + + handshake_type_name = s2n_connection_get_handshake_type_name(conn); + + /* The handshake type names must be unique */ + for (int j = 0; j < valid_tls13_handshakes_size; j++) { + conn->handshake.handshake_type = valid_tls13_handshakes[j]; + if (i == j) { + EXPECT_STRING_EQUAL(handshake_type_name, s2n_connection_get_handshake_type_name(conn)); + } else { + EXPECT_STRING_NOT_EQUAL(handshake_type_name, s2n_connection_get_handshake_type_name(conn)); + } + } + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS 1.3 message types are all properly printed */ + { + uint32_t test_handshake_type = NEGOTIATED | FULL_HANDSHAKE | MIDDLEBOX_COMPAT; + const char *expected[] = { "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" }; + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + conn->handshake.handshake_type = test_handshake_type; + + for (int i = 0; i < s2n_array_len(expected); i++) { + conn->handshake.message_number = i; + EXPECT_STRING_EQUAL(expected[i], s2n_connection_get_last_message_name(conn)); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: TLS 1.3 message types are all properly printed for client auth */ + { + uint32_t test_handshake_type = NEGOTIATED | FULL_HANDSHAKE | CLIENT_AUTH; + const char *expected[] = { "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" }; + + struct s2n_connection *conn = s2n_connection_new(S2N_SERVER); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + conn->handshake.handshake_type = test_handshake_type; + + for (int i = 0; i < s2n_array_len(expected); i++) { + conn->handshake.message_number = i; + EXPECT_STRING_EQUAL(expected[i], s2n_connection_get_last_message_name(conn)); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test: Make sure not to miss out populating any message names */ + { + for (int i = CLIENT_HELLO; i <= APPLICATION_DATA; i++) { + EXPECT_NOT_NULL(message_names[i]); + } + }; + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_tls13_handshake_test.c b/tests/unit/s2n_tls13_handshake_test.c new file mode 100644 index 00000000000..a5179800cdc --- /dev/null +++ b/tests/unit/s2n_tls13_handshake_test.c @@ -0,0 +1,332 @@ +/* + * 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 "tls/s2n_tls13_handshake.h" + +#include +#include + +#include "api/s2n.h" +#include "crypto/s2n_fips.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_client_key_share.h" +#include "tls/extensions/s2n_server_key_share.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_quic_support.h" +#include "tls/s2n_security_policies.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +/* Just to get access to the static functions / variables we need to test */ +#include "tls/s2n_handshake_io.c" +#include "tls/s2n_handshake_transcript.c" +#include "tls/s2n_tls13_handshake.c" + +#define S2N_SECRET_TYPE_COUNT 5 +#define S2N_TEST_PSK_COUNT 10 + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + /* Test wiping PSKs after use */ + { + /* PSKs are wiped when chosen PSK is NULL */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + const struct s2n_ecc_preferences *ecc_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + EXPECT_NOT_NULL(ecc_preferences); + + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.server_ecc_evp_params)); + conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + + const uint8_t psk_data[] = "test identity data"; + const uint8_t secret_data[] = "test secret data"; + for (size_t i = 0; i < S2N_TEST_PSK_COUNT; i++) { + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); + EXPECT_OK(s2n_psk_init(psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(psk, psk_data, sizeof(psk_data))); + EXPECT_NOT_EQUAL(psk->identity.size, 0); + EXPECT_NOT_EQUAL(psk->identity.data, NULL); + EXPECT_SUCCESS(s2n_psk_set_secret(psk, secret_data, sizeof(secret_data))); + EXPECT_NOT_EQUAL(psk->secret.size, 0); + EXPECT_NOT_EQUAL(psk->secret.data, NULL); + } + + EXPECT_NOT_EQUAL(conn->psk_params.psk_list.mem.allocated, 0); + EXPECT_EQUAL(conn->psk_params.psk_list.len, S2N_TEST_PSK_COUNT); + EXPECT_NULL(conn->psk_params.chosen_psk); + + DEFER_CLEANUP(struct s2n_blob shared_secret = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_tls13_compute_shared_secret(conn, &shared_secret)); + + /* Verify secrets are wiped */ + for (size_t i = 0; i < conn->psk_params.psk_list.len; i++) { + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, i, (void **) &psk)); + EXPECT_NOT_EQUAL(psk->identity.size, 0); + EXPECT_NULL(psk->secret.data); + EXPECT_EQUAL(psk->secret.size, 0); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* PSKs are wiped when chosen PSK is NOT NULL */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + const struct s2n_ecc_preferences *ecc_preferences = NULL; + EXPECT_SUCCESS(s2n_connection_get_ecc_preferences(conn, &ecc_preferences)); + EXPECT_NOT_NULL(ecc_preferences); + + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + conn->kex_params.server_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.server_ecc_evp_params)); + conn->kex_params.client_ecc_evp_params.negotiated_curve = ecc_preferences->ecc_curves[0]; + EXPECT_SUCCESS(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + + const uint8_t psk_data[] = "test identity data"; + const uint8_t secret_data[] = "test secret data"; + const uint8_t early_secret_data[SHA256_DIGEST_LENGTH] = "test early secret data"; + for (size_t i = 0; i < S2N_TEST_PSK_COUNT; i++) { + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_pushback(&conn->psk_params.psk_list, (void **) &psk)); + EXPECT_OK(s2n_psk_init(psk, S2N_PSK_TYPE_EXTERNAL)); + EXPECT_SUCCESS(s2n_psk_set_identity(psk, psk_data, sizeof(psk_data))); + EXPECT_NOT_EQUAL(psk->identity.size, 0); + EXPECT_NOT_EQUAL(psk->identity.data, NULL); + EXPECT_SUCCESS(s2n_psk_set_secret(psk, secret_data, sizeof(secret_data))); + EXPECT_NOT_EQUAL(psk->secret.size, 0); + EXPECT_NOT_EQUAL(psk->secret.data, NULL); + EXPECT_SUCCESS(s2n_realloc(&psk->early_secret, sizeof(early_secret_data))); + POSIX_CHECKED_MEMCPY(psk->early_secret.data, early_secret_data, sizeof(early_secret_data)); + EXPECT_NOT_EQUAL(psk->early_secret.size, 0); + EXPECT_NOT_EQUAL(psk->early_secret.data, NULL); + } + + /* Set chosen PSK */ + struct s2n_psk *chosen_psk = NULL; + EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, 0, (void **) &chosen_psk)); + EXPECT_NOT_NULL(chosen_psk); + conn->psk_params.chosen_psk = chosen_psk; + conn->psk_params.chosen_psk_wire_index = 0; + + EXPECT_NOT_EQUAL(conn->psk_params.psk_list.mem.allocated, 0); + EXPECT_EQUAL(conn->psk_params.psk_list.len, S2N_TEST_PSK_COUNT); + + DEFER_CLEANUP(struct s2n_blob shared_secret = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_tls13_compute_shared_secret(conn, &shared_secret)); + + /* Verify secrets are wiped */ + for (size_t i = 0; i < conn->psk_params.psk_list.len; i++) { + struct s2n_psk *psk = NULL; + EXPECT_OK(s2n_array_get(&conn->psk_params.psk_list, i, (void **) &psk)); + EXPECT_NOT_EQUAL(psk->identity.size, 0); + EXPECT_NULL(psk->secret.data); + EXPECT_EQUAL(psk->secret.size, 0); + EXPECT_NULL(psk->early_secret.data); + EXPECT_EQUAL(psk->early_secret.size, 0); + } + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Test: Handshake self-talks using s2n_handshake_write_io and s2n_handshake_read_io */ + { + struct s2n_connection *client_conn; + struct s2n_connection *server_conn; + + struct s2n_config *server_config, *client_config; + EXPECT_NOT_NULL(server_config = s2n_config_new()); + EXPECT_NOT_NULL(client_config = s2n_config_new()); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(client_config)); + + uint8_t *cert_chain = NULL; + uint8_t *private_key = NULL; + uint32_t cert_chain_len = 0; + uint32_t private_key_len = 0; + + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_NOT_NULL(private_key = malloc(S2N_MAX_TEST_PEM_SIZE)); + + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, cert_chain, &cert_chain_len, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem_and_len(S2N_ECDSA_P384_PKCS1_KEY, private_key, &private_key_len, S2N_MAX_TEST_PEM_SIZE)); + + struct s2n_cert_chain_and_key *default_cert; + EXPECT_NOT_NULL(default_cert = s2n_cert_chain_and_key_new()); + EXPECT_SUCCESS(s2n_cert_chain_and_key_load_pem_bytes(default_cert, cert_chain, cert_chain_len, private_key, private_key_len)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, default_cert)); + + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + 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_config(client_conn, client_config)); + + struct s2n_stuffer client_to_server = { 0 }; + struct s2n_stuffer server_to_client = { 0 }; + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); + + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + + struct s2n_blob server_seq = { .data = server_conn->secure->server_sequence_number, .size = sizeof(server_conn->secure->server_sequence_number) }; + S2N_BLOB_FROM_HEX(seq_0, "0000000000000000"); + S2N_BLOB_FROM_HEX(seq_1, "0000000000000001"); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + + /* Client sends ClientHello */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + + EXPECT_EQUAL(client_conn->actual_protocol_version, S2N_TLS13); + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_UNKNOWN_PROTOCOL_VERSION); + + s2n_tls13_connection_keys(server_secrets_0, server_conn); + EXPECT_EQUAL(server_secrets_0.size, 0); + + EXPECT_EQUAL(server_conn->handshake.handshake_type, INITIAL); + + /* Server reads ClientHello */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + + EXPECT_EQUAL(server_conn->actual_protocol_version, S2N_TLS13); /* Server is now on TLS13 */ + EXPECT_EQUAL(server_conn->handshake.handshake_type, NEGOTIATED | FULL_HANDSHAKE | MIDDLEBOX_COMPAT); + + s2n_tls13_connection_keys(server_secrets, server_conn); + EXPECT_EQUAL(server_secrets.size, SHA256_DIGEST_LENGTH); + + EXPECT_SUCCESS(s2n_conn_set_handshake_type(server_conn)); + + /* Server sends ServerHello */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_HELLO); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Server sends CCS */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CHANGE_CIPHER_SPEC); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + S2N_BLOB_EXPECT_EQUAL(server_seq, seq_0); + + /* Server sends EncryptedExtensions */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), ENCRYPTED_EXTENSIONS); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + S2N_BLOB_EXPECT_EQUAL(server_seq, seq_1); + + /* Server sends ServerCert */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CERT); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Server sends CertVerify */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_CERT_VERIFY); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Client reads ServerHello */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client reads CCS + * The CCS message does not affect its place in the state machine. */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), ENCRYPTED_EXTENSIONS); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + s2n_tls13_connection_keys(client_secrets, client_conn); + EXPECT_EQUAL(client_secrets.size, SHA256_DIGEST_LENGTH); + + /* Verify that derive and extract secrets match */ + S2N_BLOB_EXPECT_EQUAL(server_secrets.derive_secret, client_secrets.derive_secret); + S2N_BLOB_EXPECT_EQUAL(server_secrets.extract_secret, client_secrets.extract_secret); + + /* Client reads Encrypted extensions */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), ENCRYPTED_EXTENSIONS); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client reads ServerCert */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client reads CertVerify */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_CERT_VERIFY); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Server sends ServerFinished */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), SERVER_FINISHED); + EXPECT_SUCCESS(s2n_handshake_write_io(server_conn)); + + /* Client reads ServerFinished */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), SERVER_FINISHED); + EXPECT_SUCCESS(s2n_handshake_read_io(client_conn)); + + /* Client sends CCS */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_CHANGE_CIPHER_SPEC); + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + + /* Client sends ClientFinished */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), CLIENT_FINISHED); + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + + /* Server reads CCS + * The CCS message does not affect its place in the state machine. */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_FINISHED); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + + /* Server reads ClientFinished */ + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), CLIENT_FINISHED); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + + EXPECT_EQUAL(s2n_conn_get_current_message_type(client_conn), APPLICATION_DATA); + EXPECT_EQUAL(s2n_conn_get_current_message_type(server_conn), APPLICATION_DATA); + + /* Verify that derive and extract secrets match */ + S2N_BLOB_EXPECT_EQUAL(server_secrets.derive_secret, client_secrets.derive_secret); + S2N_BLOB_EXPECT_EQUAL(server_secrets.extract_secret, client_secrets.extract_secret); + + /* Clean up */ + EXPECT_SUCCESS(s2n_stuffer_free(&client_to_server)); + EXPECT_SUCCESS(s2n_stuffer_free(&server_to_client)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(default_cert)); + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + + free(private_key); + free(cert_chain); + }; + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_tls13_hybrid_shared_secret_test.c b/tests/unit/s2n_tls13_hybrid_shared_secret_test.c new file mode 100644 index 00000000000..0dafddbfb44 --- /dev/null +++ b/tests/unit/s2n_tls13_hybrid_shared_secret_test.c @@ -0,0 +1,651 @@ +/* + * 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 "api/s2n.h" +#include "crypto/s2n_ecc_evp.h" +#include "crypto/s2n_hash.h" +#include "pq-crypto/s2n_pq.h" +#include "tests/s2n_test.h" +#include "tests/testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_kem.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_mem.h" +#include "utils/s2n_safety.h" + +/* Included so we can test functions that are otherwise unavailable */ +#include "tls/s2n_tls13_handshake.c" + +S2N_RESULT s2n_tls13_derive_secret(struct s2n_connection *conn, s2n_extract_secret_type_t secret_type, + s2n_mode mode, struct s2n_blob *secret); + +static int read_priv_ecc(EVP_PKEY **pkey, const char *priv_ecc); +static int set_up_conns(struct s2n_connection *client_conn, struct s2n_connection *server_conn, + const char *client_priv_ecc, const char *server_priv_ecc, const struct s2n_kem_group *kem_group, + struct s2n_blob *pq_shared_secret); +static int assert_kem_group_params_freed(struct s2n_connection *conn); + +struct hybrid_test_vector { + const struct s2n_kem_group *kem_group; + struct s2n_cipher_suite *cipher_suite; + const char *transcript; + const char *client_ecc_key; + const char *server_ecc_key; + struct s2n_blob *pq_secret; + struct s2n_blob *expected_hybrid_secret; + struct s2n_blob *expected_client_traffic_secret; + struct s2n_blob *expected_server_traffic_secret; +}; + +/* PEM-encoded ECC private keys generated using openssl commands like: + * + * $ openssl ecparam -name ${CURVE_NAME} -genkey + */ +#define CLIENT_X25519_PRIV_KEY "-----BEGIN PRIVATE KEY-----\n" \ + "MC4CAQAwBQYDK2VuBCIEIIgzBrAp631nCDaoA7ilx/8S/cW1lddVQOw9869sROBF\n" \ + "-----END PRIVATE KEY-----" + +#define SERVER_X25519_PRIV_KEY "-----BEGIN PRIVATE KEY-----\n" \ + "MC4CAQAwBQYDK2VuBCIEIIBo+KJ2Zs3vRHQ3sYgHL4zTQPlJPl1y7sW8HT9qRE96\n" \ + "-----END PRIVATE KEY-----" + +#define CLIENT_SECP256R1_PRIV_KEY "-----BEGIN EC PARAMETERS-----\n" \ + "BggqhkjOPQMBBw==\n" \ + "-----END EC PARAMETERS-----\n" \ + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MHcCAQEEIFCkEmNXACRbWdizfAKP8/Qvx9aplVxLE+Sm2vmCcsY3oAoGCCqGSM49\n" \ + "AwEHoUQDQgAESk526eZ9lf6xrNOiTF8qkYvJDOfc4qqShcbB7qnT67As4pyeQzVm\n" \ + "xfMjmXYBOUnPVBL3FKnIk45sDSCfu++gug==\n" \ + "-----END EC PRIVATE KEY-----" + +#define SERVER_SECP256R1_PRIV_KEY "-----BEGIN EC PARAMETERS-----\n" \ + "BggqhkjOPQMBBw==\n" \ + "-----END EC PARAMETERS-----\n" \ + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MHcCAQEEINXLCaZuyYG0HrlSFcHLPFmSnyFm5RqrmyZfgdrxqprXoAoGCCqGSM49\n" \ + "AwEHoUQDQgAEMDuuxEQ1yaA13ceuJP+RC0sbf5ksW6DPlL+yXJiD7cUeWUPrtxbP\n" \ + "ViSR6ex8fYV69oCHgnDnElfE3xaiXiQWBw==\n" \ + "-----END EC PRIVATE KEY-----" + +#define CLIENT_SECP384R1_PRIV_KEY "-----BEGIN EC PARAMETERS-----\n" \ + "BgUrgQQAIg==\n" \ + "-----END EC PARAMETERS-----\n" \ + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MIGkAgEBBDCq+TiiEmbFT2xiIj1s6q+Tk/qw3DRHrpH1SWb36XNmv+FcASF24EmU\n" \ + "QSffpZLGRk6gBwYFK4EEACKhZANiAAQp0Y+a+SfYB9V/TDF9jzwoa5ccedThv4mY\n" \ + "ddHwoynSGE95n7f8T25/276MHOoi79P5WP82aiLoIOL68IVflQPLMPFYnN9BumVo\n" \ + "UjmCWR9yl8gEBWl4teiaRvvMf2i7ayM=\n" \ + "-----END EC PRIVATE KEY-----\n" + +#define SERVER_SECP384R1_PRIV_KEY "-----BEGIN EC PARAMETERS-----\n" \ + "BgUrgQQAIg==\n" \ + "-----END EC PARAMETERS-----\n" \ + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MIGkAgEBBDATrNZMWEQHj/8iJFBUy+X3fG1zvhZE9zWX5qHVkxlSH3iY14y7NBhh\n" \ + "6UQIrBRiPHagBwYFK4EEACKhZANiAAQlvEGmcz6hluErpKBxJPNRh6wf6qb9ceu7\n" \ + "8CwgDMHbLFYzrnLPDDIaUVRfkrfYBEtL9WSJZUIelJIw8hK1qoXkaL+D/aKWz7Wm\n" \ + "9MWDKS15M62Q2PAfjjFoO69nFPHcqM0=\n" \ + "-----END EC PRIVATE KEY-----\n" + +#define CLIENT_SECP521R1_PRIV_KEY "-----BEGIN EC PARAMETERS-----\n" \ + "BgUrgQQAIw==\n" \ + "-----END EC PARAMETERS-----\n" \ + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MIHcAgEBBEIB4Cj94bbC/xIDnrd8kqlmfum2L6C6l2uajrPXR5dnartodZl1Sswg\n" \ + "IWSimNW2k1LELdDQC+MfOIjCopANRFH5fgmgBwYFK4EEACOhgYkDgYYABAF+9lQh\n" \ + "7WgX0eNpMQEQmMDMiwfb/7QxmlVxHvl/1+Bh89pxzLwrFjGGKmwgSV5f85/vNQdo\n" \ + "jAhzWUTIes3j/qWmBAB63FI2S+yBkhD1tfZl4sUUoLX20T1OexFEk0RRPQI6oCdZ\n" \ + "TFusCC+4trkSnj9gEgLFfwShb0kUFYoBpJzmVFN1BA==\n" \ + "-----END EC PRIVATE KEY-----\n" + +#define SERVER_SECP521R1_PRIV_KEY "-----BEGIN EC PARAMETERS-----\n" \ + "BgUrgQQAIw==\n" \ + "-----END EC PARAMETERS-----\n" \ + "-----BEGIN EC PRIVATE KEY-----\n" \ + "MIHcAgEBBEIAYwEZ+1dvjYoQZhu+0ZS+gY1uB0ON1YvtblgWJI/Blw/pXv4oUfFX\n" \ + "QLXyjkx5ctQzNDKIGEdZ5BcSkBZ+3mJXyuagBwYFK4EEACOhgYkDgYYABACoLmHw\n" \ + "oiWFRf4LAKWTFXEAmx7mVLHvP5YY01PWbbjY2AL3+O5CMBODj3rGuL0lJgRWondF\n" \ + "R6KTS/zw9VK4gyDOXAAyeB4EfVx47ANXQO7bB+dS6WrmUAPY9L6MYkoqngorCf5j\n" \ + "A24QOAiftXdo/IcvXOephiffhGigGetVLd1tIfNM/w==\n" \ + "-----END EC PRIVATE KEY-----\n" + +/* ECDHE shared secrets computed from the private keys above using openssl commands like: + * + * $ openssl pkeyutl -derive -inkey ${CLIENT_PRIV}.pem -peerkey ${SERVER_PUB}.pem -hexdump + */ +#define X25519_SHARED_SECRET "519be87fa0599077e5673d6f2d910aa150d7fef783c5e1491961fdf63b255910" +#define SECP256R1_SHARED_SECRET "9348e27655539e08fffe46b35f863dd634e7437cc6bc11c7d329ef5484ec3b60" +#define SECP384R1_SHARED_SECRET "b72536062cd8e8eced91046e33413b027cabde0576747aa47863b8dcb914100585c600fafc8ff4927a34abb0aa6b3b68" +#define SECP521R1_SHARED_SECRET "009643bb20199e8f408b7c19bb98d1d19f0cef9104e2ec790c398c6abe7dc5cf47afb96de70aa14c86bc546a12f9ea3abbf2eec399b4d586083114cbc37f53ed2d8b" + +/* PQ shared secrets taken from the first entry in the NIST KAT files */ +#define KYBER512R3_SECRET "0A6925676F24B22C286F4C81A4224CEC506C9B257D480E02E3B49F44CAA3237F" +#define KYBER768R3_SECRET "914CB67FE5C38E73BF74181C0AC50428DEDF7750A98058F7D536708774535B29" +#define KYBER1024R3_SECRET "B10F7394926AD3B49C5D62D5AEB531D5757538BCC0DA9E550D438F1B61BD7419" + +/* Hybrid shared secrets are the concatenation: ECDHE || PQ */ +#define X25519_KYBER512R3_HYBRID_SECRET (X25519_SHARED_SECRET KYBER512R3_SECRET) +#define X25519_KYBER768R3_HYBRID_SECRET (X25519_SHARED_SECRET KYBER768R3_SECRET) +#define SECP256R1_KYBER512R3_HYBRID_SECRET (SECP256R1_SHARED_SECRET KYBER512R3_SECRET) +#define SECP256R1_KYBER768R3_HYBRID_SECRET (SECP256R1_SHARED_SECRET KYBER768R3_SECRET) +#define SECP384R1_KYBER768R3_HYBRID_SECRET (SECP384R1_SHARED_SECRET KYBER768R3_SECRET) +#define SECP521R1_KYBER1024R3_HYBRID_SECRET (SECP521R1_SHARED_SECRET KYBER1024R3_SECRET) + +/* The expected traffic secrets were calculated from an independent Python implementation located in the KAT directory, + * using the ECDHE & PQ secrets defined above. */ +#define AES_128_X25519_KYBER512R3_CLIENT_TRAFFIC_SECRET "2d95c9e426941b1cc4a0bd81ee8ba091c6b88edba8c5691dc1b43c0604ff7e74" +#define AES_128_X25519_KYBER512R3_SERVER_TRAFFIC_SECRET "83852c3c0b49f7d260404362eb2d0d91120bc74c149f2224c562d6ac03b29b6e" +#define AES_256_X25519_KYBER512R3_CLIENT_TRAFFIC_SECRET "b929a21fae51da944f32d55976c3da4a2f612f9594f7f4fadd853cab614b3cc4c141d85b920f665eec44c6fbd47bee6b" +#define AES_256_X25519_KYBER512R3_SERVER_TRAFFIC_SECRET "e78dbedab82db5c9fe58db87d0d5cdf031ba7e11dd0cb1c9e2bfe3615569e627142737fc31d659b423b7ebdb476d3672" + +#define AES_128_X25519_KYBER768R3_CLIENT_TRAFFIC_SECRET "0e00d63f0b013fe94d4d376674c0fe68b68a22ddff476429d2a8cee3de607f7c" +#define AES_128_X25519_KYBER768R3_SERVER_TRAFFIC_SECRET "2dba3047b037e34a9bd2413b1f2d39d1071fe97fde6ab8d1be3c53eca074b7cd" +#define AES_256_X25519_KYBER768R3_CLIENT_TRAFFIC_SECRET "84b20ee32e6df46e17b3ad035a670708acc851256ae9a579f57a8135d1f49ea9a720065f09b59b345b4c76300098a899" +#define AES_256_X25519_KYBER768R3_SERVER_TRAFFIC_SECRET "7109a3aebf9f393d53c16480db7881b70f48d464564f08d14ee9895b29ad5c1ad612ce2b45267709b77027c9fbf94599" + +#define AES_128_SECP256R1_KYBER512R3_CLIENT_TRAFFIC_SECRET "f14d3873f61f422a0b59100e0b6da0a970300103a634ad444cf4ca78d3ef4fe4" +#define AES_128_SECP256R1_KYBER512R3_SERVER_TRAFFIC_SECRET "04064ddebdbeaa7b51c15d5e919d8a31da94e6fc979fb354ffe453c15abedf3f" +#define AES_256_SECP256R1_KYBER512R3_CLIENT_TRAFFIC_SECRET "48204afd077b9620c6220fbffa30a6de8867d6b4c96e2194cba1220b603b00850baf9dd041ef5074df86bb241023a0cd" +#define AES_256_SECP256R1_KYBER512R3_SERVER_TRAFFIC_SECRET "f2939045fbe7b612da2e96959c64760e763f2f4ef9be049742f51061e063f89668b9acec12440e2b794352f43173243c" + +#define AES_128_SECP256R1_KYBER768R3_CLIENT_TRAFFIC_SECRET "7570805b40dff0c6aad5d7336e485cae75a43e6d1b7ef813102fcef3e94bb4cb" +#define AES_128_SECP256R1_KYBER768R3_SERVER_TRAFFIC_SECRET "a0f2dda5657466d2bd2de5a0805c5bd93e48da7d3cb5eb43fabf22b67134708e" +#define AES_256_SECP256R1_KYBER768R3_CLIENT_TRAFFIC_SECRET "b5ed2082847839f6fa9f1dd314f6723393c9c793b2190b5fd5d4c8942619388caf8397b856cf4464b2f4787da15f7e16" +#define AES_256_SECP256R1_KYBER768R3_SERVER_TRAFFIC_SECRET "0f3d043c96e012a3563af99ce668ec943969d667340f99619aa69abf1e0b28f3589760f683644b63b578ac0954ff22ae" + +#define AES_128_SECP384R1_KYBER768R3_CLIENT_TRAFFIC_SECRET "ab9ebbb393aa0045da704576c82ee644e8cff724bae443ec9c0e42e07d6c8a04" +#define AES_128_SECP384R1_KYBER768R3_SERVER_TRAFFIC_SECRET "77be795ac50035948de1ef49dd8966197e6056de4a78e563cdec0dcf586f0389" +#define AES_256_SECP384R1_KYBER768R3_CLIENT_TRAFFIC_SECRET "9ffabbee2bede48da18b8b9104744e4eadf3c5360103fc06ffcfb97cc90160035ae0a56a4213fb2dacfae8ff5e72349d" +#define AES_256_SECP384R1_KYBER768R3_SERVER_TRAFFIC_SECRET "ff24d13771ba73281728cf90445b1382247168163b03c87c1fdd28254b73fab6a7da6d2a5a41146c07710e44cb7057bd" + +#define AES_128_SECP521R1_KYBER1024R3_CLIENT_TRAFFIC_SECRET "bd1fda77b536e2f7619a6e7d186a39708e461e0079ffa2462dbe583a7359a890" +#define AES_128_SECP521R1_KYBER1024R3_SERVER_TRAFFIC_SECRET "ee825af01207fb7935f862018f0cd083f88ab5019c8c5e7797afcab77f9fbb0e" +#define AES_256_SECP521R1_KYBER1024R3_CLIENT_TRAFFIC_SECRET "660838cb79c4852258346112f481b75463b39aec83b961cd999741d720b18c95df0c3eabc1ec6b1505703ce1925bf396" +#define AES_256_SECP521R1_KYBER1024R3_SERVER_TRAFFIC_SECRET "19cb80a0d66c0e616891370273b92cf700d1cf32146be6402eb3de62eab6d1ce2d259b404ff29249e8c2af6df416d503" + +/* A fake transcript string to hash when deriving handshake secrets */ +#define FAKE_TRANSCRIPT "client_hello || server_hello" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + S2N_BLOB_FROM_HEX(secp256r1_secret, SECP256R1_SHARED_SECRET); + S2N_BLOB_FROM_HEX(kyber512r3_secret, KYBER512R3_SECRET); + S2N_BLOB_FROM_HEX(secp256r1_kyber512r3_hybrid_secret, SECP256R1_KYBER512R3_HYBRID_SECRET); + + S2N_BLOB_FROM_HEX(aes_128_secp256r1_kyber512r3_client_secret, AES_128_SECP256R1_KYBER512R3_CLIENT_TRAFFIC_SECRET); + S2N_BLOB_FROM_HEX(aes_128_secp256r1_kyber512r3_server_secret, AES_128_SECP256R1_KYBER512R3_SERVER_TRAFFIC_SECRET); + + const struct hybrid_test_vector aes_128_sha_256_secp256r1_kyber512r3_vector = { + .cipher_suite = &s2n_tls13_aes_128_gcm_sha256, + .transcript = FAKE_TRANSCRIPT, + .kem_group = &s2n_secp256r1_kyber_512_r3, + .client_ecc_key = CLIENT_SECP256R1_PRIV_KEY, + .server_ecc_key = SERVER_SECP256R1_PRIV_KEY, + .pq_secret = &kyber512r3_secret, + .expected_hybrid_secret = &secp256r1_kyber512r3_hybrid_secret, + .expected_client_traffic_secret = &aes_128_secp256r1_kyber512r3_client_secret, + .expected_server_traffic_secret = &aes_128_secp256r1_kyber512r3_server_secret, + }; + + S2N_BLOB_FROM_HEX(aes_256_secp256r1_kyber512r3_client_secret, AES_256_SECP256R1_KYBER512R3_CLIENT_TRAFFIC_SECRET); + S2N_BLOB_FROM_HEX(aes_256_secp256r1_kyber512r3_server_secret, AES_256_SECP256R1_KYBER512R3_SERVER_TRAFFIC_SECRET); + + const struct hybrid_test_vector aes_256_sha_384_secp256r1_kyber512r3_vector = { + .cipher_suite = &s2n_tls13_aes_256_gcm_sha384, + .transcript = FAKE_TRANSCRIPT, + .kem_group = &s2n_secp256r1_kyber_512_r3, + .client_ecc_key = CLIENT_SECP256R1_PRIV_KEY, + .server_ecc_key = SERVER_SECP256R1_PRIV_KEY, + .pq_secret = &kyber512r3_secret, + .expected_hybrid_secret = &secp256r1_kyber512r3_hybrid_secret, + .expected_client_traffic_secret = &aes_256_secp256r1_kyber512r3_client_secret, + .expected_server_traffic_secret = &aes_256_secp256r1_kyber512r3_server_secret, + }; + +#if EVP_APIS_SUPPORTED + /* All x25519 based tls13_kem_groups require EVP_APIS_SUPPORTED */ + S2N_BLOB_FROM_HEX(x25519_secret, X25519_SHARED_SECRET); + + S2N_BLOB_FROM_HEX(x25519_kyber512r3_hybrid_secret, X25519_KYBER512R3_HYBRID_SECRET); + S2N_BLOB_FROM_HEX(aes_128_x25519_kyber512r3_client_secret, AES_128_X25519_KYBER512R3_CLIENT_TRAFFIC_SECRET); + S2N_BLOB_FROM_HEX(aes_128_x25519_kyber512r3_server_secret, AES_128_X25519_KYBER512R3_SERVER_TRAFFIC_SECRET); + + const struct hybrid_test_vector aes_128_sha_256_x25519_kyber512r3_vector = { + .cipher_suite = &s2n_tls13_aes_128_gcm_sha256, + .transcript = FAKE_TRANSCRIPT, + .kem_group = &s2n_x25519_kyber_512_r3, + .client_ecc_key = CLIENT_X25519_PRIV_KEY, + .server_ecc_key = SERVER_X25519_PRIV_KEY, + .pq_secret = &kyber512r3_secret, + .expected_hybrid_secret = &x25519_kyber512r3_hybrid_secret, + .expected_client_traffic_secret = &aes_128_x25519_kyber512r3_client_secret, + .expected_server_traffic_secret = &aes_128_x25519_kyber512r3_server_secret, + }; + + S2N_BLOB_FROM_HEX(aes_256_x25519_kyber512r3_client_secret, AES_256_X25519_KYBER512R3_CLIENT_TRAFFIC_SECRET); + S2N_BLOB_FROM_HEX(aes_256_x25519_kyber512r3_server_secret, AES_256_X25519_KYBER512R3_SERVER_TRAFFIC_SECRET); + + const struct hybrid_test_vector aes_256_sha_384_x25519_kyber512r3_vector = { + .cipher_suite = &s2n_tls13_aes_256_gcm_sha384, + .transcript = FAKE_TRANSCRIPT, + .kem_group = &s2n_x25519_kyber_512_r3, + .client_ecc_key = CLIENT_X25519_PRIV_KEY, + .server_ecc_key = SERVER_X25519_PRIV_KEY, + .pq_secret = &kyber512r3_secret, + .expected_hybrid_secret = &x25519_kyber512r3_hybrid_secret, + .expected_client_traffic_secret = &aes_256_x25519_kyber512r3_client_secret, + .expected_server_traffic_secret = &aes_256_x25519_kyber512r3_server_secret, + }; +#endif + +#if defined(S2N_LIBCRYPTO_SUPPORTS_KYBER) + S2N_BLOB_FROM_HEX(secp256r1_kyber768r3_hybrid_secret, SECP256R1_KYBER768R3_HYBRID_SECRET); + + S2N_BLOB_FROM_HEX(secp384r1_secret, SECP384R1_SHARED_SECRET); + S2N_BLOB_FROM_HEX(kyber768r3_secret, KYBER768R3_SECRET); + S2N_BLOB_FROM_HEX(secp384r1_kyber768r3_hybrid_secret, SECP384R1_KYBER768R3_HYBRID_SECRET); + + S2N_BLOB_FROM_HEX(secp521r1_secret, SECP521R1_SHARED_SECRET); + S2N_BLOB_FROM_HEX(kyber1024r3_secret, KYBER1024R3_SECRET); + S2N_BLOB_FROM_HEX(secp521r1_kyber1024r3_hybrid_secret, SECP521R1_KYBER1024R3_HYBRID_SECRET); + + S2N_BLOB_FROM_HEX(aes_128_secp256r1_kyber768r3_client_secret, AES_128_SECP256R1_KYBER768R3_CLIENT_TRAFFIC_SECRET); + S2N_BLOB_FROM_HEX(aes_128_secp256r1_kyber768r3_server_secret, AES_128_SECP256R1_KYBER768R3_SERVER_TRAFFIC_SECRET); + + const struct hybrid_test_vector aes_128_sha_256_secp256r1_kyber768r3_vector = { + .cipher_suite = &s2n_tls13_aes_128_gcm_sha256, + .transcript = FAKE_TRANSCRIPT, + .kem_group = &s2n_secp256r1_kyber_768_r3, + .client_ecc_key = CLIENT_SECP256R1_PRIV_KEY, + .server_ecc_key = SERVER_SECP256R1_PRIV_KEY, + .pq_secret = &kyber768r3_secret, + .expected_hybrid_secret = &secp256r1_kyber768r3_hybrid_secret, + .expected_client_traffic_secret = &aes_128_secp256r1_kyber768r3_client_secret, + .expected_server_traffic_secret = &aes_128_secp256r1_kyber768r3_server_secret, + }; + + S2N_BLOB_FROM_HEX(aes_256_secp256r1_kyber768r3_client_secret, AES_256_SECP256R1_KYBER768R3_CLIENT_TRAFFIC_SECRET); + S2N_BLOB_FROM_HEX(aes_256_secp256r1_kyber768r3_server_secret, AES_256_SECP256R1_KYBER768R3_SERVER_TRAFFIC_SECRET); + + const struct hybrid_test_vector aes_256_sha_384_secp256r1_kyber768r3_vector = { + .cipher_suite = &s2n_tls13_aes_256_gcm_sha384, + .transcript = FAKE_TRANSCRIPT, + .kem_group = &s2n_secp256r1_kyber_768_r3, + .client_ecc_key = CLIENT_SECP256R1_PRIV_KEY, + .server_ecc_key = SERVER_SECP256R1_PRIV_KEY, + .pq_secret = &kyber768r3_secret, + .expected_hybrid_secret = &secp256r1_kyber768r3_hybrid_secret, + .expected_client_traffic_secret = &aes_256_secp256r1_kyber768r3_client_secret, + .expected_server_traffic_secret = &aes_256_secp256r1_kyber768r3_server_secret, + }; + + S2N_BLOB_FROM_HEX(aes_128_secp384r1_kyber768r3_client_secret, AES_128_SECP384R1_KYBER768R3_CLIENT_TRAFFIC_SECRET); + S2N_BLOB_FROM_HEX(aes_128_secp384r1_kyber768r3_server_secret, AES_128_SECP384R1_KYBER768R3_SERVER_TRAFFIC_SECRET); + + const struct hybrid_test_vector aes_128_sha_256_secp384r1_kyber768r3_vector = { + .cipher_suite = &s2n_tls13_aes_128_gcm_sha256, + .transcript = FAKE_TRANSCRIPT, + .kem_group = &s2n_secp384r1_kyber_768_r3, + .client_ecc_key = CLIENT_SECP384R1_PRIV_KEY, + .server_ecc_key = SERVER_SECP384R1_PRIV_KEY, + .pq_secret = &kyber768r3_secret, + .expected_hybrid_secret = &secp384r1_kyber768r3_hybrid_secret, + .expected_client_traffic_secret = &aes_128_secp384r1_kyber768r3_client_secret, + .expected_server_traffic_secret = &aes_128_secp384r1_kyber768r3_server_secret, + }; + + S2N_BLOB_FROM_HEX(aes_256_secp384r1_kyber768r3_client_secret, AES_256_SECP384R1_KYBER768R3_CLIENT_TRAFFIC_SECRET); + S2N_BLOB_FROM_HEX(aes_256_secp384r1_kyber768r3_server_secret, AES_256_SECP384R1_KYBER768R3_SERVER_TRAFFIC_SECRET); + + const struct hybrid_test_vector aes_256_sha_384_secp384r1_kyber768r3_vector = { + .cipher_suite = &s2n_tls13_aes_256_gcm_sha384, + .transcript = FAKE_TRANSCRIPT, + .kem_group = &s2n_secp384r1_kyber_768_r3, + .client_ecc_key = CLIENT_SECP384R1_PRIV_KEY, + .server_ecc_key = SERVER_SECP384R1_PRIV_KEY, + .pq_secret = &kyber768r3_secret, + .expected_hybrid_secret = &secp384r1_kyber768r3_hybrid_secret, + .expected_client_traffic_secret = &aes_256_secp384r1_kyber768r3_client_secret, + .expected_server_traffic_secret = &aes_256_secp384r1_kyber768r3_server_secret, + }; + + S2N_BLOB_FROM_HEX(aes_128_secp521r1_kyber1024r3_client_secret, AES_128_SECP521R1_KYBER1024R3_CLIENT_TRAFFIC_SECRET); + S2N_BLOB_FROM_HEX(aes_128_secp521r1_kyber1024r3_server_secret, AES_128_SECP521R1_KYBER1024R3_SERVER_TRAFFIC_SECRET); + + const struct hybrid_test_vector aes_128_sha_256_secp521r1_kyber1024r3_vector = { + .cipher_suite = &s2n_tls13_aes_128_gcm_sha256, + .transcript = FAKE_TRANSCRIPT, + .kem_group = &s2n_secp521r1_kyber_1024_r3, + .client_ecc_key = CLIENT_SECP521R1_PRIV_KEY, + .server_ecc_key = SERVER_SECP521R1_PRIV_KEY, + .pq_secret = &kyber1024r3_secret, + .expected_hybrid_secret = &secp521r1_kyber1024r3_hybrid_secret, + .expected_client_traffic_secret = &aes_128_secp521r1_kyber1024r3_client_secret, + .expected_server_traffic_secret = &aes_128_secp521r1_kyber1024r3_server_secret, + }; + + S2N_BLOB_FROM_HEX(aes_256_secp521r1_kyber1024r3_client_secret, AES_256_SECP521R1_KYBER1024R3_CLIENT_TRAFFIC_SECRET); + S2N_BLOB_FROM_HEX(aes_256_secp521r1_kyber1024r3_server_secret, AES_256_SECP521R1_KYBER1024R3_SERVER_TRAFFIC_SECRET); + + const struct hybrid_test_vector aes_256_sha_384_secp521r1_kyber1024r3_vector = { + .cipher_suite = &s2n_tls13_aes_256_gcm_sha384, + .transcript = FAKE_TRANSCRIPT, + .kem_group = &s2n_secp521r1_kyber_1024_r3, + .client_ecc_key = CLIENT_SECP521R1_PRIV_KEY, + .server_ecc_key = SERVER_SECP521R1_PRIV_KEY, + .pq_secret = &kyber1024r3_secret, + .expected_hybrid_secret = &secp521r1_kyber1024r3_hybrid_secret, + .expected_client_traffic_secret = &aes_256_secp521r1_kyber1024r3_client_secret, + .expected_server_traffic_secret = &aes_256_secp521r1_kyber1024r3_server_secret, + }; +#endif + +#if EVP_APIS_SUPPORTED && defined(S2N_LIBCRYPTO_SUPPORTS_KYBER) + S2N_BLOB_FROM_HEX(x25519_kyber768r3_hybrid_secret, X25519_KYBER768R3_HYBRID_SECRET); + S2N_BLOB_FROM_HEX(aes_128_x25519_kyber768r3_client_secret, AES_128_X25519_KYBER768R3_CLIENT_TRAFFIC_SECRET); + S2N_BLOB_FROM_HEX(aes_128_x25519_kyber768r3_server_secret, AES_128_X25519_KYBER768R3_SERVER_TRAFFIC_SECRET); + + const struct hybrid_test_vector aes_128_sha_256_x25519_kyber768r3_vector = { + .cipher_suite = &s2n_tls13_aes_128_gcm_sha256, + .transcript = FAKE_TRANSCRIPT, + .kem_group = &s2n_x25519_kyber_768_r3, + .client_ecc_key = CLIENT_X25519_PRIV_KEY, + .server_ecc_key = SERVER_X25519_PRIV_KEY, + .pq_secret = &kyber768r3_secret, + .expected_hybrid_secret = &x25519_kyber768r3_hybrid_secret, + .expected_client_traffic_secret = &aes_128_x25519_kyber768r3_client_secret, + .expected_server_traffic_secret = &aes_128_x25519_kyber768r3_server_secret, + }; + + S2N_BLOB_FROM_HEX(aes_256_x25519_kyber768r3_client_secret, AES_256_X25519_KYBER768R3_CLIENT_TRAFFIC_SECRET); + S2N_BLOB_FROM_HEX(aes_256_x25519_kyber768r3_server_secret, AES_256_X25519_KYBER768R3_SERVER_TRAFFIC_SECRET); + + const struct hybrid_test_vector aes_256_sha_384_x25519_kyber768r3_vector = { + .cipher_suite = &s2n_tls13_aes_256_gcm_sha384, + .transcript = FAKE_TRANSCRIPT, + .kem_group = &s2n_x25519_kyber_768_r3, + .client_ecc_key = CLIENT_X25519_PRIV_KEY, + .server_ecc_key = SERVER_X25519_PRIV_KEY, + .pq_secret = &kyber768r3_secret, + .expected_hybrid_secret = &x25519_kyber768r3_hybrid_secret, + .expected_client_traffic_secret = &aes_256_x25519_kyber768r3_client_secret, + .expected_server_traffic_secret = &aes_256_x25519_kyber768r3_server_secret, + }; +#endif + + const struct hybrid_test_vector *all_test_vectors[] = { + &aes_128_sha_256_secp256r1_kyber512r3_vector, + &aes_256_sha_384_secp256r1_kyber512r3_vector, +#if EVP_APIS_SUPPORTED + &aes_128_sha_256_x25519_kyber512r3_vector, + &aes_256_sha_384_x25519_kyber512r3_vector, +#endif +#if defined(S2N_LIBCRYPTO_SUPPORTS_KYBER) + &aes_128_sha_256_secp256r1_kyber768r3_vector, + &aes_256_sha_384_secp256r1_kyber768r3_vector, + &aes_128_sha_256_secp384r1_kyber768r3_vector, + &aes_256_sha_384_secp384r1_kyber768r3_vector, + &aes_128_sha_256_secp521r1_kyber1024r3_vector, + &aes_256_sha_384_secp521r1_kyber1024r3_vector, +#endif +#if EVP_APIS_SUPPORTED && defined(S2N_LIBCRYPTO_SUPPORTS_KYBER) + &aes_128_sha_256_x25519_kyber768r3_vector, + &aes_256_sha_384_x25519_kyber768r3_vector, +#endif + }; + + EXPECT_EQUAL(s2n_array_len(all_test_vectors), (2 * S2N_SUPPORTED_KEM_GROUPS_COUNT)); + + { + /* Happy cases for computing the hybrid shared secret and client & server traffic secrets */ + for (int i = 0; i < s2n_array_len(all_test_vectors); i++) { + const struct hybrid_test_vector *test_vector = all_test_vectors[i]; + const struct s2n_kem_group *kem_group = test_vector->kem_group; + + /* Set up connections */ + struct s2n_connection *client_conn = NULL; + struct s2n_connection *server_conn = NULL; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_SUCCESS(set_up_conns(client_conn, server_conn, test_vector->client_ecc_key, + test_vector->server_ecc_key, kem_group, test_vector->pq_secret)); + + /* Calculate the hybrid shared secret */ + DEFER_CLEANUP(struct s2n_blob client_calculated_shared_secret = { 0 }, s2n_free); + DEFER_CLEANUP(struct s2n_blob server_calculated_shared_secret = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_tls13_compute_shared_secret(client_conn, &client_calculated_shared_secret)); + EXPECT_SUCCESS(s2n_tls13_compute_shared_secret(server_conn, &server_calculated_shared_secret)); + + /* Assert correctness of hybrid shared secret */ + S2N_BLOB_EXPECT_EQUAL(client_calculated_shared_secret, server_calculated_shared_secret); + EXPECT_EQUAL(test_vector->expected_hybrid_secret->size, client_calculated_shared_secret.size); + EXPECT_BYTEARRAY_EQUAL(test_vector->expected_hybrid_secret->data, client_calculated_shared_secret.data, + client_calculated_shared_secret.size); + + EXPECT_SUCCESS(assert_kem_group_params_freed(client_conn)); + EXPECT_SUCCESS(assert_kem_group_params_freed(server_conn)); + + /* Reset conns. Calculating the shared secret frees necessary params. */ + EXPECT_SUCCESS(set_up_conns(client_conn, server_conn, test_vector->client_ecc_key, + test_vector->server_ecc_key, kem_group, test_vector->pq_secret)); + + /* Compute the transcript hash, then use the hybrid shared secret to derive + * the client & server traffic secrets */ + DEFER_CLEANUP(struct s2n_tls13_keys secrets = { 0 }, s2n_tls13_keys_free); + EXPECT_SUCCESS(s2n_tls13_keys_init(&secrets, test_vector->cipher_suite->prf_alg)); + client_conn->secure->cipher_suite = test_vector->cipher_suite; + + DEFER_CLEANUP(struct s2n_hash_state hash_state, s2n_hash_free); + EXPECT_SUCCESS(s2n_hash_new(&hash_state)); + EXPECT_SUCCESS(s2n_hash_init(&hash_state, secrets.hash_algorithm)); + EXPECT_SUCCESS(s2n_hash_update(&hash_state, test_vector->transcript, strlen(test_vector->transcript))); + EXPECT_SUCCESS(s2n_hash_digest(&hash_state, client_conn->handshake.hashes->transcript_hash_digest, secrets.size)); + + client_conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; + while (s2n_conn_get_current_message_type(client_conn) != SERVER_HELLO) { + client_conn->handshake.message_number++; + } + + s2n_tls13_key_blob(client_traffic_secret, secrets.size); + s2n_tls13_key_blob(server_traffic_secret, secrets.size); + EXPECT_OK(s2n_tls13_derive_secret(client_conn, S2N_HANDSHAKE_SECRET, S2N_CLIENT, &client_traffic_secret)); + EXPECT_OK(s2n_tls13_derive_secret(client_conn, S2N_HANDSHAKE_SECRET, S2N_SERVER, &server_traffic_secret)); + + /* Assert correctness of traffic secrets */ + EXPECT_EQUAL(test_vector->expected_client_traffic_secret->size, client_traffic_secret.size); + EXPECT_BYTEARRAY_EQUAL(test_vector->expected_client_traffic_secret->data, client_traffic_secret.data, + client_traffic_secret.size); + + EXPECT_EQUAL(test_vector->expected_server_traffic_secret->size, server_traffic_secret.size); + EXPECT_BYTEARRAY_EQUAL(test_vector->expected_server_traffic_secret->data, server_traffic_secret.data, + server_traffic_secret.size); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + } + }; + { + /* Various failure cases for s2n_tls13_compute_shared_secret() */ + const struct hybrid_test_vector *test_vector = &aes_128_sha_256_secp256r1_kyber512r3_vector; + s2n_mode modes[] = { S2N_SERVER, S2N_CLIENT }; + + for (size_t i = 0; i < s2n_array_len(modes); i++) { + /* Failures because of NULL arguments */ + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_compute_pq_hybrid_shared_secret(NULL, NULL), S2N_ERR_NULL); + struct s2n_connection *conn = NULL; + EXPECT_NOT_NULL(conn = s2n_connection_new(modes[i])); + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_compute_pq_hybrid_shared_secret(conn, NULL), S2N_ERR_NULL); + DEFER_CLEANUP(struct s2n_blob calculated_shared_secret = { 0 }, s2n_free); + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_compute_pq_hybrid_shared_secret(NULL, &calculated_shared_secret), S2N_ERR_NULL); + + /* Failures because classic (non-hybrid) parameters were configured */ + conn->kex_params.server_ecc_evp_params.negotiated_curve = &s2n_ecc_curve_secp256r1; + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_compute_pq_hybrid_shared_secret(conn, &calculated_shared_secret), S2N_ERR_SAFETY); + conn->kex_params.server_ecc_evp_params.negotiated_curve = NULL; + EXPECT_SUCCESS(read_priv_ecc(&conn->kex_params.server_ecc_evp_params.evp_pkey, test_vector->client_ecc_key)); + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_compute_pq_hybrid_shared_secret(conn, &calculated_shared_secret), S2N_ERR_SAFETY); + EXPECT_SUCCESS(s2n_ecc_evp_params_free(&conn->kex_params.server_ecc_evp_params)); + + /* Failure because the chosen_client_kem_group_params is NULL */ + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_compute_pq_hybrid_shared_secret(conn, &calculated_shared_secret), S2N_ERR_NULL); + + /* Failures because the kem_group_params aren't set */ + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_compute_pq_hybrid_shared_secret(conn, &calculated_shared_secret), S2N_ERR_NULL); + conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve = test_vector->kem_group->curve; + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_compute_pq_hybrid_shared_secret(conn, &calculated_shared_secret), S2N_ERR_NULL); + conn->kex_params.client_kem_group_params.ecc_params.negotiated_curve = test_vector->kem_group->curve; + + /* Failures because the ECC private keys are NULL */ + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_compute_pq_hybrid_shared_secret(conn, &calculated_shared_secret), S2N_ERR_NULL); + EXPECT_SUCCESS(read_priv_ecc(&conn->kex_params.client_kem_group_params.ecc_params.evp_pkey, test_vector->client_ecc_key)); + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_compute_pq_hybrid_shared_secret(conn, &calculated_shared_secret), S2N_ERR_NULL); + EXPECT_SUCCESS(read_priv_ecc(&conn->kex_params.server_kem_group_params.ecc_params.evp_pkey, test_vector->server_ecc_key)); + + /* Failure because pq_shared_secret is NULL */ + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_compute_pq_hybrid_shared_secret(conn, &calculated_shared_secret), S2N_ERR_NULL); + EXPECT_SUCCESS(s2n_dup(test_vector->pq_secret, &conn->kex_params.client_kem_group_params.kem_params.shared_secret)); + + /* Failure because the kem_group is NULL */ + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_compute_pq_hybrid_shared_secret(conn, &calculated_shared_secret), S2N_ERR_NULL); + conn->kex_params.server_kem_group_params.kem_group = test_vector->kem_group; + + /* Finally, success */ + EXPECT_SUCCESS(s2n_tls13_compute_pq_hybrid_shared_secret(conn, &calculated_shared_secret)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + }; + + END_TEST(); +} + +static int read_priv_ecc(EVP_PKEY **pkey, const char *priv_ecc) +{ + size_t key_len = sizeof(char) * strlen(priv_ecc); + +#if defined(LIBRESSL_VERSION_NUMBER) + /* LibreSSL's BIO_new_mem_buf() function signature requires a non-const + * input buffer. */ + + DEFER_CLEANUP(struct s2n_blob priv_ecc_blob = { 0 }, s2n_free); + POSIX_GUARD(s2n_alloc(&priv_ecc_blob, key_len)); + for (size_t i = 0; i < key_len; i++) { + priv_ecc_blob.data[i] = priv_ecc[i]; + } + + BIO *bio = BIO_new_mem_buf((void *) priv_ecc_blob.data, key_len); +#else + BIO *bio = BIO_new_mem_buf((const void *) priv_ecc, key_len); +#endif + + POSIX_ENSURE_REF(bio); + PEM_read_bio_PrivateKey(bio, pkey, 0, NULL); + /* Caller should assert notnull_check on *pkey */ + + /* BIO_free returns 1 for success */ + POSIX_ENSURE_EQ(1, BIO_free(bio)); + + return 0; +} + +static int set_up_conns(struct s2n_connection *client_conn, struct s2n_connection *server_conn, + const char *client_priv_ecc, const char *server_priv_ecc, const struct s2n_kem_group *kem_group, + struct s2n_blob *pq_shared_secret) +{ + /* These parameters would normally be set during the handshake */ + server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve = kem_group->curve; + server_conn->kex_params.client_kem_group_params.ecc_params.negotiated_curve = kem_group->curve; + client_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve = kem_group->curve; + client_conn->kex_params.client_kem_group_params.ecc_params.negotiated_curve = kem_group->curve; + + server_conn->kex_params.server_kem_group_params.kem_group = kem_group; + server_conn->kex_params.client_kem_group_params.kem_group = kem_group; + client_conn->kex_params.server_kem_group_params.kem_group = kem_group; + client_conn->kex_params.client_kem_group_params.kem_group = kem_group; + + server_conn->kex_params.server_kem_group_params.kem_params.kem = kem_group->kem; + server_conn->kex_params.client_kem_group_params.kem_params.kem = kem_group->kem; + client_conn->kex_params.server_kem_group_params.kem_params.kem = kem_group->kem; + client_conn->kex_params.client_kem_group_params.kem_params.kem = kem_group->kem; + + /* During an actual handshake, server will generate the shared secret and store it in chosen_client_kem_group_params, + * client will decapsulate the ciphertext and store the shared secret in chosen_client_kem_group_params. */ + POSIX_GUARD(s2n_dup(pq_shared_secret, &server_conn->kex_params.client_kem_group_params.kem_params.shared_secret)); + POSIX_GUARD(s2n_dup(pq_shared_secret, &client_conn->kex_params.client_kem_group_params.kem_params.shared_secret)); + + /* Populate the client's PQ private key with something - it doesn't have to be a + * legitimate private key since it doesn't get used in the shared secret derivation, + * but we want to make sure its definitely been freed after shared secret calculation */ + POSIX_GUARD(s2n_alloc(&client_conn->kex_params.client_kem_group_params.kem_params.private_key, 2)); + struct s2n_stuffer private_key_stuffer = { 0 }; + POSIX_GUARD(s2n_stuffer_init(&private_key_stuffer, + &client_conn->kex_params.client_kem_group_params.kem_params.private_key)); + uint8_t fake_private_key[] = { 3, 3 }; + POSIX_GUARD(s2n_stuffer_write_bytes(&private_key_stuffer, fake_private_key, 2)); + + /* "Import" the provided private ECC keys */ + POSIX_ENSURE_EQ(sizeof(char) * strlen(client_priv_ecc), sizeof(char) * strlen(server_priv_ecc)); + POSIX_GUARD(read_priv_ecc(&client_conn->kex_params.client_kem_group_params.ecc_params.evp_pkey, client_priv_ecc)); + POSIX_ENSURE_REF(client_conn->kex_params.client_kem_group_params.ecc_params.evp_pkey); + POSIX_GUARD(read_priv_ecc(&server_conn->kex_params.server_kem_group_params.ecc_params.evp_pkey, server_priv_ecc)); + POSIX_ENSURE_REF(server_conn->kex_params.server_kem_group_params.ecc_params.evp_pkey); + + /* Each peer sends its public ECC key to the other */ + struct s2n_stuffer wire = { 0 }; + struct s2n_blob server_point_blob, client_point_blob; + uint16_t share_size = kem_group->curve->share_size; + + POSIX_GUARD(s2n_stuffer_growable_alloc(&wire, 1024)); + + POSIX_GUARD(s2n_ecc_evp_write_params_point(&server_conn->kex_params.server_kem_group_params.ecc_params, &wire)); + POSIX_GUARD(s2n_ecc_evp_read_params_point(&wire, share_size, &server_point_blob)); + POSIX_GUARD(s2n_ecc_evp_parse_params_point(&server_point_blob, &client_conn->kex_params.server_kem_group_params.ecc_params)); + + POSIX_GUARD(s2n_ecc_evp_write_params_point(&client_conn->kex_params.client_kem_group_params.ecc_params, &wire)); + POSIX_GUARD(s2n_ecc_evp_read_params_point(&wire, share_size, &client_point_blob)); + POSIX_GUARD(s2n_ecc_evp_parse_params_point(&client_point_blob, &server_conn->kex_params.client_kem_group_params.ecc_params)); + + POSIX_GUARD(s2n_stuffer_free(&wire)); + + return S2N_SUCCESS; +} + +static int assert_kem_group_params_freed(struct s2n_connection *conn) +{ + POSIX_ENSURE_EQ(NULL, conn->kex_params.server_kem_group_params.ecc_params.evp_pkey); + POSIX_ENSURE_EQ(NULL, conn->kex_params.server_kem_group_params.kem_params.shared_secret.data); + POSIX_ENSURE_EQ(0, conn->kex_params.server_kem_group_params.kem_params.shared_secret.allocated); + POSIX_ENSURE_EQ(NULL, conn->kex_params.server_kem_group_params.kem_params.private_key.data); + POSIX_ENSURE_EQ(0, conn->kex_params.server_kem_group_params.kem_params.private_key.allocated); + POSIX_ENSURE_EQ(NULL, conn->kex_params.server_kem_group_params.kem_params.public_key.data); + POSIX_ENSURE_EQ(0, conn->kex_params.server_kem_group_params.kem_params.public_key.allocated); + + POSIX_ENSURE_EQ(NULL, conn->kex_params.client_kem_group_params.ecc_params.evp_pkey); + POSIX_ENSURE_EQ(NULL, conn->kex_params.client_kem_group_params.kem_params.shared_secret.data); + POSIX_ENSURE_EQ(0, conn->kex_params.client_kem_group_params.kem_params.shared_secret.allocated); + POSIX_ENSURE_EQ(NULL, conn->kex_params.client_kem_group_params.kem_params.private_key.data); + POSIX_ENSURE_EQ(0, conn->kex_params.client_kem_group_params.kem_params.private_key.allocated); + POSIX_ENSURE_EQ(NULL, conn->kex_params.client_kem_group_params.kem_params.public_key.data); + POSIX_ENSURE_EQ(0, conn->kex_params.client_kem_group_params.kem_params.public_key.allocated); + + return S2N_SUCCESS; +} diff --git a/tests/unit/s2n_tls13_key_schedule_rfc8448_test.c b/tests/unit/s2n_tls13_key_schedule_rfc8448_test.c new file mode 100644 index 00000000000..66dcbef76fc --- /dev/null +++ b/tests/unit/s2n_tls13_key_schedule_rfc8448_test.c @@ -0,0 +1,365 @@ +/* + * 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. + */ + +/* Needed to set up X25519 key shares */ +#include + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_tls13_key_schedule.h" +#include "tls/s2n_tls13_secrets.h" + +const s2n_mode modes[] = { S2N_SERVER, S2N_CLIENT }; + +static uint8_t test_send_key[S2N_TLS_AES_256_GCM_KEY_LEN] = { 0 }; +static int s2n_test_set_send_key(struct s2n_session_key *key, struct s2n_blob *in) +{ + POSIX_ENSURE_REF(key); + POSIX_ENSURE_REF(in); + POSIX_CHECKED_MEMCPY(test_send_key, in->data, in->size); + return S2N_SUCCESS; +} + +static uint8_t test_recv_key[S2N_TLS_AES_256_GCM_KEY_LEN] = { 0 }; +static int s2n_test_set_recv_key(struct s2n_session_key *key, struct s2n_blob *in) +{ + POSIX_ENSURE_REF(key); + POSIX_ENSURE_REF(in); + POSIX_CHECKED_MEMCPY(test_recv_key, in->data, in->size); + return S2N_SUCCESS; +} + +#define EXPECT_IVS_EQUAL(conn, iv, iv_mode) \ + if ((iv_mode) == S2N_CLIENT) { \ + EXPECT_BYTEARRAY_EQUAL((conn)->secure->client_implicit_iv, (iv).data, (iv).size); \ + } else { \ + EXPECT_BYTEARRAY_EQUAL((conn)->secure->server_implicit_iv, (iv).data, (iv).size); \ + } + +#define EXPECT_KEYS_EQUAL(conn, key, key_mode) \ + if ((conn)->mode == (key_mode)) { \ + EXPECT_BYTEARRAY_EQUAL(test_send_key, (key).data, (key).size); \ + } else { \ + EXPECT_BYTEARRAY_EQUAL(test_recv_key, (key).data, (key).size); \ + } + +static S2N_RESULT s2n_set_test_secret(struct s2n_connection *conn, uint8_t *secret_bytes, const struct s2n_blob secret) +{ + RESULT_ENSURE_REF(conn); + RESULT_ENSURE_REF(secret_bytes); + RESULT_CHECKED_MEMCPY(secret_bytes, secret.data, secret.size); + /* + * Mark the last secret extracted as the master secret to + * indicate that all secrets have already been derived. + * This test is interested in keys, not secrets. + */ + conn->secrets.extract_secret_type = S2N_MASTER_SECRET; + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* The RFC values use x25519, + * which is only supported via EVP APIs. + */ + if (!s2n_is_evp_apis_supported()) { + END_TEST(); + } + + /* Once a key is set via the standard ciphers, we are unable to retrieve it. + * So use a custom cipher to store the keys for later verification. + */ + struct s2n_cipher_suite test_cipher_suite = s2n_tls13_aes_128_gcm_sha256; + struct s2n_record_algorithm test_record_alg = *(test_cipher_suite.record_alg); + struct s2n_cipher test_cipher = *(test_record_alg.cipher); + test_cipher.set_decryption_key = &s2n_test_set_recv_key; + test_cipher.set_encryption_key = &s2n_test_set_send_key; + test_record_alg.cipher = &test_cipher; + test_cipher_suite.record_alg = &test_record_alg; + struct s2n_cipher_suite *cipher_suite = &test_cipher_suite; + + /* + * Simple 1-RTT Handshake + */ + { + const uint32_t one_rtt_handshake_type = NEGOTIATED | FULL_HANDSHAKE; + const int one_rtt_message_nums[] = { + [SERVER_HELLO] = 1, + [SERVER_FINISHED] = 5, + [CLIENT_FINISHED] = 6, + }; + + /* Derive server handshake traffic keys */ + { + /** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {client} derive read traffic keys for handshake data (same as server + *# handshake data write traffic keys) + * + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {server} derive write traffic keys for handshake data: + *# + *# PRK (32 octets): b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d 37 b4 + *# e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38 + *# + *# key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00 + *# + *# key expanded (16 octets): 3f ce 51 60 09 c2 17 27 d0 f2 e4 e8 6e + *# e4 03 bc + *# + *# iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00 + *# + *# iv expanded (12 octets): 5d 31 3e b2 67 12 76 ee 13 00 0b 30 + */ + S2N_BLOB_FROM_HEX(secret, "b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d 37 b4 \ + e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38"); + S2N_BLOB_FROM_HEX(key, "3f ce 51 60 09 c2 17 27 d0 f2 e4 e8 6e \ + e4 03 bc"); + S2N_BLOB_FROM_HEX(iv, "5d 31 3e b2 67 12 76 ee 13 00 0b 30"); + + for (size_t i = 0; i < s2n_array_len(modes); i++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(modes[i]), s2n_connection_ptr_free); + conn->secure->cipher_suite = cipher_suite; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_set_test_secret(conn, conn->secrets.version.tls13.server_handshake_secret, secret)); + + conn->handshake.handshake_type = one_rtt_handshake_type; + conn->handshake.message_number = one_rtt_message_nums[SERVER_HELLO]; + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_HELLO); + EXPECT_OK(s2n_tls13_key_schedule_update(conn)); + + EXPECT_IVS_EQUAL(conn, iv, S2N_SERVER); + EXPECT_KEYS_EQUAL(conn, key, S2N_SERVER); + } + }; + + /* Derive client handshake traffic keys */ + { + /** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {client} derive write traffic keys for handshake data (same as + *# server handshake data read traffic keys) + * + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {server} derive read traffic keys for handshake data: + *# + *# PRK (32 octets): b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e 2d 8f + *# 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21 + *# + *# key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00 + *# + *# key expanded (16 octets): db fa a6 93 d1 76 2c 5b 66 6a f5 d9 50 + *# 25 8d 01 + *# + *# iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00 + *# + *# iv expanded (12 octets): 5b d3 c7 1b 83 6e 0b 76 bb 73 26 5f + */ + S2N_BLOB_FROM_HEX(secret, "b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e 2d 8f \ + 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21"); + S2N_BLOB_FROM_HEX(key, "db fa a6 93 d1 76 2c 5b 66 6a f5 d9 50 \ + 25 8d 01"); + S2N_BLOB_FROM_HEX(iv, "5b d3 c7 1b 83 6e 0b 76 bb 73 26 5f"); + + for (size_t i = 0; i < s2n_array_len(modes); i++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(modes[i]), s2n_connection_ptr_free); + conn->secure->cipher_suite = cipher_suite; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + EXPECT_OK(s2n_set_test_secret(conn, conn->secrets.version.tls13.client_handshake_secret, secret)); + + conn->handshake.handshake_type = one_rtt_handshake_type; + conn->handshake.message_number = one_rtt_message_nums[SERVER_FINISHED]; + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), SERVER_FINISHED); + EXPECT_OK(s2n_tls13_key_schedule_update(conn)); + + EXPECT_IVS_EQUAL(conn, iv, S2N_CLIENT); + EXPECT_KEYS_EQUAL(conn, key, S2N_CLIENT); + } + }; + + /* Derive server application traffic keys */ + { + /** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {client} derive read traffic keys for application data (same as + *# server application data write traffic keys) + * + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {server} derive write traffic keys for application data: + *# + *# PRK (32 octets): a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9 50 32 + *# 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43 + *# + *# key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00 + *# + *# key expanded (16 octets): 9f 02 28 3b 6c 9c 07 ef c2 6b b9 f2 ac + *# 92 e3 56 + *# + *# iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00 + *# + *# iv expanded (12 octets): cf 78 2b 88 dd 83 54 9a ad f1 e9 84 + */ + S2N_BLOB_FROM_HEX(secret, "a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9 50 32 \ + 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43"); + S2N_BLOB_FROM_HEX(key, "9f 02 28 3b 6c 9c 07 ef c2 6b b9 f2 ac \ + 92 e3 56"); + S2N_BLOB_FROM_HEX(iv, "cf 78 2b 88 dd 83 54 9a ad f1 e9 84"); + + const message_type_t trigger_messages[] = { + [S2N_CLIENT] = CLIENT_FINISHED, + [S2N_SERVER] = SERVER_FINISHED, + }; + + for (size_t i = 0; i < s2n_array_len(modes); i++) { + message_type_t trigger_message = trigger_messages[modes[i]]; + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(modes[i]), s2n_connection_ptr_free); + conn->secure->cipher_suite = cipher_suite; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + EXPECT_OK(s2n_set_test_secret(conn, conn->secrets.version.tls13.server_app_secret, secret)); + + conn->handshake.handshake_type = one_rtt_handshake_type; + conn->handshake.message_number = one_rtt_message_nums[trigger_message]; + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), trigger_message); + EXPECT_OK(s2n_tls13_key_schedule_update(conn)); + + EXPECT_IVS_EQUAL(conn, iv, S2N_SERVER); + EXPECT_KEYS_EQUAL(conn, key, S2N_SERVER); + } + }; + + /* Derive client application traffic keys */ + { + /** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {server} derive read traffic keys for application data (same as + *# client application data write traffic keys) + * + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {client} derive write traffic keys for application data: + *# + *# PRK (32 octets): 9e 40 64 6c e7 9a 7f 9d c0 5a f8 88 9b ce 65 52 + *# 87 5a fa 0b 06 df 00 87 f7 92 eb b7 c1 75 04 a5 + *# + *# key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00 + *# + *# key expanded (16 octets): 17 42 2d da 59 6e d5 d9 ac d8 90 e3 c6 + *# 3f 50 51 + *# + *# iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00 + *# + *# iv expanded (12 octets): 5b 78 92 3d ee 08 57 90 33 e5 23 d9 + */ + S2N_BLOB_FROM_HEX(secret, "9e 40 64 6c e7 9a 7f 9d c0 5a f8 88 9b ce 65 52 \ + 87 5a fa 0b 06 df 00 87 f7 92 eb b7 c1 75 04 a5"); + S2N_BLOB_FROM_HEX(key, "17 42 2d da 59 6e d5 d9 ac d8 90 e3 c6 \ + 3f 50 51"); + S2N_BLOB_FROM_HEX(iv, "5b 78 92 3d ee 08 57 90 33 e5 23 d9"); + + for (size_t i = 0; i < s2n_array_len(modes); i++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(modes[i]), s2n_connection_ptr_free); + conn->secure->cipher_suite = cipher_suite; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + EXPECT_OK(s2n_set_test_secret(conn, conn->secrets.version.tls13.client_app_secret, secret)); + + conn->handshake.handshake_type = one_rtt_handshake_type; + conn->handshake.message_number = one_rtt_message_nums[CLIENT_FINISHED]; + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), CLIENT_FINISHED); + EXPECT_OK(s2n_tls13_key_schedule_update(conn)); + + EXPECT_IVS_EQUAL(conn, iv, S2N_CLIENT); + EXPECT_KEYS_EQUAL(conn, key, S2N_CLIENT); + } + }; + }; + + /* Resumed 0-RTT Handshake */ + { + const uint32_t resumed_handshake_type = NEGOTIATED | WITH_EARLY_DATA; + const int resumed_message_nums[] = { + [CLIENT_HELLO] = 0, + [SERVER_FINISHED] = 3, + }; + + /* Derive early application traffic keys */ + { + /** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-4 + *= type=test + *# {server} derive read traffic keys for early application data (same + *# as client early application data write traffic keys) + * + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-4 + *= type=test + *# {client} derive write traffic keys for early application data: + *# + *# PRK (32 octets): 3f bb e6 a6 0d eb 66 c3 0a 32 79 5a ba 0e ff 7e + *# aa 10 10 55 86 e7 be 5c 09 67 8d 63 b6 ca ab 62 + *# + *# key info (13 octets): 00 10 09 74 6c 73 31 33 20 6b 65 79 00 + *# + *# key expanded (16 octets): 92 02 05 a5 b7 bf 21 15 e6 fc 5c 29 42 + *# 83 4f 54 + *# + *# iv info (12 octets): 00 0c 08 74 6c 73 31 33 20 69 76 00 + *# + *# iv expanded (12 octets): 6d 47 5f 09 93 c8 e5 64 61 0d b2 b9 + */ + S2N_BLOB_FROM_HEX(secret, "3f bb e6 a6 0d eb 66 c3 0a 32 79 5a ba 0e ff 7e \ + aa 10 10 55 86 e7 be 5c 09 67 8d 63 b6 ca ab 62"); + S2N_BLOB_FROM_HEX(key, "92 02 05 a5 b7 bf 21 15 e6 fc 5c 29 42 \ + 83 4f 54"); + S2N_BLOB_FROM_HEX(iv, "6d 47 5f 09 93 c8 e5 64 61 0d b2 b9"); + + const message_type_t trigger_messages[] = { + [S2N_CLIENT] = CLIENT_HELLO, + [S2N_SERVER] = SERVER_FINISHED, + }; + + for (size_t i = 0; i < s2n_array_len(modes); i++) { + message_type_t trigger_message = trigger_messages[modes[i]]; + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(modes[i]), s2n_connection_ptr_free); + conn->secure->cipher_suite = cipher_suite; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + EXPECT_OK(s2n_set_test_secret(conn, conn->secrets.version.tls13.client_early_secret, secret)); + + conn->handshake.handshake_type = resumed_handshake_type; + conn->handshake.message_number = resumed_message_nums[trigger_message]; + EXPECT_EQUAL(s2n_conn_get_current_message_type(conn), trigger_message); + EXPECT_OK(s2n_tls13_key_schedule_update(conn)); + + EXPECT_IVS_EQUAL(conn, iv, S2N_CLIENT); + EXPECT_KEYS_EQUAL(conn, key, S2N_CLIENT); + } + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_key_schedule_test.c b/tests/unit/s2n_tls13_key_schedule_test.c new file mode 100644 index 00000000000..0cae551c952 --- /dev/null +++ b/tests/unit/s2n_tls13_key_schedule_test.c @@ -0,0 +1,290 @@ +/* + * 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 "tls/s2n_tls13_key_schedule.h" + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" + +/* For access to secret handlers. + * We override them for mocking purposees. */ +#include "tls/s2n_tls13_secrets.c" + +#define NO_TRIGGER_MSG APPLICATION_DATA +#define TRAFFIC_SECRET_COUNT 6 + +static uint8_t empty_secret[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; + +struct s2n_tls13_key_schedule_test_case { + s2n_mode conn_mode; + uint32_t handshake_type; + struct s2n_cipher_suite *cipher_suite; + bool is_early_data_requested; +}; + +struct s2n_test_secrets { + uint8_t bytes[TRAFFIC_SECRET_COUNT][S2N_TLS13_SECRET_MAX_LEN]; + struct s2n_blob blobs[TRAFFIC_SECRET_COUNT]; +}; + +static int s2n_test_secret_cb(void *context, struct s2n_connection *conn, + s2n_secret_type_t secret_type, uint8_t *secret_bytes, uint8_t secret_size) +{ + struct s2n_test_secrets *secrets = (struct s2n_test_secrets *) context; + POSIX_ENSURE_REF(secrets); + + POSIX_ENSURE_GTE(secret_type, 0); + POSIX_ENSURE_LT(secret_type, TRAFFIC_SECRET_COUNT); + POSIX_ENSURE_EQ(secrets->blobs[secret_type].size, 0); + POSIX_GUARD(s2n_blob_init(&secrets->blobs[secret_type], + secrets->bytes[secret_type], secret_size)); + POSIX_CHECKED_MEMCPY(secrets->bytes[secret_type], secret_bytes, secret_size); + return S2N_SUCCESS; +} + +static S2N_RESULT s2n_connection_verify_secrets(struct s2n_connection *conn, struct s2n_test_secrets *secrets, + bool expect_early_data_traffic_secret) +{ + /* Test: All handshake and master traffic secrets calculated */ + RESULT_ENSURE_EQ(conn->server, conn->secure); + RESULT_ENSURE_EQ(conn->client, conn->secure); + RESULT_ENSURE_GT(secrets->blobs[S2N_CLIENT_HANDSHAKE_TRAFFIC_SECRET].size, 0); + RESULT_ENSURE_GT(secrets->blobs[S2N_SERVER_HANDSHAKE_TRAFFIC_SECRET].size, 0); + RESULT_ENSURE_GT(secrets->blobs[S2N_CLIENT_APPLICATION_TRAFFIC_SECRET].size, 0); + RESULT_ENSURE_GT(secrets->blobs[S2N_SERVER_APPLICATION_TRAFFIC_SECRET].size, 0); + + /* Test: Early traffic secrets calculated if early data requested */ + if (expect_early_data_traffic_secret) { + RESULT_ENSURE_GT(secrets->blobs[S2N_CLIENT_EARLY_TRAFFIC_SECRET].size, 0); + } else { + RESULT_ENSURE_EQ(secrets->blobs[S2N_CLIENT_EARLY_TRAFFIC_SECRET].size, 0); + } + + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_mock_extract_method(struct s2n_connection *conn) +{ + return S2N_RESULT_OK; +} + +static S2N_RESULT s2n_mock_derive_method(struct s2n_connection *conn, struct s2n_blob *secret) +{ + uint8_t size = 0; + s2n_hmac_algorithm hmac_alg = conn->secure->cipher_suite->prf_alg; + RESULT_GUARD_POSIX(s2n_hmac_digest_size(hmac_alg, &size)); + RESULT_GUARD_POSIX(s2n_blob_init(secret, empty_secret, size)); + return S2N_RESULT_OK; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + const struct s2n_cipher_preferences *ciphers = &cipher_preferences_test_all_tls13; + const s2n_mode modes[] = { S2N_CLIENT, S2N_SERVER }; + const bool early_data_req_states[] = { true, false }; + + /* Calculate a set of test cases */ + struct s2n_tls13_key_schedule_test_case test_cases[2000] = { 0 }; + size_t test_cases_count = 0; + struct s2n_connection handshake_test_conn = { + .actual_protocol_version = S2N_TLS13, + .handshake = { .message_number = 1, .state_machine = S2N_STATE_MACHINE_TLS13 }, + }; + for (uint32_t handshake_type = 0; handshake_type < S2N_HANDSHAKES_COUNT; handshake_type++) { + handshake_test_conn.handshake.handshake_type = handshake_type; + for (size_t req_i = 0; req_i < s2n_array_len(early_data_req_states); req_i++) { + for (size_t cipher_i = 0; cipher_i < ciphers->count; cipher_i++) { + for (size_t mode_i = 0; mode_i < s2n_array_len(modes); mode_i++) { + if (s2n_conn_get_current_message_type(&handshake_test_conn) == CLIENT_HELLO) { + /* Skip empty, all-CLIENT_HELLO handshakes */ + continue; + } + if (WITH_EARLY_DATA(&handshake_test_conn) && !early_data_req_states[req_i]) { + /* Skip handshakes with inconsistent early data states */ + continue; + } + if (!IS_NEGOTIATED(&handshake_test_conn)) { + /* Skip initial / incomplete handshakes */ + continue; + } + if (!ciphers->suites[cipher_i]->available) { + /* Skip unavailable ciphers */ + continue; + } + test_cases[test_cases_count] = (struct s2n_tls13_key_schedule_test_case){ + .conn_mode = modes[mode_i], + .handshake_type = handshake_type, + .cipher_suite = ciphers->suites[cipher_i], + .is_early_data_requested = early_data_req_states[req_i], + }; + test_cases_count++; + } + } + } + } + EXPECT_TRUE(test_cases_count > 0); + + /* Test s2n_tls13_key_schedule_update */ + { + /* Safety */ + { + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_key_schedule_update(NULL), S2N_ERR_NULL); + + /* Minimal client connection that triggers an update */ + struct s2n_connection empty_client_conn = { + .mode = S2N_CLIENT, + .client_protocol_version = S2N_TLS13, + .early_data_state = S2N_EARLY_DATA_REQUESTED + }; + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_key_schedule_update(&empty_client_conn), S2N_ERR_NULL); + empty_client_conn.client_protocol_version = S2N_TLS12; + EXPECT_OK(s2n_tls13_key_schedule_update(&empty_client_conn)); + + /* Minimal server connection that triggers an update */ + struct s2n_connection empty_server_conn = { + .mode = S2N_SERVER, + .actual_protocol_version = S2N_TLS13, + .handshake = { .message_number = 1 } + }; + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_key_schedule_update(&empty_server_conn), S2N_ERR_NULL); + empty_server_conn.actual_protocol_version = S2N_TLS12; + EXPECT_OK(s2n_tls13_key_schedule_update(&empty_server_conn)); + }; + + /* Test key schedule ordering */ + { + /* Mock derive and extract methods */ + uint8_t saved_derive_methods[sizeof(derive_methods)] = { 0 }; + EXPECT_MEMCPY_SUCCESS(saved_derive_methods, derive_methods, sizeof(derive_methods)); + for (size_t i = 0; i < s2n_array_len(derive_methods); i++) { + derive_methods[i][S2N_CLIENT] = &s2n_mock_derive_method; + derive_methods[i][S2N_SERVER] = &s2n_mock_derive_method; + } + uint8_t saved_extract_methods[sizeof(extract_methods)] = { 0 }; + EXPECT_MEMCPY_SUCCESS(saved_extract_methods, extract_methods, sizeof(extract_methods)); + for (size_t i = 0; i < s2n_array_len(extract_methods); i++) { + extract_methods[i] = &s2n_mock_extract_method; + } + + for (size_t i = 0; i < test_cases_count; i++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(test_cases[i].conn_mode), + s2n_connection_ptr_free); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + + conn->handshake.handshake_type = test_cases[i].handshake_type; + conn->secure->cipher_suite = test_cases[i].cipher_suite; + if (test_cases[i].is_early_data_requested) { + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + } + + struct s2n_test_secrets secrets = { 0 }; + EXPECT_SUCCESS(s2n_connection_set_secret_callback(conn, + s2n_test_secret_cb, (void *) &secrets)); + + /* Perform the handshake */ + while (s2n_conn_get_current_message_type(conn) != APPLICATION_DATA) { + /* + * To avoid calculating the early traffic secret again when + * we receive the second ClientHello, we need to include + * the logic where a retry request rejects early data. + */ + if (s2n_conn_get_current_message_type(conn) == HELLO_RETRY_MSG) { + conn->early_data_state = S2N_EARLY_DATA_REJECTED; + EXPECT_OK(s2n_tls13_key_schedule_reset(conn)); + } + + /* + * We know what secrets every message should be encrypted with. + * Verify those secrets are available in time for each message. + */ + switch (s2n_conn_get_current_message_type(conn)) { + case CLIENT_HELLO: + /* Expect not encrypted */ + EXPECT_EQUAL(conn->client, conn->initial); + break; + case HELLO_RETRY_MSG: + case SERVER_HELLO: + /* Expect not encrypted */ + EXPECT_EQUAL(conn->server, conn->initial); + break; + case END_OF_EARLY_DATA: + /* Expect encrypted */ + EXPECT_EQUAL(conn->client, conn->secure); + /* Expect correct secret available */ + EXPECT_TRUE(secrets.blobs[S2N_CLIENT_EARLY_TRAFFIC_SECRET].size > 0); + break; + case ENCRYPTED_EXTENSIONS: + case SERVER_CERT: + case SERVER_CERT_VERIFY: + case SERVER_FINISHED: + case SERVER_CERT_REQ: + /* Expect encrypted */ + EXPECT_EQUAL(conn->server, conn->secure); + /* Expect correct secret available */ + EXPECT_TRUE(secrets.blobs[S2N_SERVER_HANDSHAKE_TRAFFIC_SECRET].size > 0); + break; + case CLIENT_CERT: + case CLIENT_CERT_VERIFY: + case CLIENT_FINISHED: + /* Expect encrypted */ + EXPECT_EQUAL(conn->client, conn->secure); + /* Expect correct secret available */ + EXPECT_TRUE(secrets.blobs[S2N_CLIENT_HANDSHAKE_TRAFFIC_SECRET].size > 0); + break; + case APPLICATION_DATA: + /* Expect encrypted */ + EXPECT_EQUAL(conn->client, conn->secure); + EXPECT_EQUAL(conn->server, conn->secure); + /* Expect correct secrets available */ + EXPECT_TRUE(secrets.blobs[S2N_CLIENT_APPLICATION_TRAFFIC_SECRET].size > 0); + EXPECT_TRUE(secrets.blobs[S2N_SERVER_APPLICATION_TRAFFIC_SECRET].size > 0); + break; + case SERVER_CHANGE_CIPHER_SPEC: + case CLIENT_CHANGE_CIPHER_SPEC: + /* + * Not relevant to TLS1.3 key schedule. + * We manually disable encryption when sending. + */ + break; + case CLIENT_KEY: + case SERVER_KEY: + case SERVER_NEW_SESSION_TICKET: + case SERVER_CERT_STATUS: + case SERVER_HELLO_DONE: + case CLIENT_NPN: + FAIL_MSG("Unexpected TLS1.2 message"); + break; + } + + EXPECT_OK(s2n_tls13_secrets_update(conn)); + EXPECT_OK(s2n_tls13_key_schedule_update(conn)); + conn->handshake.message_number++; + } + + EXPECT_OK(s2n_connection_verify_secrets(conn, &secrets, test_cases[i].is_early_data_requested)); + } + + /* Restore derive and extract methods */ + EXPECT_MEMCPY_SUCCESS(derive_methods, saved_derive_methods, sizeof(saved_derive_methods)); + EXPECT_MEMCPY_SUCCESS(extract_methods, saved_extract_methods, sizeof(saved_extract_methods)); + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_keys_test.c b/tests/unit/s2n_tls13_keys_test.c new file mode 100644 index 00000000000..d9b68228ebc --- /dev/null +++ b/tests/unit/s2n_tls13_keys_test.c @@ -0,0 +1,70 @@ +/* + * 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 "crypto/s2n_tls13_keys.h" + +#include + +#include "crypto/s2n_hkdf.h" +#include "crypto/s2n_hmac.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_tls13_handshake.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_safety.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + /* + * Test s2n_tls13_update_application_traffic_secret + * + * This test checks the new secret produced by the s2n_tls13_update_application_traffic_secret + * is the same one that is produced by openssl when starting with the same application secret. + */ + { + /* KeyUpdate Vectors from Openssl s_client implementation of KeyUpdate. The ciphersuite + * that produced this secret was s2n_tls13_aes_256_gcm_sha384. + */ + S2N_BLOB_FROM_HEX(application_secret, + "4bc28934ddd802b00f479e14a72d7725dab45d32b3b145f29" + "e4c5b56677560eb5236b168c71c5c75aa52f3e20ee89bfb"); + S2N_BLOB_FROM_HEX(updated_application_secret, + "ee85dd54781bd4d8a100589a9fe6ac9a3797b811e977f549cd" + "531be2441d7c63e2b9729d145c11d84af35957727565a4"); + + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + + /* get tls13 key context */ + s2n_tls13_connection_keys(keys, server_conn); + + s2n_stack_blob(app_secret_update, keys.size, S2N_TLS13_SECRET_MAX_LEN); + + /* Derives next generation of traffic secret */ + EXPECT_SUCCESS(s2n_tls13_update_application_traffic_secret(&keys, &application_secret, &app_secret_update)); + + /* Check the new secret is what was expected */ + S2N_BLOB_EXPECT_EQUAL(app_secret_update, updated_application_secret); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_new_session_ticket_test.c b/tests/unit/s2n_tls13_new_session_ticket_test.c new file mode 100644 index 00000000000..a3a684712c8 --- /dev/null +++ b/tests/unit/s2n_tls13_new_session_ticket_test.c @@ -0,0 +1,143 @@ +/* + * 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 "api/s2n.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_post_handshake.h" +#include "tls/s2n_record.h" +#include "tls/s2n_tls13.h" +#include "tls/s2n_tls13_handshake.h" + +#define MAX_TEST_SESSION_SIZE 300 + +uint8_t session_ticket_counter = 0; +static int s2n_test_session_ticket_cb(struct s2n_connection *conn, void *ctx, struct s2n_session_ticket *ticket) +{ + POSIX_ENSURE_REF(conn); + POSIX_ENSURE_REF(ticket); + + size_t cb_session_data_len = 0; + uint8_t cb_session_data[MAX_TEST_SESSION_SIZE] = { 0 }; + uint32_t cb_session_lifetime = 0; + + EXPECT_SUCCESS(s2n_session_ticket_get_data_len(ticket, &cb_session_data_len)); + EXPECT_TRUE(cb_session_data_len > 0); + EXPECT_SUCCESS(s2n_session_ticket_get_data(ticket, cb_session_data_len, cb_session_data)); + EXPECT_NOT_NULL(cb_session_data); + EXPECT_SUCCESS(s2n_session_ticket_get_lifetime(ticket, &cb_session_lifetime)); + EXPECT_TRUE(cb_session_lifetime > 0); + + session_ticket_counter++; + + return S2N_SUCCESS; +} + +static int s2n_setup_test_ticket_key(struct s2n_config *config) +{ + POSIX_ENSURE_REF(config); + + /** + *= https://tools.ietf.org/rfc/rfc5869#appendix-A.1 + *# PRK = 0x077709362c2e32df0ddc3f0dc47bba63 + *# 90b6c73bb50f9c3122ec844ad7c2b3e5 (32 octets) + **/ + S2N_BLOB_FROM_HEX(ticket_key, + "077709362c2e32df0ddc3f0dc47bba63" + "90b6c73bb50f9c3122ec844ad7c2b3e5"); + + /* Set up encryption key */ + uint64_t current_time = 0; + uint8_t ticket_key_name[S2N_TICKET_KEY_NAME_LEN] = "2016.07.26.15\0"; + EXPECT_SUCCESS(config->wall_clock(config->sys_clock_ctx, ¤t_time)); + EXPECT_SUCCESS(s2n_config_add_ticket_crypto_key(config, ticket_key_name, strlen((char *) ticket_key_name), + ticket_key.data, ticket_key.size, current_time / ONE_SEC_IN_NANOS)); + + return S2N_SUCCESS; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + /* s2n_send sends NewSessionTicket message and s2n_recv receives it */ + { + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(server_conn); + EXPECT_NOT_NULL(client_conn); + + struct s2n_config *server_config = s2n_config_new(); + EXPECT_NOT_NULL(server_config); + + struct s2n_config *client_config = s2n_config_new(); + EXPECT_NOT_NULL(client_config); + + struct s2n_cert_chain_and_key *chain_and_key; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY)); + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(server_config, 1)); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + EXPECT_SUCCESS(s2n_setup_test_ticket_key(server_config)); + + EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(client_config, 1)); + EXPECT_SUCCESS(s2n_config_set_session_ticket_cb(client_config, s2n_test_session_ticket_cb, NULL)); + EXPECT_SUCCESS(s2n_config_disable_x509_verification(client_config)); + + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(server_conn, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(client_conn, "default_tls13")); + + DEFER_CLEANUP(struct s2n_stuffer input, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer output, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&input, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&output, 0)); + + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&input, &output, server_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&output, &input, client_conn)); + + EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn)); + + /* Create conditions to send NewSessionTicket message */ + uint8_t tickets_to_send = 5; + server_conn->tickets_to_send = tickets_to_send; + + /* Next message to send will trigger a NewSessionTicket message*/ + s2n_blocked_status blocked; + uint8_t message[] = "sent message"; + EXPECT_SUCCESS(s2n_send(server_conn, message, sizeof(message), &blocked)); + + /* Receive NewSessionTicket message */ + uint8_t data[sizeof(message)]; + EXPECT_SUCCESS(s2n_recv(client_conn, data, sizeof(data), &blocked)); + EXPECT_BYTEARRAY_EQUAL(data, message, sizeof(message)); + + EXPECT_EQUAL(session_ticket_counter, tickets_to_send); + + EXPECT_SUCCESS(s2n_config_free(server_config)); + EXPECT_SUCCESS(s2n_config_free(client_config)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(chain_and_key)); + }; + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_parse_record_type_test.c b/tests/unit/s2n_tls13_parse_record_type_test.c new file mode 100644 index 00000000000..0372b229261 --- /dev/null +++ b/tests/unit/s2n_tls13_parse_record_type_test.c @@ -0,0 +1,256 @@ +/* + * 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 "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_record.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + uint8_t record_type; + + /* In tls13 the true record type is inserted in the last byte of the encrypted payload. This + * test creates a fake unencrypted payload and checks that the helper function + * s2n_tls13_parse_record_type() correctly parses the type. + */ + { + uint16_t plaintext = 0xdaf3; + struct s2n_stuffer plaintext_stuffer = { 0 }; + + EXPECT_SUCCESS(s2n_stuffer_alloc(&plaintext_stuffer, sizeof(plaintext))); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&plaintext_stuffer, plaintext)); + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&plaintext_stuffer, &record_type)); + EXPECT_EQUAL(record_type, 0xf3); + EXPECT_EQUAL(s2n_stuffer_data_available(&plaintext_stuffer), 1); + + /* Clean up */ + EXPECT_SUCCESS(s2n_stuffer_free(&plaintext_stuffer)); + } + + /* Test for failure when stuffer is completely empty */ + { + struct s2n_stuffer empty_stuffer = { 0 }; + + EXPECT_SUCCESS(s2n_stuffer_alloc(&empty_stuffer, 0)); + EXPECT_FAILURE(s2n_tls13_parse_record_type(&empty_stuffer, &record_type)); + }; + + /* Test for case where there is a record type in the stuffer but no content */ + { + uint16_t plaintext = 0xf3; + struct s2n_stuffer plaintext_stuffer = { 0 }; + + EXPECT_SUCCESS(s2n_stuffer_alloc(&plaintext_stuffer, sizeof(plaintext))); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&plaintext_stuffer, plaintext)); + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&plaintext_stuffer, &record_type)); + EXPECT_EQUAL(record_type, 0xf3); + EXPECT_EQUAL(s2n_stuffer_data_available(&plaintext_stuffer), 1); + + /* Clean up */ + EXPECT_SUCCESS(s2n_stuffer_free(&plaintext_stuffer)); + }; + + /* Test for record padding handling */ + { + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + /* no padding */ + S2N_BLOB_FROM_HEX(padding_0, "16"); + EXPECT_SUCCESS(s2n_stuffer_write(&stuffer, &padding_0)); + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); + EXPECT_EQUAL(record_type, 0x16); + + /* 1 byte padding */ + S2N_BLOB_FROM_HEX(padding_1, "1600"); + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write(&stuffer, &padding_1)); + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); + EXPECT_EQUAL(record_type, 0x16); + + /* 2 byte padding */ + S2N_BLOB_FROM_HEX(padding_2, "160000"); + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write(&stuffer, &padding_2)); + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); + EXPECT_EQUAL(record_type, 0x16); + + /** test: padding without record type should fail + * + *= https://tools.ietf.org/rfc/rfc8446#section-5.4 + *= type=test + *# If a receiving implementation does not + *# find a non-zero octet in the cleartext, it MUST terminate the + *# connection with an "unexpected_message" alert. + **/ + S2N_BLOB_FROM_HEX(no_type, "00"); + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write(&stuffer, &no_type)); + EXPECT_FAILURE(s2n_tls13_parse_record_type(&stuffer, &record_type)); + + /* multiple padding without record type should fail */ + S2N_BLOB_FROM_HEX(no_type2, "0000"); + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + EXPECT_SUCCESS(s2n_stuffer_write(&stuffer, &no_type2)); + EXPECT_FAILURE(s2n_tls13_parse_record_type(&stuffer, &record_type)); + + /* empty stuffer should fail */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&stuffer)); + EXPECT_FAILURE(s2n_tls13_parse_record_type(&stuffer, &record_type)); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + }; + + /* Defining these here as variables as they aren't used in prior tests. */ + const uint8_t padding_value = 0x00; + const uint8_t not_padding_value = 0x16; + + /* Test maximum record length size (empty data) */ + { + EXPECT_EQUAL(S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH, 16385); + + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); + + /* fill up stuffer to before the limit */ + while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); + } + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH); + + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); + EXPECT_EQUAL(record_type, not_padding_value); + /* There was no data before the record type */ + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + } + + /* Test maximum record length size (maximum data) */ + { + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + /* fill up stuffer to before the limit */ + while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); + } + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH); + + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); + EXPECT_EQUAL(record_type, not_padding_value); + /* The last byte is stripped as the content type */ + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH - 1); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + } + + /* Certain versions of Java can generate inner plaintexts with lengths up to + * S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + 16 (See JDK-8221253) + * However, after the padding is stripped, the result will always be no more than + * S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH - 1 + */ + { + const size_t extra_length_tolerated = 16; + /* Test slightly overlarge record for compatibility (empty data) */ + { + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); + /* fill up stuffer the limit + 16 */ + while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); + } + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated); + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); + EXPECT_EQUAL(record_type, not_padding_value); + /* There was no data before the record type */ + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), 0); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + } + + /* Test slightly overlarge record for compatibility (maximum data) */ + { + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + /* fill up stuffer to before the limit */ + while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); + } + /* pad up stuffer the limit + 16 */ + while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); + } + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated); + + EXPECT_SUCCESS(s2n_tls13_parse_record_type(&stuffer, &record_type)); + EXPECT_EQUAL(record_type, not_padding_value); + /* The last byte is stripped as the content type */ + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH - 1); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + } + + /* Test slightly overlarge record for compatibility (with too much data) */ + { + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + /* Finally, do this with an overall length which should pass, but too much data before the padding */ + /* fill up stuffer to the maximum amount of data */ + while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); + } + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); /* Record type */ + /* 16 bytes of padding*/ + while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); + } + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated); + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_parse_record_type(&stuffer, &record_type), S2N_ERR_MAX_INNER_PLAINTEXT_SIZE); + + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + } + + /* Test slightly overlarge + 1 record for compatibility (empty data) */ + { + struct s2n_stuffer stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&stuffer, 1024)); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, not_padding_value)); + while (s2n_stuffer_data_available(&stuffer) < S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated + 1) { + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&stuffer, padding_value)); + } + EXPECT_EQUAL(s2n_stuffer_data_available(&stuffer), S2N_MAXIMUM_INNER_PLAINTEXT_LENGTH + extra_length_tolerated + 1); + + EXPECT_FAILURE_WITH_ERRNO(s2n_tls13_parse_record_type(&stuffer, &record_type), S2N_ERR_MAX_INNER_PLAINTEXT_SIZE); + EXPECT_SUCCESS(s2n_stuffer_free(&stuffer)); + } + } + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_pq_handshake_test.c b/tests/unit/s2n_tls13_pq_handshake_test.c new file mode 100644 index 00000000000..41bb8e0f354 --- /dev/null +++ b/tests/unit/s2n_tls13_pq_handshake_test.c @@ -0,0 +1,599 @@ +/* + * 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 "api/s2n.h" +#include "crypto/s2n_rsa_signing.h" +#include "pq-crypto/s2n_pq.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_ecc_preferences.h" +#include "tls/s2n_handshake.h" +#include "tls/s2n_kem_preferences.h" +#include "tls/s2n_security_policies.h" + +/* Include C file directly to access static functions */ +#include "tls/s2n_handshake_io.c" + +int s2n_test_tls13_pq_handshake(const struct s2n_security_policy *client_sec_policy, + const struct s2n_security_policy *server_sec_policy, const struct s2n_kem_group *expected_kem_group, + const struct s2n_ecc_named_curve *expected_curve, bool hrr_expected, bool len_prefix_expected) +{ + /* XOR check: can expect to negotiate either a KEM group, or a classic EC curve, but not both/neither */ + POSIX_ENSURE((expected_kem_group == NULL) != (expected_curve == NULL), S2N_ERR_SAFETY); + + /* Set up connections */ + struct s2n_connection *client_conn = NULL, *server_conn = NULL; + POSIX_ENSURE_REF(client_conn = s2n_connection_new(S2N_CLIENT)); + POSIX_ENSURE_REF(server_conn = s2n_connection_new(S2N_SERVER)); + + struct s2n_config *client_config = NULL, *server_config = NULL; + POSIX_ENSURE_REF(client_config = s2n_config_new()); + POSIX_ENSURE_REF(server_config = s2n_config_new()); + + char cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }, private_key[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + struct s2n_cert_chain_and_key *chain_and_key = NULL; + POSIX_ENSURE_REF(chain_and_key = s2n_cert_chain_and_key_new()); + POSIX_GUARD(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + POSIX_GUARD(s2n_read_test_pem(S2N_ECDSA_P384_PKCS1_KEY, private_key, S2N_MAX_TEST_PEM_SIZE)); + POSIX_GUARD(s2n_cert_chain_and_key_load_pem(chain_and_key, cert_chain, private_key)); + POSIX_GUARD(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + POSIX_GUARD(s2n_config_add_cert_chain_and_key_to_store(server_config, chain_and_key)); + + POSIX_GUARD(s2n_connection_set_config(client_conn, client_config)); + POSIX_GUARD(s2n_connection_set_config(server_conn, server_config)); + + struct s2n_stuffer client_to_server = { 0 }, server_to_client = { 0 }; + POSIX_GUARD(s2n_stuffer_growable_alloc(&client_to_server, 2048)); + POSIX_GUARD(s2n_stuffer_growable_alloc(&server_to_client, 2048)); + + POSIX_GUARD(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + POSIX_GUARD(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + + client_conn->security_policy_override = client_sec_policy; + server_conn->security_policy_override = server_sec_policy; + + /* Client sends ClientHello */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); + POSIX_GUARD(s2n_handshake_write_io(client_conn)); + + POSIX_ENSURE_EQ(client_conn->actual_protocol_version, S2N_TLS13); + POSIX_ENSURE_EQ(server_conn->actual_protocol_version, 0); /* Won't get set until after server reads ClientHello */ + POSIX_ENSURE_EQ(client_conn->handshake.handshake_type, INITIAL); + + /* Server reads ClientHello */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); + POSIX_GUARD(s2n_handshake_read_io(server_conn)); + + POSIX_ENSURE_EQ(server_conn->actual_protocol_version, S2N_TLS13); /* Server is now on TLS13 */ + + /* Assert that the server chose the correct group */ + if (expected_kem_group) { + /* Client should always determine whether the Hybrid KEM used len_prefixed format, and server should match client's behavior. */ + POSIX_ENSURE_EQ(len_prefix_expected, client_conn->kex_params.client_kem_group_params.kem_params.len_prefixed); + POSIX_ENSURE_EQ(len_prefix_expected, s2n_tls13_client_must_use_hybrid_kem_length_prefix(client_sec_policy->kem_preferences)); + POSIX_ENSURE_EQ(server_conn->kex_params.client_kem_group_params.kem_params.len_prefixed, client_conn->kex_params.client_kem_group_params.kem_params.len_prefixed); + + POSIX_ENSURE_EQ(expected_kem_group, server_conn->kex_params.server_kem_group_params.kem_group); + POSIX_ENSURE_EQ(expected_kem_group->kem, server_conn->kex_params.server_kem_group_params.kem_params.kem); + POSIX_ENSURE_EQ(expected_kem_group->curve, server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + } else { + POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.kem_group); + POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.kem_params.kem); + POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + POSIX_ENSURE_EQ(expected_curve, server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + } + + /* Server sends ServerHello or HRR */ + POSIX_GUARD(s2n_conn_set_handshake_type(server_conn)); + if (hrr_expected) { + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), HELLO_RETRY_MSG); + } else { + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), SERVER_HELLO); + } + POSIX_GUARD(s2n_handshake_write_io(server_conn)); + + /* Server sends CCS */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), SERVER_CHANGE_CIPHER_SPEC); + POSIX_GUARD(s2n_handshake_write_io(server_conn)); + + if (hrr_expected) { + /* Client reads HRR */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + POSIX_GUARD(s2n_handshake_read_io(client_conn)); + POSIX_GUARD(s2n_conn_set_handshake_type(client_conn)); + POSIX_ENSURE_NE(0, client_conn->handshake.handshake_type & HELLO_RETRY_REQUEST); + + /* Client reads CCS */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), CLIENT_CHANGE_CIPHER_SPEC); + POSIX_GUARD(s2n_handshake_read_io(client_conn)); + + /* Client sends CCS and new ClientHello */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), CLIENT_CHANGE_CIPHER_SPEC); + POSIX_GUARD(s2n_handshake_write_io(client_conn)); + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), CLIENT_HELLO); + POSIX_GUARD(s2n_handshake_write_io(client_conn)); + + /* Server reads CCS (doesn't change state machine) */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); + POSIX_GUARD(s2n_handshake_read_io(server_conn)); + + /* Server reads new ClientHello */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), CLIENT_HELLO); + POSIX_GUARD(s2n_handshake_read_io(server_conn)); + + /* Server sends ServerHello */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(server_conn), SERVER_HELLO); + POSIX_GUARD(s2n_handshake_write_io(server_conn)); + } + + /* Client reads ServerHello */ + POSIX_ENSURE_EQ(s2n_conn_get_current_message_type(client_conn), SERVER_HELLO); + POSIX_GUARD(s2n_handshake_read_io(client_conn)); + + /* We've gotten far enough in the handshake that both client and server should have + * derived the shared secrets, so we don't send/receive any more messages. */ + + /* Assert that the correct group was negotiated (we re-check the server group to assert that + * nothing unexpected changed between then and now while e.g. processing HRR) */ + if (expected_kem_group) { + POSIX_ENSURE_EQ(expected_kem_group, client_conn->kex_params.server_kem_group_params.kem_group); + POSIX_ENSURE_EQ(expected_kem_group->kem, client_conn->kex_params.server_kem_group_params.kem_params.kem); + POSIX_ENSURE_EQ(expected_kem_group->curve, client_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + POSIX_ENSURE_EQ(NULL, client_conn->kex_params.server_ecc_evp_params.negotiated_curve); + + POSIX_ENSURE_EQ(expected_kem_group, server_conn->kex_params.server_kem_group_params.kem_group); + POSIX_ENSURE_EQ(expected_kem_group->kem, server_conn->kex_params.server_kem_group_params.kem_params.kem); + POSIX_ENSURE_EQ(expected_kem_group->curve, server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + } else { + POSIX_ENSURE_EQ(NULL, client_conn->kex_params.server_kem_group_params.kem_group); + POSIX_ENSURE_EQ(NULL, client_conn->kex_params.server_kem_group_params.kem_params.kem); + POSIX_ENSURE_EQ(NULL, client_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + POSIX_ENSURE_EQ(expected_curve, client_conn->kex_params.server_ecc_evp_params.negotiated_curve); + + POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.kem_group); + POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.kem_params.kem); + POSIX_ENSURE_EQ(NULL, server_conn->kex_params.server_kem_group_params.ecc_params.negotiated_curve); + POSIX_ENSURE_EQ(expected_curve, server_conn->kex_params.server_ecc_evp_params.negotiated_curve); + } + + /* Verify basic properties of secrets */ + s2n_tls13_connection_keys(server_secret_info, server_conn); + s2n_tls13_connection_keys(client_secret_info, client_conn); + POSIX_ENSURE_EQ(server_conn->secure->cipher_suite, client_conn->secure->cipher_suite); + if (server_conn->secure->cipher_suite == &s2n_tls13_aes_256_gcm_sha384) { + POSIX_ENSURE_EQ(server_secret_info.size, 48); + POSIX_ENSURE_EQ(client_secret_info.size, 48); + } else { + POSIX_ENSURE_EQ(server_secret_info.size, 32); + POSIX_ENSURE_EQ(client_secret_info.size, 32); + } + + /* Verify secrets aren't just zero'ed memory */ + uint8_t all_zeros[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; + POSIX_CHECKED_MEMSET((void *) all_zeros, 0, S2N_TLS13_SECRET_MAX_LEN); + struct s2n_tls13_secrets *client_secrets = &client_conn->secrets.version.tls13; + struct s2n_tls13_secrets *server_secrets = &server_conn->secrets.version.tls13; + POSIX_ENSURE_EQ(server_secret_info.size, client_secret_info.size); + uint8_t size = server_secret_info.size; + POSIX_ENSURE_EQ(client_conn->secrets.extract_secret_type, S2N_HANDSHAKE_SECRET); + POSIX_ENSURE_NE(0, memcmp(all_zeros, client_secrets->extract_secret, size)); + POSIX_ENSURE_NE(0, memcmp(all_zeros, client_secrets->client_handshake_secret, size)); + POSIX_ENSURE_NE(0, memcmp(all_zeros, client_secrets->server_handshake_secret, size)); + POSIX_ENSURE_EQ(server_conn->secrets.extract_secret_type, S2N_HANDSHAKE_SECRET); + POSIX_ENSURE_NE(0, memcmp(all_zeros, server_secrets->extract_secret, size)); + POSIX_ENSURE_NE(0, memcmp(all_zeros, server_secrets->client_handshake_secret, size)); + POSIX_ENSURE_NE(0, memcmp(all_zeros, server_secrets->server_handshake_secret, size)); + + /* Verify client and server secrets are equal to each other */ + POSIX_ENSURE_EQ(0, memcmp(server_secrets->extract_secret, client_secrets->extract_secret, size)); + POSIX_ENSURE_EQ(0, memcmp(server_secrets->client_handshake_secret, client_secrets->client_handshake_secret, size)); + POSIX_ENSURE_EQ(0, memcmp(server_secrets->server_handshake_secret, client_secrets->server_handshake_secret, size)); + + /* Clean up */ + POSIX_GUARD(s2n_stuffer_free(&client_to_server)); + POSIX_GUARD(s2n_stuffer_free(&server_to_client)); + + POSIX_GUARD(s2n_connection_free(client_conn)); + POSIX_GUARD(s2n_connection_free(server_conn)); + + POSIX_GUARD(s2n_cert_chain_and_key_free(chain_and_key)); + POSIX_GUARD(s2n_config_free(server_config)); + POSIX_GUARD(s2n_config_free(client_config)); + + return S2N_SUCCESS; +} + +int main() +{ + BEGIN_TEST(); + + if (!s2n_is_tls13_fully_supported()) { + END_TEST(); + } + + /* Additional KEM preferences/security policies to test against. These policies can only be used + * as the server's policy in this test: when generating the ClientHello, the client relies on + * the security_policy_selection[] array (in s2n_security_policies.c) to determine if it should + * write the supported_groups extension. Because these unofficial policies don't exist in that + * array, the supported_groups extension won't get sent and the handshake won't complete as expected. */ + + /* Kyber */ + const struct s2n_kem_group *kyber_test_groups[] = { +#if EVP_APIS_SUPPORTED + &s2n_x25519_kyber_512_r3, +#endif + &s2n_secp256r1_kyber_512_r3, +#if defined(S2N_LIBCRYPTO_SUPPORTS_KYBER) + &s2n_secp256r1_kyber_768_r3, + &s2n_secp384r1_kyber_768_r3, + &s2n_secp521r1_kyber_1024_r3, +#endif +#if EVP_APIS_SUPPORTED && defined(S2N_LIBCRYPTO_SUPPORTS_KYBER) + &s2n_x25519_kyber_768_r3, +#endif + }; + + const struct s2n_kem_preferences kyber_test_prefs_draft0 = { + .kem_count = 0, + .kems = NULL, + .tls13_kem_group_count = s2n_array_len(kyber_test_groups), + .tls13_kem_groups = kyber_test_groups, + .tls13_pq_hybrid_draft_revision = 0 + }; + + const struct s2n_security_policy kyber_test_policy_draft0 = { + .minimum_protocol_version = S2N_TLS10, + .cipher_preferences = &cipher_preferences_20190801, + .kem_preferences = &kyber_test_prefs_draft0, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20200310, + }; + + const struct s2n_kem_preferences kyber_test_prefs_draft5 = { + .kem_count = 0, + .kems = NULL, + .tls13_kem_group_count = s2n_array_len(kyber_test_groups), + .tls13_kem_groups = kyber_test_groups, + .tls13_pq_hybrid_draft_revision = 5 + }; + + const struct s2n_security_policy kyber_test_policy_draft5 = { + .minimum_protocol_version = S2N_TLS10, + .cipher_preferences = &cipher_preferences_20190801, + .kem_preferences = &kyber_test_prefs_draft5, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20200310, + }; + + const struct s2n_kem_group *kyber768_test_kem_groups[] = { +#if defined(S2N_LIBCRYPTO_SUPPORTS_KYBER) + &s2n_secp384r1_kyber_768_r3, +#endif + &s2n_secp256r1_kyber_512_r3, + }; + + const struct s2n_kem_preferences kyber768_test_prefs = { + .kem_count = 0, + .kems = NULL, + .tls13_kem_group_count = s2n_array_len(kyber768_test_kem_groups), + .tls13_kem_groups = kyber768_test_kem_groups, + .tls13_pq_hybrid_draft_revision = 5, + }; + + const struct s2n_security_policy kyber768_test_policy = { + .minimum_protocol_version = S2N_TLS13, + .cipher_preferences = &cipher_preferences_20190801, + .kem_preferences = &kyber768_test_prefs, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20201021, + }; + + const struct s2n_kem_group *kyber1024_test_kem_groups[] = { +#if defined(S2N_LIBCRYPTO_SUPPORTS_KYBER) + &s2n_secp521r1_kyber_1024_r3, +#endif + &s2n_secp256r1_kyber_512_r3, + }; + + const struct s2n_kem_preferences kyber1024_test_prefs = { + .kem_count = 0, + .kems = NULL, + .tls13_kem_group_count = s2n_array_len(kyber1024_test_kem_groups), + .tls13_kem_groups = kyber1024_test_kem_groups, + .tls13_pq_hybrid_draft_revision = 5, + }; + + const struct s2n_security_policy kyber1024_test_policy = { + .minimum_protocol_version = S2N_TLS13, + .cipher_preferences = &cipher_preferences_20190801, + .kem_preferences = &kyber1024_test_prefs, + .signature_preferences = &s2n_signature_preferences_20200207, + .ecc_preferences = &s2n_ecc_preferences_20201021, + }; + + const struct s2n_security_policy ecc_retry_policy = { + .minimum_protocol_version = security_policy_pq_tls_1_0_2020_12.minimum_protocol_version, + .cipher_preferences = security_policy_pq_tls_1_0_2020_12.cipher_preferences, + .kem_preferences = security_policy_pq_tls_1_0_2020_12.kem_preferences, + .signature_preferences = security_policy_pq_tls_1_0_2020_12.signature_preferences, + .ecc_preferences = security_policy_test_tls13_retry.ecc_preferences, + }; + + const struct s2n_kem_group *expected_kyber_r3_group = &s2n_x25519_kyber_512_r3; + const struct s2n_ecc_named_curve *expected_curve = &s2n_ecc_curve_x25519; + + if (!s2n_is_evp_apis_supported()) { + expected_kyber_r3_group = &s2n_secp256r1_kyber_512_r3; + expected_curve = &s2n_ecc_curve_secp256r1; + } + + struct pq_handshake_test_vector { + const struct s2n_security_policy *client_policy; + const struct s2n_security_policy *server_policy; + const struct s2n_kem_group *expected_kem_group; + const struct s2n_ecc_named_curve *expected_curve; + bool hrr_expected; + bool len_prefix_expected; + }; + + /* Test vectors that expect to negotiate PQ assume that PQ is enabled in s2n. + * If PQ is disabled, the expected negotiation outcome is overridden below + * before performing the handshake test. */ + const struct pq_handshake_test_vector test_vectors[] = { + /* Server and Client both support PQ and TLS 1.3 */ + { + .client_policy = &security_policy_pq_tls_1_1_2021_05_21, + .server_policy = &security_policy_pq_tls_1_1_2021_05_21, + .expected_kem_group = expected_kyber_r3_group, + .expected_curve = NULL, + .hrr_expected = false, + .len_prefix_expected = true, + }, + { + .client_policy = &security_policy_pq_tls_1_0_2021_05_22, + .server_policy = &security_policy_pq_tls_1_0_2021_05_22, + .expected_kem_group = expected_kyber_r3_group, + .expected_curve = NULL, + .hrr_expected = false, + .len_prefix_expected = true, + }, + { + .client_policy = &security_policy_pq_tls_1_0_2021_05_23, + .server_policy = &security_policy_pq_tls_1_0_2021_05_23, + .expected_kem_group = expected_kyber_r3_group, + .expected_curve = NULL, + .hrr_expected = false, + .len_prefix_expected = true, + }, + { + .client_policy = &security_policy_pq_tls_1_0_2021_05_24, + .server_policy = &security_policy_pq_tls_1_0_2021_05_24, + .expected_kem_group = expected_kyber_r3_group, + .expected_curve = NULL, + .hrr_expected = false, + .len_prefix_expected = true, + }, + { + .client_policy = &security_policy_pq_tls_1_0_2021_05_26, + .server_policy = &security_policy_pq_tls_1_0_2021_05_26, + .expected_kem_group = expected_kyber_r3_group, + .expected_curve = NULL, + .hrr_expected = false, + .len_prefix_expected = true, + }, + { + .client_policy = &security_policy_pq_tls_1_0_2023_01_24, + .server_policy = &security_policy_pq_tls_1_0_2023_01_24, + .expected_kem_group = expected_kyber_r3_group, + .expected_curve = NULL, + .hrr_expected = false, + .len_prefix_expected = false, + }, + /* Kyber768 should be preferred over 1024, which should be preferred over 512 + * when available. Note that unlike older KEM group preferences, 2023_06_01 + * prefers secp256r1 over x25519 for the hybrid EC. + */ + { + .client_policy = &security_policy_pq_tls_1_3_2023_06_01, + .server_policy = &security_policy_pq_tls_1_3_2023_06_01, +#if defined(S2N_LIBCRYPTO_SUPPORTS_KYBER) + .expected_kem_group = &s2n_secp256r1_kyber_768_r3, +#else + .expected_kem_group = &s2n_secp256r1_kyber_512_r3, +#endif + .expected_curve = NULL, + .hrr_expected = false, + .len_prefix_expected = false, + }, + { + .client_policy = &kyber1024_test_policy, + .server_policy = &security_policy_pq_tls_1_3_2023_06_01, +#if defined(S2N_LIBCRYPTO_SUPPORTS_KYBER) + .expected_kem_group = &s2n_secp521r1_kyber_1024_r3, +#else + .expected_kem_group = &s2n_secp256r1_kyber_512_r3, +#endif + .expected_curve = NULL, + .hrr_expected = false, + .len_prefix_expected = false, + }, + { + .client_policy = &kyber768_test_policy, + .server_policy = &security_policy_pq_tls_1_3_2023_06_01, +#if defined(S2N_LIBCRYPTO_SUPPORTS_KYBER) + .expected_kem_group = &s2n_secp384r1_kyber_768_r3, +#else + .expected_kem_group = &s2n_secp256r1_kyber_512_r3, +#endif + .expected_curve = NULL, + .hrr_expected = false, + .len_prefix_expected = false, + }, + /* Server supports Kyber768+ parameters, Client only supports Kyber512. + * Expect Kyber512 to be negotiated if PQ is enabled, else fall back to + * ECC on hello retry. + */ + { + .client_policy = &security_policy_pq_tls_1_1_2021_05_21, + .server_policy = &security_policy_pq_tls_1_3_2023_06_01, + .expected_kem_group = expected_kyber_r3_group, + .expected_curve = NULL, + .hrr_expected = !s2n_pq_is_enabled(), + .len_prefix_expected = true, + }, + /* Check that we're backwards and forwards compatible with different Hybrid PQ draft revisions*/ + { + .client_policy = &kyber_test_policy_draft0, + .server_policy = &kyber_test_policy_draft5, + .expected_kem_group = expected_kyber_r3_group, + .expected_curve = NULL, + .hrr_expected = false, + .len_prefix_expected = true, + }, + { + .client_policy = &kyber_test_policy_draft5, + .server_policy = &kyber_test_policy_draft0, + .expected_kem_group = expected_kyber_r3_group, + .expected_curve = NULL, + .hrr_expected = false, + .len_prefix_expected = false, + }, + { + .client_policy = &security_policy_pq_tls_1_0_2021_05_24, + .server_policy = &security_policy_pq_tls_1_0_2023_01_24, + .expected_kem_group = expected_kyber_r3_group, + .expected_curve = NULL, + .hrr_expected = false, + .len_prefix_expected = true, + }, + { + .client_policy = &security_policy_pq_tls_1_0_2023_01_24, + .server_policy = &security_policy_pq_tls_1_0_2021_05_24, + .expected_kem_group = expected_kyber_r3_group, + .expected_curve = NULL, + .hrr_expected = false, + .len_prefix_expected = false, + }, + + /* Server supports all KEM groups; client sends a PQ key share and an EC key + * share; server chooses to negotiate client's first choice PQ without HRR. */ + { + .client_policy = &security_policy_pq_tls_1_0_2020_12, + .server_policy = &security_policy_pq_tls_1_0_2020_12, + .expected_kem_group = expected_kyber_r3_group, + .expected_curve = NULL, + .hrr_expected = false, + .len_prefix_expected = true, + }, + + /* Server supports only one KEM group and it is the client's first choice; + * client sends a PQ share and an EC share; server chooses to negotiate PQ + * without HRR. */ + { + .client_policy = &security_policy_pq_tls_1_0_2020_12, + .server_policy = &kyber_test_policy_draft0, + .expected_kem_group = expected_kyber_r3_group, + .expected_curve = NULL, + .hrr_expected = false, + .len_prefix_expected = true, + }, + + /* Server supports only one KEM group and it is the client's first choice; + * client sends a PQ share and an EC share; server chooses to negotiate PQ + * without HRR. */ + { + .client_policy = &security_policy_pq_tls_1_0_2020_12, + .server_policy = &kyber_test_policy_draft5, + .expected_kem_group = expected_kyber_r3_group, + .expected_curve = NULL, + .hrr_expected = false, + .len_prefix_expected = true, + }, + + /* Server does not support PQ; client sends a PQ key share and an EC key share; + * server should negotiate EC without HRR. */ + { + .client_policy = &security_policy_pq_tls_1_0_2020_12, + .server_policy = &security_policy_test_all_tls13, + .expected_kem_group = NULL, + .expected_curve = expected_curve, + .hrr_expected = false, + .len_prefix_expected = true, + }, + + /* Server does not support PQ; client sends a PQ key share, but no EC shares; + * server should negotiate EC and send HRR. */ + { + .client_policy = &ecc_retry_policy, + .server_policy = &security_policy_test_all_tls13, + .expected_kem_group = NULL, + .expected_curve = expected_curve, + .hrr_expected = true, + .len_prefix_expected = true, + }, + + /* Server supports PQ, but client does not. Client sent an EC share, + * EC should be negotiated without HRR */ + { + .client_policy = &security_policy_test_all_tls13, + .server_policy = &security_policy_pq_tls_1_0_2020_12, + .expected_kem_group = NULL, + .expected_curve = expected_curve, + .hrr_expected = false, + .len_prefix_expected = true, + }, + + /* Server supports PQ, but client does not. Client did not send any EC shares, + * EC should be negotiated after exchanging HRR */ + { + .client_policy = &security_policy_test_tls13_retry, + .server_policy = &security_policy_pq_tls_1_0_2020_12, + .expected_kem_group = NULL, + .expected_curve = expected_curve, + .hrr_expected = true, + .len_prefix_expected = true, + }, + }; + + for (size_t i = 0; i < s2n_array_len(test_vectors); i++) { + const struct pq_handshake_test_vector *vector = &test_vectors[i]; + const struct s2n_security_policy *client_policy = vector->client_policy; + const struct s2n_security_policy *server_policy = vector->server_policy; + const struct s2n_kem_group *kem_group = vector->expected_kem_group; + const struct s2n_ecc_named_curve *curve = vector->expected_curve; + bool hrr_expected = vector->hrr_expected; + bool len_prefix_expected = vector->len_prefix_expected; + + if (!s2n_pq_is_enabled()) { + /* If PQ is disabled, for older policies we expect to negotiate + * x25519 ECDH if available. Newer policies only include NIST + * curves, so if the server policy doesn't contain x25519, modify + * that expectation to a NIST curve. + */ + kem_group = NULL; + if (!s2n_ecc_preferences_includes_curve(server_policy->ecc_preferences, expected_curve->iana_id)) { + curve = &s2n_ecc_curve_secp256r1; + } else { + curve = expected_curve; + } + } + + EXPECT_SUCCESS(s2n_test_tls13_pq_handshake(client_policy, server_policy, kem_group, curve, hrr_expected, len_prefix_expected)); + } + + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_prf_test.c b/tests/unit/s2n_tls13_prf_test.c new file mode 100644 index 00000000000..bbee3e26143 --- /dev/null +++ b/tests/unit/s2n_tls13_prf_test.c @@ -0,0 +1,145 @@ +/* + * 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_hkdf.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_safety.h" + +/* + * Test vectors from https://datatracker.ietf.org/doc/draft-ietf-tls-tls13-vectors/?include_text=1 + */ +int main(int argc, char **argv) +{ + char client_handshake_message_hex_in[] = "010000c003032724c0ba613abd59" + "4894f19ff6d59cde8364549555119b96ec1158fb9ac" + "ba397000006130113031302010000910000000b0009" + "000006736572766572ff01000100000a00140012001" + "d001700180019010001010102010301040023000000" + "3300260024001d00207e74fe9d31f2bb96f4f553465" + "b92ea8210971e71e258d6cf622c3b086db26104002b" + "0003027f1c000d0020001e040305030603020308040" + "805080604010501060102010402050206020202002d" + "00020101001c00024001"; + + char server_handshake_message_hex_in[] = "020000560303b9206e3d30c43c8a" + "cb1c234f9d004c6a2fecb84c6811ca285c1bbce322" + "bed16000130100002e00330024001d0020bba1ffe6" + "d10f92d4a8444aa51913fea27c3d2bfdf24489da40" + "92dfbfbfd67c53002b00027f1c"; + + char expected_secret_hex_in[] = "33ad0a1c607ec03b09e6cd9893680ce210adf300aa1f2660e1b22e10f170f92a"; + char expected_expanded_hex_in[] = "6f2615a108c702c5678f54fc9dbab69716c076189c48250cebeac3576c3611ba"; + + DEFER_CLEANUP(struct s2n_stuffer client_handshake_message_in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer server_handshake_message_in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer expected_secret_in = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer expected_expanded_in = { 0 }, s2n_stuffer_free); + + char client_handshake_message[sizeof(client_handshake_message_hex_in) / 2] = { 0 }; + char server_handshake_message[sizeof(server_handshake_message_hex_in) / 2] = { 0 }; + char expected_secret[sizeof(expected_secret_hex_in) / 2] = { 0 }; + char expected_expanded[sizeof(expected_expanded_hex_in) / 2] = { 0 }; + + uint8_t digest_buf[SHA256_DIGEST_LENGTH]; + uint8_t secret_buf[SHA256_DIGEST_LENGTH]; + struct s2n_blob digest = { 0 }; + struct s2n_blob secret = { 0 }; + + struct s2n_hash_state transcript_hash, transcript_hash_snapshot; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_SUCCESS(s2n_stuffer_alloc_ro_from_string(&client_handshake_message_in, client_handshake_message_hex_in)); + EXPECT_SUCCESS(s2n_stuffer_alloc_ro_from_string(&server_handshake_message_in, server_handshake_message_hex_in)); + EXPECT_SUCCESS(s2n_stuffer_alloc_ro_from_string(&expected_secret_in, expected_secret_hex_in)); + EXPECT_SUCCESS(s2n_stuffer_alloc_ro_from_string(&expected_expanded_in, expected_expanded_hex_in)); + + /* Parse the hex */ + for (size_t i = 0; i < sizeof(client_handshake_message); i++) { + uint8_t c; + EXPECT_SUCCESS(s2n_stuffer_read_uint8_hex(&client_handshake_message_in, &c)); + client_handshake_message[i] = c; + } + + for (size_t i = 0; i < sizeof(server_handshake_message); i++) { + uint8_t c; + EXPECT_SUCCESS(s2n_stuffer_read_uint8_hex(&server_handshake_message_in, &c)); + server_handshake_message[i] = c; + } + + for (size_t i = 0; i < sizeof(expected_secret); i++) { + uint8_t c; + EXPECT_SUCCESS(s2n_stuffer_read_uint8_hex(&expected_secret_in, &c)); + expected_secret[i] = c; + } + + for (size_t i = 0; i < sizeof(expected_expanded); i++) { + uint8_t c; + EXPECT_SUCCESS(s2n_stuffer_read_uint8_hex(&expected_expanded_in, &c)); + expected_expanded[i] = c; + } + + EXPECT_SUCCESS(s2n_hash_new(&transcript_hash)); + EXPECT_SUCCESS(s2n_hash_new(&transcript_hash_snapshot)); + EXPECT_SUCCESS(s2n_hash_init(&transcript_hash, S2N_HASH_SHA256)); + EXPECT_SUCCESS(s2n_hash_copy(&transcript_hash_snapshot, &transcript_hash)); + EXPECT_SUCCESS(s2n_hash_digest(&transcript_hash_snapshot, digest_buf, SHA256_DIGEST_LENGTH)); + + uint8_t salt_buf[32] = { 0 }; + struct s2n_blob salt = { 0 }; + + uint8_t ikm_buf[32] = { 0 }; + struct s2n_blob ikm = { 0 }; + + uint8_t output_buf[SHA256_DIGEST_LENGTH] = { 0 }; + struct s2n_blob output = { 0 }; + + EXPECT_SUCCESS(s2n_blob_init(&salt, salt_buf, sizeof(salt_buf))); + EXPECT_SUCCESS(s2n_blob_init(&ikm, ikm_buf, sizeof(ikm_buf))); + EXPECT_SUCCESS(s2n_blob_init(&digest, digest_buf, sizeof(digest_buf))); + EXPECT_SUCCESS(s2n_blob_init(&secret, secret_buf, sizeof(secret_buf))); + EXPECT_SUCCESS(s2n_blob_init(&output, output_buf, sizeof(output_buf))); + + struct s2n_hmac_state throwaway; + EXPECT_SUCCESS(s2n_hmac_new(&throwaway)); + + /* Validate the early secret */ + EXPECT_SUCCESS(s2n_hkdf_extract(&throwaway, S2N_HMAC_SHA256, &salt, &ikm, &secret)); + EXPECT_EQUAL(memcmp(secret_buf, expected_secret, sizeof(secret_buf)), 0); + + /* Validate the derived secret */ + S2N_BLOB_LABEL(label, "derived"); + + struct s2n_hmac_state hmac = { 0 }; + + EXPECT_SUCCESS(s2n_hmac_new(&hmac)); + EXPECT_SUCCESS(s2n_hkdf_expand_label(&hmac, S2N_HMAC_SHA256, &secret, &label, &digest, &output)); + + EXPECT_EQUAL(memcmp(output_buf, expected_expanded, sizeof(output_buf)), 0); + + EXPECT_SUCCESS(s2n_hmac_free(&throwaway)); + EXPECT_SUCCESS(s2n_hmac_free(&hmac)); + EXPECT_SUCCESS(s2n_hash_free(&transcript_hash)); + EXPECT_SUCCESS(s2n_hash_free(&transcript_hash_snapshot)); + + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_record_aead_test.c b/tests/unit/s2n_tls13_record_aead_test.c new file mode 100644 index 00000000000..dc4eb470fd4 --- /dev/null +++ b/tests/unit/s2n_tls13_record_aead_test.c @@ -0,0 +1,413 @@ +/* + * 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 "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_record.h" +#include "tls/s2n_record_read.h" +#include "utils/s2n_safety.h" + +const char protected_record_hex[] = "d1ff334a56f5bf" + "f6594a07cc87b580233f500f45e489e7f33af35edf" + "7869fcf40aa40aa2b8ea73f848a7ca07612ef9f945" + "cb960b4068905123ea78b111b429ba9191cd05d2a3" + "89280f526134aadc7fc78c4b729df828b5ecf7b13b" + "d9aefb0e57f271585b8ea9bb355c7c79020716cfb9" + "b1183ef3ab20e37d57a6b9d7477609aee6e122a4cf" + "51427325250c7d0e509289444c9b3a648f1d71035d" + "2ed65b0e3cdd0cbae8bf2d0b227812cbb360987255" + "cc744110c453baa4fcd610928d809810e4b7ed1a8f" + "d991f06aa6248204797e36a6a73b70a2559c09ead6" + "86945ba246ab66e5edd8044b4c6de3fcf2a89441ac" + "66272fd8fb330ef8190579b3684596c960bd596eea" + "520a56a8d650f563aad27409960dca63d3e688611e" + "a5e22f4415cf9538d51a200c27034272968a264ed6" + "540c84838d89f72c24461aad6d26f59ecaba9acbbb" + "317b66d902f4f292a36ac1b639c637ce343117b659" + "622245317b49eeda0c6258f100d7d961ffb138647e" + "92ea330faeea6dfa31c7a84dc3bd7e1b7a6c7178af" + "36879018e3f252107f243d243dc7339d5684c8b037" + "8bf30244da8c87c843f5e56eb4c5e8280a2b48052c" + "f93b16499a66db7cca71e4599426f7d461e66f9988" + "2bd89fc50800becca62d6c74116dbd2972fda1fa80" + "f85df881edbe5a37668936b335583b599186dc5c69" + "18a396fa48a181d6b6fa4f9d62d513afbb992f2b99" + "2f67f8afe67f76913fa388cb5630c8ca01e0c65d11" + "c66a1e2ac4c85977b7c7a6999bbf10dc35ae69f551" + "5614636c0b9b68c19ed2e31c0b3b66763038ebba42" + "f3b38edc0399f3a9f23faa63978c317fc9fa66a73f" + "60f0504de93b5b845e275592c12335ee340bbc4fdd" + "d502784016e4b3be7ef04dda49f4b440a30cb5d2af" + "939828fd4ae3794e44f94df5a631ede42c1719bfda" + "bf0253fe5175be898e750edc53370d2b"; /* includes tag */ + +const char plaintext_record_hex[] = + "080000240022000a00140012001d" + "00170018001901000101010201030104001c000240" + "01000000000b0001b9000001b50001b0308201ac30" + "820115a003020102020102300d06092a864886f70d" + "01010b0500300e310c300a06035504031303727361" + "301e170d3136303733303031323335395a170d3236" + "303733303031323335395a300e310c300a06035504" + "03130372736130819f300d06092a864886f70d0101" + "01050003818d0030818902818100b4bb498f827930" + "3d980836399b36c6988c0c68de55e1bdb826d3901a" + "2461eafd2de49a91d015abbc9a95137ace6c1af19e" + "aa6af98c7ced43120998e187a80ee0ccb0524b1b01" + "8c3e0b63264d449a6d38e22a5fda43084674803053" + "0ef0461c8ca9d9efbfae8ea6d1d03e2bd193eff0ab" + "9a8002c47428a6d35a8d88d79f7f1e3f0203010001" + "a31a301830090603551d1304023000300b0603551d" + "0f0404030205a0300d06092a864886f70d01010b05" + "000381810085aad2a0e5b9276b908c65f73a726717" + "0618a54c5f8a7b337d2df7a594365417f2eae8f8a5" + "8c8f8172f9319cf36b7fd6c55b80f21a0301515672" + "6096fd335e5e67f2dbf102702e608ccae6bec1fc63" + "a42a99be5c3eb7107c3c54e9b9eb2bd5203b1c3b84" + "e0a8b2f759409ba3eac9d91d402dcc0cc8f8961229" + "ac9187b42b4de100000f000084080400805a747c5d" + "88fa9bd2e55ab085a61015b7211f824cd484145ab3" + "ff52f1fda8477b0b7abc90db78e2d33a5c141a0786" + "53fa6bef780c5ea248eeaaa785c4f394cab6d30bbe" + "8d4859ee511f602957b15411ac027671459e46445c" + "9ea58c181e818e95b8c3fb0bf3278409d3be152a3d" + "a5043e063dda65cdf5aea20d53dfacd42f74f31400" + "00209b9b141d906337fbd2cbdce71df4deda4ab42c" + "309572cb7fffee5454b78f071816"; /* includes last byte for content type */ + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Test s2n_tls13_aead_aad_init() */ + { + s2n_stack_blob(aad, S2N_TLS13_AAD_LEN, S2N_TLS13_AAD_LEN); + EXPECT_OK(s2n_tls13_aead_aad_init(662, 12, &aad)); + S2N_BLOB_FROM_HEX(expected_aad, "17030302a2"); + S2N_BLOB_EXPECT_EQUAL(expected_aad, aad); + + /* record length 16640 should be valid */ + EXPECT_SUCCESS(s2n_blob_zero(&aad)); + EXPECT_OK(s2n_tls13_aead_aad_init(16628, 12, &aad)); + + /* record length 16641 should be invalid */ + EXPECT_SUCCESS(s2n_blob_zero(&aad)); + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_aead_aad_init(16629, 12, &aad), S2N_ERR_RECORD_LIMIT); + + /* Test failure case: No AAD should be invalid */ + EXPECT_SUCCESS(s2n_blob_zero(&aad)); + EXPECT_ERROR(s2n_tls13_aead_aad_init(16629, 12, NULL)); + + /* Test failure case: 0-length tag should be invalid */ + EXPECT_SUCCESS(s2n_blob_zero(&aad)); + EXPECT_ERROR(s2n_tls13_aead_aad_init(16628, 0, &aad)); + + /* Test failure case: invalid record length (-1) should be invalid */ + EXPECT_ERROR(s2n_tls13_aead_aad_init(-1, 0, &aad)); + } + + /* Test s2n_tls13_aes_128_gcm_sha256 cipher suite with TLS 1.3 test vectors */ + { + struct s2n_connection *conn; + struct s2n_session_key session_key = { 0 }; + EXPECT_SUCCESS(s2n_session_key_alloc(&session_key)); + + struct s2n_cipher_suite *cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS13; + + /* init record algorithm */ + EXPECT_SUCCESS(cipher_suite->record_alg->cipher->init(&session_key)); + S2N_BLOB_FROM_HEX(key, "3fce516009c21727d0f2e4e86ee403bc"); + EXPECT_SUCCESS(cipher_suite->record_alg->cipher->set_decryption_key(&session_key, &key)); + + /* write protected record to conn in for testing */ + S2N_BLOB_FROM_HEX(protected_record, protected_record_hex); + EXPECT_SUCCESS(s2n_stuffer_write(&conn->in, &protected_record)); + + S2N_BLOB_FROM_HEX(iv, "5d313eb2671276ee13000b30"); + + /* Test parsing of tls 1.3 aead record */ + EXPECT_SUCCESS(s2n_record_parse_aead( + cipher_suite, + conn, + 0, /* content_type doesn't matter for TLS 1.3 */ + protected_record.size, + iv.data, /* implicit_iv */ + NULL, /* mac not used for TLS 1.3 */ + conn->secure->client_sequence_number, + &session_key)); + + S2N_BLOB_FROM_HEX(plaintext_record, plaintext_record_hex); + + /* Because the decrypted payload contains both plaintext and tag, + * we copy the contents for verification. + */ + s2n_stack_blob(decrypted, plaintext_record.size, 1000); + struct s2n_stuffer decrypted_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&decrypted_stuffer, &decrypted)); + + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&decrypted_stuffer, conn->in.blob.data, plaintext_record.size)); + S2N_BLOB_EXPECT_EQUAL(plaintext_record, decrypted_stuffer.blob); + +#define RESET_TEST \ + /* wipe conn in stuffer and refill protected record */ \ + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); \ + EXPECT_SUCCESS(s2n_stuffer_write(&conn->in, &protected_record)); \ + /* reset sequence number */ \ + conn->secure->client_sequence_number[7] = 0; + + /* Repeat the test to prove RESET_TEST works */ + RESET_TEST + EXPECT_SUCCESS(s2n_record_parse_aead(cipher_suite, conn, 0, protected_record.size, + iv.data, NULL, conn->secure->client_sequence_number, &session_key)); + + /* Test record parsing failure from aead tag change */ + RESET_TEST + conn->in.blob.data[protected_record.size - 2]++; + EXPECT_FAILURE(s2n_record_parse_aead(cipher_suite, conn, 0, protected_record.size, + iv.data, NULL, conn->secure->client_sequence_number, &session_key)); + + /* Test incorrect ciphertext changes fails parsing */ + RESET_TEST + conn->in.blob.data[0]++; + EXPECT_FAILURE(s2n_record_parse_aead(cipher_suite, conn, 0, protected_record.size, + iv.data, NULL, conn->secure->client_sequence_number, &session_key)); + + /* Test wrong sequence number fails parsing */ + RESET_TEST + conn->secure->client_sequence_number[7] = 1; + EXPECT_FAILURE(s2n_record_parse_aead(cipher_suite, conn, 0, protected_record.size, + iv.data, NULL, conn->secure->client_sequence_number, &session_key)); + + /* Test IV changes fails parsing */ + RESET_TEST + iv.data[0]++; + EXPECT_FAILURE(s2n_record_parse_aead(cipher_suite, conn, 0, protected_record.size, + iv.data, NULL, conn->secure->client_sequence_number, &session_key)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(s2n_session_key_free(&session_key)); + }; + + /* Test s2n_tls13_aes_128_gcm_sha256 cipher suite ENCRYPTION with TLS 1.3 test vectors */ + { + struct s2n_connection *conn; + struct s2n_cipher_suite *cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS13; + + conn->server->cipher_suite = cipher_suite; + struct s2n_session_key *session_key = &conn->server->server_key; + + uint8_t *implicit_iv = conn->server->server_implicit_iv; + + /* init record algorithm */ + EXPECT_SUCCESS(cipher_suite->record_alg->cipher->init(session_key)); + S2N_BLOB_FROM_HEX(key, "3fce516009c21727d0f2e4e86ee403bc"); + EXPECT_SUCCESS(cipher_suite->record_alg->cipher->set_encryption_key(session_key, &key)); + + S2N_BLOB_FROM_HEX(protected_record, protected_record_hex); + + S2N_BLOB_FROM_HEX(iv, "5d313eb2671276ee13000b30"); + + /* copy iv bytes from input data */ + for (size_t i = 0; i < iv.size; i++) { + implicit_iv[i] = iv.data[i]; + } + + /* Test parsing of tls 1.3 aead record */ + S2N_BLOB_FROM_HEX(plaintext_record, plaintext_record_hex); + + /* Make plaintext blob slice */ + struct s2n_blob in = { + .data = &plaintext_record.data[0], + .size = plaintext_record.size - 1, /* 1 byte less to remove content type */ + }; + + /* Takes an input blob and writes to out stuffer then encrypt the payload */ + EXPECT_OK(s2n_record_write(conn, TLS_HANDSHAKE, &in)); + + /* Verify opaque content type in tls 1.3 */ + EXPECT_EQUAL(conn->out.blob.data[0], TLS_APPLICATION_DATA); + /* Verify TLS legacy record version */ + EXPECT_EQUAL(conn->out.blob.data[1], 3); + EXPECT_EQUAL(conn->out.blob.data[2], 3); + /* Verify payload length */ + EXPECT_EQUAL((conn->out.blob.data[3] << 8) + conn->out.blob.data[4], protected_record.size); + + /* Make a slice of output bytes to verify */ + struct s2n_blob out = { + .data = &conn->out.blob.data[S2N_TLS13_AAD_LEN], + .size = protected_record.size + }; + + S2N_BLOB_EXPECT_EQUAL(out, protected_record); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test encrypt-decrypt roundtrip */ + { + struct s2n_connection *conn; + struct s2n_cipher_suite *cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS13; + + conn->server->cipher_suite = cipher_suite; + struct s2n_session_key *session_key = &conn->server->server_key; + + uint8_t *implicit_iv = conn->server->server_implicit_iv; + + /* init record algorithm */ + EXPECT_SUCCESS(cipher_suite->record_alg->cipher->init(session_key)); + S2N_BLOB_FROM_HEX(key, "3fce516009c21727d0f2e4e86ee403bc"); + EXPECT_SUCCESS(cipher_suite->record_alg->cipher->set_encryption_key(session_key, &key)); + EXPECT_SUCCESS(cipher_suite->record_alg->cipher->set_decryption_key(session_key, &key)); + + S2N_BLOB_FROM_HEX(iv, "5d313eb2671276ee13000b30"); + + /* copy iv bytes from input data */ + for (size_t i = 0; i < iv.size; i++) { + implicit_iv[i] = iv.data[i]; + } + + /* Test parsing of tls 1.3 aead record */ + S2N_BLOB_LABEL(expect_plaintext, "Hello world"); + + static uint8_t hello_data[] = "Hello world"; + struct s2n_blob plaintext = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&plaintext, hello_data, sizeof(hello_data) - 1)); + + /* Takes an input blob and writes to out stuffer then encrypt the payload */ + EXPECT_OK(s2n_record_write(conn, TLS_HANDSHAKE, &plaintext)); + + /* Reset sequence number */ + conn->secure->client_sequence_number[7] = 0; + + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&conn->in, &conn->out.blob.data[S2N_TLS13_AAD_LEN], plaintext.size + 16 + 1)); /* tag length + content type */ + + /* Make a slice of output bytes to verify */ + struct s2n_blob encrypted = { + .data = &conn->in.blob.data[0], + .size = plaintext.size + 16 + 1 + }; + + /* Decrypt payload */ + EXPECT_SUCCESS(s2n_record_parse_aead( + cipher_suite, + conn, + 0, /* content_type */ + encrypted.size, + iv.data, /* implicit_iv */ + NULL, /* mac not used for TLS 1.3 */ + conn->secure->client_sequence_number, + session_key)); + + struct s2n_blob decrypted = { + .data = &conn->in.blob.data[0], + .size = expect_plaintext.size + }; + + /* Verify decrypted payload */ + S2N_BLOB_EXPECT_EQUAL(decrypted, expect_plaintext); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Test that CCS in TLS 1.3 modes should be sent without encryption */ + { + s2n_mode modes[] = { S2N_SERVER, S2N_CLIENT }; + for (size_t m = 0; m < s2n_array_len(modes); m++) { + struct s2n_connection *conn; + struct s2n_cipher_suite *cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + EXPECT_NOT_NULL(conn = s2n_connection_new(modes[m])); + conn->actual_protocol_version = S2N_TLS13; + conn->server_protocol_version = S2N_TLS13; + conn->secure->cipher_suite = cipher_suite; + conn->server = conn->secure; + conn->client = conn->secure; + + /* init record algorithm */ + EXPECT_SUCCESS(cipher_suite->record_alg->cipher->init(&conn->secure->server_key)); + EXPECT_SUCCESS(cipher_suite->record_alg->cipher->init(&conn->secure->client_key)); + S2N_BLOB_FROM_HEX(key, "3fce516009c21727d0f2e4e86ee403bc"); + EXPECT_SUCCESS(cipher_suite->record_alg->cipher->set_encryption_key(&conn->secure->server_key, &key)); + EXPECT_SUCCESS(cipher_suite->record_alg->cipher->set_decryption_key(&conn->secure->client_key, &key)); + + S2N_BLOB_FROM_HEX(protected_record, protected_record_hex); + S2N_BLOB_FROM_HEX(iv, "5d313eb2671276ee13000b30"); + + /* copy iv bytes from input data */ + for (size_t i = 0; i < iv.size; i++) { + conn->secure->server_implicit_iv[i] = iv.data[i]; + conn->secure->client_implicit_iv[i] = iv.data[i]; + } + + /* Test parsing of tls 1.3 aead record */ + S2N_BLOB_FROM_HEX(plaintext_record, plaintext_record_hex); + + uint8_t change_cipher_spec[] = { 1 }; + struct s2n_blob in = { .data = change_cipher_spec, .size = sizeof(change_cipher_spec) }; + + /* Takes an input blob and writes to out stuffer then encrypt the payload */ + EXPECT_OK(s2n_record_write(conn, TLS_CHANGE_CIPHER_SPEC, &in)); + + S2N_STUFFER_READ_EXPECT_EQUAL(&conn->out, TLS_CHANGE_CIPHER_SPEC, uint8); + S2N_STUFFER_READ_EXPECT_EQUAL(&conn->out, 0x0303, uint16); + S2N_STUFFER_READ_EXPECT_EQUAL(&conn->out, 1, uint16); + S2N_STUFFER_READ_EXPECT_EQUAL(&conn->out, 1, uint8); + + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->out)); + + /* An encrypted TLS 1.3 HANDSHAKE type will look like a APPLICATION_DATA over the wire */ + EXPECT_OK(s2n_record_write(conn, TLS_HANDSHAKE, &in)); + + /* now test that application data writes encrypted payload */ + S2N_STUFFER_READ_EXPECT_EQUAL(&conn->out, TLS_APPLICATION_DATA, uint8); + S2N_STUFFER_READ_EXPECT_EQUAL(&conn->out, 0x0303, uint16); + S2N_STUFFER_READ_EXPECT_EQUAL(&conn->out, 18, uint16); + S2N_STUFFER_READ_EXPECT_EQUAL(&conn->out, 216, uint8); + + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&conn->header_in, TLS_CHANGE_CIPHER_SPEC)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&conn->header_in, 0x0303)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&conn->header_in, 1)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&conn->in, 1)); + + /* Parses unencrypted CCS record correctly */ + EXPECT_SUCCESS(s2n_record_parse(conn)); + + /* now if this was an application data, it cannot be parsed */ + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->header_in)); + EXPECT_SUCCESS(s2n_stuffer_wipe(&conn->in)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&conn->header_in, TLS_APPLICATION_DATA)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&conn->header_in, 0x0303)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&conn->header_in, 1)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&conn->in, 1)); + EXPECT_FAILURE(s2n_record_parse(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_secrets_rfc8448_test.c b/tests/unit/s2n_tls13_secrets_rfc8448_test.c new file mode 100644 index 00000000000..12a8783ae58 --- /dev/null +++ b/tests/unit/s2n_tls13_secrets_rfc8448_test.c @@ -0,0 +1,666 @@ +/* + * 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. + */ + +/* Needed to set up X25519 key shares */ +#include + +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_tls13_key_schedule.h" +#include "tls/s2n_tls13_secrets.h" + +struct s2n_cipher_suite *cipher_suite = &s2n_tls13_aes_128_gcm_sha256; +const struct s2n_ecc_named_curve *curve = &s2n_ecc_curve_x25519; + +const uint32_t test_handshake_type = NEGOTIATED | FULL_HANDSHAKE; +const int server_hello_message_num = 1; + +const s2n_mode modes[] = { S2N_CLIENT, S2N_SERVER }; + +S2N_RESULT s2n_extract_early_secret(struct s2n_psk *psk); +S2N_RESULT s2n_tls13_extract_secret(struct s2n_connection *conn, s2n_extract_secret_type_t secret_type); +S2N_RESULT s2n_tls13_derive_secret(struct s2n_connection *conn, s2n_extract_secret_type_t secret_type, + s2n_mode mode, struct s2n_blob *secret); + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + struct s2n_blob derived_secret = { 0 }; + uint8_t derived_secret_bytes[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&derived_secret, + derived_secret_bytes, S2N_TLS13_SECRET_MAX_LEN)); + + /* + * Simple 1-RTT Handshake + */ + { + /** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {client} extract secret "early" (same as server early secret) + * + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {server} extract secret "early": + *# + *# salt: 0 (all zero octets) + *# + *# IKM (32 octets): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# + *# secret (32 octets): 33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c + *# e2 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a + */ + S2N_BLOB_FROM_HEX(early_secret, "33 ad 0a 1c 60 7e c0 3b 09 e6 cd 98 93 68 0c \ + e2 10 ad f3 00 aa 1f 26 60 e1 b2 2e 10 f1 70 f9 2a"); + + /* Extract EARLY_SECRET */ + { + for (size_t i = 0; i < s2n_array_len(modes); i++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(modes[i]), s2n_connection_ptr_free); + conn->secure->cipher_suite = cipher_suite; + + EXPECT_OK(s2n_tls13_extract_secret(conn, S2N_EARLY_SECRET)); + EXPECT_BYTEARRAY_EQUAL(conn->secrets.version.tls13.extract_secret, + early_secret.data, early_secret.size); + EXPECT_EQUAL(conn->secrets.extract_secret_type, S2N_EARLY_SECRET); + } + }; + + /** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {client} extract secret "handshake" (same as server handshake + *# secret) + * + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {server} extract secret "handshake": + *# + *# salt (32 octets): 6f 26 15 a1 08 c7 02 c5 67 8f 54 fc 9d ba b6 97 + *# 16 c0 76 18 9c 48 25 0c eb ea c3 57 6c 36 11 ba + *# + *# IKM (32 octets): 8b d4 05 4f b5 5b 9d 63 fd fb ac f9 f0 4b 9f 0d + *# 35 e6 d6 3f 53 75 63 ef d4 62 72 90 0f 89 49 2d + *# + *# secret (32 octets): 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b + *# 01 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac + */ + S2N_BLOB_FROM_HEX(handshake_secret, "1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b \ + 01 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac"); + + /* Extract HANDSHAKE_SECRET + * + * The RFC values use x25519, which is only supported via EVP APIs. + * Additionally, the specific APIs we use to set the EVP key require Openssl-1.1.1. + */ +#if EVP_APIS_SUPPORTED && S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 1) + { + const int openssl_type = EVP_PKEY_X25519; + + /** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {client} create an ephemeral x25519 key pair: + *# + *# private key (32 octets): 49 af 42 ba 7f 79 94 85 2d 71 3e f2 78 + *# 4b cb ca a7 91 1d e2 6a dc 56 42 cb 63 45 40 e7 ea 50 05 + *# + *# public key (32 octets): 99 38 1d e5 60 e4 bd 43 d2 3d 8e 43 5a 7d + *# ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a af 2c + */ + S2N_BLOB_FROM_HEX(client_priv, "49 af 42 ba 7f 79 94 85 2d 71 3e f2 78 \ + 4b cb ca a7 91 1d e2 6a dc 56 42 cb 63 45 40 e7 ea 50 05"); + S2N_BLOB_FROM_HEX(client_pub, "99 38 1d e5 60 e4 bd 43 d2 3d 8e 43 5a 7d \ + ba fe b3 c0 6e 51 c1 3c ae 4d 54 13 69 1e 52 9a af 2c"); + + /** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {server} create an ephemeral x25519 key pair: + *# + *# private key (32 octets): b1 58 0e ea df 6d d5 89 b8 ef 4f 2d 56 + *# 52 57 8c c8 10 e9 98 01 91 ec 8d 05 83 08 ce a2 16 a2 1e + *# + *# public key (32 octets): c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 + *# 72 e1 56 d6 cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f + */ + S2N_BLOB_FROM_HEX(server_priv, "b1 58 0e ea df 6d d5 89 b8 ef 4f 2d 56 \ + 52 57 8c c8 10 e9 98 01 91 ec 8d 05 83 08 ce a2 16 a2 1e"); + S2N_BLOB_FROM_HEX(server_pub, "c9 82 88 76 11 20 95 fe 66 76 2b db f7 c6 \ + 72 e1 56 d6 cc 25 3b 83 3d f1 dd 69 b1 b0 4e 75 1f 0f"); + + /* Server */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + conn->secure->cipher_suite = cipher_suite; + EXPECT_OK(s2n_connection_set_test_early_secret(conn, &early_secret)); + + conn->kex_params.server_ecc_evp_params.negotiated_curve = curve; + conn->kex_params.server_ecc_evp_params.evp_pkey = EVP_PKEY_new_raw_private_key( + openssl_type, NULL, (unsigned char *) server_priv.data, server_priv.size); + EXPECT_NOT_NULL(conn->kex_params.server_ecc_evp_params.evp_pkey); + + conn->kex_params.client_ecc_evp_params.negotiated_curve = curve; + conn->kex_params.client_ecc_evp_params.evp_pkey = EVP_PKEY_new_raw_public_key( + openssl_type, NULL, (unsigned char *) client_pub.data, client_pub.size); + EXPECT_NOT_NULL(conn->kex_params.client_ecc_evp_params.evp_pkey); + + EXPECT_OK(s2n_tls13_extract_secret(conn, S2N_HANDSHAKE_SECRET)); + EXPECT_BYTEARRAY_EQUAL(conn->secrets.version.tls13.extract_secret, + handshake_secret.data, handshake_secret.size); + EXPECT_EQUAL(conn->secrets.extract_secret_type, S2N_HANDSHAKE_SECRET); + }; + + /* Client */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + conn->secure->cipher_suite = cipher_suite; + EXPECT_OK(s2n_connection_set_test_early_secret(conn, &early_secret)); + + conn->kex_params.server_ecc_evp_params.negotiated_curve = curve; + conn->kex_params.server_ecc_evp_params.evp_pkey = EVP_PKEY_new_raw_public_key( + openssl_type, NULL, (unsigned char *) server_pub.data, server_pub.size); + EXPECT_NOT_NULL(conn->kex_params.server_ecc_evp_params.evp_pkey); + + conn->kex_params.client_ecc_evp_params.negotiated_curve = curve; + conn->kex_params.client_ecc_evp_params.evp_pkey = EVP_PKEY_new_raw_private_key( + openssl_type, NULL, (unsigned char *) client_priv.data, client_priv.size); + EXPECT_NOT_NULL(conn->kex_params.client_ecc_evp_params.evp_pkey); + + EXPECT_OK(s2n_tls13_extract_secret(conn, S2N_HANDSHAKE_SECRET)); + EXPECT_BYTEARRAY_EQUAL(conn->secrets.version.tls13.extract_secret, + handshake_secret.data, handshake_secret.size); + EXPECT_EQUAL(conn->secrets.extract_secret_type, S2N_HANDSHAKE_SECRET); + }; + } +#endif + + /* Derive S2N_CLIENT_HANDSHAKE_TRAFFIC_SECRET */ + { + /** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {client} derive secret "tls13 c hs traffic" (same as server) + * + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {server} derive secret "tls13 c hs traffic": + *# + *# PRK (32 octets): 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b 01 + *# 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac + ** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# hash (32 octets): 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed + *# d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8 + *# + *# info (54 octets): 00 20 12 74 6c 73 31 33 20 63 20 68 73 20 74 72 + *# 61 66 66 69 63 20 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 + *# ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8 + *# + *# expanded (32 octets): b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e + *# 2d 8f 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21 + */ + S2N_BLOB_FROM_HEX(hash, "86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed \ + d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8"); + S2N_BLOB_FROM_HEX(secret, "b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e \ + 2d 8f 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21"); + + /** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {client} calculate finished "tls13 finished" (same as server) + * + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {client} calculate finished "tls13 finished": + *# + *# PRK (32 octets): b3 ed db 12 6e 06 7f 35 a7 80 b3 ab f4 5e 2d 8f + *# 3b 1a 95 07 38 f5 2e 96 00 74 6a 0e 27 a5 5a 21 + ** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# hash (0 octets): (empty) + *# + *# info (18 octets): 00 20 0e 74 6c 73 31 33 20 66 69 6e 69 73 68 65 + *# 64 00 + *# + *# expanded (32 octets): b8 0a d0 10 15 fb 2f 0b d6 5f f7 d4 da 5d + *# 6b f8 3f 84 82 1d 1f 87 fd c7 d3 c7 5b 5a 7b 42 d9 c4 + */ + S2N_BLOB_FROM_HEX(finished_key, "b8 0a d0 10 15 fb 2f 0b d6 5f f7 d4 da 5d \ + 6b f8 3f 84 82 1d 1f 87 fd c7 d3 c7 5b 5a 7b 42 d9 c4"); + + for (size_t i = 0; i < s2n_array_len(modes); i++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(modes[i]), s2n_connection_ptr_free); + conn->secure->cipher_suite = cipher_suite; + EXPECT_OK(s2n_connection_set_test_handshake_secret(conn, &handshake_secret)); + EXPECT_OK(s2n_connection_set_test_transcript_hash(conn, SERVER_HELLO, &hash)); + + EXPECT_OK(s2n_tls13_derive_secret(conn, S2N_HANDSHAKE_SECRET, S2N_CLIENT, + &derived_secret)); + + EXPECT_EQUAL(derived_secret.size, secret.size); + EXPECT_BYTEARRAY_EQUAL(derived_secret.data, secret.data, secret.size); + EXPECT_BYTEARRAY_EQUAL(conn->handshake.client_finished, + finished_key.data, finished_key.size); + } + }; + + /* Derive S2N_SERVER_HANDSHAKE_TRAFFIC_SECRET */ + { + /** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {client} derive secret "tls13 s hs traffic" (same as server) + * + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {server} derive secret "tls13 s hs traffic": + *# + *# PRK (32 octets): 1d c8 26 e9 36 06 aa 6f dc 0a ad c1 2f 74 1b 01 + *# 04 6a a6 b9 9f 69 1e d2 21 a9 f0 ca 04 3f be ac + *# + *# hash (32 octets): 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed + *# d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8 + *# + *# info (54 octets): 00 20 12 74 6c 73 31 33 20 73 20 68 73 20 74 72 + *# 61 66 66 69 63 20 86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 + *# ed d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8 + *# + *# expanded (32 octets): b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d + *# 37 b4 e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38 + */ + S2N_BLOB_FROM_HEX(hash, "86 0c 06 ed c0 78 58 ee 8e 78 f0 e7 42 8c 58 ed \ + d6 b4 3f 2c a3 e6 e9 5f 02 ed 06 3c f0 e1 ca d8"); + S2N_BLOB_FROM_HEX(secret, "b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d \ + 37 b4 e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38"); + + /** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {client} calculate finished "tls13 finished" (same as server) + * + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {server} calculate finished "tls13 finished": + *# + *# PRK (32 octets): b6 7b 7d 69 0c c1 6c 4e 75 e5 42 13 cb 2d 37 b4 + *# e9 c9 12 bc de d9 10 5d 42 be fd 59 d3 91 ad 38 + *# + *# hash (0 octets): (empty) + *# + *# info (18 octets): 00 20 0e 74 6c 73 31 33 20 66 69 6e 69 73 68 65 + *# 64 00 + *# + *# expanded (32 octets): 00 8d 3b 66 f8 16 ea 55 9f 96 b5 37 e8 85 + *# c3 1f c0 68 bf 49 2c 65 2f 01 f2 88 a1 d8 cd c1 9f c8 + */ + S2N_BLOB_FROM_HEX(finished_key, "00 8d 3b 66 f8 16 ea 55 9f 96 b5 37 e8 85 \ + c3 1f c0 68 bf 49 2c 65 2f 01 f2 88 a1 d8 cd c1 9f c8"); + + for (size_t i = 0; i < s2n_array_len(modes); i++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(modes[i]), s2n_connection_ptr_free); + conn->secure->cipher_suite = cipher_suite; + EXPECT_OK(s2n_connection_set_test_handshake_secret(conn, &handshake_secret)); + EXPECT_OK(s2n_connection_set_test_transcript_hash(conn, SERVER_HELLO, &hash)); + + EXPECT_OK(s2n_tls13_derive_secret(conn, S2N_HANDSHAKE_SECRET, S2N_SERVER, + &derived_secret)); + EXPECT_EQUAL(derived_secret.size, secret.size); + EXPECT_BYTEARRAY_EQUAL(derived_secret.data, secret.data, secret.size); + EXPECT_BYTEARRAY_EQUAL(conn->handshake.server_finished, + finished_key.data, finished_key.size); + } + }; + + /** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {client} extract secret "master" (same as server master secret) + * + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {server} extract secret "master": + *# + *# salt (32 octets): 43 de 77 e0 c7 77 13 85 9a 94 4d b9 db 25 90 b5 + *# 31 90 a6 5b 3e e2 e4 f1 2d d7 a0 bb 7c e2 54 b4 + *# + *# IKM (32 octets): 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + *# 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 + ** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# secret (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a + *# 47 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19 + */ + S2N_BLOB_FROM_HEX(master_secret, "18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a \ + 47 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19"); + + /* + * Extract MASTER_SECRET + */ + { + for (size_t i = 0; i < s2n_array_len(modes); i++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(modes[i]), s2n_connection_ptr_free); + conn->secure->cipher_suite = cipher_suite; + EXPECT_OK(s2n_connection_set_test_handshake_secret(conn, &handshake_secret)); + + EXPECT_OK(s2n_tls13_extract_secret(conn, S2N_MASTER_SECRET)); + EXPECT_BYTEARRAY_EQUAL(conn->secrets.version.tls13.extract_secret, + master_secret.data, master_secret.size); + EXPECT_EQUAL(conn->secrets.extract_secret_type, S2N_MASTER_SECRET); + } + }; + + /* Derive CLIENT_APPLICATION_TRAFFIC_SECRET_0 */ + { + /** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {client} derive secret "tls13 c ap traffic" (same as server) + * + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {server} derive secret "tls13 c ap traffic": + *# + *# PRK (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47 + *# 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19 + *# + *# hash (32 octets): 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a + *# 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13 + *# + *# info (54 octets): 00 20 12 74 6c 73 31 33 20 63 20 61 70 20 74 72 + *# 61 66 66 69 63 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b + *# 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13 + *# + *# expanded (32 octets): 9e 40 64 6c e7 9a 7f 9d c0 5a f8 88 9b ce + *# 65 52 87 5a fa 0b 06 df 00 87 f7 92 eb b7 c1 75 04 a5 + */ + S2N_BLOB_FROM_HEX(hash, "96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a \ + 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"); + S2N_BLOB_FROM_HEX(secret, "9e 40 64 6c e7 9a 7f 9d c0 5a f8 88 9b ce \ + 65 52 87 5a fa 0b 06 df 00 87 f7 92 eb b7 c1 75 04 a5"); + + for (size_t i = 0; i < s2n_array_len(modes); i++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(modes[i]), s2n_connection_ptr_free); + conn->secure->cipher_suite = cipher_suite; + EXPECT_OK(s2n_connection_set_test_master_secret(conn, &master_secret)); + EXPECT_OK(s2n_connection_set_test_transcript_hash(conn, SERVER_FINISHED, &hash)); + + EXPECT_OK(s2n_tls13_derive_secret(conn, S2N_MASTER_SECRET, S2N_CLIENT, + &derived_secret)); + EXPECT_EQUAL(derived_secret.size, secret.size); + EXPECT_BYTEARRAY_EQUAL(derived_secret.data, secret.data, secret.size); + } + }; + + /* Derive SERVER_APPLICATION_TRAFFIC_SECRET_0 */ + { + /** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {client} derive secret "tls13 s ap traffic" (same as server) + * + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {server} derive secret "tls13 s ap traffic": + *# + *# PRK (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47 + *# 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19 + *# + *# hash (32 octets): 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a + *# 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13 + *# + *# info (54 octets): 00 20 12 74 6c 73 31 33 20 73 20 61 70 20 74 72 + *# 61 66 66 69 63 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b + *# 1a 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13 + ** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# expanded (32 octets): a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9 + *# 50 32 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43 + */ + S2N_BLOB_FROM_HEX(hash, "96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a \ + 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"); + S2N_BLOB_FROM_HEX(secret, "a1 1a f9 f0 55 31 f8 56 ad 47 11 6b 45 a9 \ + 50 32 82 04 b4 f4 4b fb 6b 3a 4b 4f 1f 3f cb 63 16 43"); + + for (size_t i = 0; i < s2n_array_len(modes); i++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(modes[i]), s2n_connection_ptr_free); + conn->secure->cipher_suite = cipher_suite; + EXPECT_OK(s2n_connection_set_test_master_secret(conn, &master_secret)); + EXPECT_OK(s2n_connection_set_test_transcript_hash(conn, SERVER_FINISHED, &hash)); + + EXPECT_OK(s2n_tls13_derive_secret(conn, S2N_MASTER_SECRET, S2N_SERVER, + &derived_secret)); + + EXPECT_EQUAL(derived_secret.size, secret.size); + EXPECT_BYTEARRAY_EQUAL(derived_secret.data, secret.data, secret.size); + } + }; + + /* Derive RESUMPTION_MASTER_SECRET */ + { + /** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {server} derive secret "tls13 res master" (same as client) + * + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {client} derive secret "tls13 res master": + *# + *# PRK (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47 + *# 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19 + *# + *# hash (32 octets): 20 91 45 a9 6e e8 e2 a1 22 ff 81 00 47 cc 95 26 + *# 84 65 8d 60 49 e8 64 29 42 6d b8 7c 54 ad 14 3d + ** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# info (52 octets): 00 20 10 74 6c 73 31 33 20 72 65 73 20 6d 61 73 + *# 74 65 72 20 20 91 45 a9 6e e8 e2 a1 22 ff 81 00 47 cc 95 26 84 + *# 65 8d 60 49 e8 64 29 42 6d b8 7c 54 ad 14 3d + *# + *# expanded (32 octets): 7d f2 35 f2 03 1d 2a 05 12 87 d0 2b 02 41 + *# b0 bf da f8 6c c8 56 23 1f 2d 5a ba 46 c4 34 ec 19 6c + */ + S2N_BLOB_FROM_HEX(hash, "20 91 45 a9 6e e8 e2 a1 22 ff 81 00 47 cc 95 26 \ + 84 65 8d 60 49 e8 64 29 42 6d b8 7c 54 ad 14 3d"); + S2N_BLOB_FROM_HEX(secret, "7d f2 35 f2 03 1d 2a 05 12 87 d0 2b 02 41 \ + b0 bf da f8 6c c8 56 23 1f 2d 5a ba 46 c4 34 ec 19 6c"); + + for (size_t i = 0; i < s2n_array_len(modes); i++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(modes[i]), s2n_connection_ptr_free); + conn->secure->cipher_suite = cipher_suite; + EXPECT_OK(s2n_connection_set_test_master_secret(conn, &master_secret)); + EXPECT_OK(s2n_connection_set_test_transcript_hash(conn, CLIENT_FINISHED, &hash)); + + EXPECT_OK(s2n_derive_resumption_master_secret(conn)); + EXPECT_EQUAL(derived_secret.size, secret.size); + EXPECT_BYTEARRAY_EQUAL(conn->secrets.version.tls13.resumption_master_secret, + secret.data, secret.size); + } + }; + + /* Derive EXPORTER_MASTER_SECRET */ + { + /** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {client} derive secret "tls13 exp master" (same as server) + * + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# {server} derive secret "tls13 exp master": + *# + *# PRK (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47 + *# 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19 + *# + *# hash (32 octets): 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a + *# 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13 + *# + ** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-3 + *= type=test + *# info (52 octets): 00 20 10 74 6c 73 31 33 20 65 78 70 20 6d 61 73 + *# 74 65 72 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a 00 + *# 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13 + *# + *# expanded (32 octets): fe 22 f8 81 17 6e da 18 eb 8f 44 52 9e 67 + *# 92 c5 0c 9a 3f 89 45 2f 68 d8 ae 31 1b 43 09 d3 cf 50 + */ + S2N_BLOB_FROM_HEX(hash, "96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a \ + 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13"); + S2N_BLOB_FROM_HEX(secret, "fe 22 f8 81 17 6e da 18 eb 8f 44 52 9e 67 \ + 92 c5 0c 9a 3f 89 45 2f 68 d8 ae 31 1b 43 09 d3 cf 50"); + + for (size_t i = 0; i < s2n_array_len(modes); i++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(modes[i]), s2n_connection_ptr_free); + conn->secure->cipher_suite = cipher_suite; + EXPECT_OK(s2n_connection_set_test_master_secret(conn, &master_secret)); + EXPECT_OK(s2n_connection_set_test_transcript_hash(conn, SERVER_FINISHED, &hash)); + + EXPECT_OK(s2n_derive_exporter_master_secret(conn, &derived_secret)); + EXPECT_EQUAL(derived_secret.size, secret.size); + EXPECT_BYTEARRAY_EQUAL(derived_secret.data, secret.data, secret.size); + } + }; + }; + + /* Resumed 0-RTT Handshake */ + { + /** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-4 + *= type=test + *# {server} extract secret "early" (same as client early secret) + * + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-4 + *= type=test + *# {client} extract secret "early": + *# + *# salt: 0 (all zero octets) + *# + *# IKM (32 octets): 4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c a4 c5 + *# 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3 + *# + *# secret (32 octets): 9b 21 88 e9 b2 fc 6d 64 d7 1d c3 29 90 0e 20 + *# bb 41 91 50 00 f6 78 aa 83 9c bb 79 7c b7 d8 33 2c + */ + S2N_BLOB_FROM_HEX(psk_secret, "4e cd 0e b6 ec 3b 4d 87 f5 d6 02 8f 92 2c a4 c5 \ + 85 1a 27 7f d4 13 11 c9 e6 2d 2c 94 92 e1 c4 f3"); + S2N_BLOB_FROM_HEX(early_secret, "9b 21 88 e9 b2 fc 6d 64 d7 1d c3 29 90 0e 20 \ + bb 41 91 50 00 f6 78 aa 83 9c bb 79 7c b7 d8 33 2c"); + + /* Extract EARLY_SECRET */ + { + for (size_t i = 0; i < s2n_array_len(modes); i++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(modes[i]), s2n_connection_ptr_free); + conn->secure->cipher_suite = cipher_suite; + + DEFER_CLEANUP(struct s2n_psk *psk = s2n_test_psk_new(conn), s2n_psk_free); + EXPECT_NOT_NULL(psk); + EXPECT_SUCCESS(s2n_psk_set_secret(psk, psk_secret.data, psk_secret.size)); + + /* Early secret calculated from PSK */ + EXPECT_OK(s2n_extract_early_secret(psk)); + EXPECT_EQUAL(psk->early_secret.size, early_secret.size); + EXPECT_BYTEARRAY_EQUAL(psk->early_secret.data, early_secret.data, early_secret.size); + + /* Early secret retrieved and saved for connection */ + conn->psk_params.chosen_psk = psk; + EXPECT_OK(s2n_tls13_extract_secret(conn, S2N_EARLY_SECRET)); + EXPECT_BYTEARRAY_EQUAL(conn->secrets.version.tls13.extract_secret, + early_secret.data, early_secret.size); + EXPECT_EQUAL(conn->secrets.extract_secret_type, S2N_EARLY_SECRET); + } + }; + + /* Derive BINDER_KEY */ + { + /** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-4 + *= type=test + *# PRK (32 octets): 69 fe 13 1a 3b ba d5 d6 3c 64 ee bc c3 0e 39 5b + *# 9d 81 07 72 6a 13 d0 74 e3 89 db c8 a4 e4 72 56 + */ + S2N_BLOB_FROM_HEX(binder_key, "69 fe 13 1a 3b ba d5 d6 3c 64 ee bc c3 0e 39 5b \ + 9d 81 07 72 6a 13 d0 74 e3 89 db c8 a4 e4 72 56"); + + for (size_t i = 0; i < s2n_array_len(modes); i++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(modes[i]), s2n_connection_ptr_free); + conn->secure->cipher_suite = cipher_suite; + + DEFER_CLEANUP(struct s2n_psk *psk = s2n_test_psk_new(conn), s2n_psk_free); + EXPECT_NOT_NULL(psk); + psk->type = S2N_PSK_TYPE_RESUMPTION; + EXPECT_SUCCESS(s2n_psk_set_secret(psk, psk_secret.data, psk_secret.size)); + + EXPECT_OK(s2n_derive_binder_key(psk, &derived_secret)); + EXPECT_BYTEARRAY_EQUAL(psk->early_secret.data, early_secret.data, early_secret.size); + EXPECT_EQUAL(derived_secret.size, binder_key.size); + EXPECT_BYTEARRAY_EQUAL(derived_secret.data, binder_key.data, binder_key.size); + } + }; + + /* Derive CLIENT_EARLY_TRAFFIC_SECRET */ + { + /** + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-4 + *= type=test + *# {server} derive secret "tls13 c e traffic" (same as client) + * + *= https://www.rfc-editor.org/rfc/rfc8448.html#section-4 + *= type=test + *# {client} derive secret "tls13 c e traffic": + *# + *# PRK (32 octets): 9b 21 88 e9 b2 fc 6d 64 d7 1d c3 29 90 0e 20 bb + *# 41 91 50 00 f6 78 aa 83 9c bb 79 7c b7 d8 33 2c + *# + *# hash (32 octets): 08 ad 0f a0 5d 7c 72 33 b1 77 5b a2 ff 9f 4c 5b + *# 8b 59 27 6b 7f 22 7f 13 a9 76 24 5f 5d 96 09 13 + *# + *# info (53 octets): 00 20 11 74 6c 73 31 33 20 63 20 65 20 74 72 61 + *# 66 66 69 63 20 08 ad 0f a0 5d 7c 72 33 b1 77 5b a2 ff 9f 4c 5b + *# 8b 59 27 6b 7f 22 7f 13 a9 76 24 5f 5d 96 09 13 + *# + *# expanded (32 octets): 3f bb e6 a6 0d eb 66 c3 0a 32 79 5a ba 0e + *# ff 7e aa 10 10 55 86 e7 be 5c 09 67 8d 63 b6 ca ab 62 + */ + S2N_BLOB_FROM_HEX(hash, "08 ad 0f a0 5d 7c 72 33 b1 77 5b a2 ff 9f 4c 5b \ + 8b 59 27 6b 7f 22 7f 13 a9 76 24 5f 5d 96 09 13"); + S2N_BLOB_FROM_HEX(secret, "3f bb e6 a6 0d eb 66 c3 0a 32 79 5a ba 0e \ + ff 7e aa 10 10 55 86 e7 be 5c 09 67 8d 63 b6 ca ab 62"); + + for (size_t i = 0; i < s2n_array_len(modes); i++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(modes[i]), s2n_connection_ptr_free); + conn->secure->cipher_suite = cipher_suite; + EXPECT_OK(s2n_connection_set_test_early_secret(conn, &early_secret)); + EXPECT_OK(s2n_connection_set_test_transcript_hash(conn, CLIENT_HELLO, &hash)); + + EXPECT_OK(s2n_tls13_derive_secret(conn, S2N_EARLY_SECRET, S2N_CLIENT, + &derived_secret)); + EXPECT_EQUAL(derived_secret.size, secret.size); + EXPECT_BYTEARRAY_EQUAL(derived_secret.data, secret.data, secret.size); + } + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_secrets_test.c b/tests/unit/s2n_tls13_secrets_test.c new file mode 100644 index 00000000000..32779e94cb4 --- /dev/null +++ b/tests/unit/s2n_tls13_secrets_test.c @@ -0,0 +1,548 @@ +/* + * 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 "tls/s2n_tls13_secrets.h" + +#include + +#include "crypto/s2n_ecc_evp.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" + +S2N_RESULT s2n_tls13_extract_secret(struct s2n_connection *conn, s2n_extract_secret_type_t secret_type); +S2N_RESULT s2n_tls13_derive_secret(struct s2n_connection *conn, s2n_extract_secret_type_t secret_type, + s2n_mode mode, struct s2n_blob *secret); + +static S2N_RESULT s2n_set_test_key_shares(struct s2n_connection *conn, const struct s2n_ecc_named_curve *curve) +{ + conn->kex_params.server_ecc_evp_params.negotiated_curve = curve; + RESULT_GUARD_POSIX(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.server_ecc_evp_params)); + + conn->kex_params.client_ecc_evp_params.negotiated_curve = curve; + RESULT_GUARD_POSIX(s2n_ecc_evp_generate_ephemeral_key(&conn->kex_params.client_ecc_evp_params)); + + return S2N_RESULT_OK; +} + +struct s2n_tls13_secrets_test_case { + s2n_extract_secret_type_t curr_secret_type; + s2n_extract_secret_type_t next_secret_type; + s2n_mode secret_mode; + s2n_mode conn_mode; + struct s2n_cipher_suite *cipher_suite; + const struct s2n_ecc_named_curve *curve; +}; + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + const uint8_t empty_secret[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; + + const struct s2n_cipher_preferences *ciphers = &cipher_preferences_test_all_tls13; + const struct s2n_ecc_preferences *curves = &s2n_ecc_preferences_test_all; + const s2n_mode modes[] = { S2N_CLIENT, S2N_SERVER }; + + struct s2n_blob test_secret = { 0 }; + uint8_t test_secret_bytes[S2N_TLS13_SECRET_MAX_LEN] = "hello world"; + EXPECT_SUCCESS(s2n_blob_init(&test_secret, test_secret_bytes, sizeof(test_secret_bytes))); + + struct s2n_tls13_secrets_test_case test_cases[1000] = { 0 }; + size_t test_cases_count = 0; + for (s2n_extract_secret_type_t next_type = S2N_EARLY_SECRET; next_type <= S2N_MASTER_SECRET; next_type++) { + for (s2n_extract_secret_type_t curr_type = S2N_NONE_SECRET; curr_type <= S2N_MASTER_SECRET; curr_type++) { + for (size_t cipher_i = 0; cipher_i < ciphers->count; cipher_i++) { + for (size_t curve_i = 0; curve_i < curves->count; curve_i++) { + for (size_t m1_i = 0; m1_i < s2n_array_len(modes); m1_i++) { + for (size_t m2_i = 0; m2_i < s2n_array_len(modes); m2_i++) { + if (curr_type > next_type) { + /* Secret schedule MUST be evaluated in order */ + continue; + } + test_cases[test_cases_count] = (struct s2n_tls13_secrets_test_case){ + .curr_secret_type = curr_type, + .next_secret_type = next_type, + .secret_mode = modes[m1_i], + .conn_mode = modes[m2_i], + .cipher_suite = ciphers->suites[cipher_i], + .curve = curves->ecc_curves[curve_i], + }; + test_cases_count++; + } + } + } + } + } + } + EXPECT_TRUE(test_cases_count > 0); + + /* Test: s2n_tls13_extract_secret */ + { + /* Safety */ + { + struct s2n_connection empty_conn = { 0 }; + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_extract_secret(NULL, S2N_EARLY_SECRET), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_extract_secret(&empty_conn, S2N_EARLY_SECRET), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_extract_secret(conn, -1), S2N_ERR_SAFETY); + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_extract_secret(conn, 255), S2N_ERR_SAFETY); + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_extract_secret(conn, (S2N_MASTER_SECRET + 1)), S2N_ERR_SAFETY); + }; + + /* No-op if secret already exists */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + conn->secrets.extract_secret_type = S2N_EARLY_SECRET; + + EXPECT_OK(s2n_tls13_extract_secret(conn, S2N_EARLY_SECRET)); + EXPECT_EQUAL(conn->secrets.extract_secret_type, S2N_EARLY_SECRET); + EXPECT_BYTEARRAY_EQUAL(conn->secrets.version.tls13.extract_secret, empty_secret, sizeof(empty_secret)); + }; + + /* Generate all secrets sequentially */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + conn->secrets.extract_secret_type = S2N_NONE_SECRET; + + EXPECT_OK(s2n_tls13_extract_secret(conn, S2N_EARLY_SECRET)); + EXPECT_EQUAL(conn->secrets.extract_secret_type, S2N_EARLY_SECRET); + EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.extract_secret, empty_secret, sizeof(empty_secret)); + + EXPECT_OK(s2n_set_test_key_shares(conn, &s2n_ecc_curve_secp256r1)); + EXPECT_OK(s2n_tls13_extract_secret(conn, S2N_HANDSHAKE_SECRET)); + EXPECT_EQUAL(conn->secrets.extract_secret_type, S2N_HANDSHAKE_SECRET); + EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.extract_secret, empty_secret, sizeof(empty_secret)); + + EXPECT_OK(s2n_tls13_extract_secret(conn, S2N_MASTER_SECRET)); + EXPECT_EQUAL(conn->secrets.extract_secret_type, S2N_MASTER_SECRET); + EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.extract_secret, empty_secret, sizeof(empty_secret)); + }; + + /* Generate all secrets at once (backfill) */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + conn->secrets.extract_secret_type = S2N_NONE_SECRET; + EXPECT_OK(s2n_set_test_key_shares(conn, &s2n_ecc_curve_secp256r1)); + + EXPECT_OK(s2n_tls13_extract_secret(conn, S2N_MASTER_SECRET)); + EXPECT_EQUAL(conn->secrets.extract_secret_type, S2N_MASTER_SECRET); + EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.extract_secret, empty_secret, sizeof(empty_secret)); + } + + /* All valid parameter combinations should succeed */ + for (size_t i = 0; i < test_cases_count; i++) { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(test_cases[i].conn_mode), + s2n_connection_ptr_free); + conn->secure->cipher_suite = test_cases[i].cipher_suite; + conn->secrets.extract_secret_type = test_cases[i].curr_secret_type; + EXPECT_OK(s2n_set_test_key_shares(conn, test_cases[i].curve)); + EXPECT_OK(s2n_tls13_extract_secret(conn, test_cases[i].next_secret_type)); + } + }; + + /* Test: s2n_tls13_derive_secret */ + { + const uint32_t handshake_type = NEGOTIATED | FULL_HANDSHAKE; + const int message_nums[] = { + [S2N_EARLY_SECRET] = 0, + [S2N_HANDSHAKE_SECRET] = 1, + [S2N_MASTER_SECRET] = 5, + }; + + /* Safety */ + { + struct s2n_blob blob = { 0 }; + struct s2n_connection empty_conn = { 0 }; + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_derive_secret(NULL, S2N_EARLY_SECRET, S2N_CLIENT, &blob), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_derive_secret(&empty_conn, S2N_EARLY_SECRET, S2N_CLIENT, &blob), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_derive_secret(conn, -1, S2N_CLIENT, &blob), S2N_ERR_SAFETY); + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_derive_secret(conn, 255, S2N_CLIENT, &blob), S2N_ERR_SAFETY); + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_derive_secret(conn, S2N_EARLY_SECRET, S2N_CLIENT, NULL), S2N_ERR_NULL); + }; + + /* Generates a secret */ + { + uint8_t output_bytes[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; + struct s2n_blob output = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&output, output_bytes, sizeof(output_bytes))); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + conn->secrets.extract_secret_type = S2N_NONE_SECRET; + + EXPECT_OK(s2n_tls13_derive_secret(conn, S2N_EARLY_SECRET, S2N_SERVER, &output)); + EXPECT_BYTEARRAY_NOT_EQUAL(output.data, empty_secret, sizeof(empty_secret)); + }; + + /* Fails if correct transcript digest not available */ + { + uint8_t output_bytes[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; + struct s2n_blob output = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&output, output_bytes, sizeof(output_bytes))); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + conn->handshake.handshake_type = handshake_type; + EXPECT_OK(s2n_set_test_key_shares(conn, &s2n_ecc_curve_secp256r1)); + + /* Fails with incorrect transcript */ + conn->handshake.message_number = message_nums[S2N_HANDSHAKE_SECRET]; + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_derive_secret(conn, S2N_MASTER_SECRET, S2N_SERVER, &output), + S2N_ERR_SECRET_SCHEDULE_STATE); + + /* Succeeds with correct transcript */ + conn->handshake.message_number = message_nums[S2N_MASTER_SECRET]; + EXPECT_OK(s2n_tls13_derive_secret(conn, S2N_MASTER_SECRET, S2N_SERVER, &output)); + }; + + /* Calculates previous extract secrets if necessary */ + { + uint8_t output_bytes[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; + struct s2n_blob output = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&output, output_bytes, sizeof(output_bytes))); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + conn->actual_protocol_version = S2N_TLS13; + conn->handshake.handshake_type = handshake_type; + conn->handshake.message_number = message_nums[S2N_HANDSHAKE_SECRET]; + EXPECT_OK(s2n_set_test_key_shares(conn, &s2n_ecc_curve_secp256r1)); + + conn->secrets.extract_secret_type = S2N_NONE_SECRET; + EXPECT_OK(s2n_tls13_derive_secret(conn, S2N_HANDSHAKE_SECRET, S2N_SERVER, &output)); + EXPECT_EQUAL(conn->secrets.extract_secret_type, S2N_HANDSHAKE_SECRET); + EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.extract_secret, empty_secret, sizeof(empty_secret)); + EXPECT_BYTEARRAY_NOT_EQUAL(output.data, empty_secret, sizeof(empty_secret)); + }; + + /* All valid parameter combinations should succeed */ + for (size_t i = 0; i < test_cases_count; i++) { + uint8_t output_bytes[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; + struct s2n_blob output = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&output, output_bytes, sizeof(output_bytes))); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(test_cases[i].conn_mode), + s2n_connection_ptr_free); + conn->secure->cipher_suite = test_cases[i].cipher_suite; + conn->secrets.extract_secret_type = test_cases[i].curr_secret_type; + EXPECT_OK(s2n_conn_choose_state_machine(conn, S2N_TLS13)); + conn->actual_protocol_version = S2N_TLS13; + conn->handshake.handshake_type = handshake_type; + conn->handshake.message_number = message_nums[test_cases[i].next_secret_type]; + EXPECT_OK(s2n_set_test_key_shares(conn, test_cases[i].curve)); + EXPECT_OK(s2n_tls13_derive_secret(conn, test_cases[i].next_secret_type, test_cases[i].secret_mode, &output)); + EXPECT_BYTEARRAY_NOT_EQUAL(output.data, empty_secret, sizeof(empty_secret)); + } + }; + + /* s2n_tls13_secrets_clean */ + { + /* Safety */ + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_secrets_clean(NULL), S2N_ERR_NULL); + + /* Wipes all secrets */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + conn->actual_protocol_version = S2N_TLS13; + + EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls13.extract_secret, test_secret.data, test_secret.size); + EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls13.client_early_secret, test_secret.data, test_secret.size); + EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls13.client_handshake_secret, test_secret.data, test_secret.size); + EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls13.server_handshake_secret, test_secret.data, test_secret.size); + EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls13.client_app_secret, test_secret.data, test_secret.size); + EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls13.server_app_secret, test_secret.data, test_secret.size); + EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls13.exporter_master_secret, test_secret.data, test_secret.size); + EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls13.resumption_master_secret, test_secret.data, test_secret.size); + + EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.extract_secret, empty_secret, sizeof(empty_secret)); + EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.client_early_secret, empty_secret, sizeof(empty_secret)); + EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.client_handshake_secret, empty_secret, sizeof(empty_secret)); + EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.server_handshake_secret, empty_secret, sizeof(empty_secret)); + EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.client_app_secret, empty_secret, sizeof(empty_secret)); + EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.server_app_secret, empty_secret, sizeof(empty_secret)); + EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.exporter_master_secret, empty_secret, sizeof(empty_secret)); + EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.resumption_master_secret, empty_secret, sizeof(empty_secret)); + + EXPECT_OK(s2n_tls13_secrets_clean(conn)); + + EXPECT_BYTEARRAY_EQUAL(conn->secrets.version.tls13.extract_secret, empty_secret, sizeof(empty_secret)); + EXPECT_BYTEARRAY_EQUAL(conn->secrets.version.tls13.client_early_secret, empty_secret, sizeof(empty_secret)); + EXPECT_BYTEARRAY_EQUAL(conn->secrets.version.tls13.client_handshake_secret, empty_secret, sizeof(empty_secret)); + EXPECT_BYTEARRAY_EQUAL(conn->secrets.version.tls13.server_handshake_secret, empty_secret, sizeof(empty_secret)); + EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.client_app_secret, empty_secret, sizeof(empty_secret)); + EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.server_app_secret, empty_secret, sizeof(empty_secret)); + EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.exporter_master_secret, empty_secret, sizeof(empty_secret)); + EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.resumption_master_secret, empty_secret, sizeof(empty_secret)); + }; + }; + + /* Test s2n_tls13_secrets_get */ + { + /* Safety */ + { + struct s2n_blob result = { 0 }; + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_secrets_get(NULL, S2N_HANDSHAKE_SECRET, S2N_CLIENT, &result), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_secrets_get(conn, S2N_HANDSHAKE_SECRET, S2N_CLIENT, NULL), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_secrets_get(conn, S2N_NONE_SECRET, S2N_CLIENT, &result), S2N_ERR_SAFETY); + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_secrets_get(conn, -1, S2N_CLIENT, &result), S2N_ERR_SAFETY); + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_secrets_get(conn, 100, S2N_CLIENT, &result), S2N_ERR_SAFETY); + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_secrets_get(conn, S2N_EARLY_SECRET, S2N_SERVER, &result), S2N_ERR_SAFETY); + + conn->secrets.extract_secret_type = S2N_NONE_SECRET; + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_secrets_get(conn, S2N_HANDSHAKE_SECRET, S2N_CLIENT, &result), S2N_ERR_SAFETY); + + struct s2n_crypto_parameters *secure = conn->secure; + conn->secure = NULL; + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_secrets_get(conn, S2N_HANDSHAKE_SECRET, S2N_CLIENT, &result), S2N_ERR_NULL); + conn->secure = secure; + + struct s2n_cipher_suite *cipher_suite = conn->secure->cipher_suite; + conn->secure->cipher_suite = NULL; + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_secrets_get(conn, S2N_HANDSHAKE_SECRET, S2N_CLIENT, &result), S2N_ERR_NULL); + conn->secure->cipher_suite = cipher_suite; + }; + + /* Retrieves a secret */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + conn->actual_protocol_version = S2N_TLS13; + + EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls13.client_handshake_secret, + test_secret.data, test_secret.size); + conn->secrets.extract_secret_type = S2N_HANDSHAKE_SECRET; + + struct s2n_blob result = { 0 }; + uint8_t result_bytes[S2N_TLS13_SECRET_MAX_LEN] = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&result, result_bytes, sizeof(result_bytes))); + EXPECT_OK(s2n_tls13_secrets_get(conn, S2N_HANDSHAKE_SECRET, S2N_CLIENT, &result)); + + EXPECT_TRUE(result.size > 0); + EXPECT_TRUE(result.size <= S2N_TLS13_SECRET_MAX_LEN); + EXPECT_BYTEARRAY_EQUAL(result.data, test_secret.data, result.size); + }; + }; + + /* s2n_tls13_secrets_update */ + { + /* Safety */ + EXPECT_ERROR_WITH_ERRNO(s2n_tls13_secrets_update(NULL), S2N_ERR_NULL); + + /* Derives early secret on CLIENT_HELLO */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_BYTEARRAY_EQUAL(conn->secrets.version.tls13.client_early_secret, + empty_secret, sizeof(empty_secret)); + + /* Early secret not derived if early data not requested */ + conn->early_data_state = S2N_EARLY_DATA_NOT_REQUESTED; + EXPECT_OK(s2n_tls13_secrets_update(conn)); + EXPECT_BYTEARRAY_EQUAL(conn->secrets.version.tls13.client_early_secret, + empty_secret, sizeof(empty_secret)); + + /* Early secret not derived if early data rejected */ + conn->early_data_state = S2N_EARLY_DATA_REJECTED; + EXPECT_OK(s2n_tls13_secrets_update(conn)); + EXPECT_BYTEARRAY_EQUAL(conn->secrets.version.tls13.client_early_secret, + empty_secret, sizeof(empty_secret)); + + /* Early secret derived if early data requested */ + conn->early_data_state = S2N_EARLY_DATA_REQUESTED; + EXPECT_OK(s2n_tls13_secrets_update(conn)); + EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.client_early_secret, + empty_secret, sizeof(empty_secret)); + + /* Clear secret */ + EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls13.client_early_secret, + empty_secret, sizeof(empty_secret)); + + /* Early secret derived if early data accepted */ + conn->early_data_state = S2N_EARLY_DATA_ACCEPTED; + EXPECT_OK(s2n_tls13_secrets_update(conn)); + EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.client_early_secret, + empty_secret, sizeof(empty_secret)); + }; + + /* Derives handshake secrets on SERVER_HELLO */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_connection_set_test_handshake_secret(conn, &test_secret)); + EXPECT_BYTEARRAY_EQUAL(conn->secrets.version.tls13.client_handshake_secret, + empty_secret, sizeof(empty_secret)); + EXPECT_BYTEARRAY_EQUAL(conn->secrets.version.tls13.server_handshake_secret, + empty_secret, sizeof(empty_secret)); + + while (s2n_conn_get_current_message_type(conn) != SERVER_HELLO) { + conn->handshake.message_number++; + } + EXPECT_OK(s2n_tls13_secrets_update(conn)); + + EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.client_handshake_secret, + empty_secret, sizeof(empty_secret)); + EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.server_handshake_secret, + empty_secret, sizeof(empty_secret)); + }; + + /* Computes finished keys on SERVER_HELLO */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_connection_set_test_handshake_secret(conn, &test_secret)); + EXPECT_EQUAL(conn->handshake.finished_len, 0); + EXPECT_BYTEARRAY_EQUAL(conn->handshake.client_finished, + empty_secret, sizeof(empty_secret)); + EXPECT_BYTEARRAY_EQUAL(conn->handshake.server_finished, + empty_secret, sizeof(empty_secret)); + + while (s2n_conn_get_current_message_type(conn) != SERVER_HELLO) { + conn->handshake.message_number++; + } + EXPECT_OK(s2n_tls13_secrets_update(conn)); + + uint8_t expected_len = 0; + EXPECT_SUCCESS(s2n_hmac_digest_size(conn->secure->cipher_suite->prf_alg, &expected_len)); + EXPECT_EQUAL(conn->handshake.finished_len, expected_len); + EXPECT_BYTEARRAY_NOT_EQUAL(conn->handshake.client_finished, + empty_secret, sizeof(empty_secret)); + EXPECT_BYTEARRAY_NOT_EQUAL(conn->handshake.server_finished, + empty_secret, sizeof(empty_secret)); + }; + + /* Derives application secrets on SERVER_FINISHED */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_connection_set_test_master_secret(conn, &test_secret)); + EXPECT_BYTEARRAY_EQUAL(conn->secrets.version.tls13.client_app_secret, + empty_secret, sizeof(empty_secret)); + EXPECT_BYTEARRAY_EQUAL(conn->secrets.version.tls13.server_app_secret, + empty_secret, sizeof(empty_secret)); + + while (s2n_conn_get_current_message_type(conn) != SERVER_FINISHED) { + conn->handshake.message_number++; + } + EXPECT_OK(s2n_tls13_secrets_update(conn)); + + EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.client_app_secret, + empty_secret, sizeof(empty_secret)); + EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.server_app_secret, + empty_secret, sizeof(empty_secret)); + }; + + /* Derives resumption secret on CLIENT_FINISHED */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_connection_set_test_master_secret(conn, &test_secret)); + EXPECT_BYTEARRAY_EQUAL(conn->secrets.version.tls13.resumption_master_secret, + empty_secret, sizeof(empty_secret)); + + while (s2n_conn_get_current_message_type(conn) != CLIENT_FINISHED) { + conn->handshake.message_number++; + } + EXPECT_OK(s2n_tls13_secrets_update(conn)); + + EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.resumption_master_secret, + empty_secret, sizeof(empty_secret)); + }; + }; + + /* s2n_connection_export_secret */ + { + /* Derives exporter secret on SERVER_FINISHED */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), + s2n_connection_ptr_free); + conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_connection_set_test_master_secret(conn, &test_secret)); + EXPECT_BYTEARRAY_EQUAL(conn->secrets.version.tls13.exporter_master_secret, + empty_secret, sizeof(empty_secret)); + + while (s2n_conn_get_current_message_type(conn) != SERVER_FINISHED) { + conn->handshake.message_number++; + } + EXPECT_OK(s2n_tls13_secrets_update(conn)); + + EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.exporter_master_secret, + empty_secret, sizeof(empty_secret)); + + /* + * s2n_connection_tls_exporter requires us to finish the handshake. + * The above is needed since s2n_tls13_secrets_update will only + * initialize when it sees the SERVER_FINISHED frame. + */ + EXPECT_OK(s2n_skip_handshake(conn)); + + uint8_t output[32] = { 0 }; + int result = s2n_connection_tls_exporter( + conn, + (const uint8_t *) "label", + sizeof("label") - 1, + (const uint8_t *) "context", + sizeof("context") - 1, + output, + sizeof(output)); + EXPECT_SUCCESS(result); + /* + * If updating this value, it's a good idea to make sure the update + * matches OpenSSL's SSL_export_keying_material. The easiest known + * way of doing that is building a simple client/server pair and + * calling the s2n and OpenSSL APIs after a handshake on both + * sides; you should get identical results with identical + * label/context parameters. This particular value though is not + * checked as its dependent on the s2n-specific test master secret. + */ + S2N_BLOB_FROM_HEX(expected, "3a 72 eb 08 10 a3 69 f3 06 f2 77 11 70 ad d5 76 bd 21 15 \ + 46 d4 c8 fb 80 1a 93 04 1e ac 59 aa 47"); + EXPECT_EQUAL(sizeof(output), expected.size); + EXPECT_BYTEARRAY_EQUAL(output, expected.data, expected.size); + }; + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_server_cert_test.c b/tests/unit/s2n_tls13_server_cert_test.c new file mode 100644 index 00000000000..26c8c7cacbe --- /dev/null +++ b/tests/unit/s2n_tls13_server_cert_test.c @@ -0,0 +1,204 @@ +/* + * 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 "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +/* Test vectors from https://tools.ietf.org/html/rfc8448#section-3 */ + +/* whole cert message without 0b0001b9 header */ +const char tls13_cert_message_hex[] = + "000001b50001b03082" + "01ac30820115a003020102020102300d06092a8648" + "86f70d01010b0500300e310c300a06035504031303" + "727361301e170d3136303733303031323335395a17" + "0d3236303733303031323335395a300e310c300a06" + "03550403130372736130819f300d06092a864886f7" + "0d010101050003818d0030818902818100b4bb498f" + "8279303d980836399b36c6988c0c68de55e1bdb826" + "d3901a2461eafd2de49a91d015abbc9a95137ace6c" + "1af19eaa6af98c7ced43120998e187a80ee0ccb052" + "4b1b018c3e0b63264d449a6d38e22a5fda43084674" + "8030530ef0461c8ca9d9efbfae8ea6d1d03e2bd193" + "eff0ab9a8002c47428a6d35a8d88d79f7f1e3f0203" + "010001a31a301830090603551d1304023000300b06" + "03551d0f0404030205a0300d06092a864886f70d01" + "010b05000381810085aad2a0e5b9276b908c65f73a" + "7267170618a54c5f8a7b337d2df7a594365417f2ea" + "e8f8a58c8f8172f9319cf36b7fd6c55b80f21a0301" + "5156726096fd335e5e67f2dbf102702e608ccae6be" + "c1fc63a42a99be5c3eb7107c3c54e9b9eb2bd5203b" + "1c3b84e0a8b2f759409ba3eac9d91d402dcc0cc8f8" + "961229ac9187b42b4de10000"; + +/* cert only */ +const char tls13_cert_hex[] = + "3082" /* without certificate chain header */ + "01ac30820115a003020102020102300d06092a8648" + "86f70d01010b0500300e310c300a06035504031303" + "727361301e170d3136303733303031323335395a17" + "0d3236303733303031323335395a300e310c300a06" + "03550403130372736130819f300d06092a864886f7" + "0d010101050003818d0030818902818100b4bb498f" + "8279303d980836399b36c6988c0c68de55e1bdb826" + "d3901a2461eafd2de49a91d015abbc9a95137ace6c" + "1af19eaa6af98c7ced43120998e187a80ee0ccb052" + "4b1b018c3e0b63264d449a6d38e22a5fda43084674" + "8030530ef0461c8ca9d9efbfae8ea6d1d03e2bd193" + "eff0ab9a8002c47428a6d35a8d88d79f7f1e3f0203" + "010001a31a301830090603551d1304023000300b06" + "03551d0f0404030205a0300d06092a864886f70d01" + "010b05000381810085aad2a0e5b9276b908c65f73a" + "7267170618a54c5f8a7b337d2df7a594365417f2ea" + "e8f8a58c8f8172f9319cf36b7fd6c55b80f21a0301" + "5156726096fd335e5e67f2dbf102702e608ccae6be" + "c1fc63a42a99be5c3eb7107c3c54e9b9eb2bd5203b" + "1c3b84e0a8b2f759409ba3eac9d91d402dcc0cc8f8" + "961229ac9187b42b4de1"; + +/* certificate chain header. It contains + 1. Request Context length (00) + 2. Cert chain length (0001b5) + 3. Cert length (0001b0) + */ +const char tls13_cert_chain_header_hex[] = + "000001b50001b0"; + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Test s2n_server_cert_recv() parses tls13 certificate */ + { + S2N_BLOB_FROM_HEX(tls13_cert, tls13_cert_message_hex); + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + conn->x509_validator.skip_cert_validation = 1; + + /* success case in tls13 parsing mode */ + conn->actual_protocol_version = S2N_TLS13; + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS13); + EXPECT_SUCCESS(s2n_stuffer_write(&conn->handshake.io, &tls13_cert)); + EXPECT_SUCCESS(s2n_server_cert_recv(conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->handshake.io), 0); + + /* failure case in tls12 parsing mode */ + conn->actual_protocol_version = S2N_TLS12; + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + EXPECT_SUCCESS(s2n_stuffer_write(&conn->handshake.io, &tls13_cert)); + EXPECT_FAILURE(s2n_server_cert_recv(conn)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* Test s2n_server_cert_send() verify server's certificate */ + { + char *tls13_cert_chain_hex; + /* creating a certificate chain by concatenating + 1. chain header + 2. certificate + */ + EXPECT_NOT_NULL(tls13_cert_chain_hex = malloc(S2N_MAX_TEST_PEM_SIZE)); + strcpy(tls13_cert_chain_hex, tls13_cert_chain_header_hex); + strcat(tls13_cert_chain_hex, tls13_cert_hex); + /* convert certificate chain hex to bytes*/ + struct s2n_blob tls13_cert = { 0 }; + EXPECT_SUCCESS(s2n_alloc(&tls13_cert, strlen(tls13_cert_chain_hex) / 2)); + POSIX_GUARD(s2n_hex_string_to_bytes((uint8_t *) tls13_cert_chain_hex, &tls13_cert)); + + S2N_BLOB_FROM_HEX(tls13_cert_chain, tls13_cert_hex); + + struct s2n_connection *conn; + uint8_t certificate_request_context_len; + + struct s2n_cert cert = { .raw = tls13_cert_chain, .next = NULL }; + /* .chain_size is size of cert + 3 for the 3 bytes to express the length */ + struct s2n_cert_chain cert_chain = { .head = &cert, .chain_size = tls13_cert_chain.size + 3 }; + struct s2n_cert_chain_and_key cert_chain_and_key = { .cert_chain = &cert_chain }; + + /* tls13 mode */ + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS13; + conn->handshake_params.our_chain_and_key = &cert_chain_and_key; + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS13); + EXPECT_SUCCESS(s2n_server_cert_send(conn)); + + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->handshake.io), tls13_cert.size + 2); + EXPECT_SUCCESS(s2n_stuffer_read_uint8(&conn->handshake.io, &certificate_request_context_len)); + + /* server's certificate request context should always be of zero length */ + EXPECT_EQUAL(certificate_request_context_len, 0); + EXPECT_SUCCESS(s2n_connection_free(conn)); + + /* tls12 mode */ + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS12; + conn->handshake_params.our_chain_and_key = &cert_chain_and_key; + EXPECT_EQUAL(conn->actual_protocol_version, S2N_TLS12); + EXPECT_SUCCESS(s2n_server_cert_send(conn)); + /* In tls1.2 there is no certificate request context. + TLS1.2 Cert length = TLS1.3 Cert length -1 (server's request context)*/ + EXPECT_EQUAL(s2n_stuffer_data_available(&conn->handshake.io), tls13_cert.size - 1); + EXPECT_SUCCESS(s2n_connection_free(conn)); + + free(tls13_cert_chain_hex); + /* free memory allocated in s2n_alloc*/ + free(tls13_cert.data); + } + + /* Test server sends cert and client receives cert for tls 1.3 */ + { + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + struct s2n_connection *server_conn; + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + server_conn->actual_protocol_version = S2N_TLS13; + client_conn->actual_protocol_version = S2N_TLS13; + client_conn->x509_validator.skip_cert_validation = 1; + + S2N_BLOB_FROM_HEX(tls13_cert_chain, tls13_cert_hex); + S2N_BLOB_FROM_HEX(tls13_cert_message, tls13_cert_message_hex); + + struct s2n_cert cert = { .raw = tls13_cert_chain, .next = NULL }; + struct s2n_cert_chain cert_chain = { .head = &cert, .chain_size = tls13_cert_chain.size + 3 }; + struct s2n_cert_chain_and_key cert_chain_and_key = { .cert_chain = &cert_chain }; + server_conn->handshake_params.our_chain_and_key = &cert_chain_and_key; + + EXPECT_SUCCESS(s2n_server_cert_send(server_conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(&server_conn->handshake.io), tls13_cert_message.size); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, s2n_stuffer_data_available(&server_conn->handshake.io))); + EXPECT_EQUAL(s2n_stuffer_data_available(&client_conn->handshake.io), tls13_cert_message.size); + EXPECT_SUCCESS(s2n_server_cert_recv(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + }; + + END_TEST(); +} diff --git a/tests/unit/s2n_tls13_server_finished_test.c b/tests/unit/s2n_tls13_server_finished_test.c new file mode 100644 index 00000000000..128290a422d --- /dev/null +++ b/tests/unit/s2n_tls13_server_finished_test.c @@ -0,0 +1,178 @@ +/* + * 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 "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "utils/s2n_safety.h" + +static int reset_stuffers(struct s2n_stuffer *reread, struct s2n_stuffer *wipe) +{ + POSIX_GUARD(s2n_stuffer_reread(reread)); + POSIX_GUARD(s2n_stuffer_wipe(wipe)); + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* Test s2n_tls13_server_finished_send and s2n_tls13_server_finished_recv */ + { + struct s2n_cipher_suite cipher_suites[] = { + s2n_tls13_aes_128_gcm_sha256, + s2n_tls13_aes_256_gcm_sha384, + s2n_tls13_chacha20_poly1305_sha256 + }; + + uint32_t hash_sizes[] = { + 32, 48, 32 + }; + + for (int i = 0; i < 3; i++) { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_CLIENT)); + + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->secure->cipher_suite = &cipher_suites[i]; + + uint32_t hash_size = hash_sizes[i]; + + EXPECT_SUCCESS(s2n_tls13_server_finished_send(server_conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(&server_conn->handshake.io), hash_size); + + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->actual_protocol_version = S2N_TLS13; + client_conn->secure->cipher_suite = &cipher_suites[i]; + + EXPECT_SUCCESS(reset_stuffers(&server_conn->handshake.io, &client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, hash_size)); + EXPECT_SUCCESS(s2n_tls13_server_finished_recv(client_conn)); + + /* Expect failure if verify has a missing byte */ + EXPECT_SUCCESS(reset_stuffers(&server_conn->handshake.io, &client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, hash_size - 1)); + EXPECT_FAILURE(s2n_tls13_server_finished_recv(client_conn)); + + /* Expect failure if verify have additional byte */ + EXPECT_SUCCESS(reset_stuffers(&server_conn->handshake.io, &client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, hash_size)); + EXPECT_SUCCESS(s2n_stuffer_write_uint8(&client_conn->handshake.io, 0)); + EXPECT_FAILURE(s2n_tls13_server_finished_recv(client_conn)); + + /* Expect failure if verify on wire is modified by 1 bit */ + EXPECT_SUCCESS(reset_stuffers(&server_conn->handshake.io, &client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, hash_size)); + client_conn->handshake.io.blob.data[0] ^= 1; + EXPECT_FAILURE(s2n_tls13_server_finished_recv(client_conn)); + + /* Expect failure if finished key differs */ + EXPECT_SUCCESS(reset_stuffers(&server_conn->handshake.io, &client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, hash_size)); + client_conn->handshake.server_finished[0] ^= 1; + EXPECT_FAILURE(s2n_tls13_server_finished_recv(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + } + }; + + /* Test that they can only run in TLS 1.3 mode */ + { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_CLIENT)); + server_conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + + EXPECT_FAILURE(s2n_tls13_server_finished_send(server_conn)); + + /* now with TLS 1.3, server finished send can run */ + server_conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_tls13_server_finished_send(server_conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(&server_conn->handshake.io), 48); + + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + + client_conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, 48)); + EXPECT_FAILURE(s2n_tls13_server_finished_recv(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test for failure cases if cipher suites are incompatible */ + { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_CLIENT)); + + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256; + + EXPECT_SUCCESS(s2n_tls13_server_finished_send(server_conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(&server_conn->handshake.io), 32); + + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->actual_protocol_version = S2N_TLS13; + client_conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + + EXPECT_SUCCESS(reset_stuffers(&server_conn->handshake.io, &client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, 32)); + EXPECT_FAILURE(s2n_tls13_server_finished_recv(client_conn)); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + /* Test for failure cases when finished secret key differs */ + { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_CLIENT)); + + server_conn->actual_protocol_version = S2N_TLS13; + server_conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + + EXPECT_SUCCESS(s2n_tls13_server_finished_send(server_conn)); + EXPECT_EQUAL(s2n_stuffer_data_available(&server_conn->handshake.io), 48); + + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->actual_protocol_version = S2N_TLS13; + client_conn->secure->cipher_suite = &s2n_tls13_aes_256_gcm_sha384; + + for (int i = 0; i < 48; i++) { + EXPECT_SUCCESS(reset_stuffers(&server_conn->handshake.io, &client_conn->handshake.io)); + EXPECT_SUCCESS(s2n_stuffer_copy(&server_conn->handshake.io, &client_conn->handshake.io, 48)); + + /* flip a bit to test failure */ + client_conn->handshake.server_finished[i] ^= 1; + EXPECT_FAILURE(s2n_tls13_server_finished_recv(client_conn)); + + /* flip the bit back */ + client_conn->handshake.server_finished[i] ^= 1; + } + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + }; + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_tls13_support_test.c b/tests/unit/s2n_tls13_support_test.c new file mode 100644 index 00000000000..23c2959e261 --- /dev/null +++ b/tests/unit/s2n_tls13_support_test.c @@ -0,0 +1,187 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/extensions/s2n_cookie.h" +#include "tls/extensions/s2n_extension_type_lists.h" +#include "tls/extensions/s2n_server_key_share.h" +#include "tls/extensions/s2n_server_supported_versions.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* TLS 1.3 is not used by default */ + EXPECT_FALSE(s2n_use_default_tls13_config()); + + /* TLS1.3 is not supported or configured by default */ + { + /* Client does not support or configure TLS 1.3 */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_NOT_EQUAL(conn->client_protocol_version, S2N_TLS13); + + const struct s2n_security_policy *security_policy; + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_FALSE(s2n_security_policy_supports_tls13(security_policy)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Server does not support or configure TLS 1.3 */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_NOT_EQUAL(conn->server_protocol_version, S2N_TLS13); + + const struct s2n_security_policy *security_policy; + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_FALSE(s2n_security_policy_supports_tls13(security_policy)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + EXPECT_TRUE(s2n_use_default_tls13_config()); + + /* Re-enabling has no effect */ + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + EXPECT_TRUE(s2n_use_default_tls13_config()); + + /* If "enabled", TLS1.3 is supported and configured */ + { + /* Client supports and configures TLS 1.3 */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + EXPECT_EQUAL(conn->client_protocol_version, S2N_TLS13); + + const struct s2n_security_policy *security_policy; + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_TRUE(s2n_security_policy_supports_tls13(security_policy)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Server supports and configures TLS 1.3 */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + EXPECT_EQUAL(conn->server_protocol_version, S2N_TLS13); + + const struct s2n_security_policy *security_policy; + EXPECT_SUCCESS(s2n_connection_get_security_policy(conn, &security_policy)); + EXPECT_TRUE(s2n_security_policy_supports_tls13(security_policy)); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + EXPECT_FALSE(s2n_use_default_tls13_config()); + + /* Re-disabling has no effect */ + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + EXPECT_FALSE(s2n_use_default_tls13_config()); + + /* Test s2n_is_valid_tls13_cipher() */ + { + uint8_t value[2] = { 0x13, 0x01 }; + EXPECT_TRUE(s2n_is_valid_tls13_cipher(value)); + value[0] = 0x13; + value[1] = 0x02; + EXPECT_TRUE(s2n_is_valid_tls13_cipher(value)); + value[0] = 0x13; + value[1] = 0x03; + EXPECT_TRUE(s2n_is_valid_tls13_cipher(value)); + value[0] = 0x13; + value[1] = 0x04; + EXPECT_TRUE(s2n_is_valid_tls13_cipher(value)); + value[0] = 0x13; + value[1] = 0x05; + EXPECT_TRUE(s2n_is_valid_tls13_cipher(value)); + value[0] = 0x13; + value[1] = 0x06; + EXPECT_FALSE(s2n_is_valid_tls13_cipher(value)); + value[0] = 0x13; + value[1] = 0x00; + EXPECT_FALSE(s2n_is_valid_tls13_cipher(value)); + value[0] = 0x12; + value[1] = 0x01; + EXPECT_FALSE(s2n_is_valid_tls13_cipher(value)); + + EXPECT_FALSE(s2n_is_valid_tls13_cipher(s2n_dhe_rsa_with_3des_ede_cbc_sha.iana_value)); + EXPECT_TRUE(s2n_is_valid_tls13_cipher(s2n_tls13_aes_128_gcm_sha256.iana_value)); + EXPECT_TRUE(s2n_is_valid_tls13_cipher(s2n_tls13_aes_256_gcm_sha384.iana_value)); + EXPECT_TRUE(s2n_is_valid_tls13_cipher(s2n_tls13_chacha20_poly1305_sha256.iana_value)); + } + + /* Server does not parse TLS 1.3 extensions unless TLS 1.3 negotiated */ + { + s2n_extension_type_list *tls13_server_hello_extensions = NULL; + EXPECT_SUCCESS(s2n_extension_type_list_get(S2N_EXTENSION_LIST_SERVER_HELLO_TLS13, &tls13_server_hello_extensions)); + EXPECT_NOT_NULL(tls13_server_hello_extensions); + EXPECT_TRUE(tls13_server_hello_extensions->count > 0); + + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + EXPECT_SUCCESS(s2n_connection_allow_all_response_extensions(server_conn)); + + struct s2n_stuffer extension_data = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&extension_data, 0)); + EXPECT_SUCCESS(s2n_stuffer_write_str(&extension_data, "bad extension")); + + s2n_parsed_extensions_list parsed_extension_list = { 0 }; + for (size_t i = 0; i < tls13_server_hello_extensions->count; i++) { + const s2n_extension_type *tls13_extension_type = tls13_server_hello_extensions->extension_types[i]; + s2n_extension_type_id extension_id; + EXPECT_SUCCESS(s2n_extension_supported_iana_value_to_id(tls13_extension_type->iana_value, &extension_id)); + s2n_parsed_extension *parsed_extension = &parsed_extension_list.parsed_extensions[extension_id]; + + /* Create parsed extension */ + parsed_extension->extension = extension_data.blob; + parsed_extension->extension_type = tls13_extension_type->iana_value; + + server_conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_extension_process(tls13_extension_type, server_conn, &parsed_extension_list)); + + /* Reuse processed extension */ + EXPECT_TRUE(parsed_extension->processed); + parsed_extension->processed = false; + + server_conn->actual_protocol_version = S2N_TLS13; + EXPECT_FAILURE(s2n_extension_process(tls13_extension_type, server_conn, &parsed_extension_list)); + } + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_stuffer_free(&extension_data)); + }; + + END_TEST(); + return 0; +} diff --git a/tests/unit/s2n_tls13_zero_length_payload_test.c b/tests/unit/s2n_tls13_zero_length_payload_test.c new file mode 100644 index 00000000000..4c3239a0cd6 --- /dev/null +++ b/tests/unit/s2n_tls13_zero_length_payload_test.c @@ -0,0 +1,178 @@ +/* + * 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 +#include + +#include "api/s2n.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_config.h" +#include "tls/s2n_connection.h" +#include "tls/s2n_handshake_io.c" +#include "tls/s2n_record.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "utils/s2n_safety.h" + +/* In TLS 1.3, encrypted handshake records would appear to be of record type TLS_APPLICATION_DATA. +* The actual record content type is found after +*/ +const char tls13_zero_length_application_record_hex[] = "170303000117"; +const char tls13_zero_length_handshake_record_hex[] = "1603030000"; +const char tls13_zero_length_alert_record_hex[] = "1503030000"; + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + EXPECT_SUCCESS(s2n_enable_tls13_in_test()); + + /** Test 0 length application data record handled gracefully in client and server mode + * + *= https://tools.ietf.org/rfc/rfc8446#section-5.4 + *= type=test + *# Application Data records may contain a zero-length + *# TLSInnerPlaintext.content if the sender desires. + **/ + { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_connection_set_secrets(server_conn)); + + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->actual_protocol_version = S2N_TLS13; + EXPECT_OK(s2n_connection_set_secrets(client_conn)); + + struct s2n_blob zero_length_data = { 0 }; + s2n_blocked_status blocked = S2N_NOT_BLOCKED; + + DEFER_CLEANUP(struct s2n_stuffer client_to_server = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer server_to_client = { 0 }, s2n_stuffer_free); + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); + + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + + EXPECT_OK(s2n_record_write(client_conn, TLS_APPLICATION_DATA, &zero_length_data)); + EXPECT_SUCCESS(s2n_flush(client_conn, &blocked)); + EXPECT_TRUE(s2n_stuffer_data_available(&client_to_server) > 0); + + EXPECT_OK(s2n_record_write(server_conn, TLS_APPLICATION_DATA, &zero_length_data)); + EXPECT_SUCCESS(s2n_flush(server_conn, &blocked)); + EXPECT_TRUE(s2n_stuffer_data_available(&server_to_client) > 0); + + uint8_t record_type; + int isSSLv2; + + EXPECT_SUCCESS(s2n_read_full_record(server_conn, &record_type, &isSSLv2)); + EXPECT_EQUAL(record_type, TLS_APPLICATION_DATA); + record_type = 0; + EXPECT_SUCCESS(s2n_read_full_record(client_conn, &record_type, &isSSLv2)); + EXPECT_EQUAL(record_type, TLS_APPLICATION_DATA); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /** Test 0 length payload in handshake record terminates connection in client and server mode + * + *= https://tools.ietf.org/rfc/rfc8446#section-5.4 + *= type=test + *# Implementations MUST NOT send Handshake and Alert records that have a zero-length + *# TLSInnerPlaintext.content; if such a message is received, the receiving + *# implementation MUST terminate the connection with an "unexpected_message" alert. + **/ + { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS13; + + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->actual_protocol_version = S2N_TLS13; + + DEFER_CLEANUP(struct s2n_stuffer client_to_server = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer server_to_client = { 0 }, s2n_stuffer_free); + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); + + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + + S2N_BLOB_FROM_HEX(record_header_blob, tls13_zero_length_handshake_record_hex); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&client_to_server, record_header_blob.data, record_header_blob.size)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&server_to_client, record_header_blob.data, record_header_blob.size)); + EXPECT_EQUAL(s2n_stuffer_data_available(&client_to_server), S2N_TLS_RECORD_HEADER_LENGTH); + EXPECT_EQUAL(s2n_stuffer_data_available(&server_to_client), S2N_TLS_RECORD_HEADER_LENGTH); + + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(server_conn), S2N_ERR_BAD_MESSAGE); + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(client_conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + /** Test 0 length payload in alert record terminates connection in client and server modes + * + *= https://tools.ietf.org/rfc/rfc8446#section-5.4 + *= type=test + *# Implementations MUST NOT send Handshake and Alert records that have a zero-length + *# TLSInnerPlaintext.content; if such a message is received, the receiving + *# implementation MUST terminate the connection with an "unexpected_message" alert. + **/ + { + struct s2n_connection *server_conn; + EXPECT_NOT_NULL(server_conn = s2n_connection_new(S2N_SERVER)); + server_conn->actual_protocol_version = S2N_TLS13; + + struct s2n_connection *client_conn; + EXPECT_NOT_NULL(client_conn = s2n_connection_new(S2N_CLIENT)); + client_conn->actual_protocol_version = S2N_TLS13; + + DEFER_CLEANUP(struct s2n_stuffer client_to_server = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer server_to_client = { 0 }, s2n_stuffer_free); + + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); + + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + + S2N_BLOB_FROM_HEX(record_header_blob, tls13_zero_length_alert_record_hex); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&client_to_server, record_header_blob.data, record_header_blob.size)); + EXPECT_SUCCESS(s2n_stuffer_write_bytes(&server_to_client, record_header_blob.data, record_header_blob.size)); + EXPECT_EQUAL(s2n_stuffer_data_available(&client_to_server), S2N_TLS_RECORD_HEADER_LENGTH); + EXPECT_EQUAL(s2n_stuffer_data_available(&server_to_client), S2N_TLS_RECORD_HEADER_LENGTH); + + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(server_conn), S2N_ERR_BAD_MESSAGE); + EXPECT_FAILURE_WITH_ERRNO(s2n_handshake_read_io(client_conn), S2N_ERR_BAD_MESSAGE); + + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + }; + + END_TEST(); + + return 0; +} diff --git a/tests/unit/s2n_tls_hybrid_prf_test.c b/tests/unit/s2n_tls_hybrid_prf_test.c new file mode 100644 index 00000000000..6c910904bd2 --- /dev/null +++ b/tests/unit/s2n_tls_hybrid_prf_test.c @@ -0,0 +1,124 @@ +/* + * 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 "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "tests/testlib/s2n_nist_kats.h" +#include "tls/s2n_cipher_suites.h" +#include "tls/s2n_prf.h" +#include "utils/s2n_safety.h" + +#define KAT_FILE_NAME "kats/hybrid_prf.kat" + +/* The lengths for premaster_kem_secret and client_key_exchange_message must be defined in the KAT file, + * since they vary based on which KEM is being used. The other lengths are fixed and can be defined here. */ +#define PREMASTER_CLASSIC_SECRET_LENGTH 48 +#define CLIENT_RANDOM_LENGTH 32 +#define SERVER_RANDOM_LENGTH 32 +#define MASTER_SECRET_LENGTH 48 + +#define NUM_TEST_VECTORS 10 + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + FILE *kat_file = fopen(KAT_FILE_NAME, "r"); + EXPECT_NOT_NULL(kat_file); + + uint8_t premaster_classic_secret[PREMASTER_CLASSIC_SECRET_LENGTH]; + uint8_t client_random[CLIENT_RANDOM_LENGTH]; + uint8_t server_random[SERVER_RANDOM_LENGTH]; + uint8_t expected_master_secret[MASTER_SECRET_LENGTH]; + + for (uint32_t i = 0; i < NUM_TEST_VECTORS; i++) { + /* Verify test index */ + uint32_t count = 0; + POSIX_GUARD(FindMarker(kat_file, "count = ")); + POSIX_ENSURE_GT(fscanf(kat_file, "%u", &count), 0); + POSIX_ENSURE_EQ(count, i); + + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS12; + /* Really only need for the hash function in the PRF */ + conn->secure->cipher_suite = &s2n_ecdhe_rsa_with_aes_256_gcm_sha384; + + /* Read test vector from KAT file */ + uint32_t premaster_kem_secret_length = 0; + uint32_t client_key_exchange_message_length = 0; + + POSIX_GUARD(ReadHex(kat_file, premaster_classic_secret, PREMASTER_CLASSIC_SECRET_LENGTH, "premaster_classic_secret = ")); + + POSIX_GUARD(FindMarker(kat_file, "premaster_kem_secret_length = ")); + POSIX_ENSURE_GT(fscanf(kat_file, "%u", &premaster_kem_secret_length), 0); + + uint8_t *premaster_kem_secret; + POSIX_ENSURE_REF(premaster_kem_secret = malloc(premaster_kem_secret_length)); + POSIX_GUARD(ReadHex(kat_file, premaster_kem_secret, premaster_kem_secret_length, "premaster_kem_secret = ")); + + POSIX_GUARD(ReadHex(kat_file, client_random, CLIENT_RANDOM_LENGTH, "client_random = ")); + POSIX_GUARD(ReadHex(kat_file, server_random, SERVER_RANDOM_LENGTH, "server_random = ")); + + POSIX_GUARD(FindMarker(kat_file, "client_key_exchange_message_length = ")); + POSIX_ENSURE_GT(fscanf(kat_file, "%u", &client_key_exchange_message_length), 0); + + uint8_t *client_key_exchange_message; + POSIX_ENSURE_REF(client_key_exchange_message = malloc(client_key_exchange_message_length)); + POSIX_GUARD(ReadHex(kat_file, client_key_exchange_message, client_key_exchange_message_length, "client_key_exchange_message = ")); + + POSIX_GUARD(ReadHex(kat_file, expected_master_secret, MASTER_SECRET_LENGTH, "master_secret = ")); + + struct s2n_blob classic_pms = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&classic_pms, premaster_classic_secret, PREMASTER_CLASSIC_SECRET_LENGTH)); + struct s2n_blob kem_pms = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&kem_pms, premaster_kem_secret, premaster_kem_secret_length)); + + /* In the future the hybrid_kex client_key_send (client side) and client_key_receive (server side) will concatenate the two parts */ + DEFER_CLEANUP(struct s2n_blob combined_pms = { 0 }, s2n_free); + EXPECT_SUCCESS(s2n_alloc(&combined_pms, classic_pms.size + kem_pms.size)); + struct s2n_stuffer combined_stuffer = { 0 }; + s2n_stuffer_init(&combined_stuffer, &combined_pms); + s2n_stuffer_write(&combined_stuffer, &classic_pms); + s2n_stuffer_write(&combined_stuffer, &kem_pms); + + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.client_random, client_random, CLIENT_RANDOM_LENGTH); + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random, SERVER_RANDOM_LENGTH); + + EXPECT_SUCCESS(s2n_alloc(&conn->kex_params.client_key_exchange_message, client_key_exchange_message_length)); + + EXPECT_MEMCPY_SUCCESS(conn->kex_params.client_key_exchange_message.data, client_key_exchange_message, client_key_exchange_message_length); + + EXPECT_SUCCESS(s2n_hybrid_prf_master_secret(conn, &combined_pms)); + EXPECT_BYTEARRAY_EQUAL(expected_master_secret, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN); + EXPECT_SUCCESS(s2n_free(&conn->kex_params.client_key_exchange_message)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + + free(premaster_kem_secret); + free(client_key_exchange_message); + } + + if (FindMarker(kat_file, "count = ") == 0) { + FAIL_MSG("Found unexpected test vectors in the KAT file. Has the KAT file been changed? Did you update NUM_TEST_VECTORS?"); + } + + fclose(kat_file); + END_TEST(); +} diff --git a/tests/unit/s2n_tls_prf_test.c b/tests/unit/s2n_tls_prf_test.c new file mode 100644 index 00000000000..975106e268b --- /dev/null +++ b/tests/unit/s2n_tls_prf_test.c @@ -0,0 +1,388 @@ +/* + * 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_openssl.h" +#include "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "testlib/s2n_testlib.h" +/* To gain access to handshake_read and handshake_write */ +#include "tls/s2n_handshake_io.c" + +#define TEST_BLOB_SIZE 64 + +/* + * Grabbed from gnutls-cli --insecure -d 9 www.example.com --ciphers AES --macs SHA --protocols TLS1.0 + * + * |<9>| INT: PREMASTER SECRET[48]: 0301bebf2a5707c7bda6bfe5a8971a351a9ebd019de412212da021fd802e03f49f231d4e959c7352679f892f9d7f9748 + * |<9>| INT: CLIENT RANDOM[32]: 537eefc1e720b311ff8483d057ae750a3667af9d5b496cc0d2edfb0dd309a286 + * |<9>| INT: SERVER RANDOM[32]: 537eefc29f337c5eedacd00a1889b031261701872d666a74fa999dc13bcd8821 + * |<9>| INT: MASTER SECRET: c8c610686237cd024a2d8e0391f61a8a4464c2c9576ea2b5ccf3af68139ec07c6a1720097063de968f2341f77b837120 + */ +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + S2N_BLOB_FROM_HEX(premaster_secret_in, + "0301bebf2a5707c7bda6bfe5a8971a351a9ebd019de412212da021fd802e03f49f231d4e959c7352679f892f9d7f9748"); + S2N_BLOB_FROM_HEX(client_random_in, + "537eefc1e720b311ff8483d057ae750a3667af9d5b496cc0d2edfb0dd309a286"); + S2N_BLOB_FROM_HEX(server_random_in, + "537eefc29f337c5eedacd00a1889b031261701872d666a74fa999dc13bcd8821"); + S2N_BLOB_FROM_HEX(master_secret_in, + "c8c610686237cd024a2d8e0391f61a8a4464c2c9576ea2b5ccf3af68139ec07c6a1720097063de968f2341f77b837120"); + + struct s2n_connection *conn = NULL; + + /* s2n_tls_prf_master_secret */ + { + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + /* Check the most common PRF */ + conn->actual_protocol_version = S2N_TLS11; + + EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size); + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.client_random, client_random_in.data, client_random_in.size); + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); + + struct s2n_blob pms = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&pms, conn->secrets.version.tls12.rsa_premaster_secret, sizeof(conn->secrets.version.tls12.rsa_premaster_secret))); + EXPECT_SUCCESS(s2n_tls_prf_master_secret(conn, &pms)); + EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* s2n_tls_prf_extended_master_secret */ + { + /* The test premaster secret, hash digest, and resulting + * extended master secret were pulled from an OpenSSL TLS1.2 EMS session + * using the s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384 ciphersuite. + */ + S2N_BLOB_FROM_HEX(premaster_secret, + "05e12675c9264d82b53fa15d589c829af9be1ae3d881ab0b023b7b8cad8bc058"); + + S2N_BLOB_FROM_HEX(hash_digest, + "e6cbbaa03909ea387714fe70c07546086dedfcee086fd2985dfdd50924393619" + "009115758e490e2e3b0c13bebdad5fbb"); + + S2N_BLOB_FROM_HEX(extended_master_secret, + "aef116e65e2cd77d4e96b1ceeadb7912ddd9aaf3a907aa3344ec3a2de6cc3b69" + "9ca768fe389eab3b53c98d8ccd830b06"); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + conn->actual_protocol_version = S2N_TLS12; + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384; + + /** + *= https://tools.ietf.org/rfc/rfc7627#section-4 + *= type=test + *# When the extended master secret extension is negotiated in a full + *# handshake, the "master_secret" is computed as + *# + *# master_secret = PRF(pre_master_secret, "extended master secret", + *# session_hash) + *# [0..47]; + */ + EXPECT_OK(s2n_tls_prf_extended_master_secret(conn, &premaster_secret, &hash_digest, NULL)); + EXPECT_BYTEARRAY_EQUAL(extended_master_secret.data, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* s2n_prf_calculate_master_secret */ + { + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + + conn->secure->cipher_suite = &s2n_ecdhe_ecdsa_with_aes_256_gcm_sha384; + + EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size); + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.client_random, client_random_in.data, client_random_in.size); + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); + + struct s2n_blob pms = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&pms, conn->secrets.version.tls12.rsa_premaster_secret, sizeof(conn->secrets.version.tls12.rsa_premaster_secret))); + + /* Errors when handshake is not at the Client Key Exchange message */ + EXPECT_FAILURE_WITH_ERRNO(s2n_prf_calculate_master_secret(conn, &pms), S2N_ERR_SAFETY); + + /* Advance handshake to Client Key Exchange message */ + conn->handshake.handshake_type = NEGOTIATED | FULL_HANDSHAKE; + while (ACTIVE_MESSAGE(conn) != CLIENT_KEY) { + conn->handshake.message_number++; + } + + /* Master secret is calculated when handshake is at Client Key Exchange message*/ + EXPECT_SUCCESS(s2n_prf_calculate_master_secret(conn, &pms)); + EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); + + /* s2n_prf_calculate_master_secret will produce the same master secret if given the same inputs */ + EXPECT_SUCCESS(s2n_prf_calculate_master_secret(conn, &pms)); + EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); + + conn->ems_negotiated = true; + EXPECT_SUCCESS(s2n_prf_calculate_master_secret(conn, &pms)); + + /* Extended master secret calculated is different than the master secret calculated */ + EXPECT_NOT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* s2n_prf_get_digest_for_ems calculates the correct digest to generate an extended master secret. + * Here we test that the retrieved digest is the same as the digest after the Client Key Exchange + * message is added to the transcript hash. + * + *= https://tools.ietf.org/rfc/rfc7627#section-3 + *= type=test + *# When a full TLS handshake takes place, we define + *# + *# session_hash = Hash(handshake_messages) + *# + *# where "handshake_messages" refers to all handshake messages sent or + *# received, starting at the ClientHello up to and including the + *# ClientKeyExchange message, including the type and length fields of + *# the handshake messages. + */ + { + struct s2n_cert_chain_and_key *tls12_chain_and_key = NULL; + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&tls12_chain_and_key, S2N_DEFAULT_TEST_CERT_CHAIN, + S2N_DEFAULT_TEST_PRIVATE_KEY)); + + struct s2n_config *config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, tls12_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20170210")); + + struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(client_conn); + + struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER); + EXPECT_NOT_NULL(server_conn); + + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_stuffer client_to_server = { 0 }, s2n_stuffer_free); + DEFER_CLEANUP(struct s2n_stuffer server_to_client = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&client_to_server, 0)); + EXPECT_SUCCESS(s2n_stuffer_growable_alloc(&server_to_client, 0)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&server_to_client, &client_to_server, client_conn)); + EXPECT_SUCCESS(s2n_connection_set_io_stuffers(&client_to_server, &server_to_client, server_conn)); + + EXPECT_OK(s2n_negotiate_test_server_and_client_until_message(server_conn, client_conn, CLIENT_KEY)); + + /* Client writes Client Key Exchange message */ + EXPECT_SUCCESS(s2n_handshake_write_io(client_conn)); + + uint8_t data[S2N_MAX_DIGEST_LEN] = { 0 }; + struct s2n_blob digest_for_ems = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&digest_for_ems, data, sizeof(data))); + + /* Get the Client Key transcript */ + EXPECT_SUCCESS(s2n_stuffer_skip_read(&client_to_server, S2N_TLS_RECORD_HEADER_LENGTH)); + uint8_t client_key_message_length = s2n_stuffer_data_available(&client_to_server); + uint8_t *client_key_message = s2n_stuffer_raw_read(&client_to_server, client_key_message_length); + struct s2n_blob message = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&message, client_key_message, client_key_message_length)); + + s2n_hmac_algorithm prf_alg = server_conn->secure->cipher_suite->prf_alg; + s2n_hash_algorithm hash_alg = 0; + POSIX_GUARD(s2n_hmac_hash_alg(prf_alg, &hash_alg)); + EXPECT_OK(s2n_prf_get_digest_for_ems(server_conn, &message, hash_alg, &digest_for_ems)); + + /* Server reads Client Key Exchange message */ + EXPECT_SUCCESS(s2n_stuffer_rewind_read(&client_to_server, S2N_TLS_RECORD_HEADER_LENGTH + client_key_message_length)); + EXPECT_SUCCESS(s2n_handshake_read_io(server_conn)); + + /* Calculate the digest message after the Server read the Client Key message */ + DEFER_CLEANUP(struct s2n_hash_state current_hash_state = { 0 }, s2n_hash_free); + uint8_t server_digest[S2N_MAX_DIGEST_LEN] = { 0 }; + uint8_t digest_size = 0; + EXPECT_SUCCESS(s2n_hash_digest_size(hash_alg, &digest_size)); + EXPECT_SUCCESS(s2n_hash_new(¤t_hash_state)); + EXPECT_OK(s2n_handshake_copy_hash_state(server_conn, hash_alg, ¤t_hash_state)); + EXPECT_SUCCESS(s2n_hash_digest(¤t_hash_state, server_digest, digest_size)); + + /* Digest for generating the EMS and digest after reading the Client Key message + * should be the same. */ + EXPECT_BYTEARRAY_EQUAL(server_digest, digest_for_ems.data, digest_size); + + EXPECT_SUCCESS(s2n_connection_free(client_conn)); + EXPECT_SUCCESS(s2n_connection_free(server_conn)); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_cert_chain_and_key_free(tls12_chain_and_key)); + } + + /* PRF lifecyle */ + { + /* Safety */ + { + EXPECT_ERROR_WITH_ERRNO(s2n_prf_new(NULL), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_prf_wipe(NULL), S2N_ERR_NULL); + EXPECT_ERROR_WITH_ERRNO(s2n_prf_free(NULL), S2N_ERR_NULL); + + struct s2n_connection conn_with_null_prf_space = { 0 }; + EXPECT_NULL(conn_with_null_prf_space.prf_space); + + EXPECT_ERROR_WITH_ERRNO(s2n_prf_wipe(&conn_with_null_prf_space), S2N_ERR_NULL); + EXPECT_OK(s2n_prf_free(&conn_with_null_prf_space)); + }; + + /* Basic lifecyle */ + { + struct s2n_connection connection = { 0 }; + EXPECT_NULL(connection.prf_space); + + EXPECT_OK(s2n_prf_new(&connection)); + EXPECT_NOT_NULL(connection.prf_space); + + EXPECT_OK(s2n_prf_wipe(&connection)); + EXPECT_NOT_NULL(connection.prf_space); + + EXPECT_OK(s2n_prf_free(&connection)); + EXPECT_NULL(connection.prf_space); + }; + + /* PRF freed by s2n_connection_free_handshake */ + { + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_NOT_NULL(conn->prf_space); + + EXPECT_SUCCESS(s2n_connection_free_handshake(conn)); + EXPECT_NULL(conn->prf_space); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* Freed PRF restored by s2n_connection_wipe */ + { + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_OK(s2n_prf_free(conn)); + EXPECT_NULL(conn->prf_space); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_NOT_NULL(conn->prf_space); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + + /* PRF usable throughout connection lifecycle */ + { + struct s2n_blob pms = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&pms, premaster_secret_in.data, premaster_secret_in.size)); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_SERVER)); + EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size); + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.client_random, client_random_in.data, client_random_in.size); + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); + EXPECT_SUCCESS(s2n_tls_prf_master_secret(conn, &pms)); + EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); + + EXPECT_SUCCESS(s2n_connection_free_handshake(conn)); + EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size); + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.client_random, client_random_in.data, client_random_in.size); + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); + EXPECT_FAILURE_WITH_ERRNO(s2n_tls_prf_master_secret(conn, &pms), S2N_ERR_NULL); + + EXPECT_SUCCESS(s2n_connection_wipe(conn)); + EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.rsa_premaster_secret, premaster_secret_in.data, premaster_secret_in.size); + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.client_random, client_random_in.data, client_random_in.size); + EXPECT_MEMCPY_SUCCESS(conn->handshake_params.server_random, server_random_in.data, server_random_in.size); + EXPECT_SUCCESS(s2n_tls_prf_master_secret(conn, &pms)); + EXPECT_EQUAL(memcmp(conn->secrets.version.tls12.master_secret, master_secret_in.data, master_secret_in.size), 0); + + EXPECT_SUCCESS(s2n_connection_free(conn)); + }; + }; + + /* Ensure that the libcrypto TLS PRF API is only enabled for AWSLC */ + if (s2n_libcrypto_is_awslc()) { + EXPECT_TRUE(s2n_libcrypto_supports_tls_prf()); + } else { + EXPECT_FALSE(s2n_libcrypto_supports_tls_prf()); + } + + /* s2n_prf tests */ + { + s2n_stack_blob(secret, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + s2n_stack_blob(label, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + s2n_stack_blob(seed_a, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + s2n_stack_blob(seed_b, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + s2n_stack_blob(seed_c, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + s2n_stack_blob(out, TEST_BLOB_SIZE, TEST_BLOB_SIZE); + + /* Safety */ + { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + + EXPECT_FAILURE_WITH_ERRNO(s2n_prf(NULL, &secret, &label, &seed_a, &seed_b, &seed_c, &out), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, NULL, &label, &seed_a, &seed_b, &seed_c, &out), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, &secret, NULL, &seed_a, &seed_b, &seed_c, &out), + S2N_ERR_NULL); + EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, &secret, &label, &seed_a, &seed_b, &seed_c, NULL), + S2N_ERR_NULL); + + /* seed_a is required */ + EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, &secret, &label, NULL, &seed_b, &seed_c, &out), + S2N_ERR_PRF_INVALID_SEED); + + /* seed_b and seed_c are optional */ + EXPECT_SUCCESS(s2n_prf(connection, &secret, &label, &seed_a, NULL, NULL, &out)); + + /* seed_b is required if seed_c is provided */ + EXPECT_FAILURE_WITH_ERRNO(s2n_prf(connection, &secret, &label, &seed_a, NULL, &seed_c, &out), + S2N_ERR_PRF_INVALID_SEED); + + /* seed_c is optional */ + EXPECT_SUCCESS(s2n_prf(connection, &secret, &label, &seed_a, &seed_b, NULL, &out)); + } + + /* The custom PRF implementation is used when s2n-tls is not operating in FIPS mode */ + if (!s2n_is_in_fips_mode()) { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + + uint8_t zeros[S2N_MAX_DIGEST_LEN] = { 0 }; + EXPECT_EQUAL(memcmp(connection->prf_space->digest0, zeros, S2N_MAX_DIGEST_LEN), 0); + + EXPECT_SUCCESS(s2n_prf(connection, &secret, &label, &seed_a, &seed_b, &seed_c, &out)); + + /* The custom PRF implementation should modify the digest fields in the prf_space */ + EXPECT_NOT_EQUAL(memcmp(connection->prf_space->digest0, zeros, S2N_MAX_DIGEST_LEN), 0); + } + + /* The libcrypto PRF implementation is used when s2n-tls is linked with AWSLC-FIPS */ + if (s2n_libcrypto_is_awslc() && s2n_is_in_fips_mode()) { + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + + uint8_t zeros[S2N_MAX_DIGEST_LEN] = { 0 }; + EXPECT_EQUAL(memcmp(connection->prf_space->digest0, zeros, S2N_MAX_DIGEST_LEN), 0); + + EXPECT_SUCCESS(s2n_prf(connection, &secret, &label, &seed_a, &seed_b, &seed_c, &out)); + + /* The libcrypto PRF implementation will not modify the digest fields in the prf_space */ + EXPECT_EQUAL(memcmp(connection->prf_space->digest0, zeros, S2N_MAX_DIGEST_LEN), 0); + } + } + + END_TEST(); +} diff --git a/tests/unit/s2n_tls_record_stuffer_test.c b/tests/unit/s2n_tls_record_stuffer_test.c new file mode 100644 index 00000000000..bedbd225131 --- /dev/null +++ b/tests/unit/s2n_tls_record_stuffer_test.c @@ -0,0 +1,101 @@ +/* + * 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 "s2n_test.h" +#include "stuffer/s2n_stuffer.h" +#include "tls/s2n_record.h" +#include "utils/s2n_random.h" + +int main(int argc, char **argv) +{ +#if 0 + uint8_t plaintext_pad[S2N_TLS_MAXIMUM_RECORD_LENGTH + 1]; + uint8_t encrypted_pad[S2N_TLS_MAXIMUM_RECORD_LENGTH + 1]; + uint8_t entropy[S2N_TLS_MAXIMUM_RECORD_LENGTH + 1]; + struct s2n_record_stuffer writer; + uint8_t protocol_version[2] = { 3, 0 }; + + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + EXPECT_FAILURE(s2n_tls_record_stuffer_init(&writer, plaintext_pad, encrypted_pad, S2N_TLS_MAXIMUM_RECORD_LENGTH - 1, &error_message)); + EXPECT_SUCCESS(s2n_tls_record_stuffer_init(&writer, plaintext_pad, encrypted_pad, S2N_TLS_MAXIMUM_RECORD_LENGTH + 1, &error_message)); + + /* Record is too short */ + EXPECT_FAILURE(s2n_tls_record_finalize(&writer, &error_message)); + + /* Add a header */ + EXPECT_SUCCESS(s2n_tls_record_write_header(&writer, 1, protocol_version, &error_message)); + EXPECT_EQUAL(s2n_stuffer_data_available(&writer.plaintext_stuffer), S2N_TLS_MINIMUM_RECORD_LENGTH); + + /* Should now be finalizable */ + EXPECT_SUCCESS(s2n_tls_record_finalize(&writer, &error_message)); + + uint8_t expected[] = { 1, 3, 0, 0, 0 }; + EXPECT_BYTEARRAY_EQUAL(plaintext_pad, expected, 5); + + /* Get some Random data */ + EXPECT_SUCCESS(s2n_get_random_data(entropy, sizeof(entropy), &error_message)); + + /* Write our maximum record payload */ + EXPECT_SUCCESS(s2n_stuffer_write(&writer.plaintext_stuffer, entropy, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, &error_message)); + EXPECT_SUCCESS(s2n_tls_record_finalize(&writer, &error_message)); + uint8_t expected2[] = { 1, 3, 0, 0x48, 0x00 }; + EXPECT_BYTEARRAY_EQUAL(plaintext_pad, expected2, 5); + + /* Try one higher, make sure it fails */ + EXPECT_SUCCESS(s2n_stuffer_write(&writer.plaintext_stuffer, entropy, 1, &error_message)); + EXPECT_FAILURE(s2n_tls_record_finalize(&writer, &error_message)); + + /* Try SSL2 now ... */ + EXPECT_SUCCESS(s2n_tls_record_stuffer_init(&writer, plaintext_pad, encrypted_pad, S2N_TLS_MAXIMUM_RECORD_LENGTH + 1, &error_message)); + + /* Record is too short */ + EXPECT_FAILURE(s2n_ssl2_record_finalize(&writer, &error_message)); + + /* Add a header */ + EXPECT_SUCCESS(s2n_ssl2_record_write_header(&writer, 1, protocol_version, &error_message)); + EXPECT_EQUAL(s2n_stuffer_data_available(&writer.plaintext_stuffer), 5); + + /* Still not finalizeable */ + EXPECT_FAILURE(s2n_ssl2_record_finalize(&writer, &error_message)); + + /* Add the 22 bytes of mandatory header data */ + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&writer.plaintext_stuffer, 0, &error_message)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&writer.plaintext_stuffer, 0, &error_message)); + EXPECT_SUCCESS(s2n_stuffer_write_uint16(&writer.plaintext_stuffer, 16, &error_message)); + EXPECT_SUCCESS(s2n_stuffer_write(&writer.plaintext_stuffer, entropy, 16, &error_message)); + + /* Now we can finalize */ + EXPECT_SUCCESS(s2n_ssl2_record_finalize(&writer, &error_message)); + + uint8_t expected3[] = { 0x80, 0x19, 1, 3, 0, 0, 0, 0, 0, 0, 16 }; + EXPECT_BYTEARRAY_EQUAL(plaintext_pad, expected3, 11); + + /* Write our maximum record payload */ + EXPECT_SUCCESS(s2n_stuffer_write(&writer.plaintext_stuffer, entropy, S2N_SSL2_MAXIMUM_MESSAGE_LENGTH, &error_message)); + EXPECT_SUCCESS(s2n_ssl2_record_finalize(&writer, &error_message)); + + uint8_t expected4[] = { 0xbf, 0xfd, 1, 3, 0, 0, 0, 0, 0, 0, 16 }; + EXPECT_BYTEARRAY_EQUAL(plaintext_pad, expected4, 11); + + /* Try one higher, make sure it fails */ + EXPECT_SUCCESS(s2n_stuffer_write(&writer.plaintext_stuffer, entropy, 1, &error_message)); + EXPECT_FAILURE(s2n_ssl2_record_finalize(&writer, &error_message)); + + END_TEST(); +#endif + return 0; +} diff --git a/tests/unit/s2n_utils_test.c b/tests/unit/s2n_utils_test.c new file mode 100644 index 00000000000..6dc5d22f535 --- /dev/null +++ b/tests/unit/s2n_utils_test.c @@ -0,0 +1,62 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_blob.h" +#include "utils/s2n_safety.h" + +#define test_stack_blob_success(test_name, macro_name, requested, max) \ + int test_name() \ + { \ + macro_name(test_name##blob, requested, max); \ + POSIX_ENSURE_EQ(test_name##blob.size, requested); \ + return 0; \ + } + +test_stack_blob_success(success_equal, s2n_stack_blob, 10, 10) + +test_stack_blob_success(success_equal_smaller, s2n_stack_blob, 10, 100) + +int requested_bigger_than_max() +{ + s2n_stack_blob(foo, 11, 10); + /* This should never be reached due to the above failure */ + POSIX_ENSURE_EQ(foo.allocated, 0); + + return 0; +} + +int successful_stack_blob() +{ + s2n_stack_blob(foo, 10, 10); + POSIX_ENSURE_EQ(foo.size, 10); + POSIX_ENSURE_EQ(foo.allocated, 0); + + s2n_stack_blob(foo2, 1, 10); + POSIX_ENSURE_EQ(foo2.size, 1); + POSIX_ENSURE_EQ(foo2.allocated, 0); + + return 0; +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + EXPECT_FAILURE(requested_bigger_than_max()); + EXPECT_SUCCESS(successful_stack_blob()); + END_TEST(); +} diff --git a/tests/unit/s2n_wildcard_hostname_test.c b/tests/unit/s2n_wildcard_hostname_test.c new file mode 100644 index 00000000000..0d059053c79 --- /dev/null +++ b/tests/unit/s2n_wildcard_hostname_test.c @@ -0,0 +1,73 @@ +/* + * 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_certificate.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "utils/s2n_safety.h" + +struct wildcardify_test_case { + const char *hostname; + const char *output; +}; + +struct wildcardify_test_case wildcardify_test_cases[] = { + { .hostname = "foo.bar.com", .output = "*.bar.com" }, + { .hostname = "localhost", .output = NULL }, + { .hostname = "one.com", .output = "*.com" }, + { .hostname = "foo*.bar*.com*", .output = "*.bar*.com*" }, + { .hostname = "foo.bar.com.", .output = "*.bar.com." }, + { .hostname = "*.a.c", .output = "*.a.c" }, + { .hostname = "*", .output = NULL }, + { .hostname = "foo.", .output = "*." }, +}; + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + const int num_wildcardify_tests = s2n_array_len(wildcardify_test_cases); + for (size_t i = 0; i < num_wildcardify_tests; i++) { + const char *hostname = wildcardify_test_cases[i].hostname; + struct s2n_blob hostname_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&hostname_blob, (uint8_t *) (uintptr_t) hostname, strlen(hostname))); + uint8_t output[S2N_MAX_SERVER_NAME] = { 0 }; + struct s2n_blob output_blob = { 0 }; + EXPECT_SUCCESS(s2n_blob_init(&output_blob, (uint8_t *) (uintptr_t) output, sizeof(output))); + struct s2n_stuffer hostname_stuffer = { 0 }; + struct s2n_stuffer output_stuffer = { 0 }; + EXPECT_SUCCESS(s2n_stuffer_init(&hostname_stuffer, &hostname_blob)); + EXPECT_SUCCESS(s2n_stuffer_skip_write(&hostname_stuffer, hostname_blob.size)); + EXPECT_SUCCESS(s2n_stuffer_init(&output_stuffer, &output_blob)); + EXPECT_SUCCESS(s2n_create_wildcard_hostname(&hostname_stuffer, &output_stuffer)); + + /* Make sure the wildcard generated matches the output we expect. */ + const uint32_t wildcard_len = s2n_stuffer_data_available(&output_stuffer); + const char *expected_output = wildcardify_test_cases[i].output; + if (wildcard_len > 0) { + EXPECT_EQUAL(wildcard_len, strlen(expected_output)); + EXPECT_SUCCESS(memcmp(output, expected_output, wildcard_len)); + } else { + EXPECT_EQUAL(expected_output, NULL); + } + } + + END_TEST(); +} diff --git a/tests/unit/s2n_x509_validator_certificate_signatures_test.c b/tests/unit/s2n_x509_validator_certificate_signatures_test.c new file mode 100644 index 00000000000..ffcb657171e --- /dev/null +++ b/tests/unit/s2n_x509_validator_certificate_signatures_test.c @@ -0,0 +1,240 @@ +/* + * 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 +#include + +#include "api/s2n.h" +#include "crypto/s2n_openssl.h" +#include "crypto/s2n_openssl_x509.h" +#include "error/s2n_errno.h" +#include "s2n_test.h" +#include "testlib/s2n_testlib.h" +#include "tls/s2n_signature_scheme.h" +#include "tls/s2n_tls.h" +#include "tls/s2n_tls13.h" +#include "tls/s2n_x509_validator.h" +#include "utils/s2n_safety.h" + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + + uint8_t cert_file[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + X509 *cert = NULL; + BIO *certBio = NULL; + size_t certLen = 0; + + const struct s2n_signature_scheme *const test_sig_scheme_list[] = { + &s2n_ecdsa_sha256, + &s2n_rsa_pkcs1_sha1, + }; + + const struct s2n_signature_preferences test_certificate_signature_preferences = { + .count = s2n_array_len(test_sig_scheme_list), + .signature_schemes = test_sig_scheme_list, + }; + + const struct s2n_signature_scheme *const pss_sig_scheme_list[] = { + &s2n_rsa_pss_pss_sha256, + &s2n_rsa_pss_pss_sha384, + &s2n_rsa_pss_pss_sha512, + &s2n_rsa_pss_rsae_sha256, + &s2n_rsa_pss_rsae_sha384, + &s2n_rsa_pss_rsae_sha512, + }; + + const struct s2n_signature_preferences pss_certificate_signature_preferences = { + .count = s2n_array_len(pss_sig_scheme_list), + .signature_schemes = pss_sig_scheme_list, + }; + + /* s2n_is_certificate_sig_scheme_supported() */ + { + struct s2n_connection *conn; + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + + /* Certificate signature algorithm is in test certificate signature preferences list */ + { + EXPECT_SUCCESS(s2n_read_test_pem(S2N_ECDSA_P256_PKCS1_CERT_CHAIN, (char *) cert_file, S2N_MAX_TEST_PEM_SIZE)); + certLen = strlen((const char *) cert_file); + + /* Read the test certificates into an Openssl X509 struct */ + EXPECT_NOT_NULL(certBio = BIO_new(BIO_s_mem())); + EXPECT_TRUE(BIO_write(certBio, cert_file, certLen) > 0); + EXPECT_NOT_NULL(cert = PEM_read_bio_X509(certBio, NULL, NULL, NULL)); + + EXPECT_OK(s2n_validate_sig_scheme_supported(conn, cert, &test_certificate_signature_preferences)); + + EXPECT_SUCCESS(BIO_free(certBio)); + X509_free(cert); + }; + + /* Certificate signature algorithm is not in test certificate signature preferences list */ + { + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, (char *) cert_file, S2N_MAX_TEST_PEM_SIZE)); + certLen = strlen((const char *) cert_file); + + /* Read the test certificates into an Openssl X509 struct */ + EXPECT_NOT_NULL(certBio = BIO_new(BIO_s_mem())); + EXPECT_TRUE(BIO_write(certBio, cert_file, certLen) > 0); + EXPECT_NOT_NULL(cert = PEM_read_bio_X509(certBio, NULL, NULL, NULL)); + + EXPECT_ERROR_WITH_ERRNO(s2n_validate_sig_scheme_supported(conn, cert, &test_certificate_signature_preferences), S2N_ERR_CERT_UNTRUSTED); + + EXPECT_SUCCESS(BIO_free(certBio)); + X509_free(cert); + }; + + /* Certificate signature algorithm is in the test certificate signature preferences list but signature is SHA-1 + * and TLS 1.3 has been negotiated. + */ + { + conn->actual_protocol_version = S2N_TLS13; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_CERT_CHAIN, (char *) cert_file, S2N_MAX_TEST_PEM_SIZE)); + certLen = strlen((const char *) cert_file); + + /* Read the test certificates into an Openssl X509 struct */ + EXPECT_NOT_NULL(certBio = BIO_new(BIO_s_mem())); + EXPECT_TRUE(BIO_write(certBio, cert_file, certLen) > 0); + EXPECT_NOT_NULL(cert = PEM_read_bio_X509(certBio, NULL, NULL, NULL)); + + EXPECT_ERROR_WITH_ERRNO(s2n_validate_sig_scheme_supported(conn, cert, &test_certificate_signature_preferences), S2N_ERR_CERT_UNTRUSTED); + + EXPECT_SUCCESS(BIO_free(certBio)); + X509_free(cert); + }; + + /* Certificate signature algorithm is in the test certificate signature preferences list and signature is SHA-1 + * and TLS 1.2 has been negotiated. + */ + { + conn->actual_protocol_version = S2N_TLS12; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_CERT_CHAIN, (char *) cert_file, S2N_MAX_TEST_PEM_SIZE)); + certLen = strlen((const char *) cert_file); + + /* Read the test certificates into an Openssl X509 struct */ + EXPECT_NOT_NULL(certBio = BIO_new(BIO_s_mem())); + EXPECT_TRUE(BIO_write(certBio, cert_file, certLen) > 0); + EXPECT_NOT_NULL(cert = PEM_read_bio_X509(certBio, NULL, NULL, NULL)); + + EXPECT_OK(s2n_validate_sig_scheme_supported(conn, cert, &test_certificate_signature_preferences)); + + EXPECT_SUCCESS(BIO_free(certBio)); + X509_free(cert); + }; + + /* Certificates signed with an RSA PSS signature can be validated */ + { + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_PSS_2048_SHA256_LEAF_CERT, (char *) cert_file, S2N_MAX_TEST_PEM_SIZE)); + certLen = strlen((const char *) cert_file); + + /* Read the test certificates into an Openssl X509 struct */ + EXPECT_NOT_NULL(certBio = BIO_new(BIO_s_mem())); + EXPECT_TRUE(BIO_write(certBio, cert_file, certLen) > 0); + EXPECT_NOT_NULL(cert = PEM_read_bio_X509(certBio, NULL, NULL, NULL)); + + EXPECT_OK(s2n_validate_sig_scheme_supported(conn, cert, &pss_certificate_signature_preferences)); + + EXPECT_SUCCESS(BIO_free(certBio)); + X509_free(cert); + }; + + EXPECT_SUCCESS(s2n_connection_free(conn)); + } + + /* s2n_validate_certificate_signature */ + { + /* Connection using a security policy with no certificate_signature_preferences allows SHA-1 signatures in certificates */ + { + struct s2n_connection *conn; + struct s2n_config *config = s2n_config_new(); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + /* 20140601 is a security policy with no certificate_signature_preferences list */ + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "20140601")); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_CERT_CHAIN, (char *) cert_file, S2N_MAX_TEST_PEM_SIZE)); + certLen = strlen((const char *) cert_file); + + /* Read the test certificates into an Openssl X509 struct */ + EXPECT_NOT_NULL(certBio = BIO_new(BIO_s_mem())); + EXPECT_TRUE(BIO_write(certBio, cert_file, certLen) > 0); + EXPECT_NOT_NULL(cert = PEM_read_bio_X509(certBio, NULL, NULL, NULL)); + + EXPECT_OK(s2n_validate_certificate_signature(conn, cert)); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(BIO_free(certBio)); + X509_free(cert); + }; + + /* Connection using the default_tls13 security policy does not validate SHA-1 signatures in certificates */ + { + struct s2n_connection *conn; + struct s2n_config *config = s2n_config_new(); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_RSA_2048_PKCS1_CERT_CHAIN, (char *) cert_file, S2N_MAX_TEST_PEM_SIZE)); + certLen = strlen((const char *) cert_file); + + /* Read the test certificates into an Openssl X509 struct */ + EXPECT_NOT_NULL(certBio = BIO_new(BIO_s_mem())); + EXPECT_TRUE(BIO_write(certBio, cert_file, certLen) > 0); + EXPECT_NOT_NULL(cert = PEM_read_bio_X509(certBio, NULL, NULL, NULL)); + + EXPECT_ERROR_WITH_ERRNO(s2n_validate_certificate_signature(conn, cert), S2N_ERR_CERT_UNTRUSTED); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(BIO_free(certBio)); + X509_free(cert); + }; + + /* Connection using the default_tls13 security policy ignores a SHA-1 signature on a root certificate */ + { + struct s2n_connection *conn; + struct s2n_config *config = s2n_config_new(); + + EXPECT_NOT_NULL(conn = s2n_connection_new(S2N_CLIENT)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + + EXPECT_SUCCESS(s2n_read_test_pem(S2N_SHA1_ROOT_SIGNATURE_CA_CERT, (char *) cert_file, S2N_MAX_TEST_PEM_SIZE)); + certLen = strlen((const char *) cert_file); + + /* Read the test certificates into an Openssl X509 struct */ + EXPECT_NOT_NULL(certBio = BIO_new(BIO_s_mem())); + EXPECT_TRUE(BIO_write(certBio, cert_file, certLen) > 0); + EXPECT_NOT_NULL(cert = PEM_read_bio_X509(certBio, NULL, NULL, NULL)); + + EXPECT_OK(s2n_validate_certificate_signature(conn, cert)); + + EXPECT_SUCCESS(s2n_config_free(config)); + EXPECT_SUCCESS(s2n_connection_free(conn)); + EXPECT_SUCCESS(BIO_free(certBio)); + X509_free(cert); + }; + }; + END_TEST(); + return S2N_SUCCESS; +} diff --git a/tests/unit/s2n_x509_validator_test.c b/tests/unit/s2n_x509_validator_test.c new file mode 100644 index 00000000000..cf0cc3c7662 --- /dev/null +++ b/tests/unit/s2n_x509_validator_test.c @@ -0,0 +1,2099 @@ +/* + * 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" + +DEFINE_POINTER_CLEANUP_FUNC(X509 *, X509_free); + +static int mock_time(void *data, uint64_t *timestamp) +{ + *timestamp = *(uint64_t *) data; + return 0; +} + +static int fetch_expired_after_ocsp_timestamp(void *data, uint64_t *timestamp) +{ + /* 2200-11-27 */ + *timestamp = 7283958536000000000; + return 0; +} + +static int fetch_early_expired_after_ocsp_timestamp(void *data, uint64_t *timestamp) +{ + /* 2038-01-01 */ + *timestamp = 2145920461000000000; + return 0; +} + +#if S2N_OCSP_STAPLING_SUPPORTED +static int fetch_invalid_before_ocsp_timestamp(void *data, uint64_t *timestamp) +{ + /* 2015-02-27 */ + *timestamp = 1425019604000000000; + return 0; +} + +static int fetch_not_expired_ocsp_timestamp(void *data, uint64_t *timestamp) +{ + /* 2019-03-17 */ + *timestamp = 1552824239000000000; + return 0; +} +#endif /* S2N_OCSP_STAPLING_SUPPORTED */ + +static int read_file(struct s2n_stuffer *file_output, const char *path, uint32_t max_len) +{ + FILE *fd = fopen(path, "rb"); + s2n_stuffer_alloc(file_output, max_len); + + if (fd) { + char data[1024]; + size_t r = 0; + while ((r = fread(data, 1, sizeof(data), fd)) > 0) { + s2n_stuffer_write_bytes(file_output, (const uint8_t *) data, (const uint32_t) r); + } + fclose(fd); + return s2n_stuffer_data_available(file_output) > 0; + } + + return -1; +} + +struct host_verify_data { + const char *name; + uint8_t found_name; + uint8_t callback_invoked; +}; + +static uint8_t verify_host_reject_everything(const char *host_name, size_t host_name_len, void *data) +{ + struct host_verify_data *verify_data = (struct host_verify_data *) data; + verify_data->callback_invoked = 1; + return 0; +} + +static uint8_t verify_host_accept_everything(const char *host_name, size_t host_name_len, void *data) +{ + struct host_verify_data *verify_data = (struct host_verify_data *) data; + verify_data->callback_invoked = 1; + return 1; +} + +static uint8_t verify_host_verify_alt(const char *host_name, size_t host_name_len, void *data) +{ + struct host_verify_data *verify_data = (struct host_verify_data *) data; + + verify_data->callback_invoked = 1; + if (!strcmp(host_name, verify_data->name)) { + verify_data->found_name = 1; + return 1; + } + + return 0; +} + +/* some tests try to mock the system time to a date post 2038. If this test is + * run on a platform where time_t is 32 bits, the time_t will overflow, so we + * only run these tests on platforms with a 64 bit time_t. + */ +static bool s2n_supports_large_time_t() +{ + return sizeof(time_t) == 8; +} + +/* Early versions of Openssl (Openssl-1.0.2k confirmed) included a bug where UTCTime + * formatted dates in certificates could not be compared to dates after the year 2050, + * because Openssl would assume that the validation date was also UTCTime formatted + * and therefore reject any date with a year after 2050. + * This is an issue because RFC5280 requires that dates in certificates be in + * UTCTime format for years before 2050. + * Affected tests are modified to account for this bug. + * See https://github.com/openssl/openssl/blob/OpenSSL_1_0_2k/crypto/x509/x509_vfy.c#L2027C1-L2027C26 + */ +static bool s2n_libcrypto_supports_2050() +{ + ASN1_TIME *utc_time = ASN1_UTCTIME_set(NULL, 0); + time_t time_2050 = 2524608000; + int result = X509_cmp_time(utc_time, &time_2050); + ASN1_STRING_free(utc_time); + return (result != 0); +} + +int main(int argc, char **argv) +{ + BEGIN_TEST(); + EXPECT_SUCCESS(s2n_disable_tls13_in_test()); + + /* The issues with 2050 only affected openssl-1.0.2 */ + if (S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0)) { + EXPECT_TRUE(s2n_libcrypto_supports_2050()); + } + + /* test empty trust store */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&trust_store)); + }; + + /* test trust store from PEM file */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + int err_code = s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL); + EXPECT_EQUAL(0, err_code); + EXPECT_TRUE(s2n_x509_trust_store_has_certs(&trust_store)); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test trust store from PEM */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + char *cert_chain = NULL; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + int err_code = s2n_x509_trust_store_add_pem(&trust_store, cert_chain); + EXPECT_EQUAL(0, err_code); + EXPECT_TRUE(s2n_x509_trust_store_has_certs(&trust_store)); + + /* s2n_x509_trust_store_add_pem returns success when trying to add a + * certificate that already exists in the trust store */ + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, cert_chain)); + + free(cert_chain); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test trust store from non-existent PEM file */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + int err_code = s2n_x509_trust_store_from_ca_file(&trust_store, "dskfjasdklfjsdkl", NULL); + EXPECT_EQUAL(-1, err_code); + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&trust_store)); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test trust store from invalid PEM file */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + int err_code = s2n_x509_trust_store_from_ca_file(&trust_store, S2N_INVALID_HEADER_KEY, NULL); + EXPECT_EQUAL(-1, err_code); + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&trust_store)); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in unsafe mode */ + { + struct s2n_x509_validator validator; + s2n_x509_validator_init_no_x509_validation(&validator); + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + /* The default cert chain includes a SHA1 signature, so the security policy must allow SHA1 cert signatures. */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + }; + + /* test validator in unsafe mode, make sure max depth is honored on the read, but not an error condition */ + { + struct s2n_x509_validator validator; + s2n_x509_validator_init_no_x509_validation(&validator); + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + /* The default cert chain includes a SHA1 signature, so the security policy must allow SHA1 cert signatures. */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); + + EXPECT_SUCCESS(s2n_x509_validator_set_max_chain_depth(&validator, 2)); + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + }; + + /* test validator in safe mode, but no configured trust store */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + EXPECT_FAILURE_WITH_ERRNO(s2n_x509_validator_set_max_chain_depth(&validator, 0), S2N_ERR_INVALID_ARGUMENT); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, but no configured trust store */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + + EXPECT_NOT_NULL(connection); + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + /* The default cert chain includes a SHA1 signature, so the security policy must allow SHA1 cert signatures. */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_UNTRUSTED); + EXPECT_EQUAL(0, verify_data.callback_invoked); + + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + /* The default cert chain includes a SHA1 signature, so the security policy must allow SHA1 cert signatures. */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store and test that SAN URI callback is invoked. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_URI_SANS_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = "foo://bar" }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK( + s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_URI_SANS_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_EQUAL(1, verify_data.found_name); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store, using s2n PEM Parser. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + + char *cert_chain = NULL; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + int err_code = s2n_x509_trust_store_add_pem(&trust_store, cert_chain); + free(cert_chain); + EXPECT_EQUAL(0, err_code); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + /* The default cert chain includes a SHA1 signature, so the security policy must allow SHA1 cert signatures. */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store, but max chain depth is exceeded*/ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + /* The default cert chain includes a SHA1 signature, so the security policy must allow SHA1 cert signatures. */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); + + EXPECT_SUCCESS(s2n_x509_validator_set_max_chain_depth(&validator, 2)); + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out), + S2N_ERR_CERT_MAX_CHAIN_DEPTH_EXCEEDED); + + EXPECT_EQUAL(0, verify_data.callback_invoked); + EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test post-2038 certificate expiration. + * + * The expired certificate should fail as untrusted. This test fails on + * platforms where time_t is 4 bytes because representing dates past 2038 as + * unix seconds overflows the time_t. + */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + /* The default cert chain includes a SHA1 signature, so the security policy must allow SHA1 cert signatures. */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); + + s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; + s2n_config_set_wall_clock(connection->config, fetch_expired_after_ocsp_timestamp, NULL); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + int expected_errno = S2N_ERR_CERT_EXPIRED; + /* In some cases validation may fail with a less specific error due to + * issues with large dates, but validation does always fail. */ + if (!s2n_supports_large_time_t()) { + expected_errno = S2N_ERR_SAFETY; + } else if (!s2n_libcrypto_supports_2050()) { + expected_errno = S2N_ERR_CERT_UNTRUSTED; + } + EXPECT_ERROR_WITH_ERRNO( + s2n_x509_validator_validate_cert_chain(&validator, connection, + chain_data, chain_len, &pkey_type, &public_key_out), + expected_errno); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + s2n_config_set_wall_clock(connection->config, old_clock, NULL); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test pre-2038 certificate expiration + * + * After the expiration date, the certificate should fail as untrusted. This + * test uses pre-2038 dates for 32 bit time_t concerns + */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT_EARLY_EXPIRE, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + /* The default cert chain includes a SHA1 signature, so the security policy must allow SHA1 cert signatures. */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); + + s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; + EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, fetch_early_expired_after_ocsp_timestamp, NULL)); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO( + s2n_x509_validator_validate_cert_chain(&validator, connection, + chain_data, chain_len, &pkey_type, &public_key_out), + S2N_ERR_CERT_EXPIRED); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, old_clock, NULL)); + }; + + /* test validator in safe mode, with properly configured trust store, but the server's end-entity cert is invalid. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + /* The default cert chain includes a SHA1 signature, so the security policy must allow SHA1 cert signatures. */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); + + /* alter a random byte in the certificate to make it invalid */ + chain_data[500] = (uint8_t) (chain_data[500] << 2); + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_UNTRUSTED); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store, but host isn't trusted*/ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + struct host_verify_data verify_data = { + .name = "127.0.0.1", + .found_name = 0, + .callback_invoked = 0, + }; + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_reject_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + /* The default cert chain includes a SHA1 signature, so the security policy must allow SHA1 cert signatures. */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_UNTRUSTED); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store, but host isn't trusted, using s2n PEM Parser */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + + char *cert_chain = NULL; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + int err_code = s2n_x509_trust_store_add_pem(&trust_store, cert_chain); + free(cert_chain); + EXPECT_EQUAL(0, err_code); + + struct host_verify_data verify_data = { + .name = "127.0.0.1", + .found_name = 0, + .callback_invoked = 0, + }; + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_reject_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + /* The default cert chain includes a SHA1 signature, so the security policy must allow SHA1 cert signatures. */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_UNTRUSTED); + EXPECT_EQUAL(1, verify_data.callback_invoked); + s2n_connection_free(connection); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store. host name validation succeeds */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + struct host_verify_data verify_data = { + .name = "127.0.0.1", + .found_name = 0, + .callback_invoked = 0, + }; + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + /* The default cert chain includes a SHA1 signature, so the security policy must allow SHA1 cert signatures. */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store. host name validation succeeds, using s2n PEM Parser */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + + char *cert_chain = NULL; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + int err_code = s2n_x509_trust_store_add_pem(&trust_store, cert_chain); + free(cert_chain); + EXPECT_EQUAL(0, err_code); + + struct host_verify_data verify_data = { + .name = "127.0.0.1", + .found_name = 0, + .callback_invoked = 0, + }; + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + /* The default cert chain includes a SHA1 signature, so the security policy must allow SHA1 cert signatures. */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_pkey_free(&public_key_out); + + s2n_connection_free(connection); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store. host name via alternative name validation succeeds + * note: in this case, we don't have valid certs but it's enough to make sure we are properly pulling alternative names + * from the certificate. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + struct host_verify_data verify_data = { + .name = "127.0.0.1", + .found_name = 0, + .callback_invoked = 0, + }; + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_CLIENT_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_UNTRUSTED); + + EXPECT_EQUAL(1, verify_data.found_name); + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); + + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store. host name via alternative name validation fails, and + * no Common Name validation happens as DNS alternative name is present. note: in this case, we don't have valid certs but + * it's enough to make sure we are properly validating alternative names and common name.*/ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* Name matches CN on certificate (CN=localhost), but no match in alternative names */ + struct host_verify_data verify_data = { + .name = "localhost", + .found_name = 0, + .callback_invoked = 0, + }; + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_CLIENT_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_UNTRUSTED); + + EXPECT_EQUAL(0, verify_data.found_name); + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); + + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with properly configured trust store. host name via common name validation succeeds, + * non-dns alternative names are ignored. note: in this case, we don't have valid certs but it's enough to make sure + * we are properly validating alternative names and common name.*/ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + + /* Name matches CN on certificate (CN=localhost) */ + struct host_verify_data verify_data = { + .name = "localhost", + .found_name = 0, + .callback_invoked = 0, + }; + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK( + s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_NO_DNS_SANS_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_UNTRUSTED); + + EXPECT_EQUAL(1, verify_data.found_name); + EXPECT_EQUAL(1, verify_data.callback_invoked); + EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); + + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; +#if S2N_OCSP_STAPLING_SUPPORTED + /* Test valid OCSP date range */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + EXPECT_OK(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len)); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* Test valid OCSP date range without nextUpdate field */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_NO_NEXT_UPDATE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + + s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; + s2n_config_set_wall_clock(connection->config, fetch_not_expired_ocsp_timestamp, NULL); + + EXPECT_OK(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len)); + EXPECT_EQUAL(1, verify_data.callback_invoked); + s2n_config_set_wall_clock(connection->config, old_clock, NULL); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* Test valid OCSP date range, but with s2n PEM Parser */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + + char *cert_chain = NULL; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_OCSP_CA_CERT, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + int err_code = s2n_x509_trust_store_add_pem(&trust_store, cert_chain); + free(cert_chain); + EXPECT_EQUAL(0, err_code); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + s2n_pkey_free(&public_key_out); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + EXPECT_OK(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len)); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /** + * Test invalid OCSP date range post-2038 + * + * After the "Next Update" time in the OCSP response, the certificate should + * fail as expired. + */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; + s2n_config_set_wall_clock(connection->config, fetch_expired_after_ocsp_timestamp, NULL); + + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + + if (s2n_supports_large_time_t()) { + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), + S2N_ERR_CERT_EXPIRED); + } else { + /* fetch_expired_after_ocsp_timestamp is in 2200 which is not + * representable for 32 bit time_t's. + */ + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), + S2N_ERR_SAFETY); + } + + s2n_config_set_wall_clock(connection->config, old_clock, NULL); + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + } + + /** + * Test invalid OCSP date range pre-2038 + * + * This test sets the clock time to be after the expiration date of the cert + * and after the "Next Update" field of the OCSP response. + */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 1)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT_EARLY_EXPIRE, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; + EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, fetch_early_expired_after_ocsp_timestamp, NULL)); + + DEFER_CLEANUP(struct s2n_stuffer ocsp_data_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_EARLY_EXPIRE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), + S2N_ERR_CERT_EXPIRED); + + EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, old_clock, NULL)); + } + + /* Test invalid OCSP date range (thisupdate is off) */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; + s2n_config_set_wall_clock(connection->config, fetch_invalid_before_ocsp_timestamp, NULL); + + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), + S2N_ERR_CERT_INVALID); + + s2n_config_set_wall_clock(connection->config, old_clock, NULL); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + } + + /* Test valid OCSP date range, but the data itself is untrusted */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + + /* flip a byte right in the middle of the cert */ + uint8_t *raw_data = (uint8_t *) s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len); + raw_data[800] = (uint8_t) (raw_data[800] + 1); + + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + raw_data, ocsp_data_len), + S2N_ERR_CERT_UNTRUSTED); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* Test valid OCSP date range and data, but the stapled response was signed with an issuer not in the chain of trust */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_WILDCARD_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK( + s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_WILDCARD_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + + uint8_t *raw_data = (uint8_t *) s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len); + + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + raw_data, ocsp_data_len), + S2N_ERR_CERT_UNTRUSTED); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* Test OCSP response signed by the correct responder certificate, but not for the requested certificate. + * (So this would be a completely valid response to a different OCSP request for the other certificate.) */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_ECDSA_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + + uint8_t *raw_data = (uint8_t *) s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len); + + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + raw_data, ocsp_data_len), + S2N_ERR_CERT_UNTRUSTED); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + } + + /* Test OCSP response signed by the wrong responder certificate, but the requested certificate was signed. + * (however this incorrect OCSP responder certificate is a valid OCSP responder for some other case and chains + * to a trusted root). Thus, this response is not valid for any request. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_ECDSA_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_WRONG_SIGNER_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + + uint8_t *raw_data = (uint8_t *) s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len); + + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + raw_data, ocsp_data_len), + S2N_ERR_CERT_UNTRUSTED); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + } + + /* Test OCSP response status is revoked */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + EXPECT_EQUAL(1, verify_data.callback_invoked); + struct s2n_stuffer ocsp_data_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_REVOKED_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), + S2N_ERR_CERT_REVOKED); + + s2n_stuffer_free(&ocsp_data_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /** + * Test OCSP validation at various offsets from update times. + * + * libcrypto ASN1 comparison calculates differences in terms of days and seconds, + * so try mocking the system time to a collection of more than day & less + * than day differences. + * The T's in the below diagram represent test cases that should fail + * The F's represent test cases that should succeed + * S2N_ERR_CERT_INVALID S2N_ERR_CERT_EXPIRED + * | | | | + * v v v v + * F F T T T T F F + * v v v v v v v v + * <----------|---|---|----------------------------|---|---|---> + * ^ ^ + * this update next update + * |---| + * one day + * + * If this test is failing make sure that the this_update_timestamp_nanoseconds + * matches the actual timestamp of ocsp_response_early_expire.der + * + * openssl ocsp -respin ocsp_response_early_expire.der -text -noverify | grep "This Update" + */ + { + /* Apr 28 22:11:56 2023 GMT */ + uint64_t this_update_timestamp_nanoseconds = (uint64_t) 1682719916 * ONE_SEC_IN_NANOS; + + /* Apr 28 22:11:56 2023 GMT */ + uint64_t next_update_timestamp_nanoseconds = (uint64_t) 2082838316 * ONE_SEC_IN_NANOS; + + uint64_t one_hour_nanoseconds = (uint64_t) 60 * 60 * ONE_SEC_IN_NANOS; + uint64_t one_day_nanoseconds = 24 * one_hour_nanoseconds; + + struct { + uint64_t time; + int result; + } test_cases[] = { + { + .time = this_update_timestamp_nanoseconds - (one_day_nanoseconds + one_hour_nanoseconds), + .result = S2N_ERR_CERT_INVALID, + }, + { + .time = this_update_timestamp_nanoseconds - one_hour_nanoseconds, + .result = S2N_ERR_CERT_INVALID, + }, + { + .time = this_update_timestamp_nanoseconds + one_hour_nanoseconds, + .result = S2N_ERR_OK, + }, + { + .time = this_update_timestamp_nanoseconds + (one_day_nanoseconds + one_hour_nanoseconds), + .result = S2N_ERR_OK, + }, + { + .time = next_update_timestamp_nanoseconds - (one_day_nanoseconds + one_hour_nanoseconds), + .result = S2N_ERR_OK, + }, + { + .time = next_update_timestamp_nanoseconds - one_hour_nanoseconds, + .result = S2N_ERR_OK, + }, + { + .time = next_update_timestamp_nanoseconds + one_hour_nanoseconds, + .result = S2N_ERR_CERT_EXPIRED, + }, + { + .time = next_update_timestamp_nanoseconds + (one_day_nanoseconds + one_hour_nanoseconds), + .result = S2N_ERR_CERT_EXPIRED, + } + }; + + for (int i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_SUCCESS(s2n_x509_trust_store_from_ca_file(&trust_store, S2N_OCSP_CA_CERT, NULL)); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 1)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_OCSP_SERVER_CERT_EARLY_EXPIRE, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + /** + * keep track of the old clock, because we want cert validation to happen + * with the default system clock, and not the "mock_time" clock. + */ + s2n_clock_time_nanoseconds old_clock = connection->config->wall_clock; + uint64_t timestamp_nanoseconds = test_cases[i].time; + EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, mock_time, ×tamp_nanoseconds)); + + DEFER_CLEANUP(struct s2n_stuffer ocsp_data_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_SUCCESS(read_file(&ocsp_data_stuffer, S2N_OCSP_RESPONSE_EARLY_EXPIRE_DER, S2N_MAX_TEST_PEM_SIZE)); + uint32_t ocsp_data_len = s2n_stuffer_data_available(&ocsp_data_stuffer); + EXPECT_TRUE(ocsp_data_len > 0); + + if (test_cases[i].result != S2N_ERR_OK) { + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len), + test_cases[i].result); + } else { + EXPECT_OK(s2n_x509_validator_validate_cert_stapled_ocsp_response(&validator, connection, + s2n_stuffer_raw_read(&ocsp_data_stuffer, ocsp_data_len), ocsp_data_len)); + } + + EXPECT_SUCCESS(s2n_config_set_wall_clock(connection->config, old_clock, NULL)); + }; + }; +#endif /* S2N_OCSP_STAPLING_SUPPORTED */ + /* test validator in safe mode, with default host name validator. Connection server name matches alternative name on a certificate. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_WILDCARD_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + EXPECT_SUCCESS(s2n_set_server_name(connection, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK( + s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_WILDCARD_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with default host name validator. Connection server name matches wildcard alternative name on a certificate. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_WILDCARD_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + EXPECT_SUCCESS(s2n_set_server_name(connection, "test.localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK( + s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_WILDCARD_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with default host name validator. Connection server does not match alternative names on a certificate. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_WILDCARD_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + EXPECT_SUCCESS(s2n_set_server_name(connection, "127.0.0.1")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK( + s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_WILDCARD_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_UNTRUSTED); + + EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* test validator in safe mode, with default host name validator. Connection server matches the IPv6 address on the certificate. */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_IP_V6_LO_RSA_CERT, NULL)); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + s2n_x509_validator_init(&validator, &trust_store, 1); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + + /* the provided hostname should be an empty string */ + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = "::1" }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem( + connection, + S2N_IP_V6_LO_RSA_CERT, + &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + }; + + /* Server matches the empty string when there are no usable identifiers in the cert. */ + { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_WITHOUT_CN_RSA_CERT, NULL)); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + s2n_x509_validator_init(&validator, &trust_store, 1); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + + /* the provided hostname should be an empty string */ + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = "" }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_verify_alt, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem( + connection, + S2N_WITHOUT_CN_RSA_CERT, + &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + }; + + /* test validator in safe mode, with default host name validator. No connection server name supplied. */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_SHA256_WILDCARD_CERT, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK( + s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_SHA256_WILDCARD_CERT, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_UNTRUSTED); + + EXPECT_EQUAL(S2N_PKEY_TYPE_UNKNOWN, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* Test trust store in a configuration can handle invalid PEM without crashing */ + { + struct s2n_config *cfg = s2n_config_new(); + s2n_config_add_pem_to_trust_store(cfg, ""); + s2n_config_free(cfg); + /* Expect no crash. */ + }; + + /* Test one trailing byte in cert validator */ + { + struct s2n_x509_validator validator; + s2n_x509_validator_init_no_x509_validation(&validator); + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct s2n_stuffer chain_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&chain_stuffer, S2N_ONE_TRAILING_BYTE_CERT_BIN, S2N_MAX_TEST_PEM_SIZE)); + uint32_t chain_len = s2n_stuffer_data_available(&chain_stuffer); + EXPECT_TRUE(chain_len > 0); + uint8_t *chain_data = s2n_stuffer_raw_read(&chain_stuffer, chain_len); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + s2n_stuffer_free(&chain_stuffer); + EXPECT_EQUAL(S2N_PKEY_TYPE_RSA, pkey_type); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + }; + + /* Test more trailing bytes in cert validator for negative case */ + { + struct s2n_x509_validator validator; + s2n_x509_validator_init_no_x509_validation(&validator); + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_NOT_NULL(connection); + + struct s2n_stuffer chain_stuffer = { 0 }; + EXPECT_SUCCESS(read_file(&chain_stuffer, S2N_FOUR_TRAILING_BYTE_CERT_BIN, S2N_MAX_TEST_PEM_SIZE)); + uint32_t chain_len = s2n_stuffer_data_available(&chain_stuffer); + EXPECT_TRUE(chain_len > 0); + uint8_t *chain_data = s2n_stuffer_raw_read(&chain_stuffer, chain_len); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + /* Expect to return S2N_CERT_ERR_UNTRUSTED */ + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_UNTRUSTED); + + s2n_stuffer_free(&chain_stuffer); + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + s2n_x509_validator_wipe(&validator); + }; + + /* Test validator trusts a SHA-1 signature in a certificate chain if certificate validation is off */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_PKCS1_CERT_CHAIN, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_config *config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + + EXPECT_NOT_NULL(connection); + connection->actual_protocol_version = S2N_TLS13; + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_PKCS1_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + /* This cert chain includes a SHA1 signature, so the security policy must allow SHA1 cert signatures. */ + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + validator.skip_cert_validation = 1; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, &public_key_out)); + + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_config_free(config); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* Test validator does not trust a SHA-1 signature in a certificate chain */ + { + struct s2n_x509_trust_store trust_store; + s2n_x509_trust_store_init_empty(&trust_store); + EXPECT_EQUAL(0, s2n_x509_trust_store_from_ca_file(&trust_store, S2N_RSA_2048_PKCS1_CERT_CHAIN, NULL)); + + struct s2n_x509_validator validator; + s2n_x509_validator_init(&validator, &trust_store, 1); + + struct s2n_config *config = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default_tls13")); + + struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT); + EXPECT_SUCCESS(s2n_connection_set_config(connection, config)); + + EXPECT_NOT_NULL(connection); + connection->actual_protocol_version = S2N_TLS13; + + struct host_verify_data verify_data = { .callback_invoked = 0, .found_name = 0, .name = NULL }; + EXPECT_SUCCESS(s2n_connection_set_verify_host_callback(connection, verify_host_accept_everything, &verify_data)); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, S2N_RSA_2048_PKCS1_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + struct s2n_pkey public_key_out; + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_ERROR_WITH_ERRNO(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, + &pkey_type, &public_key_out), + S2N_ERR_CERT_UNTRUSTED); + + s2n_connection_free(connection); + s2n_pkey_free(&public_key_out); + + s2n_config_free(config); + s2n_x509_validator_wipe(&validator); + s2n_x509_trust_store_wipe(&trust_store); + }; + + /* Test trust store can be wiped */ + { + /* Wipe new s2n_config, which is initialized with certs from the system default locations. */ + { + struct s2n_config *cfg = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + s2n_config_free(cfg); + }; + + /* Wipe repeatedly without crash */ + { + struct s2n_config *cfg = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); + EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + s2n_config_free(cfg); + }; + + /* Wipe after setting verification location */ + { + struct s2n_config *cfg = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(cfg, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_TRUE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + + EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + s2n_config_free(cfg); + }; + + /* Set verification location after wipe */ + { + struct s2n_config *cfg = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(cfg, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_TRUE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + s2n_config_free(cfg); + }; + + /* Wipe after adding PEM */ + { + struct s2n_config *cfg = s2n_config_new(); + char *cert_chain = NULL; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(cfg, cert_chain)); + EXPECT_TRUE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + + EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + free(cert_chain); + s2n_config_free(cfg); + }; + + /* Add PEM after wipe */ + { + struct s2n_config *cfg = s2n_config_new(); + EXPECT_SUCCESS(s2n_config_wipe_trust_store(cfg)); + EXPECT_FALSE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + + char *cert_chain = NULL; + EXPECT_NOT_NULL(cert_chain = malloc(S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(cfg, cert_chain)); + EXPECT_TRUE(s2n_x509_trust_store_has_certs(&cfg->trust_store)); + free(cert_chain); + s2n_config_free(cfg); + }; + }; + + /* Ensure that non-root certificates added to the trust store are trusted */ + { + const char *non_root_cert_path = S2N_RSA_2048_PKCS1_LEAF_CERT; + +#if S2N_OPENSSL_VERSION_AT_LEAST(1, 1, 0) + /* Ensure that the test certificate isn't self-signed, and is therefore not a root. + * + * The X509_get_extension_flags API wasn't added to OpenSSL until 1.1.0. + */ + { + const char *non_root_key_path = S2N_RSA_2048_PKCS1_KEY; + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, non_root_cert_path, non_root_key_path)); + struct s2n_cert *cert = NULL; + EXPECT_SUCCESS(s2n_cert_chain_get_cert(chain_and_key, &cert, 0)); + EXPECT_NOT_NULL(cert); + + /* Use the s2n_cert to convert the PEM to ASN.1. */ + const uint8_t *asn1_data = NULL; + uint32_t asn1_len = 0; + EXPECT_SUCCESS(s2n_cert_get_der(cert, &asn1_data, &asn1_len)); + EXPECT_NOT_NULL(asn1_data); + + /* Parse the ASN.1 data with the libcrypto */ + DEFER_CLEANUP(X509 *x509 = d2i_X509(NULL, &asn1_data, asn1_len), X509_free_pointer); + EXPECT_NOT_NULL(x509); + + /* Ensure that the self-signed flag isn't set */ + uint32_t extension_flags = X509_get_extension_flags(x509); + EXPECT_EQUAL(extension_flags & EXFLAG_SS, 0); + } +#endif + + /* Test s2n_config_set_verification_ca_location */ + { + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, non_root_cert_path, NULL)); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &config->trust_store, 0)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); + EXPECT_SUCCESS(s2n_set_server_name(connection, "s2nTestServer")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, non_root_cert_path, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, + &public_key_out)); + } + + /* Test s2n_config_add_pem_to_trust_store */ + { + char non_root_cert_pem[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(non_root_cert_path, non_root_cert_pem, S2N_MAX_TEST_PEM_SIZE)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_SUCCESS(s2n_config_add_pem_to_trust_store(config, non_root_cert_pem)); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &config->trust_store, 0)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); + EXPECT_SUCCESS(s2n_set_server_name(connection, "s2nTestServer")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, non_root_cert_path, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, + &public_key_out)); + } + + /* Test system trust store + * + * This test uses the SSL_CERT_FILE environment variable to override the system trust store + * location, which isn't supported by LibreSSL. + */ + if (!s2n_libcrypto_is_libressl()) { + /* Override the system cert file with the non-root test cert. */ + EXPECT_SUCCESS(setenv("SSL_CERT_FILE", non_root_cert_path, 1)); + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + + DEFER_CLEANUP(struct s2n_x509_validator validator = { 0 }, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &config->trust_store, 0)); + + DEFER_CLEANUP(struct s2n_connection *connection = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(connection); + EXPECT_SUCCESS(s2n_connection_set_cipher_preferences(connection, "default")); + EXPECT_SUCCESS(s2n_set_server_name(connection, "s2nTestServer")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(connection, non_root_cert_path, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + EXPECT_OK(s2n_x509_validator_validate_cert_chain(&validator, connection, chain_data, chain_len, &pkey_type, + &public_key_out)); + + EXPECT_SUCCESS(unsetenv("SSL_CERT_FILE")); + } + } + + END_TEST(); +} diff --git a/tests/unit/s2n_x509_validator_time_verification_test.c b/tests/unit/s2n_x509_validator_time_verification_test.c new file mode 100644 index 00000000000..3abefac35a2 --- /dev/null +++ b/tests/unit/s2n_x509_validator_time_verification_test.c @@ -0,0 +1,285 @@ +/* +* 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 "s2n_test.h" +#include "testlib/s2n_testlib.h" + +bool s2n_libcrypto_supports_flag_no_check_time(); +uint64_t s2n_libcrypto_awslc_api_version(void); + +static uint8_t s2n_verify_host_accept_everything(const char *host_name, size_t host_name_len, void *data) +{ + return 1; +} + +int main(int argc, char *argv[]) +{ + BEGIN_TEST(); + + /* Test the NO_CHECK_TIME flag feature probe. The flag was added to AWS-LC in API version 19. */ + if (s2n_libcrypto_is_awslc() && s2n_libcrypto_awslc_api_version() > 19) { + EXPECT_TRUE(s2n_libcrypto_supports_flag_no_check_time()); + } + + /* Test disabling x509 time validation. + * + * By default, validation should fail for certificates with invalid timestamps. However, if + * x509 time validation is disabled, validation should succeed. + * + * When time validation is disabled, s2n_config_set_wall_clock() will not set a custom time on + * the libcrypto, so this function cannot be used to set a fake time for testing. Instead, the + * test certificates themselves contain invalid timestamps. + */ + { + /* clang-format off */ + struct { + const char *cert_pem_path; + const char *key_pem_path; + bool disable_x509_time_validation; + s2n_error expected_error; + } test_cases[] = { + /* Validation should fail for a certificate that is not yet valid. */ + { + .cert_pem_path = S2N_NOT_YET_VALID_CERT_CHAIN, + .key_pem_path = S2N_NOT_YET_VALID_KEY, + .disable_x509_time_validation = false, + .expected_error = S2N_ERR_CERT_NOT_YET_VALID, + }, + + /* Validation should succeed for a certificate that is not yet valid when time + * validation is disabled. + */ + { + .cert_pem_path = S2N_NOT_YET_VALID_CERT_CHAIN, + .key_pem_path = S2N_NOT_YET_VALID_KEY, + .disable_x509_time_validation = true, + .expected_error = S2N_ERR_OK, + }, + + /* Validation should fail for an expired certificate. */ + { + .cert_pem_path = S2N_EXPIRED_CERT_CHAIN, + .key_pem_path = S2N_EXPIRED_KEY, + .disable_x509_time_validation = false, + .expected_error = S2N_ERR_CERT_EXPIRED, + }, + + /* Validation should succeed for an expired certificate when time validation is + * disabled. + */ + { + .cert_pem_path = S2N_EXPIRED_CERT_CHAIN, + .key_pem_path = S2N_EXPIRED_KEY, + .disable_x509_time_validation = true, + .expected_error = S2N_ERR_OK, + }, + }; + /* clang-format on */ + + /* s2n_x509_validator test */ + for (int i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + char cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(test_cases[i].cert_pem_path, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, cert_chain)); + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + + if (test_cases[i].disable_x509_time_validation) { + EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(config)); + } + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_set_server_name(conn, "localhost")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(conn, test_cases[i].cert_pem_path, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + s2n_result ret = s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, &pkey_type, + &public_key_out); + + s2n_error expected_error = test_cases[i].expected_error; + if (expected_error == S2N_ERR_OK) { + EXPECT_OK(ret); + } else { + EXPECT_ERROR_WITH_ERRNO(ret, expected_error); + } + } + + /* Self-talk: Disable validity period validation on client for server auth */ + for (int i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + test_cases[i].cert_pem_path, test_cases[i].key_pem_path)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(config, test_cases[i].cert_pem_path, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + + if (test_cases[i].disable_x509_time_validation) { + EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(config)); + } + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, config)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(client_conn, S2N_SELF_SERVICE_BLINDING)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "localhost")); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + int ret = s2n_negotiate_test_server_and_client(server_conn, client_conn); + + s2n_error expected_error = test_cases[i].expected_error; + if (expected_error == S2N_ERR_OK) { + EXPECT_SUCCESS(ret); + } else { + EXPECT_FAILURE_WITH_ERRNO(ret, expected_error); + } + } + + /* Self-talk: Disable validity period validation on server for client auth */ + for (int i = 0; i < s2n_array_len(test_cases); i++) { + DEFER_CLEANUP(struct s2n_cert_chain_and_key *default_chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&default_chain_and_key, + S2N_DEFAULT_TEST_CERT_CHAIN, S2N_DEFAULT_TEST_PRIVATE_KEY)); + + DEFER_CLEANUP(struct s2n_config *server_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(server_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(server_config, default_chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(server_config, test_cases[i].cert_pem_path, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(server_config, "default")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(server_config, S2N_CERT_AUTH_REQUIRED)); + + if (test_cases[i].disable_x509_time_validation) { + EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(server_config)); + } + + /* Disable verify host validation for client auth */ + EXPECT_SUCCESS(s2n_config_set_verify_host_callback(server_config, s2n_verify_host_accept_everything, NULL)); + + DEFER_CLEANUP(struct s2n_connection *server_conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_NOT_NULL(server_conn); + EXPECT_SUCCESS(s2n_connection_set_config(server_conn, server_config)); + EXPECT_SUCCESS(s2n_connection_set_blinding(server_conn, S2N_SELF_SERVICE_BLINDING)); + + DEFER_CLEANUP(struct s2n_cert_chain_and_key *chain_and_key = NULL, s2n_cert_chain_and_key_ptr_free); + EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&chain_and_key, + test_cases[i].cert_pem_path, test_cases[i].key_pem_path)); + + DEFER_CLEANUP(struct s2n_config *client_config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(client_config); + EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(client_config, chain_and_key)); + EXPECT_SUCCESS(s2n_config_set_verification_ca_location(client_config, S2N_DEFAULT_TEST_CERT_CHAIN, NULL)); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(client_config, "default")); + EXPECT_SUCCESS(s2n_config_set_client_auth_type(client_config, S2N_CERT_AUTH_OPTIONAL)); + + DEFER_CLEANUP(struct s2n_connection *client_conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(client_conn); + EXPECT_SUCCESS(s2n_connection_set_config(client_conn, client_config)); + EXPECT_SUCCESS(s2n_set_server_name(client_conn, "s2nTestServer")); + + DEFER_CLEANUP(struct s2n_test_io_pair io_pair = { 0 }, s2n_io_pair_close); + EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(client_conn, &io_pair)); + EXPECT_SUCCESS(s2n_connection_set_io_pair(server_conn, &io_pair)); + + int ret = s2n_negotiate_test_server_and_client(server_conn, client_conn); + + s2n_error expected_error = test_cases[i].expected_error; + if (expected_error == S2N_ERR_OK) { + EXPECT_SUCCESS(ret); + } else { + EXPECT_FAILURE_WITH_ERRNO(ret, expected_error); + } + } + } + + /* Ensure that certificate validation can fail for reasons other than time validation when time + * validation is disabled. + */ + for (int trust_cert = 0; trust_cert <= 1; trust_cert += 1) { + DEFER_CLEANUP(struct s2n_x509_trust_store trust_store = { 0 }, s2n_x509_trust_store_wipe); + s2n_x509_trust_store_init_empty(&trust_store); + + if (trust_cert) { + char cert_chain[S2N_MAX_TEST_PEM_SIZE] = { 0 }; + EXPECT_SUCCESS(s2n_read_test_pem(S2N_DEFAULT_TEST_CERT_CHAIN, cert_chain, S2N_MAX_TEST_PEM_SIZE)); + EXPECT_SUCCESS(s2n_x509_trust_store_add_pem(&trust_store, cert_chain)); + } + + DEFER_CLEANUP(struct s2n_x509_validator validator, s2n_x509_validator_wipe); + EXPECT_SUCCESS(s2n_x509_validator_init(&validator, &trust_store, 0)); + + DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free); + EXPECT_NOT_NULL(config); + EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "default")); + EXPECT_SUCCESS(s2n_config_disable_x509_time_verification(config)); + + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free); + EXPECT_NOT_NULL(conn); + EXPECT_SUCCESS(s2n_connection_set_config(conn, config)); + EXPECT_SUCCESS(s2n_set_server_name(conn, "s2nTestServer")); + + DEFER_CLEANUP(struct s2n_stuffer cert_chain_stuffer = { 0 }, s2n_stuffer_free); + EXPECT_OK(s2n_test_cert_chain_data_from_pem(conn, S2N_DEFAULT_TEST_CERT_CHAIN, &cert_chain_stuffer)); + uint32_t chain_len = s2n_stuffer_data_available(&cert_chain_stuffer); + uint8_t *chain_data = s2n_stuffer_raw_read(&cert_chain_stuffer, chain_len); + EXPECT_NOT_NULL(chain_data); + + DEFER_CLEANUP(struct s2n_pkey public_key_out = { 0 }, s2n_pkey_free); + EXPECT_SUCCESS(s2n_pkey_zero_init(&public_key_out)); + s2n_pkey_type pkey_type = S2N_PKEY_TYPE_UNKNOWN; + + s2n_result ret = s2n_x509_validator_validate_cert_chain(&validator, conn, chain_data, chain_len, &pkey_type, + &public_key_out); + + if (trust_cert) { + EXPECT_OK(ret); + } else { + /* If the certificate was not added to the trust store, validation should fail even + * though time validation was disabled. + */ + EXPECT_ERROR_WITH_ERRNO(ret, S2N_ERR_CERT_UNTRUSTED); + } + } + + END_TEST(); +}