Skip to content

Commit

Permalink
feat: Configurable blinding (#4562)
Browse files Browse the repository at this point in the history
  • Loading branch information
maddeleine authored Jun 3, 2024
1 parent 3c9a802 commit 14c0ad5
Show file tree
Hide file tree
Showing 7 changed files with 159 additions and 3 deletions.
26 changes: 26 additions & 0 deletions api/s2n.h
Original file line number Diff line number Diff line change
Expand Up @@ -1973,6 +1973,32 @@ S2N_API extern int s2n_connection_set_blinding(struct s2n_connection *conn, s2n_
*/
S2N_API extern uint64_t s2n_connection_get_delay(struct s2n_connection *conn);

/**
* Configures the maximum blinding delay enforced after errors.
*
* Blinding protects your application from timing side channel attacks like Lucky13. While s2n-tls
* implements other, more specific mitigations for known timing side channels, blinding is important
* as a defense against currently unknown or unreported timing attacks.
*
* Setting a maximum delay lower than the recommended default (30s) will make timing attacks against
* your application easier. The lower you set the delay, the fewer requests and less total time an
* attacker will require to execute an attack. If you must lower the delay for reasons such as client
* timeouts, then you should choose the highest value practically possible to limit your risk.
*
* If you lower the blinding delay, you should also consider implementing monitoring and filtering
* to detect and reject suspicious traffic that could be gathering timing information from a potential
* side channel. Timing attacks usually involve repeatedly triggering TLS errors.
*
* @warning Do NOT set a lower blinding delay unless you understand the risks and have other
* mitigations for timing side channels in place.
*
* @param config The config object being updated.
* @param seconds The maximum number of seconds that s2n-tls will delay for in the event of a
* sensitive error.
* @returns S2N_SUCCESS on success. S2N_FAILURE on failure.
*/
S2N_API extern int s2n_config_set_max_blinding_delay(struct s2n_config *config, uint32_t seconds);

/**
* Sets the cipher preference override for the s2n_connection. Calling this function is not necessary
* unless you want to set the cipher preferences on the connection to something different than what is in the s2n_config.
Expand Down
6 changes: 6 additions & 0 deletions bindings/rust/s2n-tls/src/config.rs
Original file line number Diff line number Diff line change
Expand Up @@ -746,6 +746,12 @@ impl Builder {
Ok(self)
}

/// Sets a configurable blinding delay instead of the default
pub fn set_max_blinding_delay(&mut self, seconds: u32) -> Result<&mut Self, Error> {
unsafe { s2n_config_set_max_blinding_delay(self.as_mut_ptr(), seconds).into_result() }?;
Ok(self)
}

pub fn build(mut self) -> Result<Config, Error> {
if self.load_system_certs {
unsafe {
Expand Down
67 changes: 67 additions & 0 deletions tests/unit/s2n_connection_blinding_test.c
Original file line number Diff line number Diff line change
@@ -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 "s2n_test.h"
#include "testlib/s2n_testlib.h"

#define ONE_S INT64_C(1000000000)

S2N_RESULT s2n_connection_calculate_blinding(struct s2n_connection *conn, int64_t *min, int64_t *max);

int main(int argc, char **argv)
{
BEGIN_TEST();

struct {
uint32_t custom_blinding;
uint64_t expected_min;
uint64_t expected_max;
} test_cases[] = {
{ .custom_blinding = 0, .expected_min = 0, .expected_max = 0 },
{ .custom_blinding = 1, .expected_min = 333333333, .expected_max = 1000000000 },
{ .custom_blinding = 3, .expected_min = 1000000000, .expected_max = 3000000000 },
{ .custom_blinding = 30, .expected_min = S2N_DEFAULT_BLINDING_MIN * ONE_S, .expected_max = S2N_DEFAULT_BLINDING_MAX * ONE_S },
{ .custom_blinding = UINT32_MAX, .expected_min = 1431655765000000000, .expected_max = 4294967295000000000 },
};

/* s2n_connection_calculate_blinding */
{
DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_CLIENT), s2n_connection_ptr_free);
EXPECT_NOT_NULL(conn);
EXPECT_NOT_NULL(conn->config);

int64_t min = 0;
int64_t max = 0;

/* The default max blinding delay is 10-30 seconds */
EXPECT_OK(s2n_connection_calculate_blinding(conn, &min, &max));
EXPECT_EQUAL(min, S2N_DEFAULT_BLINDING_MIN * ONE_S);
EXPECT_EQUAL(max, S2N_DEFAULT_BLINDING_MAX * ONE_S);

for (size_t i = 0; i < s2n_array_len(test_cases); i++) {
EXPECT_SUCCESS(s2n_config_set_max_blinding_delay(conn->config, test_cases[i].custom_blinding));

min = 0;
max = 0;
EXPECT_OK(s2n_connection_calculate_blinding(conn, &min, &max));

EXPECT_EQUAL(min, test_cases[i].expected_min);
EXPECT_EQUAL(max, test_cases[i].expected_max);
}
}

END_TEST();
return 0;
}
10 changes: 10 additions & 0 deletions tls/s2n_config.c
Original file line number Diff line number Diff line change
Expand Up @@ -1238,3 +1238,13 @@ int s2n_config_set_serialization_version(struct s2n_config *config, s2n_serializ

return S2N_SUCCESS;
}

int s2n_config_set_max_blinding_delay(struct s2n_config *config, uint32_t seconds)
{
POSIX_ENSURE_REF(config);

config->custom_blinding_set = 1;
config->max_blinding = seconds;

return S2N_SUCCESS;
}
4 changes: 4 additions & 0 deletions tls/s2n_config.h
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,8 @@ struct s2n_config {
/* TLS1.3 can be dangerous with kTLS. Require it to be explicitly enabled. */
unsigned ktls_tls13_enabled : 1;

unsigned custom_blinding_set : 1;

struct s2n_dh_params *dhparams;
/* Needed until we can deprecate s2n_config_add_cert_chain_and_key. This is
* used to release memory allocated only in the deprecated API that the application
Expand All @@ -123,6 +125,8 @@ struct s2n_config {
s2n_client_hello_fn *client_hello_cb;
s2n_client_hello_cb_mode client_hello_cb_mode;

uint32_t max_blinding;

void *client_hello_cb_ctx;

uint64_t session_state_lifetime_in_nanos;
Expand Down
46 changes: 43 additions & 3 deletions tls/s2n_connection.c
Original file line number Diff line number Diff line change
Expand Up @@ -1153,7 +1153,6 @@ int s2n_connection_set_blinding(struct s2n_connection *conn, s2n_blinding blindi
}

#define ONE_S INT64_C(1000000000)
#define TEN_S INT64_C(10000000000)

static S2N_RESULT s2n_connection_get_delay_impl(struct s2n_connection *conn, uint64_t *delay)
{
Expand Down Expand Up @@ -1188,13 +1187,54 @@ uint64_t s2n_connection_get_delay(struct s2n_connection *conn)
}
}

/* s2n-tls has a random delay that will trigger for sensitive errors. This is a mitigation
* for possible timing sidechannels.
*
* The historical sidechannel that inspired s2n-tls blinding was the Lucky 13 attack, which takes
* advantage of potential timing differences when removing padding from a record encrypted in CBC mode.
* The attack is only theoretical in TLS; the attack criteria is unlikely to ever occur
* (See: Fardan, N. J. A., & Paterson, K. G. (2013, May 1). Lucky Thirteen: Breaking the TLS and
* DTLS Record Protocols.) However, we still include blinding to provide a defense in depth mitigation.
*/
S2N_RESULT s2n_connection_calculate_blinding(struct s2n_connection *conn, int64_t *min, int64_t *max)
{
RESULT_ENSURE_REF(conn);
RESULT_ENSURE_REF(min);
RESULT_ENSURE_REF(max);
RESULT_ENSURE_REF(conn->config);

/*
* The default delay is a random value between 10-30s. The rational behind the range is that the
* floor is the fixed cost that an attacker must pay per attempt, in this case, 10s. The length of
* the range then affects the number of attempts that an attacker must perform in order to recover a
* byte of plaintext with a certain degree of confidence.
*
* A uniform distribution of the range [a, b] has a variance of ((b - a)^2)/12. Therefore, given a
* hypothetical timing difference of 1us, the number of attempts necessary to distinguish the correct
* byte from an incorrect byte in a Lucky13-style attack is (((30 - 10) * 10 ^6)^2)/12 ~= 3.3 trillion
* (note that we first have to convert from seconds to microseconds to match the unit of the timing difference.)
*/
*min = S2N_DEFAULT_BLINDING_MIN * ONE_S;
*max = S2N_DEFAULT_BLINDING_MAX * ONE_S;

/* Setting the min to 1/3 of the max is an arbitrary ratio of fixed to variable delay.
* It is based on the ratio of our original default values.
*/
if (conn->config->custom_blinding_set) {
*max = conn->config->max_blinding * ONE_S;
*min = *max / 3;
}

return S2N_RESULT_OK;
}

static S2N_RESULT s2n_connection_kill(struct s2n_connection *conn)
{
RESULT_ENSURE_REF(conn);
RESULT_GUARD(s2n_connection_set_closed(conn));

/* Delay between 10 and 30 seconds in nanoseconds */
int64_t min = TEN_S, max = 3 * TEN_S;
int64_t min = 0, max = 0;
RESULT_GUARD(s2n_connection_calculate_blinding(conn, &min, &max));

/* Keep track of the delay so that it can be enforced */
uint64_t rand_delay = 0;
Expand Down
3 changes: 3 additions & 0 deletions tls/s2n_connection.h
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,9 @@

#define is_handshake_complete(conn) (APPLICATION_DATA == s2n_conn_get_current_message_type(conn))

#define S2N_DEFAULT_BLINDING_MAX 30
#define S2N_DEFAULT_BLINDING_MIN 10

typedef enum {
S2N_NO_TICKET = 0,
S2N_DECRYPT_TICKET,
Expand Down

0 comments on commit 14c0ad5

Please sign in to comment.