#include <stdint.h>
#include <sys/param.h>
#include "crypto/s2n_cipher.h"
#include "crypto/s2n_hmac.h"
#include "crypto/s2n_sequence.h"
#include "error/s2n_errno.h"
#include "stuffer/s2n_stuffer.h"
#include "tls/s2n_cipher_suites.h"
#include "tls/s2n_connection.h"
#include "tls/s2n_crypto.h"
#include "tls/s2n_ktls.h"
#include "tls/s2n_record.h"
#include "utils/s2n_blob.h"
#include "utils/s2n_random.h"
#include "utils/s2n_safety.h"
extern uint8_t s2n_unknown_protocol_version;
#define RECORD_TYPE(is_tls13_record, content_type) (is_tls13_record ? TLS_APPLICATION_DATA : content_type)
static S2N_RESULT s2n_tls_record_overhead(struct s2n_connection *conn, uint16_t *out)
{
RESULT_ENSURE_REF(conn);
RESULT_ENSURE_MUT(out);
struct s2n_crypto_parameters *active = conn->server;
if (conn->mode == S2N_CLIENT) {
active = conn->client;
}
uint8_t extra = 0;
RESULT_GUARD_POSIX(s2n_hmac_digest_size(active->cipher_suite->record_alg->hmac_alg, &extra));
if (active->cipher_suite->record_alg->cipher->type == S2N_CBC) {
extra += 1;
if (conn->actual_protocol_version > S2N_TLS10) {
extra += active->cipher_suite->record_alg->cipher->io.cbc.record_iv_size;
}
} else if (active->cipher_suite->record_alg->cipher->type == S2N_AEAD) {
extra += active->cipher_suite->record_alg->cipher->io.aead.tag_size;
extra += active->cipher_suite->record_alg->cipher->io.aead.record_iv_size;
} else if (active->cipher_suite->record_alg->cipher->type == S2N_COMPOSITE && conn->actual_protocol_version > S2N_TLS10) {
extra += active->cipher_suite->record_alg->cipher->io.comp.record_iv_size;
}
*out = extra;
return S2N_RESULT_OK;
}
S2N_RESULT s2n_record_max_write_payload_size(struct s2n_connection *conn, uint16_t *max_fragment_size)
{
RESULT_ENSURE_REF(conn);
RESULT_ENSURE_REF(conn->config);
RESULT_ENSURE_MUT(max_fragment_size);
RESULT_ENSURE(conn->max_outgoing_fragment_length > 0, S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL);
*max_fragment_size = MIN(conn->max_outgoing_fragment_length, S2N_TLS_MAXIMUM_FRAGMENT_LENGTH);
uint32_t send_buffer_override = conn->config->send_buffer_size_override;
if (send_buffer_override) {
uint16_t max_record_size = 0;
RESULT_GUARD(s2n_record_max_write_size(conn, *max_fragment_size, &max_record_size));
if (send_buffer_override < max_record_size) {
size_t overhead = (max_record_size - *max_fragment_size);
RESULT_ENSURE_GT(send_buffer_override, overhead);
*max_fragment_size = send_buffer_override - overhead;
}
}
return S2N_RESULT_OK;
}
S2N_RESULT s2n_record_max_write_size(struct s2n_connection *conn, uint16_t max_fragment_size, uint16_t *max_record_size)
{
RESULT_ENSURE_REF(conn);
RESULT_ENSURE_MUT(max_record_size);
if (!IS_NEGOTIATED(conn)) {
*max_record_size = S2N_TLS_MAX_RECORD_LEN_FOR(max_fragment_size);
} else if (conn->actual_protocol_version < S2N_TLS13) {
*max_record_size = S2N_TLS12_MAX_RECORD_LEN_FOR(max_fragment_size);
} else {
*max_record_size = S2N_TLS13_MAX_RECORD_LEN_FOR(max_fragment_size);
}
return S2N_RESULT_OK;
}
S2N_RESULT s2n_record_min_write_payload_size(struct s2n_connection *conn, uint16_t *payload_size)
{
RESULT_ENSURE_REF(conn);
RESULT_ENSURE_MUT(payload_size);
const uint16_t min_outgoing_fragment_length = ETH_MTU - (conn->ipv6 ? IP_V6_HEADER_LENGTH : IP_V4_HEADER_LENGTH)
- TCP_HEADER_LENGTH - TCP_OPTIONS_LENGTH - S2N_TLS_RECORD_HEADER_LENGTH;
RESULT_ENSURE(min_outgoing_fragment_length <= S2N_TLS_MAXIMUM_FRAGMENT_LENGTH, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE);
uint16_t size = min_outgoing_fragment_length;
const struct s2n_crypto_parameters *active = conn->mode == S2N_CLIENT ? conn->client : conn->server;
if (active->cipher_suite->record_alg->cipher->type == S2N_CBC) {
size -= size % active->cipher_suite->record_alg->cipher->io.cbc.block_size;
} else if (active->cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) {
size -= size % active->cipher_suite->record_alg->cipher->io.comp.block_size;
size -= active->cipher_suite->record_alg->cipher->io.comp.mac_key_size;
size -= 1;
}
if (conn->actual_protocol_version >= S2N_TLS13) {
RESULT_ENSURE(size > S2N_TLS_CONTENT_TYPE_LENGTH, S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL);
size -= S2N_TLS_CONTENT_TYPE_LENGTH;
}
uint16_t overhead = 0;
RESULT_GUARD(s2n_tls_record_overhead(conn, &overhead));
RESULT_ENSURE(size > overhead, S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL);
size -= overhead;
RESULT_ENSURE(size > 0, S2N_ERR_FRAGMENT_LENGTH_TOO_SMALL);
RESULT_ENSURE(size <= ETH_MTU, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE);
*payload_size = size;
return S2N_RESULT_OK;
}
int s2n_record_write_protocol_version(struct s2n_connection *conn, uint8_t record_type, struct s2n_stuffer *out)
{
uint8_t record_protocol_version = conn->actual_protocol_version;
if (conn->server_protocol_version == s2n_unknown_protocol_version
&& record_type == TLS_HANDSHAKE) {
record_protocol_version = MIN(record_protocol_version, S2N_TLS10);
}
record_protocol_version = MIN(record_protocol_version, S2N_TLS12);
if (record_protocol_version == s2n_unknown_protocol_version) {
record_protocol_version = S2N_TLS10;
}
uint8_t protocol_version[S2N_TLS_PROTOCOL_VERSION_LEN];
protocol_version[0] = record_protocol_version / 10;
protocol_version[1] = record_protocol_version % 10;
POSIX_GUARD(s2n_stuffer_write_bytes(out, protocol_version, S2N_TLS_PROTOCOL_VERSION_LEN));
return 0;
}
static inline int s2n_record_encrypt(
struct s2n_connection *conn,
const struct s2n_cipher_suite *cipher_suite,
struct s2n_session_key *session_key,
struct s2n_blob *iv,
struct s2n_blob *aad,
struct s2n_blob *en,
uint8_t *implicit_iv, uint16_t block_size)
{
POSIX_ENSURE_REF(en->data);
switch (cipher_suite->record_alg->cipher->type) {
case S2N_STREAM:
POSIX_GUARD(cipher_suite->record_alg->cipher->io.stream.encrypt(session_key, en, en));
break;
case S2N_CBC:
POSIX_GUARD(cipher_suite->record_alg->cipher->io.cbc.encrypt(session_key, iv, en, en));
if (conn->actual_protocol_version < S2N_TLS11) {
POSIX_ENSURE_GTE(en->size, block_size);
POSIX_CHECKED_MEMCPY(implicit_iv, en->data + en->size - block_size, block_size);
}
break;
case S2N_AEAD:
POSIX_GUARD(cipher_suite->record_alg->cipher->io.aead.encrypt(session_key, iv, aad, en, en));
break;
case S2N_COMPOSITE:
POSIX_GUARD(cipher_suite->record_alg->cipher->io.comp.encrypt(session_key, iv, en, en));
POSIX_ENSURE_GTE(en->size, block_size);
POSIX_CHECKED_MEMCPY(implicit_iv, en->data + en->size - block_size, block_size);
break;
default:
POSIX_BAIL(S2N_ERR_CIPHER_TYPE);
break;
}
return 0;
}
static S2N_RESULT s2n_record_write_mac(struct s2n_connection *conn, struct s2n_blob *record_header,
struct s2n_blob *plaintext, struct s2n_stuffer *out, uint32_t *bytes_written)
{
RESULT_ENSURE_REF(conn);
RESULT_ENSURE_REF(conn->server);
RESULT_ENSURE_REF(conn->client);
RESULT_ENSURE_REF(record_header);
RESULT_ENSURE_REF(plaintext);
RESULT_ENSURE_REF(out);
RESULT_ENSURE_REF(bytes_written);
*bytes_written = 0;
struct s2n_hmac_state *mac = &conn->server->server_record_mac;
const struct s2n_cipher_suite *cipher_suite = conn->server->cipher_suite;
uint8_t *sequence_number = conn->server->server_sequence_number;
if (conn->mode == S2N_CLIENT) {
mac = &conn->client->client_record_mac;
cipher_suite = conn->client->cipher_suite;
sequence_number = conn->client->client_sequence_number;
}
RESULT_ENSURE_REF(cipher_suite);
RESULT_ENSURE_REF(cipher_suite->record_alg);
if (cipher_suite->record_alg->hmac_alg == S2N_HMAC_NONE) {
return S2N_RESULT_OK;
}
RESULT_GUARD_POSIX(s2n_hmac_update(mac, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN));
struct s2n_stuffer header_stuffer = { 0 };
RESULT_GUARD_POSIX(s2n_stuffer_init_written(&header_stuffer, record_header));
void *record_type_byte = s2n_stuffer_raw_read(&header_stuffer, sizeof(uint8_t));
RESULT_ENSURE_REF(record_type_byte);
RESULT_GUARD_POSIX(s2n_hmac_update(mac, record_type_byte, sizeof(uint8_t)));
void *protocol_version_bytes = s2n_stuffer_raw_read(&header_stuffer, S2N_TLS_PROTOCOL_VERSION_LEN);
RESULT_ENSURE_REF(protocol_version_bytes);
if (conn->actual_protocol_version > S2N_SSLv3) {
RESULT_GUARD_POSIX(s2n_hmac_update(mac, protocol_version_bytes, S2N_TLS_PROTOCOL_VERSION_LEN));
}
uint8_t content_length_bytes[sizeof(uint16_t)] = { 0 };
struct s2n_blob content_length_blob = { 0 };
RESULT_GUARD_POSIX(s2n_blob_init(&content_length_blob, content_length_bytes, sizeof(content_length_bytes)));
struct s2n_stuffer content_length_stuffer = { 0 };
RESULT_GUARD_POSIX(s2n_stuffer_init(&content_length_stuffer, &content_length_blob));
RESULT_GUARD_POSIX(s2n_stuffer_write_uint16(&content_length_stuffer, plaintext->size));
RESULT_GUARD_POSIX(s2n_hmac_update(mac, content_length_bytes, sizeof(content_length_bytes)));
RESULT_GUARD_POSIX(s2n_hmac_update(mac, plaintext->data, plaintext->size));
uint8_t mac_digest_size = 0;
RESULT_GUARD_POSIX(s2n_hmac_digest_size(mac->alg, &mac_digest_size));
uint8_t *digest = s2n_stuffer_raw_write(out, mac_digest_size);
RESULT_ENSURE_REF(digest);
RESULT_GUARD_POSIX(s2n_hmac_digest(mac, digest, mac_digest_size));
*bytes_written = mac_digest_size;
RESULT_GUARD_POSIX(s2n_hmac_reset(mac));
return S2N_RESULT_OK;
}
int s2n_record_writev(struct s2n_connection *conn, uint8_t content_type, const struct iovec *in, int in_count, size_t offs, size_t to_write)
{
if (conn->ktls_send_enabled) {
return s2n_ktls_record_writev(conn, content_type, in, in_count, offs, to_write);
}
struct s2n_blob iv = { 0 };
uint8_t padding = 0;
uint16_t block_size = 0;
uint8_t aad_iv[S2N_TLS_MAX_IV_LEN] = { 0 };
struct s2n_crypto_parameters *current_client_crypto = conn->client;
struct s2n_crypto_parameters *current_server_crypto = conn->server;
if (conn->actual_protocol_version == S2N_TLS13 && content_type == TLS_CHANGE_CIPHER_SPEC) {
POSIX_ENSURE_REF(conn->initial);
conn->client = conn->initial;
conn->server = conn->initial;
}
uint8_t *sequence_number = conn->server->server_sequence_number;
struct s2n_session_key *session_key = &conn->server->server_key;
const struct s2n_cipher_suite *cipher_suite = conn->server->cipher_suite;
uint8_t *implicit_iv = conn->server->server_implicit_iv;
if (conn->mode == S2N_CLIENT) {
sequence_number = conn->client->client_sequence_number;
session_key = &conn->client->client_key;
cipher_suite = conn->client->cipher_suite;
implicit_iv = conn->client->client_implicit_iv;
}
if (cipher_suite->record_alg->cipher == &s2n_null_cipher) {
POSIX_ENSURE(content_type != TLS_APPLICATION_DATA, S2N_ERR_ENCRYPT);
}
const int is_tls13_record = cipher_suite->record_alg->flags & S2N_TLS13_RECORD_AEAD_NONCE;
s2n_stack_blob(aad, is_tls13_record ? S2N_TLS13_AAD_LEN : S2N_TLS_MAX_AAD_LEN, S2N_TLS_MAX_AAD_LEN);
if (!conn->multirecord_send) {
POSIX_ENSURE(s2n_stuffer_data_available(&conn->out) == 0, S2N_ERR_RECORD_STUFFER_NEEDS_DRAINING);
}
uint16_t max_write_payload_size = 0;
POSIX_GUARD_RESULT(s2n_record_max_write_payload_size(conn, &max_write_payload_size));
const uint16_t data_bytes_to_take = MIN(to_write, max_write_payload_size);
uint16_t extra = 0;
POSIX_GUARD_RESULT(s2n_tls_record_overhead(conn, &extra));
if (cipher_suite->record_alg->cipher->type == S2N_CBC) {
block_size = cipher_suite->record_alg->cipher->io.cbc.block_size;
if (((data_bytes_to_take + extra) % block_size)) {
padding = block_size - ((data_bytes_to_take + extra) % block_size);
}
} else if (cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) {
block_size = cipher_suite->record_alg->cipher->io.comp.block_size;
}
if (s2n_stuffer_is_freed(&conn->out)) {
uint16_t max_wire_record_size = 0;
POSIX_GUARD_RESULT(s2n_record_max_write_size(conn, max_write_payload_size, &max_wire_record_size));
uint32_t buffer_size = MAX(conn->config->send_buffer_size_override, max_wire_record_size);
POSIX_GUARD(s2n_stuffer_growable_alloc(&conn->out, buffer_size));
}
struct s2n_blob record_blob = { 0 };
struct s2n_stuffer record_stuffer = { 0 };
POSIX_GUARD(s2n_blob_init(&record_blob,
conn->out.blob.data + conn->out.write_cursor,
s2n_stuffer_space_remaining(&conn->out)));
POSIX_GUARD(s2n_stuffer_init(&record_stuffer, &record_blob));
uint8_t record_type = RECORD_TYPE(is_tls13_record, content_type);
POSIX_GUARD(s2n_stuffer_write_uint8(&record_stuffer, record_type));
POSIX_GUARD(s2n_record_write_protocol_version(conn, record_type, &record_stuffer));
if (cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) {
uint16_t payload_and_eiv_len = data_bytes_to_take;
if (conn->actual_protocol_version > S2N_TLS10) {
payload_and_eiv_len += block_size;
}
int pad_and_mac_len = 0;
POSIX_GUARD(cipher_suite->record_alg->cipher->io.comp.initial_hmac(session_key, sequence_number, content_type, conn->actual_protocol_version,
payload_and_eiv_len, &pad_and_mac_len));
extra += pad_and_mac_len;
}
if (is_tls13_record) {
extra += S2N_TLS_CONTENT_TYPE_LENGTH;
}
const uint16_t actual_fragment_length = data_bytes_to_take + padding + extra;
const uint16_t max_record_length = is_tls13_record ? S2N_TLS13_MAXIMUM_RECORD_LENGTH : S2N_TLS_MAXIMUM_RECORD_LENGTH;
S2N_ERROR_IF(actual_fragment_length + S2N_TLS_RECORD_HEADER_LENGTH > max_record_length, S2N_ERR_RECORD_LENGTH_TOO_LARGE);
POSIX_GUARD(s2n_stuffer_write_uint16(&record_stuffer, actual_fragment_length));
if (cipher_suite->record_alg->cipher->type == S2N_AEAD) {
struct s2n_stuffer iv_stuffer = { 0 };
POSIX_GUARD(s2n_blob_init(&iv, aad_iv, sizeof(aad_iv)));
POSIX_GUARD(s2n_stuffer_init(&iv_stuffer, &iv));
if (cipher_suite->record_alg->flags & S2N_TLS12_AES_GCM_AEAD_NONCE) {
POSIX_GUARD(s2n_stuffer_write_bytes(&record_stuffer, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN));
POSIX_GUARD(s2n_stuffer_write_bytes(&iv_stuffer, implicit_iv, cipher_suite->record_alg->cipher->io.aead.fixed_iv_size));
POSIX_GUARD(s2n_stuffer_write_bytes(&iv_stuffer, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN));
} else if (cipher_suite->record_alg->flags & S2N_TLS12_CHACHA_POLY_AEAD_NONCE || is_tls13_record) {
uint8_t four_zeroes[4] = { 0 };
POSIX_GUARD(s2n_stuffer_write_bytes(&iv_stuffer, four_zeroes, 4));
POSIX_GUARD(s2n_stuffer_write_bytes(&iv_stuffer, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN));
for (int i = 0; i < cipher_suite->record_alg->cipher->io.aead.fixed_iv_size; i++) {
aad_iv[i] = aad_iv[i] ^ implicit_iv[i];
}
} else {
POSIX_BAIL(S2N_ERR_INVALID_NONCE_TYPE);
}
iv.size = s2n_stuffer_data_available(&iv_stuffer);
if (is_tls13_record) {
POSIX_GUARD_RESULT(s2n_tls13_aead_aad_init(data_bytes_to_take + S2N_TLS_CONTENT_TYPE_LENGTH, cipher_suite->record_alg->cipher->io.aead.tag_size, &aad));
} else {
POSIX_GUARD_RESULT(s2n_aead_aad_init(conn, sequence_number, content_type, data_bytes_to_take, &aad));
}
} else if (cipher_suite->record_alg->cipher->type == S2N_CBC || cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) {
POSIX_GUARD(s2n_blob_init(&iv, implicit_iv, block_size));
if (conn->actual_protocol_version > S2N_TLS10) {
POSIX_GUARD_RESULT(s2n_get_public_random_data(&iv));
if (cipher_suite->record_alg->cipher->type == S2N_COMPOSITE) {
struct s2n_blob explicit_iv_placeholder = { 0 };
uint8_t zero_block[S2N_TLS_MAX_IV_LEN] = { 0 };
POSIX_GUARD(s2n_blob_init(&explicit_iv_placeholder, zero_block, block_size));
POSIX_GUARD_RESULT(s2n_get_public_random_data(&explicit_iv_placeholder));
POSIX_GUARD(s2n_stuffer_write(&record_stuffer, &explicit_iv_placeholder));
} else {
POSIX_GUARD(s2n_stuffer_write(&record_stuffer, &iv));
}
}
}
POSIX_GUARD(s2n_stuffer_writev_bytes(&record_stuffer, in, in_count, offs, data_bytes_to_take));
void *orig_write_ptr = record_stuffer.blob.data + record_stuffer.write_cursor - data_bytes_to_take;
struct s2n_blob header_blob = { 0 };
POSIX_GUARD(s2n_blob_slice(&record_blob, &header_blob, 0, S2N_TLS_RECORD_HEADER_LENGTH));
struct s2n_blob plaintext_blob = { 0 };
POSIX_GUARD(s2n_blob_init(&plaintext_blob, orig_write_ptr, data_bytes_to_take));
uint32_t mac_digest_size = 0;
POSIX_GUARD_RESULT(s2n_record_write_mac(conn, &header_blob, &plaintext_blob, &record_stuffer, &mac_digest_size));
struct s2n_blob seq = { 0 };
POSIX_GUARD(s2n_blob_init(&seq, sequence_number, S2N_TLS_SEQUENCE_NUM_LEN));
POSIX_GUARD(s2n_increment_sequence_number(&seq));
if (is_tls13_record) {
POSIX_GUARD(s2n_stuffer_write_uint8(&record_stuffer, content_type));
}
if (cipher_suite->record_alg->cipher->type == S2N_CBC) {
for (int i = 0; i <= padding; i++) {
POSIX_GUARD(s2n_stuffer_write_uint8(&record_stuffer, padding));
}
}
POSIX_GUARD(s2n_stuffer_rewrite(&record_stuffer));
POSIX_GUARD(s2n_stuffer_skip_write(&record_stuffer, S2N_TLS_RECORD_HEADER_LENGTH));
uint16_t encrypted_length = data_bytes_to_take + mac_digest_size;
switch (cipher_suite->record_alg->cipher->type) {
case S2N_AEAD:
POSIX_GUARD(s2n_stuffer_skip_write(&record_stuffer, cipher_suite->record_alg->cipher->io.aead.record_iv_size));
encrypted_length += cipher_suite->record_alg->cipher->io.aead.tag_size;
if (is_tls13_record) {
encrypted_length += S2N_TLS_CONTENT_TYPE_LENGTH;
}
break;
case S2N_CBC:
if (conn->actual_protocol_version > S2N_TLS10) {
POSIX_GUARD(s2n_stuffer_skip_write(&record_stuffer, iv.size));
}
encrypted_length += padding + 1;
break;
case S2N_COMPOSITE:
encrypted_length += extra;
break;
default:
break;
}
S2N_ERROR_IF(s2n_stuffer_space_remaining(&record_stuffer) < encrypted_length, S2N_ERR_RECORD_STUFFER_SIZE);
struct s2n_blob en = { .size = encrypted_length, .data = s2n_stuffer_raw_write(&record_stuffer, encrypted_length) };
POSIX_GUARD(s2n_record_encrypt(conn, cipher_suite, session_key, &iv, &aad, &en, implicit_iv, block_size));
POSIX_GUARD(s2n_stuffer_skip_write(&conn->out, s2n_stuffer_data_available(&record_stuffer)));
if (conn->actual_protocol_version == S2N_TLS13 && content_type == TLS_CHANGE_CIPHER_SPEC) {
conn->client = current_client_crypto;
conn->server = current_server_crypto;
}
return data_bytes_to_take;
}
S2N_RESULT s2n_record_write(struct s2n_connection *conn, uint8_t content_type, struct s2n_blob *in)
{
struct iovec iov;
iov.iov_base = in->data;
iov.iov_len = in->size;
int written = s2n_record_writev(conn, content_type, &iov, 1, 0, in->size);
RESULT_GUARD_POSIX(written);
RESULT_ENSURE((uint32_t) written == in->size, S2N_ERR_FRAGMENT_LENGTH_TOO_LARGE);
return S2N_RESULT_OK;
}