From 09997f3d41692022beb138f1e238f51af93a8024 Mon Sep 17 00:00:00 2001 From: jsing <> Date: Sun, 30 Aug 2020 15:40:20 +0000 Subject: 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@ --- src/lib/libssl/Makefile | 3 +- src/lib/libssl/d1_pkt.c | 90 +----- src/lib/libssl/ssl_lib.c | 15 +- src/lib/libssl/ssl_locl.h | 33 ++- src/lib/libssl/ssl_pkt.c | 103 +------ src/lib/libssl/t1_enc.c | 32 ++- src/lib/libssl/tls12_record_layer.c | 533 ++++++++++++++++++++++++++++++++++++ 7 files changed, 614 insertions(+), 195 deletions(-) create mode 100644 src/lib/libssl/tls12_record_layer.c (limited to 'src') diff --git a/src/lib/libssl/Makefile b/src/lib/libssl/Makefile index c162e84b77..2e06f13e9c 100644 --- a/src/lib/libssl/Makefile +++ b/src/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 .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/src/lib/libssl/d1_pkt.c b/src/lib/libssl/d1_pkt.c index 4a6b3b7dcf..748ff988da 100644 --- a/src/lib/libssl/d1_pkt.c +++ b/src/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)) @@ -1287,15 +1216,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/src/lib/libssl/ssl_lib.c b/src/lib/libssl/ssl_lib.c index bd3188cdf6..bf10cea685 100644 --- a/src/lib/libssl/ssl_lib.c +++ b/src/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/src/lib/libssl/ssl_locl.h b/src/lib/libssl/ssl_locl.h index 676f404352..e41465419a 100644 --- a/src/lib/libssl/ssl_locl.h +++ b/src/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/src/lib/libssl/ssl_pkt.c b/src/lib/libssl/ssl_pkt.c index 5c9b3be2ff..c9c86471d3 100644 --- a/src/lib/libssl/ssl_pkt.c +++ b/src/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. * @@ -616,100 +616,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) { @@ -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/src/lib/libssl/t1_enc.c b/src/lib/libssl/t1_enc.c index 2893e1d4dc..a66c82bdca 100644 --- a/src/lib/libssl/t1_enc.c +++ b/src/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/src/lib/libssl/tls12_record_layer.c b/src/lib/libssl/tls12_record_layer.c new file mode 100644 index 0000000000..5e7a3a610c --- /dev/null +++ b/src/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 + * + * 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 + +#include + +#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; +} -- cgit v1.2.3-55-g6feb