summaryrefslogtreecommitdiffstats
path: root/lib/libssl/tls12_record_layer.c
diff options
context:
space:
mode:
authorjsing <jsing@openbsd.org>2020-10-03 17:35:16 +0000
committerjsing <jsing@openbsd.org>2020-10-03 17:35:16 +0000
commit40038cb8284d25cad0ce565482768874e644ab3f (patch)
tree9661fd42e877dd318cbe4d0bf42f36a97b0aa300 /lib/libssl/tls12_record_layer.c
parentRename tls13_record_layer_alert() to tls13_record_layer_enqueue_alert() (diff)
downloadwireguard-openbsd-40038cb8284d25cad0ce565482768874e644ab3f.tar.xz
wireguard-openbsd-40038cb8284d25cad0ce565482768874e644ab3f.zip
Reimplement the TLSv1.2 record handling for the read side.
This is the next step in replacing the TLSv1.2 record layer. The existing record handling code does decryption and processing in place, which is not ideal for various reasons, however it is retained for now as other code depends on this behaviour. Additionally, CBC requires special handling to avoid timing oracles - for now the existing timing safe code is largely retained. ok beck@ inoguchi@ tb@
Diffstat (limited to 'lib/libssl/tls12_record_layer.c')
-rw-r--r--lib/libssl/tls12_record_layer.c348
1 files changed, 343 insertions, 5 deletions
diff --git a/lib/libssl/tls12_record_layer.c b/lib/libssl/tls12_record_layer.c
index 10d0f1194b4..56ff94d95c9 100644
--- a/lib/libssl/tls12_record_layer.c
+++ b/lib/libssl/tls12_record_layer.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: tls12_record_layer.c,v 1.4 2020/09/16 17:15:01 jsing Exp $ */
+/* $OpenBSD: tls12_record_layer.c,v 1.5 2020/10/03 17:35:17 jsing Exp $ */
/*
* Copyright (c) 2020 Joel Sing <jsing@openbsd.org>
*
@@ -25,6 +25,8 @@ struct tls12_record_layer {
uint16_t version;
int dtls;
+ uint8_t alert_desc;
+
uint16_t read_epoch;
uint16_t write_epoch;
@@ -43,6 +45,9 @@ struct tls12_record_layer {
EVP_CIPHER_CTX *write_cipher_ctx;
EVP_MD_CTX *write_hash_ctx;
+ const uint8_t *read_mac_key;
+ size_t read_mac_key_len;
+
uint8_t *read_seq_num;
uint8_t *write_seq_num;
};
@@ -65,6 +70,12 @@ tls12_record_layer_free(struct tls12_record_layer *rl)
}
void
+tls12_record_layer_alert(struct tls12_record_layer *rl, uint8_t *alert_desc)
+{
+ *alert_desc = rl->alert_desc;
+}
+
+void
tls12_record_layer_set_version(struct tls12_record_layer *rl, uint16_t version)
{
rl->version = version;
@@ -111,6 +122,7 @@ void
tls12_record_layer_clear_read_state(struct tls12_record_layer *rl)
{
tls12_record_layer_set_read_state(rl, NULL, NULL, NULL, 0);
+ tls12_record_layer_set_read_mac_key(rl, NULL, 0);
rl->read_seq_num = NULL;
}
@@ -173,6 +185,16 @@ tls12_record_layer_set_write_cipher_hash(struct tls12_record_layer *rl,
return 1;
}
+int
+tls12_record_layer_set_read_mac_key(struct tls12_record_layer *rl,
+ const uint8_t *mac_key, size_t mac_key_len)
+{
+ rl->read_mac_key = mac_key;
+ rl->read_mac_key_len = mac_key_len;
+
+ return 1;
+}
+
static int
tls12_record_layer_build_seq_num(struct tls12_record_layer *rl, CBB *cbb,
uint16_t epoch, uint8_t *seq_num, size_t seq_num_len)
@@ -234,7 +256,7 @@ tls12_record_layer_mac(struct tls12_record_layer *rl, CBB *cbb,
{
EVP_MD_CTX *mac_ctx = NULL;
uint8_t *header = NULL;
- size_t header_len;
+ size_t header_len = 0;
size_t mac_len;
uint8_t *mac;
int ret = 0;
@@ -258,6 +280,8 @@ tls12_record_layer_mac(struct tls12_record_layer *rl, CBB *cbb,
goto err;
if (EVP_DigestSignFinal(mac_ctx, mac, &mac_len) <= 0)
goto err;
+ if (mac_len == 0)
+ goto err;
if (stream_mac) {
if (!EVP_MD_CTX_copy(hash_ctx, mac_ctx))
@@ -269,12 +293,67 @@ tls12_record_layer_mac(struct tls12_record_layer *rl, CBB *cbb,
err:
EVP_MD_CTX_free(mac_ctx);
- free(header);
+ freezero(header, header_len);
return ret;
}
static int
+tls12_record_layer_read_mac_cbc(struct tls12_record_layer *rl, CBB *cbb,
+ uint8_t content_type, const uint8_t *content, size_t content_len,
+ size_t mac_len, size_t padding_len)
+{
+ uint8_t *header = NULL;
+ size_t header_len = 0;
+ uint8_t *mac = NULL;
+ size_t out_mac_len = 0;
+ int ret = 0;
+
+ /*
+ * Must be constant time to avoid leaking details about CBC padding.
+ */
+
+ if (!ssl3_cbc_record_digest_supported(rl->read_hash_ctx))
+ goto err;
+
+ if (!tls12_record_layer_pseudo_header(rl, content_type, content_len,
+ rl->read_epoch, rl->read_seq_num, SSL3_SEQUENCE_SIZE,
+ &header, &header_len))
+ goto err;
+
+ if (!CBB_add_space(cbb, &mac, mac_len))
+ goto err;
+ if (!ssl3_cbc_digest_record(rl->read_hash_ctx, mac, &out_mac_len, header,
+ content, content_len + mac_len, content_len + mac_len + padding_len,
+ rl->read_mac_key, rl->read_mac_key_len))
+ goto err;
+ if (mac_len != out_mac_len)
+ goto err;
+
+ ret = 1;
+
+ err:
+ freezero(header, header_len);
+
+ return ret;
+}
+
+static int
+tls12_record_layer_read_mac(struct tls12_record_layer *rl, CBB *cbb,
+ uint8_t content_type, const uint8_t *content, size_t content_len)
+{
+ EVP_CIPHER_CTX *enc = rl->read_cipher_ctx;
+ size_t out_len;
+
+ if (EVP_CIPHER_CTX_mode(enc) == EVP_CIPH_CBC_MODE)
+ return 0;
+
+ return tls12_record_layer_mac(rl, cbb, rl->read_hash_ctx,
+ rl->read_stream_mac, rl->read_epoch, rl->read_seq_num,
+ SSL3_SEQUENCE_SIZE, content_type, content, content_len, &out_len);
+}
+
+static int
tls12_record_layer_write_mac(struct tls12_record_layer *rl, CBB *cbb,
uint8_t content_type, const uint8_t *content, size_t content_len,
size_t *out_len)
@@ -286,7 +365,8 @@ tls12_record_layer_write_mac(struct tls12_record_layer *rl, CBB *cbb,
static int
tls12_record_layer_aead_concat_nonce(struct tls12_record_layer *rl,
- const SSL_AEAD_CTX *aead, uint8_t *seq_num, uint8_t **out, size_t *out_len)
+ const SSL_AEAD_CTX *aead, const uint8_t *seq_num,
+ uint8_t **out, size_t *out_len)
{
CBB cbb;
@@ -314,7 +394,8 @@ tls12_record_layer_aead_concat_nonce(struct tls12_record_layer *rl,
static int
tls12_record_layer_aead_xored_nonce(struct tls12_record_layer *rl,
- const SSL_AEAD_CTX *aead, uint8_t *seq_num, uint8_t **out, size_t *out_len)
+ const SSL_AEAD_CTX *aead, const uint8_t *seq_num,
+ uint8_t **out, size_t *out_len)
{
uint8_t *nonce = NULL;
size_t nonce_len = 0;
@@ -357,6 +438,263 @@ tls12_record_layer_aead_xored_nonce(struct tls12_record_layer *rl,
}
static int
+tls12_record_layer_open_record_plaintext(struct tls12_record_layer *rl,
+ uint8_t content_type, CBS *fragment, uint8_t **out, size_t *out_len)
+{
+ if (rl->read_aead_ctx != NULL || rl->read_cipher_ctx != NULL)
+ return 0;
+
+ /* XXX - decrypt/process in place for now. */
+ *out = (uint8_t *)CBS_data(fragment);
+ *out_len = CBS_len(fragment);
+
+ return 1;
+}
+
+static int
+tls12_record_layer_open_record_protected_aead(struct tls12_record_layer *rl,
+ uint8_t content_type, CBS *fragment, uint8_t **out, size_t *out_len)
+{
+ const SSL_AEAD_CTX *aead = rl->read_aead_ctx;
+ uint8_t *header = NULL, *nonce = NULL;
+ size_t header_len = 0, nonce_len = 0;
+ uint8_t *plain;
+ size_t plain_len;
+ uint16_t epoch = 0;
+ CBS var_nonce;
+ int ret = 0;
+
+ /* XXX - move to nonce allocated in record layer, matching TLSv1.3 */
+ if (aead->xor_fixed_nonce) {
+ if (!tls12_record_layer_aead_xored_nonce(rl, aead,
+ rl->read_seq_num, &nonce, &nonce_len))
+ goto err;
+ } else if (aead->variable_nonce_in_record) {
+ if (!CBS_get_bytes(fragment, &var_nonce,
+ aead->variable_nonce_len))
+ goto err;
+ if (!tls12_record_layer_aead_concat_nonce(rl, aead,
+ CBS_data(&var_nonce), &nonce, &nonce_len))
+ goto err;
+ } else {
+ if (!tls12_record_layer_aead_concat_nonce(rl, aead,
+ rl->read_seq_num, &nonce, &nonce_len))
+ goto err;
+ }
+
+ /* XXX EVP_AEAD_max_tag_len vs EVP_AEAD_CTX_tag_len. */
+ if (CBS_len(fragment) < aead->tag_len) {
+ rl->alert_desc = SSL_AD_BAD_RECORD_MAC;
+ goto err;
+ }
+ if (CBS_len(fragment) > SSL3_RT_MAX_ENCRYPTED_LENGTH) {
+ rl->alert_desc = SSL_AD_RECORD_OVERFLOW;
+ goto err;
+ }
+
+ /* XXX - decrypt/process in place for now. */
+ plain = (uint8_t *)CBS_data(fragment);
+ plain_len = CBS_len(fragment) - aead->tag_len;
+
+ if (!tls12_record_layer_pseudo_header(rl, content_type, plain_len,
+ epoch, rl->read_seq_num, SSL3_SEQUENCE_SIZE, &header, &header_len))
+ goto err;
+
+ if (!EVP_AEAD_CTX_open(&aead->ctx, plain, out_len, plain_len,
+ nonce, nonce_len, CBS_data(fragment), CBS_len(fragment),
+ header, header_len)) {
+ rl->alert_desc = SSL_AD_BAD_RECORD_MAC;
+ goto err;
+ }
+
+ if (*out_len > SSL3_RT_MAX_PLAIN_LENGTH) {
+ rl->alert_desc = SSL_AD_RECORD_OVERFLOW;
+ goto err;
+ }
+
+ if (*out_len != plain_len)
+ goto err;
+
+ *out = plain;
+
+ ret = 1;
+
+ err:
+ freezero(header, header_len);
+ freezero(nonce, nonce_len);
+
+ return ret;
+}
+
+static int
+tls12_record_layer_open_record_protected_cipher(struct tls12_record_layer *rl,
+ uint8_t content_type, CBS *fragment, uint8_t **out, size_t *out_len)
+{
+ EVP_CIPHER_CTX *enc = rl->read_cipher_ctx;
+ SSL3_RECORD_INTERNAL rrec;
+ int block_size, eiv_len;
+ uint8_t *mac = NULL;
+ int mac_len = 0;
+ uint8_t *out_mac = NULL;
+ size_t out_mac_len = 0;
+ uint8_t *plain;
+ size_t plain_len;
+ size_t min_len;
+ CBB cbb_mac;
+ int ret = 0;
+
+ memset(&cbb_mac, 0, sizeof(cbb_mac));
+
+ block_size = EVP_CIPHER_CTX_block_size(enc);
+ if (block_size < 0 || block_size > EVP_MAX_BLOCK_LENGTH)
+ goto err;
+
+ /* Determine explicit IV length. */
+ eiv_len = 0;
+ if (rl->version != TLS1_VERSION &&
+ EVP_CIPHER_CTX_mode(enc) == EVP_CIPH_CBC_MODE)
+ eiv_len = EVP_CIPHER_CTX_iv_length(enc);
+ if (eiv_len < 0 || eiv_len > EVP_MAX_IV_LENGTH)
+ goto err;
+
+ mac_len = 0;
+ if (rl->read_hash_ctx != NULL) {
+ mac_len = EVP_MD_CTX_size(rl->read_hash_ctx);
+ if (mac_len <= 0 || mac_len > EVP_MAX_MD_SIZE)
+ goto err;
+ }
+
+ /* CBC has at least one padding byte. */
+ min_len = eiv_len + mac_len;
+ if (EVP_CIPHER_CTX_mode(enc) == EVP_CIPH_CBC_MODE)
+ min_len += 1;
+
+ if (CBS_len(fragment) < min_len) {
+ rl->alert_desc = SSL_AD_BAD_RECORD_MAC;
+ goto err;
+ }
+ if (CBS_len(fragment) > SSL3_RT_MAX_ENCRYPTED_LENGTH) {
+ rl->alert_desc = SSL_AD_RECORD_OVERFLOW;
+ goto err;
+ }
+ if (CBS_len(fragment) % block_size != 0) {
+ rl->alert_desc = SSL_AD_BAD_RECORD_MAC;
+ goto err;
+ }
+
+ /* XXX - decrypt/process in place for now. */
+ plain = (uint8_t *)CBS_data(fragment);
+ plain_len = CBS_len(fragment);
+
+ if (!EVP_Cipher(enc, plain, CBS_data(fragment), plain_len))
+ goto err;
+
+ rrec.data = plain;
+ rrec.input = plain;
+ rrec.length = plain_len;
+
+ /*
+ * We now have to remove padding, extract MAC, calculate MAC
+ * and compare MAC in constant time.
+ */
+ if (block_size > 1)
+ ssl3_cbc_remove_padding(&rrec, eiv_len, mac_len);
+
+ if ((mac = calloc(1, mac_len)) == NULL)
+ goto err;
+
+ if (!CBB_init(&cbb_mac, EVP_MAX_MD_SIZE))
+ goto err;
+ if (EVP_CIPHER_CTX_mode(enc) == EVP_CIPH_CBC_MODE) {
+ ssl3_cbc_copy_mac(mac, &rrec, mac_len, rrec.length +
+ rrec.padding_length);
+ rrec.length -= mac_len;
+ if (!tls12_record_layer_read_mac_cbc(rl, &cbb_mac, content_type,
+ rrec.input, rrec.length, mac_len, rrec.padding_length))
+ goto err;
+ } else {
+ rrec.length -= mac_len;
+ memcpy(mac, rrec.data + rrec.length, mac_len);
+ if (!tls12_record_layer_read_mac(rl, &cbb_mac, content_type,
+ rrec.input, rrec.length))
+ goto err;
+ }
+ if (!CBB_finish(&cbb_mac, &out_mac, &out_mac_len))
+ goto err;
+ if (mac_len != out_mac_len)
+ goto err;
+
+ if (timingsafe_memcmp(mac, out_mac, mac_len) != 0) {
+ rl->alert_desc = SSL_AD_BAD_RECORD_MAC;
+ goto err;
+ }
+
+ if (rrec.length > SSL3_RT_MAX_COMPRESSED_LENGTH + mac_len) {
+ rl->alert_desc = SSL_AD_BAD_RECORD_MAC;
+ goto err;
+ }
+ if (rrec.length > SSL3_RT_MAX_PLAIN_LENGTH) {
+ rl->alert_desc = SSL_AD_RECORD_OVERFLOW;
+ goto err;
+ }
+
+ *out = rrec.data;
+ *out_len = rrec.length;
+
+ ret = 1;
+
+ err:
+ CBB_cleanup(&cbb_mac);
+ freezero(mac, mac_len);
+ freezero(out_mac, out_mac_len);
+
+ return ret;
+}
+
+int
+tls12_record_layer_open_record(struct tls12_record_layer *rl, uint8_t *buf,
+ size_t buf_len, uint8_t **out, size_t *out_len)
+{
+ CBS cbs, fragment, seq_no;
+ uint16_t epoch, version;
+ uint8_t content_type;
+
+ CBS_init(&cbs, buf, buf_len);
+
+ if (!CBS_get_u8(&cbs, &content_type))
+ return 0;
+ if (!CBS_get_u16(&cbs, &version))
+ return 0;
+ if (rl->dtls) {
+ if (!CBS_get_u16(&cbs, &epoch))
+ return 0;
+ if (!CBS_get_bytes(&cbs, &seq_no, 6))
+ return 0;
+ }
+ if (!CBS_get_u16_length_prefixed(&cbs, &fragment))
+ return 0;
+
+ if (rl->read_aead_ctx != NULL) {
+ if (!tls12_record_layer_open_record_protected_aead(rl,
+ content_type, &fragment, out, out_len))
+ return 0;
+ } else if (rl->read_cipher_ctx != NULL) {
+ if (!tls12_record_layer_open_record_protected_cipher(rl,
+ content_type, &fragment, out, out_len))
+ return 0;
+ } else {
+ if (!tls12_record_layer_open_record_plaintext(rl,
+ content_type, &fragment, out, out_len))
+ return 0;
+ }
+
+ if (!rl->dtls)
+ tls1_record_sequence_increment(rl->read_seq_num);
+
+ return 1;
+}
+
+static int
tls12_record_layer_seal_record_plaintext(struct tls12_record_layer *rl,
uint8_t content_type, const uint8_t *content, size_t content_len, CBB *out)
{