diff options
Diffstat (limited to 'lib/libssl/tls13_record_layer.c')
-rw-r--r-- | lib/libssl/tls13_record_layer.c | 758 |
1 files changed, 758 insertions, 0 deletions
diff --git a/lib/libssl/tls13_record_layer.c b/lib/libssl/tls13_record_layer.c new file mode 100644 index 00000000000..b70f9f174e1 --- /dev/null +++ b/lib/libssl/tls13_record_layer.c @@ -0,0 +1,758 @@ +/* $OpenBSD: tls13_record_layer.c,v 1.1 2019/01/20 10:31:54 jsing Exp $ */ +/* + * Copyright (c) 2018, 2019 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 "ssl_locl.h" + +#include <openssl/curve25519.h> + +#include "tls13_internal.h" +#include "tls13_record.h" + +struct tls13_record_layer { + int change_cipher_spec_seen; + int handshake_completed; + + struct tls13_record *rrec; + struct tls13_record *wrec; + + /* Buffer containing plaintext from opened records. */ + uint8_t rbuf_content_type; + uint8_t *rbuf; + size_t rbuf_len; + CBS rbuf_cbs; + + /* Record protection. */ + const EVP_MD *hash; + const EVP_AEAD *aead; + EVP_AEAD_CTX read_aead_ctx; + EVP_AEAD_CTX write_aead_ctx; + struct tls13_secret read_iv; + struct tls13_secret write_iv; + struct tls13_secret read_nonce; + struct tls13_secret write_nonce; + uint8_t read_seq_num[TLS13_RECORD_SEQ_NUM_LEN]; + uint8_t write_seq_num[TLS13_RECORD_SEQ_NUM_LEN]; + + /* Record callbacks. */ + tls13_alert_cb alert_cb; + tls13_post_handshake_cb post_handshake_cb; + + /* Wire read/write callbacks. */ + tls13_read_cb wire_read; + tls13_write_cb wire_write; + void *cb_arg; +}; + +static void +tls13_record_layer_rbuf_free(struct tls13_record_layer *rl) +{ + CBS_init(&rl->rbuf_cbs, NULL, 0); + freezero(rl->rbuf, rl->rbuf_len); + rl->rbuf = NULL; + rl->rbuf_len = 0; + rl->rbuf_content_type = 0; +} + +static void +tls13_record_layer_rrec_free(struct tls13_record_layer *rl) +{ + tls13_record_free(rl->rrec); + rl->rrec = NULL; +} + +static void +tls13_record_layer_wrec_free(struct tls13_record_layer *rl) +{ + tls13_record_free(rl->wrec); + rl->wrec = NULL; +} + +struct tls13_record_layer * +tls13_record_layer_new(tls13_read_cb wire_read, tls13_write_cb wire_write, + tls13_alert_cb alert_cb, tls13_post_handshake_cb post_handshake_cb, + void *cb_arg) +{ + struct tls13_record_layer *rl; + + if ((rl = calloc(1, sizeof(struct tls13_record_layer))) == NULL) + return NULL; + + rl->wire_read = wire_read; + rl->wire_write = wire_write; + rl->alert_cb = alert_cb; + rl->post_handshake_cb = post_handshake_cb; + rl->cb_arg = cb_arg; + + return rl; +} + +void +tls13_record_layer_free(struct tls13_record_layer *rl) +{ + if (rl == NULL) + return; + + tls13_record_layer_rbuf_free(rl); + + tls13_record_layer_rrec_free(rl); + tls13_record_layer_wrec_free(rl); + + EVP_AEAD_CTX_cleanup(&rl->read_aead_ctx); + EVP_AEAD_CTX_cleanup(&rl->write_aead_ctx); + + freezero(rl->read_iv.data, rl->read_iv.len); + freezero(rl->write_iv.data, rl->write_iv.len); + freezero(rl->read_nonce.data, rl->read_nonce.len); + freezero(rl->write_nonce.data, rl->write_nonce.len); + + freezero(rl, sizeof(struct tls13_record_layer)); +} + +static int +tls13_record_layer_inc_seq_num(uint8_t *seq_num) +{ + size_t i; + + for (i = TLS13_RECORD_SEQ_NUM_LEN - 1; i > 0; i--) { + if (++seq_num[i] != 0) + break; + } + + /* RFC 8446 section 5.3 - sequence numbers must not wrap. */ + return (i != 0 || seq_num[0] != 0); +} + +static int +tls13_record_layer_update_nonce(struct tls13_secret *nonce, + struct tls13_secret *iv, uint8_t *seq_num) +{ + ssize_t i, j; + + if (nonce->len != iv->len) + return 0; + + /* + * RFC 8446 section 5.3 - sequence number is zero padded and XOR'd + * with the IV to produce a per-record nonce. The IV will also be + * at least 8-bytes in length. + */ + for (i = nonce->len - 1, j = TLS13_RECORD_SEQ_NUM_LEN - 1; i >= 0; i--, j--) + nonce->data[i] = iv->data[i] ^ (j >= 0 ? seq_num[j] : 0); + + return 1; +} + +void +tls13_record_layer_set_aead(struct tls13_record_layer *rl, + const EVP_AEAD *aead) +{ + rl->aead = aead; +} + +void +tls13_record_layer_set_hash(struct tls13_record_layer *rl, + const EVP_MD *hash) +{ + rl->hash = hash; +} + +void +tls13_record_layer_handshake_completed(struct tls13_record_layer *rl) +{ + rl->handshake_completed = 1; +} + +static int +tls13_record_layer_process_alert(struct tls13_record_layer *rl) +{ + uint8_t alert_level, alert_desc; + + /* + * A TLSv1.3 alert record can only contain a single alert - this means + * that processing the alert must consume all of the record. The alert + * will result in one of three things - continuation (user_cancelled), + * read channel closure (close_notify) or termination (all others). + */ + if (rl->rbuf == NULL) + return -1; + if (rl->rbuf_content_type != SSL3_RT_ALERT) + return -1; + + if (!CBS_get_u8(&rl->rbuf_cbs, &alert_level)) + return -1; /* XXX - decode error alert. */ + if (!CBS_get_u8(&rl->rbuf_cbs, &alert_desc)) + return -1; /* XXX - decode error alert. */ + + if (CBS_len(&rl->rbuf_cbs) != 0) + return -1; + + tls13_record_layer_rbuf_free(rl); + + return rl->alert_cb(alert_level, alert_desc, rl->cb_arg); +} + +int +tls13_record_layer_send_alert(struct tls13_record_layer *rl, + uint8_t alert_level, uint8_t alert_desc) +{ + /* XXX - implement. */ + return -1; +} + +static int +tls13_record_layer_set_traffic_key(const EVP_AEAD *aead, EVP_AEAD_CTX *aead_ctx, + const EVP_MD *hash, struct tls13_secret *iv, struct tls13_secret *nonce, + struct tls13_secret *traffic_key) +{ + struct tls13_secret context = { .data = "", .len = 0 }; + struct tls13_secret key = { .data = NULL, .len = 0 }; + int ret = 0; + + freezero(iv->data, iv->len); + iv->data = NULL; + iv->len = 0; + + freezero(nonce->data, nonce->len); + nonce->data = NULL; + nonce->len = 0; + + if ((iv->data = calloc(1, EVP_AEAD_nonce_length(aead))) == NULL) + goto err; + iv->len = EVP_AEAD_nonce_length(aead); + + if ((nonce->data = calloc(1, EVP_AEAD_nonce_length(aead))) == NULL) + goto err; + nonce->len = EVP_AEAD_nonce_length(aead); + + if ((key.data = calloc(1, EVP_AEAD_key_length(aead))) == NULL) + goto err; + key.len = EVP_AEAD_key_length(aead); + + if (!tls13_hkdf_expand_label(iv, hash, traffic_key, "iv", &context)) + goto err; + if (!tls13_hkdf_expand_label(&key, hash, traffic_key, "key", &context)) + goto err; + + if (!EVP_AEAD_CTX_init(aead_ctx, aead, key.data, key.len, + EVP_AEAD_DEFAULT_TAG_LENGTH, NULL)) + goto err; + + ret = 1; + + err: + freezero(key.data, key.len); + + return ret; +} + +int +tls13_record_layer_set_traffic_keys(struct tls13_record_layer *rl, + struct tls13_secret *read_key, struct tls13_secret *write_key) +{ + memset(rl->read_seq_num, 0, TLS13_RECORD_SEQ_NUM_LEN); + memset(rl->write_seq_num, 0, TLS13_RECORD_SEQ_NUM_LEN); + + if (!tls13_record_layer_set_traffic_key(rl->aead, &rl->read_aead_ctx, + rl->hash, &rl->read_iv, &rl->read_nonce, read_key)) + return 0; + + if (!tls13_record_layer_set_traffic_key(rl->aead, &rl->write_aead_ctx, + rl->hash, &rl->write_iv, &rl->write_nonce, write_key)) + return 0; + + return 1; +} + +static int +tls13_record_layer_open_record_plaintext(struct tls13_record_layer *rl) +{ + CBS cbs; + + if (rl->aead != NULL) + return 0; + + /* + * We're still operating in plaintext mode, so just copy the + * content from the record to the plaintext buffer. + */ + if (!tls13_record_content(rl->rrec, &cbs)) + return 0; + + tls13_record_layer_rbuf_free(rl); + + if (!CBS_stow(&cbs, &rl->rbuf, &rl->rbuf_len)) + return 0; + + rl->rbuf_content_type = tls13_record_content_type(rl->rrec); + + CBS_init(&rl->rbuf_cbs, rl->rbuf, rl->rbuf_len); + + return 1; +} + +static int +tls13_record_layer_open_record_protected(struct tls13_record_layer *rl) +{ + CBS header, enc_record; + uint8_t *content = NULL; + ssize_t content_len = 0; + uint8_t content_type; + size_t out_len; + + if (rl->aead == NULL) + goto err; + + if (!tls13_record_header(rl->rrec, &header)) + goto err; + if (!tls13_record_content(rl->rrec, &enc_record)) + goto err; + + if ((content = calloc(1, CBS_len(&enc_record))) == NULL) + goto err; + content_len = CBS_len(&enc_record); + + if (!tls13_record_layer_update_nonce(&rl->read_nonce, &rl->read_iv, + rl->read_seq_num)) + goto err; + + if (!EVP_AEAD_CTX_open(&rl->read_aead_ctx, + content, &out_len, content_len, + rl->read_nonce.data, rl->read_nonce.len, + CBS_data(&enc_record), CBS_len(&enc_record), + CBS_data(&header), CBS_len(&header))) + goto err; + + if (!tls13_record_layer_inc_seq_num(rl->read_seq_num)) + goto err; + + /* + * The real content type is hidden at the end of the record content and + * it may be followed by padding that consists of one or more zeroes. + * Time to hunt for that elusive content type! + */ + /* XXX - CBS from end? CBS_get_end_u8()? */ + content_len = out_len - 1; + while (content_len >= 0 && content[content_len] == 0) + content_len--; + if (content_len < 0) + goto err; + content_type = content[content_len]; + + tls13_record_layer_rbuf_free(rl); + + rl->rbuf_content_type = content_type; + rl->rbuf = content; + rl->rbuf_len = content_len; + + CBS_init(&rl->rbuf_cbs, rl->rbuf, rl->rbuf_len); + + return 1; + + err: + freezero(content, content_len); + + return 0; +} + +static int +tls13_record_layer_open_record(struct tls13_record_layer *rl) +{ + if (rl->aead == NULL) + return tls13_record_layer_open_record_plaintext(rl); + + return tls13_record_layer_open_record_protected(rl); +} + +static int +tls13_record_layer_seal_record_plaintext(struct tls13_record_layer *rl, + uint8_t content_type, const uint8_t *content, size_t content_len) +{ + uint8_t *data = NULL; + size_t data_len = 0; + uint16_t version; + CBB cbb, body; + + if (rl->aead != NULL) + return 0; + + /* XXX - TLS1_VERSION for first client hello... */ + version = TLS1_2_VERSION; + + /* + * We're still operating in plaintext mode, so just copy the + * content into the record. + */ + if (!CBB_init(&cbb, TLS13_RECORD_HEADER_LEN + content_len)) + goto err; + + if (!CBB_add_u8(&cbb, content_type)) + goto err; + if (!CBB_add_u16(&cbb, version)) + goto err; + if (!CBB_add_u16_length_prefixed(&cbb, &body)) + goto err; + if (!CBB_add_bytes(&body, content, content_len)) + goto err; + + if (!CBB_finish(&cbb, &data, &data_len)) + goto err; + + if (!tls13_record_set_data(rl->wrec, data, data_len)) + goto err; + + return 1; + + err: + CBB_cleanup(&cbb); + freezero(data, data_len); + + return 0; +} + +static int +tls13_record_layer_seal_record_protected(struct tls13_record_layer *rl, + uint8_t content_type, const uint8_t *content, size_t content_len) +{ + uint8_t *data = NULL, *header = NULL, *inner = NULL; + size_t data_len = 0, header_len = 0, inner_len = 0; + uint8_t *enc_record; + size_t enc_record_len; + ssize_t ret = 0; + size_t out_len; + CBB cbb; + + if (rl->aead == NULL) + return 0; + + memset(&cbb, 0, sizeof(cbb)); + + /* Build inner plaintext. */ + if (!CBB_init(&cbb, content_len + 1)) + goto err; + if (!CBB_add_bytes(&cbb, content, content_len)) + goto err; + if (!CBB_add_u8(&cbb, content_type)) + goto err; + /* XXX - padding? */ + if (!CBB_finish(&cbb, &inner, &inner_len)) + goto err; + + if (inner_len > TLS13_RECORD_MAX_INNER_PLAINTEXT_LEN) + goto err; + + /* XXX EVP_AEAD_max_tag_len vs EVP_AEAD_CTX_tag_len. */ + enc_record_len = inner_len + EVP_AEAD_max_tag_len(rl->aead); + if (enc_record_len > TLS13_RECORD_MAX_CIPHERTEXT_LEN) + goto err; + + /* Build the record header. */ + if (!CBB_init(&cbb, TLS13_RECORD_HEADER_LEN)) + goto err; + if (!CBB_add_u8(&cbb, SSL3_RT_APPLICATION_DATA)) + goto err; + if (!CBB_add_u16(&cbb, TLS1_2_VERSION)) + goto err; + if (!CBB_add_u16(&cbb, enc_record_len)) + goto err; + if (!CBB_finish(&cbb, &header, &header_len)) + goto err; + + /* Build the actual record. */ + if (!CBB_init(&cbb, TLS13_RECORD_HEADER_LEN + enc_record_len)) + goto err; + if (!CBB_add_bytes(&cbb, header, header_len)) + goto err; + if (!CBB_add_space(&cbb, &enc_record, enc_record_len)) + goto err; + if (!CBB_finish(&cbb, &data, &data_len)) + goto err; + + if (!tls13_record_layer_update_nonce(&rl->write_nonce, + &rl->write_iv, rl->write_seq_num)) + goto err; + + /* + * XXX - consider a EVP_AEAD_CTX_seal_iov() that takes an iovec... + * this would avoid a copy since the inner would be passed as two + * separate pieces. + */ + if (!EVP_AEAD_CTX_seal(&rl->write_aead_ctx, + enc_record, &out_len, enc_record_len, + rl->write_nonce.data, rl->write_nonce.len, + inner, inner_len, header, header_len)) + goto err; + + if (out_len != enc_record_len) + goto err; + + if (!tls13_record_layer_inc_seq_num(rl->write_seq_num)) + goto err; + + if (!tls13_record_set_data(rl->wrec, data, data_len)) + goto err; + + data = NULL; + data_len = 0; + + ret = 1; + + err: + CBB_cleanup(&cbb); + + freezero(data, data_len); + freezero(header, header_len); + freezero(inner, inner_len); + + return ret; +} + +static int +tls13_record_layer_seal_record(struct tls13_record_layer *rl, + uint8_t content_type, const uint8_t *content, size_t content_len) +{ + tls13_record_layer_wrec_free(rl); + + if ((rl->wrec = tls13_record_new()) == NULL) + return 0; + + if (rl->aead == NULL) + return tls13_record_layer_seal_record_plaintext(rl, + content_type, content, content_len); + + return tls13_record_layer_seal_record_protected(rl, content_type, + content, content_len); +} + +static ssize_t +tls13_record_layer_read_record(struct tls13_record_layer *rl) +{ + uint8_t content_type, ccs; + ssize_t ret; + CBS cbs; + + again: /* XXX */ + + if (rl->rrec == NULL) { + if ((rl->rrec = tls13_record_new()) == NULL) + goto err; + } + + if ((ret = tls13_record_recv(rl->rrec, rl->wire_read, rl->cb_arg)) <= 0) + return ret; + + /* XXX - record version checks. */ + + content_type = tls13_record_content_type(rl->rrec); + + /* + * Bag of hacks ahead... after the first ClientHello message has been + * sent or received and before the peer's Finished message has been + * received, we may receive an unencrypted ChangeCipherSpec record + * (see RFC 8446 section 5 and appendix D.4). This record must be + * ignored. + */ + if (content_type == SSL3_RT_CHANGE_CIPHER_SPEC) { + /* XXX - need to check after ClientHello, before Finished. */ + if (rl->handshake_completed || rl->change_cipher_spec_seen) { + /* XXX - unexpected message alert. */ + goto err; + } + if (!tls13_record_content(rl->rrec, &cbs)) { + /* XXX - decode error alert. */ + goto err; + } + if (!CBS_get_u8(&cbs, &ccs)) { + /* XXX - decode error alert. */ + goto err; + } + if (ccs != 1) { + /* XXX - something alert. */ + goto err; + } + rl->change_cipher_spec_seen = 1; + tls13_record_layer_rrec_free(rl); + goto again; /* XXX */ + } + + /* + * Once record protection is engaged, we should only receive + * protected application data messages (aside from the + * dummy ChangeCipherSpec messages, handled above). + */ + if (rl->aead != NULL && content_type != SSL3_RT_APPLICATION_DATA) { + /* XXX - unexpected message alert. */ + goto err; + } + + if (!tls13_record_layer_open_record(rl)) + goto err; + + tls13_record_layer_rrec_free(rl); + + switch (rl->rbuf_content_type) { + case SSL3_RT_ALERT: + return tls13_record_layer_process_alert(rl); + + case SSL3_RT_HANDSHAKE: + break; + + case SSL3_RT_APPLICATION_DATA: + if (!rl->handshake_completed) { + /* XXX - unexpected message alert. */ + goto err; + } + break; + + default: + /* XXX - unexpected message alert. */ + goto err; + } + + return TLS13_IO_SUCCESS; + + err: + return TLS13_IO_FAILURE; +} + +ssize_t +tls13_record_layer_read(struct tls13_record_layer *rl, uint8_t content_type, + uint8_t *buf, size_t n) +{ + int ret; + + /* XXX - loop here with record and byte limits. */ + /* XXX - send alert... */ + + again: /* XXX */ + + /* If necessary, pull up the next record. */ + if (CBS_len(&rl->rbuf_cbs) == 0) { + if ((ret = tls13_record_layer_read_record(rl)) <= 0) + return ret; + + /* XXX - need to check record version. */ + } + if (rl->rbuf_content_type != content_type) { + /* + * Handshake content can appear as post-handshake messages (yup, + * the RFC reused the same content type...), which means we can + * be trying to read application data and need to handle a + * post-handshake handshake message instead... + */ + if (rl->rbuf_content_type == SSL3_RT_HANDSHAKE) { + if (rl->handshake_completed) { + /* XXX - call callback, drop for now... */ + tls13_record_layer_rbuf_free(rl); + goto again; + } + } + + /* XXX - unexpected message alert. */ + goto err; + } + + if (n > CBS_len(&rl->rbuf_cbs)) + n = CBS_len(&rl->rbuf_cbs); + + /* XXX - CBS_memcpy? CBS_copy_bytes? */ + memcpy(buf, CBS_data(&rl->rbuf_cbs), n); + if (!CBS_skip(&rl->rbuf_cbs, n)) + goto err; + + if (CBS_len(&rl->rbuf_cbs) == 0) + tls13_record_layer_rbuf_free(rl); + + return n; + + err: + return TLS13_IO_FAILURE; +} + +static ssize_t +tls13_record_layer_write_record(struct tls13_record_layer *rl, + uint8_t content_type, const uint8_t *content, size_t content_len) +{ + ssize_t ret; + + /* See if there is an existing record and attempt to push it out... */ + if (rl->wrec != NULL) { + if ((ret = tls13_record_send(rl->wrec, rl->wire_write, + rl->cb_arg)) <= 0) + return ret; + + tls13_record_layer_wrec_free(rl); + + /* XXX - could be pushing out different data... */ + return content_len; + } + + if (content_len > TLS13_RECORD_MAX_PLAINTEXT_LEN) + goto err; + + if (!tls13_record_layer_seal_record(rl, content_type, content, content_len)) + goto err; + + if ((ret = tls13_record_send(rl->wrec, rl->wire_write, rl->cb_arg)) <= 0) + return ret; + + tls13_record_layer_wrec_free(rl); + + return content_len; + + err: + return TLS13_IO_FAILURE; +} + +static ssize_t +tls13_record_layer_write(struct tls13_record_layer *rl, uint8_t content_type, + const uint8_t *buf, size_t n) +{ + /* XXX - handle fragmenting... */ + return tls13_record_layer_write_record(rl, content_type, buf, n); +} + +ssize_t +tls13_read_handshake_data(struct tls13_record_layer *rl, uint8_t *buf, size_t n) +{ + return tls13_record_layer_read(rl, SSL3_RT_HANDSHAKE, buf, n); +} + +ssize_t +tls13_write_handshake_data(struct tls13_record_layer *rl, const uint8_t *buf, + size_t n) +{ + return tls13_record_layer_write(rl, SSL3_RT_HANDSHAKE, buf, n); +} + +ssize_t +tls13_read_application_data(struct tls13_record_layer *rl, uint8_t *buf, size_t n) +{ + if (!rl->handshake_completed) + return TLS13_IO_FAILURE; + + return tls13_record_layer_read(rl, SSL3_RT_APPLICATION_DATA, buf, n); +} + +ssize_t +tls13_write_application_data(struct tls13_record_layer *rl, const uint8_t *buf, + size_t n) +{ + if (!rl->handshake_completed) + return TLS13_IO_FAILURE; + + return tls13_record_layer_write(rl, SSL3_RT_APPLICATION_DATA, buf, n); +} |