diff options
Diffstat (limited to 'crypto/openssl/ssl/record/methods/tls13_meth.c')
| -rw-r--r-- | crypto/openssl/ssl/record/methods/tls13_meth.c | 427 |
1 files changed, 427 insertions, 0 deletions
diff --git a/crypto/openssl/ssl/record/methods/tls13_meth.c b/crypto/openssl/ssl/record/methods/tls13_meth.c new file mode 100644 index 000000000000..6bbba84d0d89 --- /dev/null +++ b/crypto/openssl/ssl/record/methods/tls13_meth.c @@ -0,0 +1,427 @@ +/* + * Copyright 2022-2024 The OpenSSL Project Authors. All Rights Reserved. + * + * Licensed under the Apache License 2.0 (the "License"). You may not use + * this file except in compliance with the License. You can obtain a copy + * in the file LICENSE in the source distribution or at + * https://www.openssl.org/source/license.html + */ + +#include <openssl/evp.h> +#include <openssl/core_names.h> +#include "../../ssl_local.h" +#include "../record_local.h" +#include "recmethod_local.h" + +static int tls13_set_crypto_state(OSSL_RECORD_LAYER *rl, int level, + unsigned char *key, size_t keylen, + unsigned char *iv, size_t ivlen, + unsigned char *mackey, size_t mackeylen, + const EVP_CIPHER *ciph, + size_t taglen, + int mactype, + const EVP_MD *md, + COMP_METHOD *comp) +{ + EVP_CIPHER_CTX *ciph_ctx; + EVP_MAC_CTX *mac_ctx; + EVP_MAC *mac; + OSSL_PARAM params[2], *p = params; + int mode; + int enc = (rl->direction == OSSL_RECORD_DIRECTION_WRITE) ? 1 : 0; + + rl->iv = OPENSSL_malloc(ivlen); + if (rl->iv == NULL) + return OSSL_RECORD_RETURN_FATAL; + + rl->nonce = OPENSSL_malloc(ivlen); + if (rl->nonce == NULL) + return OSSL_RECORD_RETURN_FATAL; + + memcpy(rl->iv, iv, ivlen); + + /* Integrity only */ + if (EVP_CIPHER_is_a(ciph, "NULL") && mactype == NID_hmac && md != NULL) { + mac = EVP_MAC_fetch(rl->libctx, "HMAC", rl->propq); + if (mac == NULL + || (mac_ctx = rl->mac_ctx = EVP_MAC_CTX_new(mac)) == NULL) { + EVP_MAC_free(mac); + ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR); + return OSSL_RECORD_RETURN_FATAL; + } + EVP_MAC_free(mac); + *p++ = OSSL_PARAM_construct_utf8_string(OSSL_MAC_PARAM_DIGEST, + (char *)EVP_MD_name(md), 0); + *p = OSSL_PARAM_construct_end(); + if (!EVP_MAC_init(mac_ctx, key, keylen, params)) { + ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR); + return OSSL_RECORD_RETURN_FATAL; + } + goto end; + } + + ciph_ctx = rl->enc_ctx = EVP_CIPHER_CTX_new(); + if (ciph_ctx == NULL) { + ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR); + return OSSL_RECORD_RETURN_FATAL; + } + + mode = EVP_CIPHER_get_mode(ciph); + + if (EVP_CipherInit_ex(ciph_ctx, ciph, NULL, NULL, NULL, enc) <= 0 + || EVP_CIPHER_CTX_ctrl(ciph_ctx, EVP_CTRL_AEAD_SET_IVLEN, ivlen, + NULL) <= 0 + || (mode == EVP_CIPH_CCM_MODE + && EVP_CIPHER_CTX_ctrl(ciph_ctx, EVP_CTRL_AEAD_SET_TAG, taglen, + NULL) <= 0) + || EVP_CipherInit_ex(ciph_ctx, NULL, NULL, key, NULL, enc) <= 0) { + ERR_raise(ERR_LIB_SSL, ERR_R_INTERNAL_ERROR); + return OSSL_RECORD_RETURN_FATAL; + } + end: + return OSSL_RECORD_RETURN_SUCCESS; +} + +static int tls13_cipher(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *recs, + size_t n_recs, int sending, SSL_MAC_BUF *mac, + size_t macsize) +{ + EVP_CIPHER_CTX *enc_ctx; + unsigned char recheader[SSL3_RT_HEADER_LENGTH]; + unsigned char tag[EVP_MAX_MD_SIZE]; + size_t nonce_len, offset, loop, hdrlen, taglen; + unsigned char *staticiv; + unsigned char *nonce; + unsigned char *seq = rl->sequence; + int lenu, lenf; + TLS_RL_RECORD *rec = &recs[0]; + WPACKET wpkt; + const EVP_CIPHER *cipher; + EVP_MAC_CTX *mac_ctx = NULL; + int mode; + + if (n_recs != 1) { + /* Should not happen */ + RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + + enc_ctx = rl->enc_ctx; /* enc_ctx is ignored when rl->mac_ctx != NULL */ + staticiv = rl->iv; + nonce = rl->nonce; + + if (enc_ctx == NULL && rl->mac_ctx == NULL) { + RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + + /* + * If we're sending an alert and ctx != NULL then we must be forcing + * plaintext alerts. If we're reading and ctx != NULL then we allow + * plaintext alerts at certain points in the handshake. If we've got this + * far then we have already validated that a plaintext alert is ok here. + */ + if (rec->type == SSL3_RT_ALERT) { + memmove(rec->data, rec->input, rec->length); + rec->input = rec->data; + return 1; + } + + /* For integrity-only ciphers, nonce_len is same as MAC size */ + if (rl->mac_ctx != NULL) { + nonce_len = EVP_MAC_CTX_get_mac_size(rl->mac_ctx); + } else { + int ivlen = EVP_CIPHER_CTX_get_iv_length(enc_ctx); + + if (ivlen < 0) { + /* Should not happen */ + RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + nonce_len = (size_t)ivlen; + } + + if (!sending) { + /* + * Take off tag. There must be at least one byte of content type as + * well as the tag + */ + if (rec->length < rl->taglen + 1) + return 0; + rec->length -= rl->taglen; + } + + /* Set up nonce: part of static IV followed by sequence number */ + if (nonce_len < SEQ_NUM_SIZE) { + /* Should not happen */ + RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + offset = nonce_len - SEQ_NUM_SIZE; + memcpy(nonce, staticiv, offset); + for (loop = 0; loop < SEQ_NUM_SIZE; loop++) + nonce[offset + loop] = staticiv[offset + loop] ^ seq[loop]; + + if (!tls_increment_sequence_ctr(rl)) { + /* RLAYERfatal already called */ + return 0; + } + + /* Set up the AAD */ + if (!WPACKET_init_static_len(&wpkt, recheader, sizeof(recheader), 0) + || !WPACKET_put_bytes_u8(&wpkt, rec->type) + || !WPACKET_put_bytes_u16(&wpkt, rec->rec_version) + || !WPACKET_put_bytes_u16(&wpkt, rec->length + rl->taglen) + || !WPACKET_get_total_written(&wpkt, &hdrlen) + || hdrlen != SSL3_RT_HEADER_LENGTH + || !WPACKET_finish(&wpkt)) { + RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + WPACKET_cleanup(&wpkt); + return 0; + } + + if (rl->mac_ctx != NULL) { + int ret = 0; + + if ((mac_ctx = EVP_MAC_CTX_dup(rl->mac_ctx)) == NULL + || !EVP_MAC_update(mac_ctx, nonce, nonce_len) + || !EVP_MAC_update(mac_ctx, recheader, sizeof(recheader)) + || !EVP_MAC_update(mac_ctx, rec->input, rec->length) + || !EVP_MAC_final(mac_ctx, tag, &taglen, rl->taglen)) { + RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + goto end_mac; + } + + if (sending) { + memcpy(rec->data + rec->length, tag, rl->taglen); + rec->length += rl->taglen; + } else if (CRYPTO_memcmp(tag, rec->data + rec->length, + rl->taglen) != 0) { + goto end_mac; + } + ret = 1; + end_mac: + EVP_MAC_CTX_free(mac_ctx); + return ret; + } + + cipher = EVP_CIPHER_CTX_get0_cipher(enc_ctx); + if (cipher == NULL) { + RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + mode = EVP_CIPHER_get_mode(cipher); + + if (EVP_CipherInit_ex(enc_ctx, NULL, NULL, NULL, nonce, sending) <= 0 + || (!sending && EVP_CIPHER_CTX_ctrl(enc_ctx, EVP_CTRL_AEAD_SET_TAG, + rl->taglen, + rec->data + rec->length) <= 0)) { + RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + + /* + * For CCM we must explicitly set the total plaintext length before we add + * any AAD. + */ + if ((mode == EVP_CIPH_CCM_MODE + && EVP_CipherUpdate(enc_ctx, NULL, &lenu, NULL, + (unsigned int)rec->length) <= 0) + || EVP_CipherUpdate(enc_ctx, NULL, &lenu, recheader, + sizeof(recheader)) <= 0 + || EVP_CipherUpdate(enc_ctx, rec->data, &lenu, rec->input, + (unsigned int)rec->length) <= 0 + || EVP_CipherFinal_ex(enc_ctx, rec->data + lenu, &lenf) <= 0 + || (size_t)(lenu + lenf) != rec->length) { + return 0; + } + if (sending) { + /* Add the tag */ + if (EVP_CIPHER_CTX_ctrl(enc_ctx, EVP_CTRL_AEAD_GET_TAG, rl->taglen, + rec->data + rec->length) <= 0) { + RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + rec->length += rl->taglen; + } + + return 1; +} + +static int tls13_validate_record_header(OSSL_RECORD_LAYER *rl, + TLS_RL_RECORD *rec) +{ + if (rec->type != SSL3_RT_APPLICATION_DATA + && (rec->type != SSL3_RT_CHANGE_CIPHER_SPEC + || !rl->is_first_handshake) + && (rec->type != SSL3_RT_ALERT || !rl->allow_plain_alerts)) { + RLAYERfatal(rl, SSL_AD_UNEXPECTED_MESSAGE, SSL_R_BAD_RECORD_TYPE); + return 0; + } + + if (rec->rec_version != TLS1_2_VERSION) { + RLAYERfatal(rl, SSL_AD_DECODE_ERROR, SSL_R_WRONG_VERSION_NUMBER); + return 0; + } + + if (rec->length > SSL3_RT_MAX_TLS13_ENCRYPTED_LENGTH) { + RLAYERfatal(rl, SSL_AD_RECORD_OVERFLOW, + SSL_R_ENCRYPTED_LENGTH_TOO_LONG); + return 0; + } + return 1; +} + +static int tls13_post_process_record(OSSL_RECORD_LAYER *rl, TLS_RL_RECORD *rec) +{ + /* Skip this if we've received a plaintext alert */ + if (rec->type != SSL3_RT_ALERT) { + size_t end; + + if (rec->length == 0 + || rec->type != SSL3_RT_APPLICATION_DATA) { + RLAYERfatal(rl, SSL_AD_UNEXPECTED_MESSAGE, + SSL_R_BAD_RECORD_TYPE); + return 0; + } + + /* Strip trailing padding */ + for (end = rec->length - 1; end > 0 && rec->data[end] == 0; end--) + continue; + + rec->length = end; + rec->type = rec->data[end]; + } + + if (rec->length > SSL3_RT_MAX_PLAIN_LENGTH) { + RLAYERfatal(rl, SSL_AD_RECORD_OVERFLOW, SSL_R_DATA_LENGTH_TOO_LONG); + return 0; + } + + if (!tls13_common_post_process_record(rl, rec)) { + /* RLAYERfatal already called */ + return 0; + } + + return 1; +} + +static uint8_t tls13_get_record_type(OSSL_RECORD_LAYER *rl, + OSSL_RECORD_TEMPLATE *template) +{ + if (rl->allow_plain_alerts && template->type == SSL3_RT_ALERT) + return SSL3_RT_ALERT; + + /* + * Aside from the above case we always use the application data record type + * when encrypting in TLSv1.3. The "inner" record type encodes the "real" + * record type from the template. + */ + return SSL3_RT_APPLICATION_DATA; +} + +static int tls13_add_record_padding(OSSL_RECORD_LAYER *rl, + OSSL_RECORD_TEMPLATE *thistempl, + WPACKET *thispkt, + TLS_RL_RECORD *thiswr) +{ + size_t rlen; + + /* Nothing to be done in the case of a plaintext alert */ + if (rl->allow_plain_alerts && thistempl->type != SSL3_RT_ALERT) + return 1; + + if (!WPACKET_put_bytes_u8(thispkt, thistempl->type)) { + RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); + return 0; + } + TLS_RL_RECORD_add_length(thiswr, 1); + + /* Add TLS1.3 padding */ + rlen = TLS_RL_RECORD_get_length(thiswr); + if (rlen < rl->max_frag_len) { + size_t padding = 0; + size_t max_padding = rl->max_frag_len - rlen; + + /* + * We might want to change the "else if" below so that + * library-added padding can still happen even if there + * is an application-layer callback. The reason being + * the application may not be aware that the effectiveness + * of ECH could be damaged if the callback e.g. only + * padded application data. However, doing so would be + * a change that could break some application that has + * a client and server that both know what padding they + * like, and that dislike any other padding. That'd need + * one of those to have been updated though so the + * probability may be low enough that we could change + * the "else if" below to just an "if" and pick the + * larger of the library and callback's idea of padding. + * (Still subject to max_padding though.) + */ + if (rl->padding != NULL) { + padding = rl->padding(rl->cbarg, thistempl->type, rlen); + } else if (rl->block_padding > 0 || rl->hs_padding > 0) { + size_t mask, bp = 0, remainder; + + /* + * pad handshake or alert messages based on |hs_padding| + * but application data based on |block_padding| + */ + if (thistempl->type == SSL3_RT_HANDSHAKE && rl->hs_padding > 0) + bp = rl->hs_padding; + else if (thistempl->type == SSL3_RT_ALERT && rl->hs_padding > 0) + bp = rl->hs_padding; + else if (thistempl->type == SSL3_RT_APPLICATION_DATA + && rl->block_padding > 0) + bp = rl->block_padding; + if (bp > 0) { + mask = bp - 1; + /* optimize for power of 2 */ + if ((bp & mask) == 0) + remainder = rlen & mask; + else + remainder = rlen % bp; + /* don't want to add a block of padding if we don't have to */ + if (remainder == 0) + padding = 0; + else + padding = bp - remainder; + } + } + if (padding > 0) { + /* do not allow the record to exceed max plaintext length */ + if (padding > max_padding) + padding = max_padding; + if (!WPACKET_memset(thispkt, 0, padding)) { + RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, + ERR_R_INTERNAL_ERROR); + return 0; + } + TLS_RL_RECORD_add_length(thiswr, padding); + } + } + + return 1; +} + +const struct record_functions_st tls_1_3_funcs = { + tls13_set_crypto_state, + tls13_cipher, + NULL, + tls_default_set_protocol_version, + tls_default_read_n, + tls_get_more_records, + tls13_validate_record_header, + tls13_post_process_record, + tls_get_max_records_default, + tls_write_records_default, + tls_allocate_write_buffers_default, + tls_initialise_write_packets_default, + tls13_get_record_type, + tls_prepare_record_header_default, + tls13_add_record_padding, + tls_prepare_for_encryption_default, + tls_post_encryption_processing_default, + NULL +}; |
