Skip to content

Commit e1f6f01

Browse files
authored
feat: getter for TLS1.2 master secrets (#4470)
1 parent 365e10b commit e1f6f01

File tree

10 files changed

+276
-8
lines changed

10 files changed

+276
-8
lines changed

api/s2n.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3022,6 +3022,38 @@ S2N_API extern int s2n_connection_client_cert_used(struct s2n_connection *conn);
30223022
*/
30233023
S2N_API extern const char *s2n_connection_get_cipher(struct s2n_connection *conn);
30243024

3025+
/**
3026+
* Provides access to the TLS master secret.
3027+
*
3028+
* This is a dangerous method and should not be used unless absolutely necessary.
3029+
* Mishandling the master secret can compromise both the current connection
3030+
* and any past or future connections that use the same master secret due to
3031+
* session resumption.
3032+
*
3033+
* This method is only supported for older TLS versions, and will report an S2N_ERR_INVALID_STATE
3034+
* usage error if called for a TLS1.3 connection. TLS1.3 includes a new key schedule
3035+
* that derives independent secrets from the master secret for specific purposes,
3036+
* such as separate traffic, session ticket, and exporter secrets. Using the master
3037+
* secret directly circumvents that security feature, reducing the security of
3038+
* the protocol.
3039+
*
3040+
* If you need cryptographic material tied to the current TLS session, consider
3041+
* `s2n_connection_tls_exporter` instead. Although s2n_connection_tls_exporter
3042+
* currently only supports TLS1.3, there is also an RFC that describes exporters
3043+
* for older TLS versions: https://datatracker.ietf.org/doc/html/rfc5705
3044+
* Using the master secret as-is or defining your own exporter is dangerous.
3045+
*
3046+
* @param conn A pointer to the connection.
3047+
* @param secret_bytes Memory to copy the master secret into. The secret
3048+
* is always 48 bytes long.
3049+
* @param max_size The size of the memory available at `secret_bytes`. Must be
3050+
* at least 48 bytes.
3051+
* @returns S2N_SUCCESS on success, S2N_FAILURE otherwise. `secret_bytes`
3052+
* will be set on success.
3053+
*/
3054+
S2N_API extern int s2n_connection_get_master_secret(const struct s2n_connection *conn,
3055+
uint8_t *secret_bytes, size_t max_size);
3056+
30253057
/**
30263058
* Provides access to the TLS-Exporter functionality.
30273059
*

bindings/rust/s2n-tls/src/connection.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -907,6 +907,20 @@ impl Connection {
907907
}
908908
}
909909
}
910+
911+
pub fn master_secret(&self) -> Result<Vec<u8>, Error> {
912+
// TLS1.2 master secrets are always 48 bytes
913+
let mut secret = vec![0; 48];
914+
unsafe {
915+
s2n_connection_get_master_secret(
916+
self.connection.as_ptr(),
917+
secret.as_mut_ptr(),
918+
secret.len(),
919+
)
920+
.into_result()?;
921+
}
922+
Ok(secret)
923+
}
910924
}
911925

912926
struct Context {

bindings/rust/s2n-tls/src/testing/s2n_tls.rs

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -235,6 +235,7 @@ mod tests {
235235
use crate::{
236236
callbacks::{ClientHelloCallback, ConnectionFuture},
237237
enums::ClientAuthType,
238+
error::ErrorType,
238239
testing::{client_hello::*, s2n_tls::*, *},
239240
};
240241
use alloc::sync::Arc;
@@ -892,4 +893,36 @@ mod tests {
892893

893894
Ok(())
894895
}
896+
897+
#[test]
898+
fn master_secret_success() -> Result<(), Error> {
899+
let policy = security::Policy::from_version("test_all_tls12")?;
900+
let config = config_builder(&policy)?.build()?;
901+
let pair = poll_tls_pair(tls_pair(config));
902+
let server = pair.server.0.connection;
903+
let client = pair.client.0.connection;
904+
905+
let server_secret = server.master_secret()?;
906+
let client_secret = client.master_secret()?;
907+
assert_eq!(server_secret, client_secret);
908+
909+
Ok(())
910+
}
911+
912+
#[test]
913+
fn master_secret_failure() -> Result<(), Error> {
914+
// TLS1.3 does not support getting the master secret
915+
let config = config_builder(&security::DEFAULT_TLS13)?.build()?;
916+
let pair = poll_tls_pair(tls_pair(config));
917+
let server = pair.server.0.connection;
918+
let client = pair.client.0.connection;
919+
920+
let server_error = server.master_secret().unwrap_err();
921+
assert_eq!(server_error.kind(), ErrorType::UsageError);
922+
923+
let client_error = client.master_secret().unwrap_err();
924+
assert_eq!(client_error.kind(), ErrorType::UsageError);
925+
926+
Ok(())
927+
}
895928
}

tests/unit/s2n_client_extensions_test.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,8 +59,6 @@ static uint8_t sct_list[] = {
5959
0xff, 0xff, 0xff, 0xff, 0xff
6060
};
6161

62-
message_type_t s2n_conn_get_current_message_type(struct s2n_connection *conn);
63-
6462
/* Helper function to allow us to easily repeat the PQ extension test for many scenarios.
6563
* If the KEM negotiation is expected to fail (because of e.g. a client/server extension
6664
* mismatch), pass in expected_kem_id = -1. The tests should always EXPECT_SUCCESS when

tests/unit/s2n_crypto_test.c

Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
/*
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License").
5+
* You may not use this file except in compliance with the License.
6+
* A copy of the License is located at
7+
*
8+
* http://aws.amazon.com/apache2.0
9+
*
10+
* or in the "license" file accompanying this file. This file is distributed
11+
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
12+
* express or implied. See the License for the specific language governing
13+
* permissions and limitations under the License.
14+
*/
15+
16+
#include "tls/s2n_crypto.h"
17+
18+
#include "crypto/s2n_fips.h"
19+
#include "s2n_test.h"
20+
#include "testlib/s2n_testlib.h"
21+
22+
int main()
23+
{
24+
BEGIN_TEST();
25+
26+
DEFER_CLEANUP(struct s2n_cert_chain_and_key *ecdsa_chain_and_key = NULL,
27+
s2n_cert_chain_and_key_ptr_free);
28+
EXPECT_SUCCESS(s2n_test_cert_chain_and_key_new(&ecdsa_chain_and_key,
29+
S2N_DEFAULT_ECDSA_TEST_CERT_CHAIN, S2N_DEFAULT_ECDSA_TEST_PRIVATE_KEY));
30+
31+
/* Test s2n_connection_get_master_secret */
32+
{
33+
const uint8_t test_secret[S2N_TLS_SECRET_LEN] = {
34+
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
35+
0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20,
36+
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10,
37+
0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFF,
38+
0x88, 0x87, 0x86, 0x85, 0x84, 0x83, 0x82, 0x81
39+
};
40+
41+
const uint8_t supported_versions[] = { S2N_SSLv3, S2N_TLS10, S2N_TLS11, S2N_TLS12 };
42+
43+
/* s2n_connection_get_master_secret takes a constant connection, so our
44+
* tests can share the same connection.
45+
*/
46+
DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER),
47+
s2n_connection_ptr_free);
48+
EXPECT_OK(s2n_skip_handshake(conn));
49+
EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls12.master_secret,
50+
test_secret, sizeof(test_secret));
51+
52+
/* Test safety checks */
53+
{
54+
uint8_t output[S2N_TLS_SECRET_LEN] = { 0 };
55+
EXPECT_FAILURE_WITH_ERRNO(
56+
s2n_connection_get_master_secret(conn, NULL, 0),
57+
S2N_ERR_NULL);
58+
EXPECT_FAILURE_WITH_ERRNO(
59+
s2n_connection_get_master_secret(NULL, output, 0),
60+
S2N_ERR_NULL);
61+
};
62+
63+
/* Test: successfully get master secret */
64+
{
65+
uint8_t output[S2N_TLS_SECRET_LEN] = { 0 };
66+
EXPECT_SUCCESS(s2n_connection_get_master_secret(conn, output, sizeof(output)));
67+
EXPECT_BYTEARRAY_EQUAL(test_secret, output, sizeof(output));
68+
};
69+
70+
/* Test: TLS1.3 not supported */
71+
{
72+
uint8_t output[S2N_TLS_SECRET_LEN] = { 0 };
73+
74+
conn->actual_protocol_version = S2N_TLS13;
75+
EXPECT_FAILURE_WITH_ERRNO(
76+
s2n_connection_get_master_secret(conn, output, sizeof(output)),
77+
S2N_ERR_INVALID_STATE);
78+
79+
conn->actual_protocol_version = S2N_TLS12;
80+
EXPECT_SUCCESS(s2n_connection_get_master_secret(conn, output, sizeof(output)));
81+
EXPECT_BYTEARRAY_EQUAL(test_secret, output, sizeof(output));
82+
};
83+
84+
/* Test: at least S2N_TLS_SECRET_LEN of output required */
85+
{
86+
uint8_t output[S2N_TLS_SECRET_LEN] = { 0 };
87+
88+
/* Fail if insufficient memory */
89+
EXPECT_FAILURE_WITH_ERRNO(
90+
s2n_connection_get_master_secret(conn, output, 0),
91+
S2N_ERR_INSUFFICIENT_MEM_SIZE);
92+
EXPECT_FAILURE_WITH_ERRNO(
93+
s2n_connection_get_master_secret(conn, output, 1),
94+
S2N_ERR_INSUFFICIENT_MEM_SIZE);
95+
EXPECT_FAILURE_WITH_ERRNO(
96+
s2n_connection_get_master_secret(conn, output, S2N_TLS_SECRET_LEN - 1),
97+
S2N_ERR_INSUFFICIENT_MEM_SIZE);
98+
99+
/* Succeed if exactly S2N_TLS_SECRET_LEN bytes */
100+
EXPECT_SUCCESS(s2n_connection_get_master_secret(conn, output, S2N_TLS_SECRET_LEN));
101+
EXPECT_BYTEARRAY_EQUAL(test_secret, output, sizeof(output));
102+
103+
/* Succeed if more than S2N_TLS_SECRET_LEN bytes */
104+
EXPECT_SUCCESS(s2n_connection_get_master_secret(conn, output, S2N_TLS_SECRET_LEN + 1));
105+
EXPECT_BYTEARRAY_EQUAL(test_secret, output, sizeof(output));
106+
};
107+
108+
/* Test: handshake must be complete */
109+
{
110+
uint8_t output[S2N_TLS_SECRET_LEN] = { 0 };
111+
112+
conn->handshake.message_number = 0;
113+
EXPECT_FAILURE_WITH_ERRNO(
114+
s2n_connection_get_master_secret(conn, output, sizeof(output)),
115+
S2N_ERR_HANDSHAKE_NOT_COMPLETE);
116+
117+
EXPECT_OK(s2n_skip_handshake(conn));
118+
EXPECT_SUCCESS(s2n_connection_get_master_secret(conn, output, sizeof(output)));
119+
EXPECT_BYTEARRAY_EQUAL(test_secret, output, sizeof(output));
120+
};
121+
122+
/* Test: self-talk */
123+
for (size_t i = 0; i < s2n_array_len(supported_versions); i++) {
124+
const uint8_t version = supported_versions[i];
125+
126+
/* See https://github.com/aws/s2n-tls/issues/4476
127+
* Retrieving the master secret won't vary between FIPS and non-FIPS,
128+
* so this testing limitation is not a concern.
129+
*/
130+
if (s2n_is_in_fips_mode() && version == S2N_SSLv3) {
131+
continue;
132+
}
133+
134+
DEFER_CLEANUP(struct s2n_config *config = s2n_config_new(), s2n_config_ptr_free);
135+
EXPECT_NOT_NULL(config);
136+
EXPECT_SUCCESS(s2n_config_add_cert_chain_and_key_to_store(config, ecdsa_chain_and_key));
137+
EXPECT_SUCCESS(s2n_config_set_unsafe_for_testing(config));
138+
EXPECT_SUCCESS(s2n_config_set_cipher_preferences(config, "test_all"));
139+
140+
DEFER_CLEANUP(struct s2n_connection *client = s2n_connection_new(S2N_CLIENT),
141+
s2n_connection_ptr_free);
142+
EXPECT_NOT_NULL(client);
143+
EXPECT_SUCCESS(s2n_connection_set_config(client, config));
144+
client->client_protocol_version = version;
145+
146+
DEFER_CLEANUP(struct s2n_connection *server = s2n_connection_new(S2N_SERVER),
147+
s2n_connection_ptr_free);
148+
EXPECT_NOT_NULL(server);
149+
EXPECT_SUCCESS(s2n_connection_set_config(server, config));
150+
/* Set server master secret to known value to ensure overridden later */
151+
memset(server->secrets.version.tls12.master_secret, 1, S2N_TLS_SECRET_LEN);
152+
153+
struct s2n_test_io_pair io_pair = { 0 };
154+
EXPECT_SUCCESS(s2n_io_pair_init_non_blocking(&io_pair));
155+
EXPECT_SUCCESS(s2n_connections_set_io_pair(client, server, &io_pair));
156+
EXPECT_SUCCESS(s2n_negotiate_test_server_and_client(server, client));
157+
EXPECT_EQUAL(server->actual_protocol_version, version);
158+
159+
/* server output matches master secret */
160+
uint8_t server_output[S2N_TLS_SECRET_LEN] = { 0 };
161+
EXPECT_SUCCESS(s2n_connection_get_master_secret(server,
162+
server_output, sizeof(server_output)));
163+
EXPECT_BYTEARRAY_EQUAL(server->secrets.version.tls12.master_secret,
164+
server_output, sizeof(server_output));
165+
166+
/* client output matches master secret */
167+
uint8_t client_output[S2N_TLS_SECRET_LEN] = { 0 };
168+
EXPECT_SUCCESS(s2n_connection_get_master_secret(client,
169+
client_output, sizeof(client_output)));
170+
EXPECT_BYTEARRAY_EQUAL(client->secrets.version.tls12.master_secret,
171+
client_output, sizeof(client_output));
172+
173+
/* client and server output match */
174+
EXPECT_BYTEARRAY_EQUAL(server_output, client_output, sizeof(client_output));
175+
};
176+
};
177+
178+
END_TEST();
179+
}

tests/unit/s2n_fragmentation_coalescing_test.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -163,8 +163,6 @@ uint8_t fatal_alert[] = { /* Fatal: unexpected message */
163163
0x02, 0x0a
164164
};
165165

166-
message_type_t s2n_conn_get_current_message_type(struct s2n_connection *conn);
167-
168166
void fragmented_message(int write_fd)
169167
{
170168
int written = 0;

tests/unit/s2n_malformed_handshake_test.c

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,8 +196,6 @@ static uint8_t certificate_too_large[] = {
196196
0x00, 0x00, 0x10
197197
};
198198

199-
message_type_t s2n_conn_get_current_message_type(struct s2n_connection *conn);
200-
201199
void send_messages(int write_fd, uint8_t *server_hello, uint32_t server_hello_len, uint8_t *server_cert, uint32_t server_cert_len)
202200
{
203201
uint8_t record_header[5] = { TLS_HANDSHAKE, 0x03, 0x03, (server_hello_len >> 8), server_hello_len & 0xff };

tls/s2n_crypto.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,3 +119,19 @@ S2N_RESULT s2n_crypto_parameters_switch(struct s2n_connection *conn)
119119

120120
return S2N_RESULT_OK;
121121
}
122+
123+
int s2n_connection_get_master_secret(const struct s2n_connection *conn,
124+
uint8_t *secret_bytes, size_t max_size)
125+
{
126+
POSIX_ENSURE_REF(conn);
127+
POSIX_ENSURE_REF(secret_bytes);
128+
POSIX_ENSURE(max_size >= S2N_TLS_SECRET_LEN, S2N_ERR_INSUFFICIENT_MEM_SIZE);
129+
POSIX_ENSURE(conn->actual_protocol_version < S2N_TLS13, S2N_ERR_INVALID_STATE);
130+
/* Technically the master secret is available earlier, but after the handshake
131+
* is the simplest rule and matches our TLS1.3 exporter behavior. */
132+
POSIX_ENSURE(is_handshake_complete(conn), S2N_ERR_HANDSHAKE_NOT_COMPLETE);
133+
/* Final sanity check: TLS1.2 doesn't use the extract_secret_type field */
134+
POSIX_ENSURE_EQ(conn->secrets.extract_secret_type, S2N_NONE_SECRET);
135+
POSIX_CHECKED_MEMCPY(secret_bytes, conn->secrets.version.tls12.master_secret, S2N_TLS_SECRET_LEN);
136+
return S2N_SUCCESS;
137+
}

tls/s2n_handshake.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -204,7 +204,7 @@ struct s2n_handshake {
204204
};
205205

206206
/* Only used in our test cases. */
207-
message_type_t s2n_conn_get_current_message_type(struct s2n_connection *conn);
207+
message_type_t s2n_conn_get_current_message_type(const struct s2n_connection *conn);
208208

209209
/* s2n_handshake */
210210
int s2n_handshake_require_all_hashes(struct s2n_handshake *handshake);

tls/s2n_handshake_io.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -841,7 +841,7 @@ static const char *tls13_handshake_type_names[] = {
841841
#define CONNECTION_IS_WRITER(conn) (ACTIVE_STATE(conn).writer == CONNECTION_WRITER(conn))
842842

843843
/* Only used in our test cases. */
844-
message_type_t s2n_conn_get_current_message_type(struct s2n_connection *conn)
844+
message_type_t s2n_conn_get_current_message_type(const struct s2n_connection *conn)
845845
{
846846
return ACTIVE_MESSAGE(conn);
847847
}

0 commit comments

Comments
 (0)