| /* |
| * Copyright 2022-2025 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 "../../ssl_local.h" |
| #include "../record_local.h" |
| #include "recmethod_local.h" |
| |
| #if defined(OPENSSL_SMALL_FOOTPRINT) \ |
| || !(defined(AES_ASM) && (defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || defined(_M_X64))) |
| #undef EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK |
| #define EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK 0 |
| #endif |
| |
| static int tls_is_multiblock_capable(OSSL_RECORD_LAYER *rl, uint8_t type, |
| size_t len, size_t fraglen) |
| { |
| #if !defined(OPENSSL_NO_MULTIBLOCK) && EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK |
| if (type == SSL3_RT_APPLICATION_DATA |
| && len >= 4 * fraglen |
| && rl->compctx == NULL |
| && rl->msg_callback == NULL |
| && !rl->use_etm |
| && RLAYER_USE_EXPLICIT_IV(rl) |
| && !BIO_get_ktls_send(rl->bio) |
| && (EVP_CIPHER_get_flags(EVP_CIPHER_CTX_get0_cipher(rl->enc_ctx)) |
| & EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK) |
| != 0) |
| return 1; |
| #endif |
| return 0; |
| } |
| |
| size_t tls_get_max_records_multiblock(OSSL_RECORD_LAYER *rl, uint8_t type, |
| size_t len, size_t maxfrag, |
| size_t *preffrag) |
| { |
| if (tls_is_multiblock_capable(rl, type, len, *preffrag)) { |
| /* minimize address aliasing conflicts */ |
| if ((*preffrag & 0xfff) == 0) |
| *preffrag -= 512; |
| |
| if (len >= 8 * (*preffrag)) |
| return 8; |
| |
| return 4; |
| } |
| |
| return tls_get_max_records_default(rl, type, len, maxfrag, preffrag); |
| } |
| |
| /* |
| * Write records using the multiblock method. |
| * |
| * Returns 1 on success, 0 if multiblock isn't suitable (non-fatal error), or |
| * -1 on fatal error. |
| */ |
| static int tls_write_records_multiblock_int(OSSL_RECORD_LAYER *rl, |
| OSSL_RECORD_TEMPLATE *templates, |
| size_t numtempl) |
| { |
| #if !defined(OPENSSL_NO_MULTIBLOCK) && EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK |
| size_t i; |
| size_t totlen; |
| TLS_BUFFER *wb; |
| unsigned char aad[13]; |
| EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM mb_param; |
| size_t packlen; |
| int packleni; |
| |
| if (numtempl != 4 && numtempl != 8) |
| return 0; |
| |
| /* |
| * Check templates have contiguous buffers and are all the same type and |
| * length |
| */ |
| for (i = 1; i < numtempl; i++) { |
| if (templates[i - 1].type != templates[i].type |
| || templates[i - 1].buflen != templates[i].buflen |
| || templates[i - 1].buf + templates[i - 1].buflen |
| != templates[i].buf) |
| return 0; |
| } |
| |
| totlen = templates[0].buflen * numtempl; |
| if (!tls_is_multiblock_capable(rl, templates[0].type, totlen, |
| templates[0].buflen)) |
| return 0; |
| |
| /* |
| * If we get this far, then multiblock is suitable |
| * Depending on platform multi-block can deliver several *times* |
| * better performance. Downside is that it has to allocate |
| * jumbo buffer to accommodate up to 8 records, but the |
| * compromise is considered worthy. |
| */ |
| |
| /* |
| * Allocate jumbo buffer. This will get freed next time we do a non |
| * multiblock write in the call to tls_setup_write_buffer() - the different |
| * buffer sizes will be spotted and the buffer reallocated. |
| */ |
| packlen = EVP_CIPHER_CTX_ctrl(rl->enc_ctx, |
| EVP_CTRL_TLS1_1_MULTIBLOCK_MAX_BUFSIZE, |
| (int)templates[0].buflen, NULL); |
| packlen *= numtempl; |
| if (!tls_setup_write_buffer(rl, 1, packlen, packlen)) { |
| /* RLAYERfatal() already called */ |
| return -1; |
| } |
| wb = &rl->wbuf[0]; |
| |
| mb_param.interleave = (unsigned int)numtempl; |
| memcpy(aad, rl->sequence, 8); |
| aad[8] = templates[0].type; |
| aad[9] = (unsigned char)(templates[0].version >> 8); |
| aad[10] = (unsigned char)(templates[0].version); |
| aad[11] = 0; |
| aad[12] = 0; |
| mb_param.out = NULL; |
| mb_param.inp = aad; |
| mb_param.len = totlen; |
| |
| packleni = EVP_CIPHER_CTX_ctrl(rl->enc_ctx, |
| EVP_CTRL_TLS1_1_MULTIBLOCK_AAD, |
| sizeof(mb_param), &mb_param); |
| packlen = (size_t)packleni; |
| if (packleni <= 0 || packlen > wb->len) { /* never happens */ |
| RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); |
| return -1; |
| } |
| |
| mb_param.out = wb->buf; |
| mb_param.inp = templates[0].buf; |
| mb_param.len = totlen; |
| |
| if (EVP_CIPHER_CTX_ctrl(rl->enc_ctx, |
| EVP_CTRL_TLS1_1_MULTIBLOCK_ENCRYPT, |
| sizeof(mb_param), &mb_param) |
| <= 0) { |
| RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR); |
| return -1; |
| } |
| |
| rl->sequence[7] += mb_param.interleave; |
| if (rl->sequence[7] < mb_param.interleave) { |
| int j = 6; |
| while (j >= 0 && (++rl->sequence[j--]) == 0) |
| ; |
| } |
| |
| wb->offset = 0; |
| wb->left = packlen; |
| |
| return 1; |
| #else /* !defined(OPENSSL_NO_MULTIBLOCK) && EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK */ |
| return 0; |
| #endif |
| } |
| |
| int tls_write_records_multiblock(OSSL_RECORD_LAYER *rl, |
| OSSL_RECORD_TEMPLATE *templates, |
| size_t numtempl) |
| { |
| int ret; |
| |
| ret = tls_write_records_multiblock_int(rl, templates, numtempl); |
| if (ret < 0) { |
| /* RLAYERfatal already called */ |
| return 0; |
| } |
| if (ret == 0) { |
| /* Multiblock wasn't suitable so just do a standard write */ |
| if (!tls_write_records_default(rl, templates, numtempl)) { |
| /* RLAYERfatal already called */ |
| return 0; |
| } |
| } |
| |
| return 1; |
| } |