diff --git a/api/s2n.h b/api/s2n.h index 6bce51c0342..43ad5e085ce 100644 --- a/api/s2n.h +++ b/api/s2n.h @@ -1882,6 +1882,30 @@ S2N_API extern uint64_t s2n_connection_get_delay(struct s2n_connection *conn); */ S2N_API extern int s2n_connection_set_cipher_preferences(struct s2n_connection *conn, const char *version); +/** + * Used to indicate the type of key update that is being requested. For further + * information refer to `s2n_connection_request_key_update`. +*/ +typedef enum { + S2N_KEY_UPDATE_NOT_REQUESTED = 0, + S2N_KEY_UPDATE_REQUESTED +} s2n_peer_key_update; + +/** + * Signals the connection to do a key_update at the next possible opportunity. Note that the resulting key update message + * will not be sent until `s2n_send` is called. + * + * @param conn The connection object to trigger the key update on. + * @param peer_request Indicates if a key update should also be requested + * of the peer. When set to `S2N_KEY_UPDATE_NOT_REQUESTED`, then only the sending + * key of `conn` will be updated. If set to `S2N_KEY_UPDATE_REQUESTED`, then + * the sending key of conn will be updated AND the peer will be requested to + * update their sending key. Note that s2n-tls currently only supports + * `peer_request` being set to `S2N_KEY_UPDATE_NOT_REQUESTED` and will return + * S2N_FAILURE if any other value is used. + * @returns S2N_SUCCESS on success. S2N_FAILURE on failure +*/ +S2N_API extern int s2n_connection_request_key_update(struct s2n_connection *conn, s2n_peer_key_update peer_request); /** * Appends the provided application protocol to the preference list * diff --git a/tests/unit/s2n_key_update_test.c b/tests/unit/s2n_key_update_test.c index 23955cdd2a8..be8bf8b1874 100644 --- a/tests/unit/s2n_key_update_test.c +++ b/tests/unit/s2n_key_update_test.c @@ -333,7 +333,7 @@ int main(int argc, char **argv) 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); + EXPECT_SUCCESS(s2n_connection_request_key_update(client_conn, S2N_KEY_UPDATE_NOT_REQUESTED)); s2n_blocked_status blocked = 0; EXPECT_SUCCESS(s2n_key_update_send(client_conn, &blocked)); @@ -677,5 +677,27 @@ int main(int argc, char **argv) } } + /* s2n_connection_key_update_requested */ + { + /* null safety */ + { + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_request_key_update(NULL, S2N_KEY_UPDATE_NOT_REQUESTED), S2N_ERR_NULL); + }; + + /* usage */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_FAILURE_WITH_ERRNO(s2n_connection_request_key_update(conn, S2N_KEY_UPDATE_REQUESTED), S2N_ERR_INVALID_ARGUMENT); + }; + + /* happy path */ + { + DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER), s2n_connection_ptr_free); + EXPECT_FALSE(s2n_atomic_flag_test(&conn->key_update_pending)); + EXPECT_SUCCESS(s2n_connection_request_key_update(conn, S2N_KEY_UPDATE_NOT_REQUESTED)); + EXPECT_TRUE(s2n_atomic_flag_test(&conn->key_update_pending)); + }; + }; + END_TEST(); } diff --git a/tests/unit/s2n_key_update_threads_test.c b/tests/unit/s2n_key_update_threads_test.c index 0a7ef4dd4c0..d54807c6587 100644 --- a/tests/unit/s2n_key_update_threads_test.c +++ b/tests/unit/s2n_key_update_threads_test.c @@ -35,7 +35,7 @@ _##name##_record_alg.encryption_limit = limit; \ name.record_alg = &_##name##_record_alg; -S2N_RESULT s2n_set_key_update_request_for_testing(keyupdate_request request); +S2N_RESULT s2n_set_key_update_request_for_testing(s2n_peer_key_update request); static void *s2n_send_random_data(void *arg) { diff --git a/tls/s2n_key_update.c b/tls/s2n_key_update.c index d10d7c5b244..90c49c9756e 100644 --- a/tls/s2n_key_update.c +++ b/tls/s2n_key_update.c @@ -24,12 +24,12 @@ #include "utils/s2n_atomic.h" #include "utils/s2n_safety.h" -static keyupdate_request key_update_request_val = S2N_KEY_UPDATE_NOT_REQUESTED; +static s2n_peer_key_update key_update_request_val = S2N_KEY_UPDATE_NOT_REQUESTED; int s2n_key_update_write(struct s2n_blob *out); int s2n_check_record_limit(struct s2n_connection *conn, struct s2n_blob *sequence_number); -S2N_RESULT s2n_set_key_update_request_for_testing(keyupdate_request request) +S2N_RESULT s2n_set_key_update_request_for_testing(s2n_peer_key_update request) { RESULT_ENSURE(s2n_in_unit_test(), S2N_ERR_NOT_IN_UNIT_TEST); key_update_request_val = request; @@ -146,3 +146,12 @@ int s2n_check_record_limit(struct s2n_connection *conn, struct s2n_blob *sequenc return S2N_SUCCESS; } + +int s2n_connection_request_key_update(struct s2n_connection *conn, s2n_peer_key_update peer_request) +{ + POSIX_ENSURE_REF(conn); + /* s2n-tls does not currently support requesting key updates from peers */ + POSIX_ENSURE(peer_request == S2N_KEY_UPDATE_NOT_REQUESTED, S2N_ERR_INVALID_ARGUMENT); + s2n_atomic_flag_set(&conn->key_update_pending); + return S2N_SUCCESS; +} diff --git a/tls/s2n_key_update.h b/tls/s2n_key_update.h index 2b787f08487..79a103d8261 100644 --- a/tls/s2n_key_update.h +++ b/tls/s2n_key_update.h @@ -25,10 +25,5 @@ typedef enum { RECEIVING } keyupdate_status; -typedef enum { - S2N_KEY_UPDATE_NOT_REQUESTED = 0, - S2N_KEY_UPDATE_REQUESTED -} keyupdate_request; - int s2n_key_update_recv(struct s2n_connection *conn, struct s2n_stuffer *request); int s2n_key_update_send(struct s2n_connection *conn, s2n_blocked_status *blocked);