1 /*
2 * Copyright (C) 2012 INRIA Paris-Rocquencourt
3 *
4 * Author: Alfredo Pironti
5 *
6 * This file is part of GnuTLS.
7 *
8 * The GnuTLS is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public License
10 * as published by the Free Software Foundation; either version 2.1 of
11 * the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program. If not, see <https://www.gnu.org/licenses/>
20 *
21 */
22
23 #include "gnutls_int.h"
24 #include "errors.h"
25 #include "algorithms.h"
26 #include "constate.h"
27 #include "record.h"
28
29 static void
_gnutls_set_range(gnutls_range_st * dst,const size_t low,const size_t high)30 _gnutls_set_range(gnutls_range_st * dst, const size_t low,
31 const size_t high)
32 {
33 dst->low = low;
34 dst->high = high;
35 return;
36 }
37
38 /*
39 * Returns how much LH pad we can put in this fragment, given we'll
40 * put at least data_length bytes of user data.
41 */
42 static ssize_t
_gnutls_range_max_lh_pad(gnutls_session_t session,ssize_t data_length,ssize_t max_frag)43 _gnutls_range_max_lh_pad(gnutls_session_t session, ssize_t data_length,
44 ssize_t max_frag)
45 {
46 int ret;
47 ssize_t max_pad;
48 unsigned int fixed_pad;
49 record_parameters_st *record_params;
50 ssize_t this_pad;
51 ssize_t block_size;
52 ssize_t tag_size, overflow;
53 const version_entry_st *vers = get_version(session);
54
55 if (unlikely(vers == NULL))
56 return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
57
58 ret =
59 _gnutls_epoch_get(session, EPOCH_WRITE_CURRENT,
60 &record_params);
61 if (ret < 0) {
62 return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
63 }
64
65 if (!vers->tls13_sem && record_params->write.is_aead) /* not yet ready */
66 return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
67
68 if (vers->tls13_sem) {
69 max_pad = max_record_send_size(session, record_params);
70 fixed_pad = 2;
71 } else {
72 max_pad = MAX_PAD_SIZE;
73 fixed_pad = 1;
74 }
75
76 this_pad = MIN(max_pad, max_frag - data_length);
77
78 block_size = _gnutls_cipher_get_block_size(record_params->cipher);
79 tag_size =
80 _gnutls_auth_cipher_tag_len(&record_params->write.
81 ctx.tls12);
82 switch (_gnutls_cipher_type(record_params->cipher)) {
83 case CIPHER_AEAD:
84 case CIPHER_STREAM:
85 return this_pad;
86
87 case CIPHER_BLOCK:
88 overflow =
89 (data_length + this_pad + tag_size +
90 fixed_pad) % block_size;
91 if (overflow > this_pad) {
92 return this_pad;
93 } else {
94 return this_pad - overflow;
95 }
96 default:
97 return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
98 }
99 }
100
101 /**
102 * gnutls_record_can_use_length_hiding:
103 * @session: is a #gnutls_session_t type.
104 *
105 * If the session supports length-hiding padding, you can
106 * invoke gnutls_record_send_range() to send a message whose
107 * length is hidden in the given range. If the session does not
108 * support length hiding padding, you can use the standard
109 * gnutls_record_send() function, or gnutls_record_send_range()
110 * making sure that the range is the same as the length of the
111 * message you are trying to send.
112 *
113 * Returns: true (1) if the current session supports length-hiding
114 * padding, false (0) if the current session does not.
115 **/
gnutls_record_can_use_length_hiding(gnutls_session_t session)116 unsigned gnutls_record_can_use_length_hiding(gnutls_session_t session)
117 {
118 int ret;
119 record_parameters_st *record_params;
120 const version_entry_st *vers = get_version(session);
121
122 if (unlikely(vers == NULL))
123 return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
124
125 if (vers->tls13_sem)
126 return 1;
127
128 #ifdef ENABLE_SSL3
129 if (vers->id == GNUTLS_SSL3)
130 return 0;
131 #endif
132
133 ret =
134 _gnutls_epoch_get(session, EPOCH_WRITE_CURRENT,
135 &record_params);
136 if (ret < 0) {
137 return 0;
138 }
139
140 switch (_gnutls_cipher_type(record_params->cipher)) {
141 case CIPHER_BLOCK:
142 return 1;
143 case CIPHER_STREAM:
144 case CIPHER_AEAD:
145 default:
146 return 0;
147 }
148 }
149
150 /**
151 * gnutls_range_split:
152 * @session: is a #gnutls_session_t type
153 * @orig: is the original range provided by the user
154 * @next: is the returned range that can be conveyed in a TLS record
155 * @remainder: is the returned remaining range
156 *
157 * This function should be used when it is required to hide the length
158 * of very long data that cannot be directly provided to gnutls_record_send_range().
159 * In that case this function should be called with the desired length
160 * hiding range in @orig. The returned @next value should then be used in
161 * the next call to gnutls_record_send_range() with the partial data.
162 * That process should be repeated until @remainder is (0,0).
163 *
164 * Returns: 0 in case splitting succeeds, non zero in case of error.
165 * Note that @orig is not changed, while the values of @next
166 * and @remainder are modified to store the resulting values.
167 */
168 int
gnutls_range_split(gnutls_session_t session,const gnutls_range_st * orig,gnutls_range_st * next,gnutls_range_st * remainder)169 gnutls_range_split(gnutls_session_t session,
170 const gnutls_range_st * orig,
171 gnutls_range_st * next, gnutls_range_st * remainder)
172 {
173 int ret;
174 ssize_t max_frag;
175 ssize_t orig_low = (ssize_t) orig->low;
176 ssize_t orig_high = (ssize_t) orig->high;
177 record_parameters_st *record_params;
178
179 ret =
180 _gnutls_epoch_get(session, EPOCH_WRITE_CURRENT,
181 &record_params);
182 if (ret < 0)
183 return gnutls_assert_val(ret);
184
185 max_frag = max_record_send_size(session, record_params);
186
187 if (orig_high == orig_low) {
188 int length = MIN(orig_high, max_frag);
189 int rem = orig_high - length;
190 _gnutls_set_range(next, length, length);
191 _gnutls_set_range(remainder, rem, rem);
192
193 return 0;
194 } else {
195 if (orig_low >= max_frag) {
196 _gnutls_set_range(next, max_frag, max_frag);
197 _gnutls_set_range(remainder, orig_low - max_frag,
198 orig_high - max_frag);
199 } else {
200 ret =
201 _gnutls_range_max_lh_pad(session, orig_low,
202 max_frag);
203 if (ret < 0)
204 return gnutls_assert_val(ret);
205
206 ssize_t this_pad = MIN(ret, orig_high - orig_low);
207
208 _gnutls_set_range(next, orig_low,
209 orig_low + this_pad);
210 _gnutls_set_range(remainder, 0,
211 orig_high - (orig_low +
212 this_pad));
213 }
214
215 return 0;
216 }
217 }
218
219 static size_t
_gnutls_range_fragment(size_t data_size,gnutls_range_st cur,gnutls_range_st next)220 _gnutls_range_fragment(size_t data_size, gnutls_range_st cur,
221 gnutls_range_st next)
222 {
223 return MIN(cur.high, data_size - next.low);
224 }
225
226 /**
227 * gnutls_record_send_range:
228 * @session: is a #gnutls_session_t type.
229 * @data: contains the data to send.
230 * @data_size: is the length of the data.
231 * @range: is the range of lengths in which the real data length must be hidden.
232 *
233 * This function operates like gnutls_record_send() but, while
234 * gnutls_record_send() adds minimal padding to each TLS record,
235 * this function uses the TLS extra-padding feature to conceal the real
236 * data size within the range of lengths provided.
237 * Some TLS sessions do not support extra padding (e.g. stream ciphers in standard
238 * TLS or SSL3 sessions). To know whether the current session supports extra
239 * padding, and hence length hiding, use the gnutls_record_can_use_length_hiding()
240 * function.
241 *
242 * Note: This function currently is limited to blocking sockets.
243 *
244 * Returns: The number of bytes sent (that is data_size in a successful invocation),
245 * or a negative error code.
246 **/
247 ssize_t
gnutls_record_send_range(gnutls_session_t session,const void * data,size_t data_size,const gnutls_range_st * range)248 gnutls_record_send_range(gnutls_session_t session, const void *data,
249 size_t data_size, const gnutls_range_st * range)
250 {
251 size_t sent = 0;
252 size_t next_fragment_length;
253 ssize_t ret;
254 gnutls_range_st cur_range, next_range;
255
256 /* sanity check on range and data size */
257 if (range->low > range->high ||
258 data_size < range->low || data_size > range->high) {
259 return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
260 }
261
262 ret = gnutls_record_can_use_length_hiding(session);
263 if (ret == 0)
264 return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
265
266 _gnutls_set_range(&cur_range, range->low, range->high);
267
268 _gnutls_record_log
269 ("RANGE: Preparing message with size %d, range (%d,%d)\n",
270 (int) data_size, (int) range->low, (int) range->high);
271
272 while (cur_range.high != 0) {
273 ret =
274 gnutls_range_split(session, &cur_range, &cur_range,
275 &next_range);
276 if (ret < 0) {
277 return ret; /* already gnutls_assert_val'd */
278 }
279
280 next_fragment_length =
281 _gnutls_range_fragment(data_size, cur_range,
282 next_range);
283
284 _gnutls_record_log
285 ("RANGE: Next fragment size: %d (%d,%d); remaining range: (%d,%d)\n",
286 (int) next_fragment_length, (int) cur_range.low,
287 (int) cur_range.high, (int) next_range.low,
288 (int) next_range.high);
289
290 ret =
291 _gnutls_send_tlen_int(session, GNUTLS_APPLICATION_DATA,
292 -1, EPOCH_WRITE_CURRENT,
293 &(((char *) data)[sent]),
294 next_fragment_length,
295 cur_range.high -
296 next_fragment_length,
297 MBUFFER_FLUSH);
298
299 while (ret == GNUTLS_E_AGAIN
300 || ret == GNUTLS_E_INTERRUPTED) {
301 ret =
302 _gnutls_send_tlen_int(session,
303 GNUTLS_APPLICATION_DATA,
304 -1, EPOCH_WRITE_CURRENT,
305 NULL, 0, 0,
306 MBUFFER_FLUSH);
307 }
308
309 if (ret < 0) {
310 return gnutls_assert_val(ret);
311 }
312 if (ret != (ssize_t) next_fragment_length) {
313 _gnutls_record_log
314 ("RANGE: ERROR: ret = %d; next_fragment_length = %d\n",
315 (int) ret, (int) next_fragment_length);
316 return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
317 }
318 sent += next_fragment_length;
319 data_size -= next_fragment_length;
320 _gnutls_set_range(&cur_range, next_range.low,
321 next_range.high);
322 }
323
324 return sent;
325 }
326