xref: /openssl/ssl/record/methods/tls_multib.c (revision da1c088f)
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) \
16                               || defined(__x86_64__) \
17                               || defined(_M_AMD64) \
18                               || defined(_M_X64)))
19 # undef EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK
20 # define EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK 0
21 #endif
22 
tls_is_multiblock_capable(OSSL_RECORD_LAYER * rl,uint8_t type,size_t len,size_t fraglen)23 static int tls_is_multiblock_capable(OSSL_RECORD_LAYER *rl, uint8_t type,
24                                      size_t len, size_t fraglen)
25 {
26 #if !defined(OPENSSL_NO_MULTIBLOCK) && EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK
27     if (type == SSL3_RT_APPLICATION_DATA
28             && len >= 4 * fraglen
29             && rl->compctx == NULL
30             && rl->msg_callback == NULL
31             && !rl->use_etm
32             && RLAYER_USE_EXPLICIT_IV(rl)
33             && !BIO_get_ktls_send(rl->bio)
34             && (EVP_CIPHER_get_flags(EVP_CIPHER_CTX_get0_cipher(rl->enc_ctx))
35                 & EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK) != 0)
36         return 1;
37 #endif
38     return 0;
39 }
40 
tls_get_max_records_multiblock(OSSL_RECORD_LAYER * rl,uint8_t type,size_t len,size_t maxfrag,size_t * preffrag)41 size_t tls_get_max_records_multiblock(OSSL_RECORD_LAYER *rl, uint8_t type,
42                                       size_t len, size_t maxfrag,
43                                       size_t *preffrag)
44 {
45     if (tls_is_multiblock_capable(rl, type, len, *preffrag)) {
46         /* minimize address aliasing conflicts */
47         if ((*preffrag & 0xfff) == 0)
48             *preffrag -= 512;
49 
50         if (len >= 8 * (*preffrag))
51             return 8;
52 
53         return 4;
54     }
55 
56     return tls_get_max_records_default(rl, type, len, maxfrag, preffrag);
57 }
58 
59 /*
60  * Write records using the multiblock method.
61  *
62  * Returns 1 on success, 0 if multiblock isn't suitable (non-fatal error), or
63  * -1 on fatal error.
64  */
tls_write_records_multiblock_int(OSSL_RECORD_LAYER * rl,OSSL_RECORD_TEMPLATE * templates,size_t numtempl)65 static int tls_write_records_multiblock_int(OSSL_RECORD_LAYER *rl,
66                                             OSSL_RECORD_TEMPLATE *templates,
67                                             size_t numtempl)
68 {
69 #if !defined(OPENSSL_NO_MULTIBLOCK) && EVP_CIPH_FLAG_TLS1_1_MULTIBLOCK
70     size_t i;
71     size_t totlen;
72     TLS_BUFFER *wb;
73     unsigned char aad[13];
74     EVP_CTRL_TLS1_1_MULTIBLOCK_PARAM mb_param;
75     size_t packlen;
76     int packleni;
77 
78     if (numtempl != 4 && numtempl != 8)
79         return 0;
80 
81     /*
82      * Check templates have contiguous buffers and are all the same type and
83      * length
84      */
85     for (i = 1; i < numtempl; i++) {
86         if (templates[i - 1].type != templates[i].type
87                 || templates[i - 1].buflen != templates[i].buflen
88                 || templates[i - 1].buf + templates[i - 1].buflen
89                    != templates[i].buf)
90             return 0;
91     }
92 
93     totlen = templates[0].buflen * numtempl;
94     if (!tls_is_multiblock_capable(rl, templates[0].type, totlen,
95                                    templates[0].buflen))
96         return 0;
97 
98     /*
99      * If we get this far, then multiblock is suitable
100      * Depending on platform multi-block can deliver several *times*
101      * better performance. Downside is that it has to allocate
102      * jumbo buffer to accommodate up to 8 records, but the
103      * compromise is considered worthy.
104      */
105 
106     /*
107      * Allocate jumbo buffer. This will get freed next time we do a non
108      * multiblock write in the call to tls_setup_write_buffer() - the different
109      * buffer sizes will be spotted and the buffer reallocated.
110      */
111     packlen = EVP_CIPHER_CTX_ctrl(rl->enc_ctx,
112                                   EVP_CTRL_TLS1_1_MULTIBLOCK_MAX_BUFSIZE,
113                                   (int)templates[0].buflen, NULL);
114     packlen *= numtempl;
115     if (!tls_setup_write_buffer(rl, 1, packlen, packlen)) {
116         /* RLAYERfatal() already called */
117         return -1;
118     }
119     wb = &rl->wbuf[0];
120 
121     mb_param.interleave = numtempl;
122     memcpy(aad, rl->sequence, 8);
123     aad[8] = templates[0].type;
124     aad[9] = (unsigned char)(templates[0].version >> 8);
125     aad[10] = (unsigned char)(templates[0].version);
126     aad[11] = 0;
127     aad[12] = 0;
128     mb_param.out = NULL;
129     mb_param.inp = aad;
130     mb_param.len = totlen;
131 
132     packleni = EVP_CIPHER_CTX_ctrl(rl->enc_ctx,
133                                    EVP_CTRL_TLS1_1_MULTIBLOCK_AAD,
134                                    sizeof(mb_param), &mb_param);
135     packlen = (size_t)packleni;
136     if (packleni <= 0 || packlen > wb->len) { /* never happens */
137         RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
138         return -1;
139     }
140 
141     mb_param.out = wb->buf;
142     mb_param.inp = templates[0].buf;
143     mb_param.len = totlen;
144 
145     if (EVP_CIPHER_CTX_ctrl(rl->enc_ctx,
146                             EVP_CTRL_TLS1_1_MULTIBLOCK_ENCRYPT,
147                             sizeof(mb_param), &mb_param) <= 0) {
148         RLAYERfatal(rl, SSL_AD_INTERNAL_ERROR, ERR_R_INTERNAL_ERROR);
149         return -1;
150     }
151 
152     rl->sequence[7] += mb_param.interleave;
153     if (rl->sequence[7] < mb_param.interleave) {
154         int j = 6;
155         while (j >= 0 && (++rl->sequence[j--]) == 0) ;
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