summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorjsing <jsing@openbsd.org>2020-08-30 15:40:19 +0000
committerjsing <jsing@openbsd.org>2020-08-30 15:40:19 +0000
commitacef91a04bad05a857d0fd8af28c5795c0afc5ec (patch)
tree13fe517f18a9b3c3b4fa02d6dfff5061d8c6356e
parentadd missing wakeup for the unlikely dying case (diff)
downloadwireguard-openbsd-acef91a04bad05a857d0fd8af28c5795c0afc5ec.tar.xz
wireguard-openbsd-acef91a04bad05a857d0fd8af28c5795c0afc5ec.zip
Start replacing the existing TLSv1.2 record layer.
This takes the same design/approach used in TLSv1.3 and provides an opaque struct that is self contained and cannot reach back into other layers. For now this just implements/replaces the writing of records for DTLSv1/TLSv1.0/TLSv1.1/TLSv1.2. In doing so we stop copying the plaintext into the same buffer that is used to transmit to the wire. ok inoguchi@ tb@
-rw-r--r--lib/libssl/Makefile3
-rw-r--r--lib/libssl/d1_pkt.c90
-rw-r--r--lib/libssl/ssl_lib.c15
-rw-r--r--lib/libssl/ssl_locl.h33
-rw-r--r--lib/libssl/ssl_pkt.c103
-rw-r--r--lib/libssl/t1_enc.c32
-rw-r--r--lib/libssl/tls12_record_layer.c533
7 files changed, 614 insertions, 195 deletions
diff --git a/lib/libssl/Makefile b/lib/libssl/Makefile
index c162e84b77d..2e06f13e9c0 100644
--- a/lib/libssl/Makefile
+++ b/lib/libssl/Makefile
@@ -1,4 +1,4 @@
-# $OpenBSD: Makefile,v 1.66 2020/06/09 16:53:53 deraadt Exp $
+# $OpenBSD: Makefile,v 1.67 2020/08/30 15:40:19 jsing Exp $
.include <bsd.own.mk>
.ifndef NOMAN
@@ -69,6 +69,7 @@ SRCS= \
ssl_versions.c \
t1_enc.c \
t1_lib.c \
+ tls12_record_layer.c \
tls13_buffer.c \
tls13_client.c \
tls13_error.c \
diff --git a/lib/libssl/d1_pkt.c b/lib/libssl/d1_pkt.c
index 4a6b3b7dcf1..748ff988da8 100644
--- a/lib/libssl/d1_pkt.c
+++ b/lib/libssl/d1_pkt.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: d1_pkt.c,v 1.80 2020/08/11 19:21:54 jsing Exp $ */
+/* $OpenBSD: d1_pkt.c,v 1.81 2020/08/30 15:40:19 jsing Exp $ */
/*
* DTLS implementation written by Nagendra Modadugu
* (nagendra@cs.stanford.edu) for the OpenSSL project 2005.
@@ -1174,14 +1174,9 @@ dtls1_write_bytes(SSL *s, int type, const void *buf, int len)
int
do_dtls1_write(SSL *s, int type, const unsigned char *buf, unsigned int len)
{
- SSL3_RECORD_INTERNAL *wr = &(S3I(s)->wrec);
SSL3_BUFFER_INTERNAL *wb = &(S3I(s)->wbuf);
- SSL_SESSION *sess = s->session;
- int block_size = 0, eivlen = 0, mac_size = 0;
- size_t pad_len, record_len;
- CBB cbb, fragment;
size_t out_len;
- uint8_t *p;
+ CBB cbb;
int ret;
memset(&cbb, 0, sizeof(cbb));
@@ -1205,81 +1200,15 @@ do_dtls1_write(SSL *s, int type, const unsigned char *buf, unsigned int len)
if (len == 0)
return 0;
- if (sess != NULL && s->internal->enc_write_ctx != NULL &&
- EVP_MD_CTX_md(s->internal->write_hash) != NULL) {
- if ((mac_size = EVP_MD_CTX_size(s->internal->write_hash)) < 0)
- goto err;
- }
-
- /* Explicit IV length. */
- if (s->internal->enc_write_ctx && SSL_USE_EXPLICIT_IV(s)) {
- int mode = EVP_CIPHER_CTX_mode(s->internal->enc_write_ctx);
- if (mode == EVP_CIPH_CBC_MODE) {
- eivlen = EVP_CIPHER_CTX_iv_length(s->internal->enc_write_ctx);
- if (eivlen <= 1)
- eivlen = 0;
- }
- } else if (s->internal->aead_write_ctx != NULL &&
- s->internal->aead_write_ctx->variable_nonce_in_record) {
- eivlen = s->internal->aead_write_ctx->variable_nonce_len;
- }
-
- /* Determine length of record fragment. */
- record_len = eivlen + len + mac_size;
- if (s->internal->enc_write_ctx != NULL) {
- block_size = EVP_CIPHER_CTX_block_size(s->internal->enc_write_ctx);
- if (block_size <= 0 || block_size > EVP_MAX_BLOCK_LENGTH)
- goto err;
- if (block_size > 1) {
- pad_len = block_size - (record_len % block_size);
- record_len += pad_len;
- }
- } else if (s->internal->aead_write_ctx != NULL) {
- record_len += s->internal->aead_write_ctx->tag_len;
- }
-
- /* DTLS implements explicit IV, so no need for empty fragments. */
-
wb->offset = 0;
if (!CBB_init_fixed(&cbb, wb->buf, wb->len))
goto err;
- /* Write the header. */
- if (!CBB_add_u8(&cbb, type))
- goto err;
- if (!CBB_add_u16(&cbb, s->version))
- goto err;
- if (!CBB_add_u16(&cbb, D1I(s)->w_epoch))
- goto err;
- if (!CBB_add_bytes(&cbb, &(S3I(s)->write_sequence[2]), 6))
- goto err;
- if (!CBB_add_u16_length_prefixed(&cbb, &fragment))
- goto err;
- if (!CBB_add_space(&fragment, &p, record_len))
- goto err;
-
- wr->type = type;
- wr->data = p + eivlen;
- wr->length = (int)len;
- wr->input = wr->data;
-
- memcpy(wr->data, buf, len);
-
- if (mac_size != 0) {
- if (tls1_mac(s, &(p[wr->length + eivlen]), 1) < 0)
- goto err;
- wr->length += mac_size;
- }
+ tls12_record_layer_set_version(s->internal->rl, s->version);
+ tls12_record_layer_set_write_epoch(s->internal->rl, D1I(s)->w_epoch);
- wr->data = p;
- wr->input = p;
- wr->length += eivlen;
-
- if (tls1_enc(s, 1) != 1)
- goto err;
-
- if (wr->length != record_len)
+ if (!tls12_record_layer_seal_record(s->internal->rl, type, buf, len, &cbb))
goto err;
if (!CBB_finish(&cbb, NULL, &out_len))
@@ -1288,15 +1217,6 @@ do_dtls1_write(SSL *s, int type, const unsigned char *buf, unsigned int len)
wb->left = out_len;
/*
- * We should now have wr->data pointing to the encrypted data,
- * which is wr->length long.
- */
- wr->type = type; /* not needed but helps for debugging */
- wr->length += DTLS1_RT_HEADER_LENGTH;
-
- tls1_record_sequence_increment(S3I(s)->write_sequence);
-
- /*
* Memorize arguments so that ssl3_write_pending can detect
* bad write retries later.
*/
diff --git a/lib/libssl/ssl_lib.c b/lib/libssl/ssl_lib.c
index bd3188cdf6d..bf10cea6857 100644
--- a/lib/libssl/ssl_lib.c
+++ b/lib/libssl/ssl_lib.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssl_lib.c,v 1.220 2020/08/11 18:39:40 jsing Exp $ */
+/* $OpenBSD: ssl_lib.c,v 1.221 2020/08/30 15:40:19 jsing Exp $ */
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
* All rights reserved.
*
@@ -344,6 +344,9 @@ SSL_new(SSL_CTX *ctx)
if (!s->method->internal->ssl_new(s))
goto err;
+ if ((s->internal->rl = tls12_record_layer_new()) == NULL)
+ goto err;
+
s->references = 1;
s->server = (ctx->method->internal->ssl_accept == ssl_undefined_function) ? 0 : 1;
@@ -564,6 +567,8 @@ SSL_free(SSL *s)
sk_SRTP_PROTECTION_PROFILE_free(s->internal->srtp_profiles);
#endif
+ tls12_record_layer_free(s->internal->rl);
+
free(s->internal);
free(s);
}
@@ -2535,6 +2540,10 @@ ssl_clear_cipher_read_state(SSL *s)
EVP_MD_CTX_free(s->read_hash);
s->read_hash = NULL;
+ tls12_record_layer_clear_read_state(s->internal->rl);
+ tls12_record_layer_set_read_seq_num(s->internal->rl,
+ S3I(s)->read_sequence);
+
if (s->internal->aead_read_ctx != NULL) {
EVP_AEAD_CTX_cleanup(&s->internal->aead_read_ctx->ctx);
free(s->internal->aead_read_ctx);
@@ -2550,6 +2559,10 @@ ssl_clear_cipher_write_state(SSL *s)
EVP_MD_CTX_free(s->internal->write_hash);
s->internal->write_hash = NULL;
+ tls12_record_layer_clear_write_state(s->internal->rl);
+ tls12_record_layer_set_write_seq_num(s->internal->rl,
+ S3I(s)->write_sequence);
+
if (s->internal->aead_write_ctx != NULL) {
EVP_AEAD_CTX_cleanup(&s->internal->aead_write_ctx->ctx);
free(s->internal->aead_write_ctx);
diff --git a/lib/libssl/ssl_locl.h b/lib/libssl/ssl_locl.h
index 676f404352a..e41465419a6 100644
--- a/lib/libssl/ssl_locl.h
+++ b/lib/libssl/ssl_locl.h
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssl_locl.h,v 1.283 2020/08/11 18:40:24 jsing Exp $ */
+/* $OpenBSD: ssl_locl.h,v 1.284 2020/08/30 15:40:20 jsing Exp $ */
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
* All rights reserved.
*
@@ -475,6 +475,34 @@ typedef struct ssl_handshake_tls13_st {
} SSL_HANDSHAKE_TLS13;
+struct tls12_record_layer;
+
+struct tls12_record_layer *tls12_record_layer_new(void);
+void tls12_record_layer_free(struct tls12_record_layer *rl);
+void tls12_record_layer_set_version(struct tls12_record_layer *rl,
+ uint16_t version);
+void tls12_record_layer_set_read_epoch(struct tls12_record_layer *rl,
+ uint16_t epoch);
+void tls12_record_layer_set_write_epoch(struct tls12_record_layer *rl,
+ uint16_t epoch);
+void tls12_record_layer_clear_read_state(struct tls12_record_layer *rl);
+void tls12_record_layer_clear_write_state(struct tls12_record_layer *rl);
+void tls12_record_layer_set_read_seq_num(struct tls12_record_layer *rl,
+ uint8_t *seq_num);
+void tls12_record_layer_set_write_seq_num(struct tls12_record_layer *rl,
+ uint8_t *seq_num);
+int tls12_record_layer_set_read_aead(struct tls12_record_layer *rl,
+ SSL_AEAD_CTX *aead_ctx);
+int tls12_record_layer_set_write_aead(struct tls12_record_layer *rl,
+ SSL_AEAD_CTX *aead_ctx);
+int tls12_record_layer_set_read_cipher_hash(struct tls12_record_layer *rl,
+ EVP_CIPHER_CTX *cipher_ctx, EVP_MD_CTX *hash_ctx, int stream_mac);
+int tls12_record_layer_set_write_cipher_hash(struct tls12_record_layer *rl,
+ EVP_CIPHER_CTX *cipher_ctx, EVP_MD_CTX *hash_ctx, int stream_mac);
+int tls12_record_layer_seal_record(struct tls12_record_layer *rl,
+ uint8_t content_type, const uint8_t *content, size_t content_len,
+ CBB *out);
+
typedef struct ssl_ctx_internal_st {
uint16_t min_version;
uint16_t max_version;
@@ -736,6 +764,8 @@ typedef struct ssl_internal_st {
EVP_CIPHER_CTX *enc_write_ctx; /* cryptographic state */
EVP_MD_CTX *write_hash; /* used for mac generation */
+ struct tls12_record_layer *rl;
+
/* session info */
/* extra application data */
@@ -826,7 +856,6 @@ typedef struct ssl3_state_internal_st {
int empty_fragment_done;
SSL3_RECORD_INTERNAL rrec; /* each decoded record goes in here */
- SSL3_RECORD_INTERNAL wrec; /* goes out from here */
/* storage for Alert/Handshake protocol data received but not
* yet processed by ssl3_read_bytes: */
diff --git a/lib/libssl/ssl_pkt.c b/lib/libssl/ssl_pkt.c
index 5c9b3be2ff0..c9c86471d3c 100644
--- a/lib/libssl/ssl_pkt.c
+++ b/lib/libssl/ssl_pkt.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: ssl_pkt.c,v 1.30 2020/08/09 16:54:16 jsing Exp $ */
+/* $OpenBSD: ssl_pkt.c,v 1.31 2020/08/30 15:40:20 jsing Exp $ */
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
* All rights reserved.
*
@@ -617,100 +617,6 @@ ssl3_write_bytes(SSL *s, int type, const void *buf_, int len)
}
static int
-ssl3_create_record(SSL *s, CBB *cbb, uint16_t version, uint8_t type,
- const unsigned char *buf, unsigned int len)
-{
- SSL3_RECORD_INTERNAL *wr = &(S3I(s)->wrec);
- SSL_SESSION *sess = s->session;
- int block_size = 0, eivlen = 0, mac_size = 0;
- size_t pad_len, record_len;
- CBB fragment;
- uint8_t *p;
-
- if (sess != NULL && s->internal->enc_write_ctx != NULL &&
- EVP_MD_CTX_md(s->internal->write_hash) != NULL) {
- if ((mac_size = EVP_MD_CTX_size(s->internal->write_hash)) < 0)
- goto err;
- }
-
- /* Explicit IV length. */
- if (s->internal->enc_write_ctx && SSL_USE_EXPLICIT_IV(s)) {
- int mode = EVP_CIPHER_CTX_mode(s->internal->enc_write_ctx);
- if (mode == EVP_CIPH_CBC_MODE) {
- eivlen = EVP_CIPHER_CTX_iv_length(s->internal->enc_write_ctx);
- if (eivlen <= 1)
- eivlen = 0;
- }
- } else if (s->internal->aead_write_ctx != NULL &&
- s->internal->aead_write_ctx->variable_nonce_in_record) {
- eivlen = s->internal->aead_write_ctx->variable_nonce_len;
- }
-
- /* Determine length of record fragment. */
- record_len = eivlen + len + mac_size;
- if (s->internal->enc_write_ctx != NULL) {
- block_size = EVP_CIPHER_CTX_block_size(s->internal->enc_write_ctx);
- if (block_size <= 0 || block_size > EVP_MAX_BLOCK_LENGTH)
- goto err;
- if (block_size > 1) {
- pad_len = block_size - (record_len % block_size);
- record_len += pad_len;
- }
- } else if (s->internal->aead_write_ctx != NULL) {
- record_len += s->internal->aead_write_ctx->tag_len;
- }
-
- /* Write the header. */
- if (!CBB_add_u8(cbb, type))
- goto err;
- if (!CBB_add_u16(cbb, version))
- goto err;
- if (!CBB_add_u16_length_prefixed(cbb, &fragment))
- goto err;
- if (!CBB_add_space(&fragment, &p, record_len))
- goto err;
-
- /* Set up the record. */
- wr->type = type;
- wr->data = p + eivlen;
- wr->length = (int)len;
- wr->input = wr->data;
-
- memcpy(wr->data, buf, len);
-
- if (mac_size != 0) {
- if (tls1_mac(s, &(p[wr->length + eivlen]), 1) < 0)
- goto err;
- wr->length += mac_size;
- }
-
- wr->data = p;
- wr->input = p;
- wr->length += eivlen;
-
- if (tls1_enc(s, 1) != 1)
- goto err;
-
- if (wr->length != record_len)
- goto err;
-
- if (!CBB_flush(cbb))
- goto err;
-
- /*
- * We should now have wr->data pointing to the encrypted data,
- * which is wr->length long.
- */
- wr->type = type; /* not needed but helps for debugging */
- wr->length += SSL3_RT_HEADER_LENGTH;
-
- return 1;
-
- err:
- return 0;
-}
-
-static int
do_ssl3_write(SSL *s, int type, const unsigned char *buf, unsigned int len)
{
SSL3_BUFFER_INTERNAL *wb = &(S3I(s)->wbuf);
@@ -785,13 +691,16 @@ do_ssl3_write(SSL *s, int type, const unsigned char *buf, unsigned int len)
if (!CBB_init_fixed(&cbb, wb->buf + align, wb->len - align))
goto err;
+ tls12_record_layer_set_version(s->internal->rl, version);
+
if (need_empty_fragment) {
- if (!ssl3_create_record(s, &cbb, version, type, buf, 0))
+ if (!tls12_record_layer_seal_record(s->internal->rl, type,
+ buf, 0, &cbb))
goto err;
S3I(s)->empty_fragment_done = 1;
}
- if (!ssl3_create_record(s, &cbb, version, type, buf, len))
+ if (!tls12_record_layer_seal_record(s->internal->rl, type, buf, len, &cbb))
goto err;
if (!CBB_finish(&cbb, NULL, &out_len))
diff --git a/lib/libssl/t1_enc.c b/lib/libssl/t1_enc.c
index 2893e1d4dc5..a66c82bdca6 100644
--- a/lib/libssl/t1_enc.c
+++ b/lib/libssl/t1_enc.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: t1_enc.c,v 1.122 2020/03/16 15:25:14 tb Exp $ */
+/* $OpenBSD: t1_enc.c,v 1.123 2020/08/30 15:40:20 jsing Exp $ */
/* Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
* All rights reserved.
*
@@ -350,11 +350,17 @@ tls1_change_cipher_state_aead(SSL *s, char is_read, const unsigned char *key,
if (!tls1_aead_ctx_init(&s->internal->aead_read_ctx))
return 0;
aead_ctx = s->internal->aead_read_ctx;
+
+ if (!tls12_record_layer_set_read_aead(s->internal->rl, aead_ctx))
+ return 0;
} else {
ssl_clear_cipher_write_state(s);
if (!tls1_aead_ctx_init(&s->internal->aead_write_ctx))
return 0;
aead_ctx = s->internal->aead_write_ctx;
+
+ if (!tls12_record_layer_set_write_aead(s->internal->rl, aead_ctx))
+ return 0;
}
if (!EVP_AEAD_CTX_init(&aead_ctx->ctx, aead, key, key_len,
@@ -408,14 +414,16 @@ tls1_change_cipher_state_cipher(SSL *s, char is_read,
EVP_MD_CTX *mac_ctx;
EVP_PKEY *mac_key;
const EVP_MD *mac;
+ int stream_mac;
int mac_type;
cipher = S3I(s)->tmp.new_sym_enc;
mac = S3I(s)->tmp.new_hash;
mac_type = S3I(s)->tmp.new_mac_pkey_type;
+ stream_mac = S3I(s)->hs.new_cipher->algorithm2 & TLS1_STREAM_MAC;
if (is_read) {
- if (S3I(s)->hs.new_cipher->algorithm2 & TLS1_STREAM_MAC)
+ if (stream_mac)
s->internal->mac_flags |= SSL_MAC_FLAG_READ_MAC_STREAM;
else
s->internal->mac_flags &= ~SSL_MAC_FLAG_READ_MAC_STREAM;
@@ -428,8 +436,12 @@ tls1_change_cipher_state_cipher(SSL *s, char is_read,
if ((mac_ctx = EVP_MD_CTX_new()) == NULL)
goto err;
s->read_hash = mac_ctx;
+
+ if (!tls12_record_layer_set_read_cipher_hash(s->internal->rl,
+ cipher_ctx, mac_ctx, stream_mac))
+ goto err;
} else {
- if (S3I(s)->hs.new_cipher->algorithm2 & TLS1_STREAM_MAC)
+ if (stream_mac)
s->internal->mac_flags |= SSL_MAC_FLAG_WRITE_MAC_STREAM;
else
s->internal->mac_flags &= ~SSL_MAC_FLAG_WRITE_MAC_STREAM;
@@ -450,6 +462,10 @@ tls1_change_cipher_state_cipher(SSL *s, char is_read,
if ((mac_ctx = EVP_MD_CTX_new()) == NULL)
goto err;
s->internal->write_hash = mac_ctx;
+
+ if (!tls12_record_layer_set_write_cipher_hash(s->internal->rl,
+ cipher_ctx, mac_ctx, stream_mac))
+ goto err;
}
EVP_CipherInit_ex(cipher_ctx, cipher, NULL, key, iv, !is_read);
@@ -677,9 +693,8 @@ tls1_enc(SSL *s, int send)
int bs, i, j, k, ret, mac_size = 0;
if (send) {
- aead = s->internal->aead_write_ctx;
- rec = &S3I(s)->wrec;
- seq = S3I(s)->write_sequence;
+ /* No longer supported. */
+ return -1;
} else {
aead = s->internal->aead_read_ctx;
rec = &S3I(s)->rrec;
@@ -946,9 +961,8 @@ tls1_mac(SSL *ssl, unsigned char *md, int send)
int t;
if (send) {
- rec = &(ssl->s3->internal->wrec);
- seq = &(ssl->s3->internal->write_sequence[0]);
- hash = ssl->internal->write_hash;
+ /* No longer supported. */
+ return -1;
} else {
rec = &(ssl->s3->internal->rrec);
seq = &(ssl->s3->internal->read_sequence[0]);
diff --git a/lib/libssl/tls12_record_layer.c b/lib/libssl/tls12_record_layer.c
new file mode 100644
index 00000000000..5e7a3a610c6
--- /dev/null
+++ b/lib/libssl/tls12_record_layer.c
@@ -0,0 +1,533 @@
+/* $OpenBSD: tls12_record_layer.c,v 1.1 2020/08/30 15:40:20 jsing Exp $ */
+/*
+ * Copyright (c) 2020 Joel Sing <jsing@openbsd.org>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <stdlib.h>
+
+#include <openssl/evp.h>
+
+#include "ssl_locl.h"
+
+struct tls12_record_layer {
+ uint16_t version;
+ int dtls;
+
+ uint16_t read_epoch;
+ uint16_t write_epoch;
+
+ int read_stream_mac;
+ int write_stream_mac;
+
+ /*
+ * XXX - for now these are just pointers to externally managed
+ * structs/memory. These should eventually be owned by the record layer.
+ */
+ SSL_AEAD_CTX *read_aead_ctx;
+ SSL_AEAD_CTX *write_aead_ctx;
+
+ EVP_CIPHER_CTX *read_cipher_ctx;
+ EVP_MD_CTX *read_hash_ctx;
+ EVP_CIPHER_CTX *write_cipher_ctx;
+ EVP_MD_CTX *write_hash_ctx;
+
+ uint8_t *read_seq_num;
+ uint8_t *write_seq_num;
+};
+
+struct tls12_record_layer *
+tls12_record_layer_new(void)
+{
+ struct tls12_record_layer *rl;
+
+ if ((rl = calloc(1, sizeof(struct tls12_record_layer))) == NULL)
+ return NULL;
+
+ return rl;
+}
+
+void
+tls12_record_layer_free(struct tls12_record_layer *rl)
+{
+ freezero(rl, sizeof(struct tls12_record_layer));
+}
+
+void
+tls12_record_layer_set_version(struct tls12_record_layer *rl, uint16_t version)
+{
+ rl->version = version;
+ rl->dtls = (version == DTLS1_VERSION);
+}
+
+void
+tls12_record_layer_set_read_epoch(struct tls12_record_layer *rl, uint16_t epoch)
+{
+ rl->read_epoch = epoch;
+}
+
+void
+tls12_record_layer_set_write_epoch(struct tls12_record_layer *rl, uint16_t epoch)
+{
+ rl->write_epoch = epoch;
+}
+
+static void
+tls12_record_layer_set_read_state(struct tls12_record_layer *rl,
+ SSL_AEAD_CTX *aead_ctx, EVP_CIPHER_CTX *cipher_ctx, EVP_MD_CTX *hash_ctx,
+ int stream_mac)
+{
+ rl->read_aead_ctx = aead_ctx;
+
+ rl->read_cipher_ctx = cipher_ctx;
+ rl->read_hash_ctx = hash_ctx;
+ rl->read_stream_mac = stream_mac;
+}
+
+static void
+tls12_record_layer_set_write_state(struct tls12_record_layer *rl,
+ SSL_AEAD_CTX *aead_ctx, EVP_CIPHER_CTX *cipher_ctx, EVP_MD_CTX *hash_ctx,
+ int stream_mac)
+{
+ rl->write_aead_ctx = aead_ctx;
+
+ rl->write_cipher_ctx = cipher_ctx;
+ rl->write_hash_ctx = hash_ctx;
+ rl->write_stream_mac = stream_mac;
+}
+
+void
+tls12_record_layer_clear_read_state(struct tls12_record_layer *rl)
+{
+ tls12_record_layer_set_read_state(rl, NULL, NULL, NULL, 0);
+ rl->read_seq_num = NULL;
+}
+
+void
+tls12_record_layer_clear_write_state(struct tls12_record_layer *rl)
+{
+ tls12_record_layer_set_write_state(rl, NULL, NULL, NULL, 0);
+ rl->write_seq_num = NULL;
+}
+
+void
+tls12_record_layer_set_read_seq_num(struct tls12_record_layer *rl,
+ uint8_t *seq_num)
+{
+ rl->read_seq_num = seq_num;
+}
+
+void
+tls12_record_layer_set_write_seq_num(struct tls12_record_layer *rl,
+ uint8_t *seq_num)
+{
+ rl->write_seq_num = seq_num;
+}
+
+int
+tls12_record_layer_set_read_aead(struct tls12_record_layer *rl,
+ SSL_AEAD_CTX *aead_ctx)
+{
+ tls12_record_layer_set_read_state(rl, aead_ctx, NULL, NULL, 0);
+
+ return 1;
+}
+
+int
+tls12_record_layer_set_write_aead(struct tls12_record_layer *rl,
+ SSL_AEAD_CTX *aead_ctx)
+{
+ tls12_record_layer_set_write_state(rl, aead_ctx, NULL, NULL, 0);
+
+ return 1;
+}
+
+int
+tls12_record_layer_set_read_cipher_hash(struct tls12_record_layer *rl,
+ EVP_CIPHER_CTX *cipher_ctx, EVP_MD_CTX *hash_ctx, int stream_mac)
+{
+ tls12_record_layer_set_read_state(rl, NULL, cipher_ctx, hash_ctx,
+ stream_mac);
+
+ return 1;
+}
+
+int
+tls12_record_layer_set_write_cipher_hash(struct tls12_record_layer *rl,
+ EVP_CIPHER_CTX *cipher_ctx, EVP_MD_CTX *hash_ctx, int stream_mac)
+{
+ tls12_record_layer_set_write_state(rl, NULL, cipher_ctx, hash_ctx,
+ stream_mac);
+
+ 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)
+{
+ CBS seq;
+
+ CBS_init(&seq, seq_num, seq_num_len);
+
+ if (rl->dtls) {
+ if (!CBB_add_u16(cbb, epoch))
+ return 0;
+ if (!CBS_skip(&seq, 2))
+ return 0;
+ }
+
+ return CBB_add_bytes(cbb, CBS_data(&seq), CBS_len(&seq));
+}
+
+static int
+tls12_record_layer_pseudo_header(struct tls12_record_layer *rl,
+ uint8_t content_type, uint16_t record_len, uint16_t epoch, uint8_t *seq_num,
+ size_t seq_num_len, uint8_t **out, size_t *out_len)
+{
+ CBB cbb;
+
+ *out = NULL;
+ *out_len = 0;
+
+ /* Build the pseudo-header used for MAC/AEAD. */
+ if (!CBB_init(&cbb, 13))
+ goto err;
+
+ if (!tls12_record_layer_build_seq_num(rl, &cbb, epoch,
+ seq_num, seq_num_len))
+ goto err;
+ if (!CBB_add_u8(&cbb, content_type))
+ goto err;
+ if (!CBB_add_u16(&cbb, rl->version))
+ goto err;
+ if (!CBB_add_u16(&cbb, record_len))
+ goto err;
+
+ if (!CBB_finish(&cbb, out, out_len))
+ goto err;
+
+ return 1;
+
+ err:
+ CBB_cleanup(&cbb);
+
+ return 0;
+}
+
+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)
+{
+ EVP_MD_CTX *mac_ctx = NULL;
+ uint8_t *header = NULL;
+ size_t header_len;
+ size_t mac_len;
+ uint8_t *mac;
+ int ret = 0;
+
+ if ((mac_ctx = EVP_MD_CTX_new()) == NULL)
+ goto err;
+ if (!EVP_MD_CTX_copy(mac_ctx, rl->write_hash_ctx))
+ goto err;
+
+ if (!tls12_record_layer_pseudo_header(rl, content_type, content_len,
+ rl->write_epoch, rl->write_seq_num, SSL3_SEQUENCE_SIZE,
+ &header, &header_len))
+ goto err;
+
+ if (EVP_DigestSignUpdate(mac_ctx, header, header_len) <= 0)
+ goto err;
+ if (EVP_DigestSignUpdate(mac_ctx, content, content_len) <= 0)
+ goto err;
+ if (EVP_DigestSignFinal(mac_ctx, NULL, &mac_len) <= 0)
+ goto err;
+ if (!CBB_add_space(cbb, &mac, mac_len))
+ goto err;
+ if (EVP_DigestSignFinal(mac_ctx, mac, &mac_len) <= 0)
+ goto err;
+
+ if (rl->write_stream_mac) {
+ if (!EVP_MD_CTX_copy(rl->write_hash_ctx, mac_ctx))
+ goto err;
+ }
+
+ *out_len = mac_len;
+
+ ret = 1;
+
+ err:
+ EVP_MD_CTX_free(mac_ctx);
+ free(header);
+
+ return ret;
+}
+
+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)
+{
+ if (rl->write_aead_ctx != NULL || rl->write_cipher_ctx != NULL)
+ return 0;
+
+ return CBB_add_bytes(out, content, content_len);
+}
+
+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)
+{
+ CBB cbb;
+
+ if (aead->variable_nonce_len > SSL3_SEQUENCE_SIZE)
+ return 0;
+
+ /* Fixed nonce and variable nonce (sequence number) are concatenated. */
+ if (!CBB_init(&cbb, 16))
+ goto err;
+ if (!CBB_add_bytes(&cbb, aead->fixed_nonce,
+ aead->fixed_nonce_len))
+ goto err;
+ if (!CBB_add_bytes(&cbb, seq_num, aead->variable_nonce_len))
+ goto err;
+ if (!CBB_finish(&cbb, out, out_len))
+ goto err;
+
+ return 1;
+
+ err:
+ CBB_cleanup(&cbb);
+
+ return 0;
+}
+
+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)
+{
+ uint8_t *nonce = NULL;
+ size_t nonce_len = 0;
+ uint8_t *pad;
+ CBB cbb;
+ int i;
+
+ if (aead->variable_nonce_len > SSL3_SEQUENCE_SIZE)
+ return 0;
+ if (aead->fixed_nonce_len < aead->variable_nonce_len)
+ return 0;
+
+ /*
+ * Variable nonce (sequence number) is right padded, before the fixed
+ * nonce is XOR'd in.
+ */
+ if (!CBB_init(&cbb, 16))
+ goto err;
+ if (!CBB_add_space(&cbb, &pad,
+ aead->fixed_nonce_len - aead->variable_nonce_len))
+ goto err;
+ if (!CBB_add_bytes(&cbb, seq_num, aead->variable_nonce_len))
+ goto err;
+ if (!CBB_finish(&cbb, &nonce, &nonce_len))
+ goto err;
+
+ for (i = 0; i < aead->fixed_nonce_len; i++)
+ nonce[i] ^= aead->fixed_nonce[i];
+
+ *out = nonce;
+ *out_len = nonce_len;
+
+ return 1;
+
+ err:
+ CBB_cleanup(&cbb);
+ freezero(nonce, nonce_len);
+
+ return 0;
+}
+
+static int
+tls12_record_layer_seal_record_protected_aead(struct tls12_record_layer *rl,
+ uint8_t content_type, const uint8_t *content, size_t content_len, CBB *out)
+{
+ const SSL_AEAD_CTX *aead = rl->write_aead_ctx;
+ uint8_t *header = NULL, *nonce = NULL;
+ size_t header_len = 0, nonce_len = 0;
+ size_t enc_record_len, out_len;
+ uint16_t epoch = 0;
+ uint8_t *enc_data;
+ 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->write_seq_num, &nonce, &nonce_len))
+ goto err;
+ } else {
+ if (!tls12_record_layer_aead_concat_nonce(rl, aead,
+ rl->write_seq_num, &nonce, &nonce_len))
+ goto err;
+ }
+
+ if (aead->variable_nonce_in_record) {
+ /* XXX - length check? */
+ if (!CBB_add_bytes(out, rl->write_seq_num, aead->variable_nonce_len))
+ goto err;
+ }
+
+ if (!tls12_record_layer_pseudo_header(rl, content_type, content_len,
+ epoch, rl->write_seq_num, SSL3_SEQUENCE_SIZE, &header, &header_len))
+ goto err;
+
+ /* XXX EVP_AEAD_max_tag_len vs EVP_AEAD_CTX_tag_len. */
+ enc_record_len = content_len + aead->tag_len;
+ if (enc_record_len > SSL3_RT_MAX_ENCRYPTED_LENGTH)
+ goto err;
+ if (!CBB_add_space(out, &enc_data, enc_record_len))
+ goto err;
+
+ if (!EVP_AEAD_CTX_seal(&aead->ctx, enc_data, &out_len, enc_record_len,
+ nonce, nonce_len, content, content_len, header, header_len))
+ goto err;
+
+ if (out_len != enc_record_len)
+ goto err;
+
+ ret = 1;
+
+ err:
+ freezero(header, header_len);
+ freezero(nonce, nonce_len);
+
+ return ret;
+}
+
+static int
+tls12_record_layer_seal_record_protected_cipher(struct tls12_record_layer *rl,
+ uint8_t content_type, const uint8_t *content, size_t content_len, CBB *out)
+{
+ EVP_CIPHER_CTX *enc = rl->write_cipher_ctx;
+ size_t mac_len, pad_len;
+ int block_size, eiv_len;
+ uint8_t *enc_data, *eiv, *pad, pad_val;
+ uint8_t *plain = NULL;
+ size_t plain_len = 0;
+ int ret = 0;
+ CBB cbb;
+
+ if (!CBB_init(&cbb, SSL3_RT_MAX_PLAIN_LENGTH))
+ goto err;
+
+ /* Add explicit IV if necessary. */
+ 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;
+ if (eiv_len > 0) {
+ if (!CBB_add_space(&cbb, &eiv, eiv_len))
+ goto err;
+ arc4random_buf(eiv, eiv_len);
+ }
+
+ if (!CBB_add_bytes(&cbb, content, content_len))
+ goto err;
+
+ mac_len = 0;
+ if (rl->write_hash_ctx != NULL) {
+ if (!tls12_record_layer_write_mac(rl, &cbb, content_type,
+ content, content_len, &mac_len))
+ goto err;
+ }
+
+ plain_len = (size_t)eiv_len + content_len + mac_len;
+
+ /* Add padding to block size, if necessary. */
+ block_size = EVP_CIPHER_CTX_block_size(enc);
+ if (block_size < 0 || block_size > EVP_MAX_BLOCK_LENGTH)
+ return 0;
+ if (block_size > 1) {
+ pad_len = block_size - (plain_len % block_size);
+ pad_val = pad_len - 1;
+
+ if (pad_len > 255)
+ goto err;
+ if (!CBB_add_space(&cbb, &pad, pad_len))
+ goto err;
+ memset(pad, pad_val, pad_len);
+ }
+
+ if (!CBB_finish(&cbb, &plain, &plain_len))
+ goto err;
+
+ if (plain_len % block_size != 0)
+ goto err;
+ if (plain_len > SSL3_RT_MAX_ENCRYPTED_LENGTH)
+ goto err;
+
+ if (!CBB_add_space(out, &enc_data, plain_len))
+ goto err;
+ if (!EVP_Cipher(enc, enc_data, plain, plain_len))
+ goto err;
+
+ ret = 1;
+
+ err:
+ CBB_cleanup(&cbb);
+ freezero(plain, plain_len);
+
+ return ret;
+}
+
+int
+tls12_record_layer_seal_record(struct tls12_record_layer *rl,
+ uint8_t content_type, const uint8_t *content, size_t content_len, CBB *cbb)
+{
+ CBB fragment;
+
+ if (!CBB_add_u8(cbb, content_type))
+ return 0;
+ if (!CBB_add_u16(cbb, rl->version))
+ return 0;
+ if (rl->dtls) {
+ if (!tls12_record_layer_build_seq_num(rl, cbb,
+ rl->write_epoch, rl->write_seq_num,
+ SSL3_SEQUENCE_SIZE))
+ return 0;
+ }
+ if (!CBB_add_u16_length_prefixed(cbb, &fragment))
+ return 0;
+
+ if (rl->write_aead_ctx != NULL) {
+ if (!tls12_record_layer_seal_record_protected_aead(rl,
+ content_type, content, content_len, &fragment))
+ return 0;
+ } else if (rl->write_cipher_ctx != NULL) {
+ if (!tls12_record_layer_seal_record_protected_cipher(rl,
+ content_type, content, content_len, &fragment))
+ return 0;
+ } else {
+ if (!tls12_record_layer_seal_record_plaintext(rl,
+ content_type, content, content_len, &fragment))
+ return 0;
+ }
+
+ if (!CBB_flush(cbb))
+ return 0;
+
+ tls1_record_sequence_increment(rl->write_seq_num);
+
+ return 1;
+}