Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Configurable blinding #4562

Merged
merged 18 commits into from
Jun 3, 2024
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 for 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
69 changes: 69 additions & 0 deletions tests/unit/s2n_connection_blinding_test.c
Original file line number Diff line number Diff line change
@@ -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 "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 = ONE_S / 3, .expected_max = ONE_S },
{ .custom_blinding = 3, .expected_min = (3 * ONE_S) / 3, .expected_max = 3 * ONE_S },
{ .custom_blinding = 5, .expected_min = (5 * ONE_S) / 3, .expected_max = 5 * ONE_S },
{ .custom_blinding = 10, .expected_min = (10 * ONE_S) / 3, .expected_max = 10 * ONE_S },
{ .custom_blinding = 15, .expected_min = (15 * ONE_S) / 3, .expected_max = 15 * ONE_S },
{ .custom_blinding = 100, .expected_min = (100 * ONE_S) / 3, .expected_max = 100 * ONE_S },
};

/* 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_FLOOR * ONE_S);
EXPECT_EQUAL(max, S2N_DEFAULT_BLINDING_CEILING * 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
47 changes: 44 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,55 @@ 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 recover a byte is
* (((30 - 10) * 10 ^6)^2)/12 ~= 3.3 trillion (note that we first have to convert from seconds to
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What does recovering a byte mean here? I would have assumed the calculation would be in terms of number of samples required to observe a timing difference. Or is this description specifically related to lucky 13?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's the number of samples required to distinguish the timing distributions from a correctly guessed byte versus incorrectly guessed bytes. In lucky13 in particular, if you've correctly guessed the last byte of padding, the timing distribution that is created from sampling that byte would be slightly different than the timing distributions of an incorrect byte.
😮‍💨 So hard to describe this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think I could add something to make this paragraph more clear?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No I think it makes sense. I guess I just wasn't sure if this same description could be applied to all sidechannels or just lucky 13. Like, could there be a timing difference that could be exploited without the goal of differentiating a correct byte from an incorrect byte? If so, then it might help to clarify that this example of recovering bytes is specific to lucky 13.

* microseconds to match the unit of the timing difference.)
*/
*min = S2N_DEFAULT_BLINDING_FLOOR * ONE_S;
*max = S2N_DEFAULT_BLINDING_CEILING * ONE_S;

/* Some users may not be able to sleep for our default duration. We offer a custom delay in this case.
* The floor of the delay range is a third of the upper bound. This custom range isn't based in any math
* but it is strictly better than turning off the blinding mitigation entirely.
*/
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_CEILING 30
#define S2N_DEFAULT_BLINDING_FLOOR 10

typedef enum {
S2N_NO_TICKET = 0,
S2N_DECRYPT_TICKET,
Expand Down
Loading