xref: /src/crypto/openssl/ssl/record/methods/tls_multib.c (revision f25b8c9fb4f58cf61adb47d7570abe7caa6d385d)
1 /*
2  * Copyright 2022-2023 The OpenSSL Project Authors. All Rights Reserved.
3  *
4  * Licensed under the Apache License 2.0 (the "License").  You may not use
5  * this file except in compliance with the License.  You can obtain a copy
6  * in the file LICENSE in the source distribution or at
7  * https://www.openssl.org/source/license.html
8  */
9 
10 #include "../../ssl_local.h"
11 #include "../record_local.h"
12 #include "recmethod_local.h"
13 
14 #if defined(OPENSSL_SMALL_FOOTPRINT) \
15     || !(defined(AES_ASM) && (defined(__x86_64) || defined(__x86_64__) || defined(_M_AMD64) || defined(_M_X64)))
16 #undef EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK
17 #define EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK 0
18 #endif
19 
tls_is_multiblock_capable(OSSL_RECORD_LAYER * rl,uint8_t type,size_t len,size_t fraglen)20 static int tls_is_multiblock_capable(OSSL_RECORD_LAYER *rl, uint8_t type,
21     size_t len, size_t fraglen)
22 {
23 #if !defined(OPENSSL_NO_MULTIBLOCK) && EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK
24     if (type == SSL3_RT_APPLICATION_DATA
25         && len >= 4 * fraglen
26         && rl->compctx == NULL
27         && rl->msg_callback == NULL
28         && !rl->use_etm
29         && RLAYER_USE_EXPLICIT_IV(rl)
30         && !BIO_get_ktls_send(rl->bio)
31         && (EVP_CIPHER_get_flags(EVP_CIPHER_CTX_get0_cipher(rl->enc_ctx))
32                & EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK)
33             != 0)
34         return 1;
35 #endif
36     return 0;
37 }
38 
tls_get_max_records_multiblock(OSSL_RECORD_LAYER * rl,uint8_t type,size_t len,size_t maxfrag,size_t * preffrag)39 size_t tls_get_max_records_multiblock(OSSL_RECORD_LAYER *rl, uint8_t type,
40     size_t len, size_t maxfrag,
41     size_t *preffrag)
42 {
43     if (tls_is_multiblock_capable(rl, type, len, *preffrag)) {
44         /* minimize address aliasing conflicts */
45         if ((*preffrag & 0xfff) == 0)
46             *preffrag -= 512;
47 
48         if (len >= 8 * (*preffrag))
49             return 8;
50 
51         return 4;
52     }
53 
54     return tls_get_max_records_default(rl, type, len, maxfrag, preffrag);
55 }
56 
57 /*
58  * Write records using the multiblock method.
59  *
60  * Returns 1 on success, 0 if multiblock isn't suitable (non-fatal error), or
61  * -1 on fatal error.
62  */
tls_write_records_multiblock_int(OSSL_RECORD_LAYER * rl,OSSL_RECORD_TEMPLATE * templates,size_t numtempl)63 static int tls_write_records_multiblock_int(OSSL_RECORD_LAYER *rl,
64     OSSL_RECORD_TEMPLATE *templates,
65     size_t numtempl)
66 {
67 #if !defined(OPENSSL_NO_MULTIBLOCK) && EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK
68     size_t i;
69     size_t totlen;
70     TLS_BUFFER *wb;
71     unsigned char aad[13];
72     EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM mb_param;
73     size_t packlen;
74     int packleni;
75 
76     if (numtempl != 4 && numtempl != 8)
77         return 0;
78 
79     /*
80      * Check templates have contiguous buffers and are all the same type and
81      * length
82      */
83     for (i = 1; i < numtempl; i++) {
84         if (templates[i - 1].type != templates[i].type
85             || templates[i - 1].buflen != templates[i].buflen
86             || templates[i - 1].buf + templates[i - 1].buflen
87                 != templates[i].buf)
88             return 0;
89     }
90 
91     totlen = templates[0].buflen * numtempl;
92     if (!tls_is_multiblock_capable(rl, templates[0].type, totlen,
93             templates[0].buflen))
94         return 0;
95 
96     /*
97      * If we get this far, then multiblock is suitable
98      * Depending on platform multi-block can deliver several *times*
99      * better performance. Downside is that it has to allocate
100      * jumbo buffer to accommodate up to 8 records, but the
101      * compromise is considered worthy.
102      */
103 
104     /*
105      * Allocate jumbo buffer. This will get freed next time we do a non
106      * multiblock write in the call to tls_setup_write_buffer() - the different
107      * buffer sizes will be spotted and the buffer reallocated.
108      */
109     packlen = EVP_CIPHER_CTX_ctrl(rl->enc_ctx,
110         EVP_CTRL_TLS1_1_MULTIBLOCK_MAX_BUFSIZE,
111         (int)templates[0].buflen, NULL);
112     packlen *= numtempl;
113     if (!tls_setup_write_buffer(rl, 1, packlen, packlen)) {
114         /* RLAYERfatal() already called */
115         return -1;
116     }
117     wb = &rl->wbuf[0];
118 
119     mb_param.interleave = numtempl;
120     memcpy(aad, rl->sequence, 8);
121     aad[8] = templates[0].type;
122     aad[9] = (unsigned char)(templates[0].version >> 8);
123     aad[10] = (unsigned char)(templates[0].version);
124     aad[11] = 0;
125     aad[12] = 0;
126     mb_param.out = NULL;
127     mb_param.inp = aad;
128     mb_param.len = totlen;
129 
130     packleni = EVP_CIPHER_CTX_ctrl(rl->enc_ctx,
131         EVP_CTRL_TLS1_1_MULTIBLOCK_AAD,
132         sizeof(mb_param), &mb_param);
133     packlen = (size_t)packleni;
134     if (packleni <= 0 || packlen > wb->len) { /* never happens */
135         RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
136         return -1;
137     }
138 
139     mb_param.out = wb->buf;
140     mb_param.inp = templates[0].buf;
141     mb_param.len = totlen;
142 
143     if (EVP_CIPHER_CTX_ctrl(rl->enc_ctx,
144             EVP_CTRL_TLS1_1_MULTIBLOCK_ENCRYPT,
145             sizeof(mb_param), &mb_param)
146         <= 0) {
147         RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
148         return -1;
149     }
150 
151     rl->sequence[7] += mb_param.interleave;
152     if (rl->sequence[7] < mb_param.interleave) {
153         int j = 6;
154         while (j >= 0 && (++rl->sequence[j--]) == 0)
155             ;
156     }
157 
158     wb->offset = 0;
159     wb->left = packlen;
160 
161     return 1;
162 #else /* !defined(OPENSSL_NO_MULTIBLOCK) && EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK */
163     return 0;
164 #endif
165 }
166 
tls_write_records_multiblock(OSSL_RECORD_LAYER * rl,OSSL_RECORD_TEMPLATE * templates,size_t numtempl)167 int tls_write_records_multiblock(OSSL_RECORD_LAYER *rl,
168     OSSL_RECORD_TEMPLATE *templates,
169     size_t numtempl)
170 {
171     int ret;
172 
173     ret = tls_write_records_multiblock_int(rl, templates, numtempl);
174     if (ret < 0) {
175         /* RLAYERfatal already called */
176         return 0;
177     }
178     if (ret == 0) {
179         /* Multiblock wasn't suitable so just do a standard write */
180         if (!tls_write_records_default(rl, templates, numtempl)) {
181             /* RLAYERfatal already called */
182             return 0;
183         }
184     }
185 
186     return 1;
187 }
188