Repository: aws/s2n-tls (C)

Issue:
TLS1.3 handshakes fail if no encrypt/decrypt keys are available when session resumption is on
### Problem:

TLS1.3 handshakes fail with `S2N_ERR_NO_TICKET_ENCRYPT_DECRYPT_KEY` if no encrypt/decrypt keys are available when session resumption is turned on in s2n config. This is different from TLS1.2 which just avoids issuing a session ticket and has a risk to the service if key rotation stalls for any reason.

### Solution:

Similar to TLS1.2, the TLS1.3 codepath should check if no encrypt/decrypt keys are available and just avoid issuing session ticket instead of failing the handshake.

* **Does this change what S2N sends over the wire?** No
* **Does this change any public APIs?** No
* **Which versions of TLS will this impact?** TLS1.3

### Requirements / Acceptance Criteria:

What must a solution address in order to solve the problem? How do we know the solution is complete?

* **RFC links:** Links to relevant RFC(s)
* **Related Issues:** Link any relevant issues
* **Will the Usage Guide or other documentation need to be updated?**
* **Testing:** How will this change be tested? Call out new integration tests, functional tests, or particularly interesting/important unit tests.
  * **Will this change trigger SAW changes?** Changes to the state machine, the s2n_handshake_io code that controls state transitions, the DRBG, or the corking/uncorking logic could trigger SAW failures.
  * **Should this change be fuzz tested?** Will it handle untrusted input? Create a separate issue to track the fuzzing work.

### Out of scope:

Is there anything the solution will intentionally NOT address?

[//]: #  (NOTE: If you believe this might be a security issue, please email aws-security@amazon.com instead of creating a GitHub issue. For more details, see the AWS Vulnerability Reporting Guide: https://aws.amazon.com/security/vulnerability-reporting/ )



Discussion:
For reference, the TLS1.2 code uses `s2n_config_is_encrypt_decrypt_key_available` function to check the existence of encrypt/decrypt ticket before deciding to issue NST.



Patch:
diff --git a/tls/s2n_server_new_session_ticket.c b/tls/s2n_server_new_session_ticket.c
index 9f15644aeca..abbee21ff89 100644
--- a/tls/s2n_server_new_session_ticket.c
+++ b/tls/s2n_server_new_session_ticket.c
@@ -138,7 +138,9 @@ S2N_RESULT s2n_tls13_server_nst_send(struct s2n_connection *conn, s2n_blocked_st
     RESULT_GUARD_POSIX(s2n_stuffer_growable_alloc(&nst_stuffer, maximum_nst_size));
 
     while (conn->tickets_to_send - conn->tickets_sent > 0) {
-        RESULT_GUARD(s2n_tls13_server_nst_write(conn, &nst_stuffer));
+        if (s2n_result_is_error(s2n_tls13_server_nst_write(conn, &nst_stuffer))) {
+            return S2N_RESULT_OK;
+        }
 
         struct s2n_blob nst_blob = { 0 };
         uint16_t nst_size = s2n_stuffer_data_available(&nst_stuffer);


Test patch:
diff --git a/tests/unit/s2n_self_talk_session_resumption_test.c b/tests/unit/s2n_self_talk_session_resumption_test.c
index 8da67eb1764..ceae9f22dc1 100644
--- a/tests/unit/s2n_self_talk_session_resumption_test.c
+++ b/tests/unit/s2n_self_talk_session_resumption_test.c
@@ -664,6 +664,42 @@ int main(int argc, char **argv)
         EXPECT_SUCCESS(s2n_connection_free(client_conn));
     }
 
+    /* If the server has no ticket key, no session ticket is issued. */
+    {
+        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);
+
+        /* Setup config without session ticket key */
+        struct s2n_config *no_key_config = s2n_config_new();
+        EXPECT_NOT_NULL(no_key_config);
+        EXPECT_SUCCESS(s2n_config_set_cipher_preferences(no_key_config, "default_tls13"));
+        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_add_cert_chain_and_key_to_store(no_key_config, tls12_chain_and_key));
+        EXPECT_SUCCESS(s2n_config_set_session_tickets_onoff(no_key_config, true));
+
+        EXPECT_SUCCESS(s2n_connection_set_config(server_conn, no_key_config));
+        EXPECT_SUCCESS(s2n_connection_set_config(client_conn, tls13_client_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));
+
+        EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server_conn, client_conn));
+
+        /* Server never sent a ticket to the client */
+        EXPECT_EQUAL(s2n_connection_get_session_length(client_conn), 0);
+
+        EXPECT_SUCCESS(s2n_io_pair_close(&io_pair));
+        EXPECT_SUCCESS(s2n_config_free(no_key_config));
+        EXPECT_SUCCESS(s2n_connection_free(server_conn));
+        EXPECT_SUCCESS(s2n_connection_free(client_conn));
+    }
+
     /* Clean-up */
     EXPECT_SUCCESS(s2n_config_free(server_config));
     EXPECT_SUCCESS(s2n_config_free(tls13_client_config));