Skip to content

Commit

Permalink
Add an API for TLS 1.3 exporter
Browse files Browse the repository at this point in the history
  • Loading branch information
Mark-Simulacrum committed Sep 29, 2023
1 parent 6da4a64 commit a7eec2a
Show file tree
Hide file tree
Showing 7 changed files with 181 additions and 1 deletion.
13 changes: 13 additions & 0 deletions api/s2n.h
Original file line number Diff line number Diff line change
Expand Up @@ -2870,6 +2870,19 @@ S2N_API extern int s2n_connection_client_cert_used(struct s2n_connection *conn);
*/
S2N_API extern const char *s2n_connection_get_cipher(struct s2n_connection *conn);

/**
* Provides access to the TLS-Exporter functionality.
*
* See https://datatracker.ietf.org/doc/html/rfc5705 and https://www.rfc-editor.org/rfc/rfc8446.
*
* @note This is currently only available with TLS 1.3 connections which have finished a handshake.
*
* @param conn A pointer to the connection
* @returns A POSIX error signal. If an error was returned, the value contained in `output` should be considered invalid.
*/
S2N_API extern int s2n_connection_tls_exporter(struct s2n_connection *conn, uint8_t *output, uint32_t output_length,
const uint8_t *label_, uint32_t label_length, const uint8_t *context, uint32_t context_length);

/**
* Returns the IANA value for the connection's negotiated cipher suite.
*
Expand Down
7 changes: 6 additions & 1 deletion crypto/s2n_tls13_keys.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@
* [x] server_handshake_traffic_secret
* [x] client_application_traffic_secret_0
* [x] server_application_traffic_secret_0
* [ ] exporter_master_secret
* [x] exporter_master_secret
* [x] resumption_master_secret
*
* The TLS 1.3 key generation can be divided into 3 phases
Expand Down Expand Up @@ -79,6 +79,11 @@ S2N_BLOB_LABEL(s2n_tls13_label_session_ticket_secret, "resumption")
S2N_BLOB_LABEL(s2n_tls13_label_traffic_secret_key, "key")
S2N_BLOB_LABEL(s2n_tls13_label_traffic_secret_iv, "iv")

/*
* TLS 1.3 Exporter label
*/
S2N_BLOB_LABEL(s2n_tls13_label_exporter, "exporter")

/*
* TLS 1.3 Finished label
*/
Expand Down
2 changes: 2 additions & 0 deletions crypto/s2n_tls13_keys.h
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,8 @@ extern const struct s2n_blob s2n_tls13_label_resumption_master_secret;

extern const struct s2n_blob s2n_tls13_label_finished;

extern const struct s2n_blob s2n_tls13_label_exporter;

/* Traffic secret labels */

extern const struct s2n_blob s2n_tls13_label_traffic_secret_key;
Expand Down
46 changes: 46 additions & 0 deletions tests/unit/s2n_tls13_secrets_rfc8448_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -498,6 +498,52 @@ int main(int argc, char **argv)
secret.data, secret.size);
}
};

/* Derive EXPORTER_MASTER_SECRET */
{
/**
*= https://www.rfc-editor.org/rfc/rfc8448.html#section-3
*= type=test
*# {server} derive secret "tls13 exp master":
*
*= https://www.rfc-editor.org/rfc/rfc8448.html#section-3
*= type=test
*# {client} derive secret "tls13 res master":
*#
*# PRK (32 octets): 18 df 06 84 3d 13 a0 8b f2 a4 49 84 4c 5f 8a 47
*# 80 01 bc 4d 4c 62 79 84 d5 a4 1d a8 d0 40 29 19
*#
*# hash (32 octets): 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a
*# 00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13
*#
**
*= https://www.rfc-editor.org/rfc/rfc8448.html#section-3
*= type=test
*# info (52 octets): 00 20 10 74 6c 73 31 33 20 65 78 70 20 6d 61 73
*# 74 65 72 20 96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a 00
*# 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13
*#
*# expanded (32 octets): fe 22 f8 81 17 6e da 18 eb 8f 44 52 9e 67
*# 92 c5 0c 9a 3f 89 45 2f 68 d8 ae 31 1b 43 09 d3 cf 50
*/
S2N_BLOB_FROM_HEX(hash, "96 08 10 2a 0f 1c cc 6d b6 25 0b 7b 7e 41 7b 1a \
00 0e aa da 3d aa e4 77 7a 76 86 c9 ff 83 df 13");
S2N_BLOB_FROM_HEX(secret, "fe 22 f8 81 17 6e da 18 eb 8f 44 52 9e 67 \
92 c5 0c 9a 3f 89 45 2f 68 d8 ae 31 1b 43 09 d3 cf 50");

for (size_t i = 0; i < s2n_array_len(modes); i++) {
DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(modes[i]), s2n_connection_ptr_free);
conn->secure->cipher_suite = cipher_suite;
EXPECT_OK(s2n_connection_set_test_master_secret(conn, &master_secret));
EXPECT_OK(s2n_connection_set_test_transcript_hash(conn, SERVER_FINISHED, &hash));

EXPECT_OK(s2n_derive_exporter_master_secret(conn));
EXPECT_EQUAL(derived_secret.size, secret.size);
EXPECT_BYTEARRAY_EQUAL(conn->secrets.version.tls13.exporter_master_secret,
secret.data, secret.size);
}
};
};

/* Resumed 0-RTT Handshake */
Expand Down
39 changes: 39 additions & 0 deletions tests/unit/s2n_tls13_secrets_test.c
Original file line number Diff line number Diff line change
Expand Up @@ -281,6 +281,7 @@ int main(int argc, char **argv)
EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls13.server_handshake_secret, test_secret.data, test_secret.size);
EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls13.client_app_secret, test_secret.data, test_secret.size);
EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls13.server_app_secret, test_secret.data, test_secret.size);
EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls13.exporter_master_secret, test_secret.data, test_secret.size);
EXPECT_MEMCPY_SUCCESS(conn->secrets.version.tls13.resumption_master_secret, test_secret.data, test_secret.size);

EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.extract_secret, empty_secret, sizeof(empty_secret));
Expand All @@ -289,6 +290,7 @@ int main(int argc, char **argv)
EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.server_handshake_secret, empty_secret, sizeof(empty_secret));
EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.client_app_secret, empty_secret, sizeof(empty_secret));
EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.server_app_secret, empty_secret, sizeof(empty_secret));
EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.exporter_master_secret, empty_secret, sizeof(empty_secret));
EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.resumption_master_secret, empty_secret, sizeof(empty_secret));

EXPECT_OK(s2n_tls13_secrets_clean(conn));
Expand All @@ -299,6 +301,7 @@ int main(int argc, char **argv)
EXPECT_BYTEARRAY_EQUAL(conn->secrets.version.tls13.server_handshake_secret, empty_secret, sizeof(empty_secret));
EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.client_app_secret, empty_secret, sizeof(empty_secret));
EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.server_app_secret, empty_secret, sizeof(empty_secret));
EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.exporter_master_secret, empty_secret, sizeof(empty_secret));
EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.resumption_master_secret, empty_secret, sizeof(empty_secret));
};
};
Expand Down Expand Up @@ -488,5 +491,41 @@ int main(int argc, char **argv)
};
};

/* s2n_connection_export_secret */
{
/* Derives exporter secret on SERVER_FINISHED */
{
DEFER_CLEANUP(struct s2n_connection *conn = s2n_connection_new(S2N_SERVER),
s2n_connection_ptr_free);
conn->secure->cipher_suite = &s2n_tls13_aes_128_gcm_sha256;
conn->actual_protocol_version = S2N_TLS13;
EXPECT_OK(s2n_connection_set_test_master_secret(conn, &test_secret));
EXPECT_BYTEARRAY_EQUAL(conn->secrets.version.tls13.resumption_master_secret,
empty_secret, sizeof(empty_secret));

while (s2n_conn_get_current_message_type(conn) != SERVER_FINISHED) {
conn->handshake.message_number++;
}
EXPECT_OK(s2n_tls13_secrets_update(conn));

EXPECT_BYTEARRAY_NOT_EQUAL(conn->secrets.version.tls13.exporter_master_secret,
empty_secret, sizeof(empty_secret));

uint8_t output[32];
ssize_t result = s2n_connection_tls_exporter(
conn,
output,
sizeof(output),
(uint8_t *) "label",
sizeof("label"),
(uint8_t *) "context",
sizeof("context"));
EXPECT_SUCCESS(result);
S2N_BLOB_FROM_HEX(expected, "e8 39 08 03 29 9f 40 c5 51 04 46 74 ff \
37 42 2f 3a 0c e5 8c 45 f3 87 99 f3 e1 29 5c ce 6f f8 ca");
EXPECT_BYTEARRAY_EQUAL(output, expected.data, expected.size);
};
};

END_TEST();
}
73 changes: 73 additions & 0 deletions tls/s2n_tls13_secrets.c
Original file line number Diff line number Diff line change
Expand Up @@ -520,6 +520,28 @@ S2N_RESULT s2n_derive_resumption_master_secret(struct s2n_connection *conn)
return S2N_RESULT_OK;
}

/**
*= https://tools.ietf.org/rfc/rfc8446#section-7.1
*# |
*# +-----> Derive-Secret(., "exp master",
*# ClientHello...server Finished)
*# = exporter_master_secret
*/
S2N_RESULT s2n_derive_exporter_master_secret(struct s2n_connection *conn)
{
RESULT_ENSURE_REF(conn);
/* Secret derivation requires these fields to be non-null. */
RESULT_ENSURE_REF(conn->secure);
RESULT_ENSURE_REF(conn->secure->cipher_suite);

RESULT_GUARD(s2n_derive_secret_with_context(conn,
S2N_MASTER_SECRET,
&s2n_tls13_label_exporter_master_secret,
SERVER_FINISHED,
&CONN_SECRET(conn, exporter_master_secret)));
return S2N_RESULT_OK;
}

static s2n_result (*extract_methods[])(struct s2n_connection *conn) = {
[S2N_EARLY_SECRET] = &s2n_extract_early_secret_for_schedule,
[S2N_HANDSHAKE_SECRET] = &s2n_extract_handshake_secret,
Expand Down Expand Up @@ -636,6 +658,7 @@ S2N_RESULT s2n_tls13_secrets_update(struct s2n_connection *conn)
S2N_CLIENT, &CONN_SECRET(conn, client_app_secret)));
RESULT_GUARD(s2n_tls13_derive_secret(conn, S2N_MASTER_SECRET,
S2N_SERVER, &CONN_SECRET(conn, server_app_secret)));
RESULT_GUARD(s2n_derive_exporter_master_secret(conn));
break;
case CLIENT_FINISHED:
RESULT_GUARD(s2n_calculate_transcript_digest(conn));
Expand Down Expand Up @@ -671,3 +694,53 @@ S2N_RESULT s2n_tls13_secrets_get(struct s2n_connection *conn, s2n_extract_secret
RESULT_ENSURE_GT(secret->size, 0);
return S2N_RESULT_OK;
}

int s2n_connection_tls_exporter(
struct s2n_connection *conn,
uint8_t *output_,
uint32_t output_length,
const uint8_t *label_,
uint32_t label_length,
const uint8_t *context,
uint32_t context_length)
{
POSIX_ENSURE_REF(conn);
POSIX_ENSURE_REF(output_);
POSIX_ENSURE_REF(label_);
POSIX_ENSURE_REF(context);
POSIX_ENSURE(s2n_connection_get_protocol_version(conn) == S2N_TLS13, S2N_ERR_INVALID_STATE);

s2n_hmac_algorithm hmac_alg = conn->secure->cipher_suite->prf_alg;

uint8_t size = 0;
if (s2n_hmac_digest_size(hmac_alg, &size) != S2N_SUCCESS) {
size = 0;
}
POSIX_ENSURE_GT(size, 0);
struct s2n_blob exporter_master_secret = (struct s2n_blob){ .data = conn->secrets.version.tls13.exporter_master_secret, .size = size };
const struct s2n_blob label = (struct s2n_blob){ .data = (uint8_t *) label_, .size = label_length };

uint8_t derived_secret_bytes[S2N_TLS13_SECRET_MAX_LEN] = { 0 };
struct s2n_blob derived_secret = { 0 };
POSIX_GUARD(s2n_blob_init(&derived_secret, derived_secret_bytes, S2N_TLS13_SECRET_MAX_LEN));
POSIX_GUARD_RESULT(s2n_derive_secret(hmac_alg, &exporter_master_secret, &label, &CONN_HASH(conn, transcript_hash_digest), &derived_secret));

DEFER_CLEANUP(struct s2n_hmac_state hmac_state = { 0 }, s2n_hmac_free);
POSIX_GUARD(s2n_hmac_new(&hmac_state));

DEFER_CLEANUP(struct s2n_hash_state hash = { 0 }, s2n_hash_free);
POSIX_GUARD(s2n_hash_new(&hash));

s2n_hash_algorithm hash_alg;
POSIX_GUARD(s2n_hmac_hash_alg(hmac_alg, &hash_alg));
struct s2n_blob digest = EMPTY_CONTEXT(hmac_alg);

POSIX_GUARD(s2n_hash_init(&hash, hash_alg));
POSIX_GUARD(s2n_hash_digest(&hash, digest.data, digest.size));

struct s2n_blob output = (struct s2n_blob){ .data = output_, .size = output_length };
POSIX_GUARD(s2n_hkdf_expand_label(&hmac_state, hmac_alg,
&derived_secret, &s2n_tls13_label_exporter, &digest, &output));

return 0;
}
2 changes: 2 additions & 0 deletions tls/s2n_tls13_secrets.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ struct s2n_tls13_secrets {
uint8_t client_app_secret[S2N_TLS13_SECRET_MAX_LEN];
uint8_t server_app_secret[S2N_TLS13_SECRET_MAX_LEN];
uint8_t resumption_master_secret[S2N_TLS13_SECRET_MAX_LEN];
uint8_t exporter_master_secret[S2N_TLS13_SECRET_MAX_LEN];

s2n_extract_secret_type_t extract_secret_type;
};
Expand All @@ -53,3 +54,4 @@ S2N_RESULT s2n_tls13_secrets_clean(struct s2n_connection *conn);

S2N_RESULT s2n_derive_binder_key(struct s2n_psk *psk, struct s2n_blob *output);
S2N_RESULT s2n_derive_resumption_master_secret(struct s2n_connection *conn);
S2N_RESULT s2n_derive_exporter_master_secret(struct s2n_connection *conn);

0 comments on commit a7eec2a

Please sign in to comment.