Line data Source code
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
30 689 : _gnutls_set_range(gnutls_range_st * dst, const size_t low,
31 : const size_t high)
32 : {
33 689 : dst->low = low;
34 689 : dst->high = high;
35 689 : 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
43 226 : _gnutls_range_max_lh_pad(gnutls_session_t session, ssize_t data_length,
44 : ssize_t max_frag)
45 : {
46 226 : int ret;
47 226 : ssize_t max_pad;
48 226 : unsigned int fixed_pad;
49 226 : record_parameters_st *record_params;
50 226 : ssize_t this_pad;
51 226 : ssize_t block_size;
52 226 : ssize_t tag_size, overflow;
53 226 : const version_entry_st *vers = get_version(session);
54 :
55 226 : if (unlikely(vers == NULL))
56 0 : return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
57 :
58 226 : ret =
59 226 : _gnutls_epoch_get(session, EPOCH_WRITE_CURRENT,
60 : &record_params);
61 226 : if (ret < 0) {
62 0 : return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
63 : }
64 :
65 226 : if (!vers->tls13_sem && record_params->write.is_aead) /* not yet ready */
66 0 : return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
67 :
68 226 : if (vers->tls13_sem) {
69 4 : max_pad = max_record_send_size(session, record_params);
70 4 : fixed_pad = 2;
71 : } else {
72 : max_pad = MAX_PAD_SIZE;
73 : fixed_pad = 1;
74 : }
75 :
76 226 : this_pad = MIN(max_pad, max_frag - data_length);
77 :
78 226 : block_size = _gnutls_cipher_get_block_size(record_params->cipher);
79 226 : tag_size =
80 226 : _gnutls_auth_cipher_tag_len(&record_params->write.
81 : ctx.tls12);
82 226 : switch (_gnutls_cipher_type(record_params->cipher)) {
83 : case CIPHER_AEAD:
84 : case CIPHER_STREAM:
85 : return this_pad;
86 :
87 222 : case CIPHER_BLOCK:
88 222 : overflow =
89 222 : (data_length + this_pad + tag_size +
90 : fixed_pad) % block_size;
91 222 : if (overflow > this_pad) {
92 : return this_pad;
93 : } else {
94 222 : return this_pad - overflow;
95 : }
96 : default:
97 0 : 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 : **/
116 13 : unsigned gnutls_record_can_use_length_hiding(gnutls_session_t session)
117 : {
118 13 : int ret;
119 13 : record_parameters_st *record_params;
120 13 : const version_entry_st *vers = get_version(session);
121 :
122 13 : if (unlikely(vers == NULL))
123 0 : return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
124 :
125 13 : 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 8 : ret =
134 8 : _gnutls_epoch_get(session, EPOCH_WRITE_CURRENT,
135 : &record_params);
136 8 : if (ret < 0) {
137 : return 0;
138 : }
139 :
140 8 : switch (_gnutls_cipher_type(record_params->cipher)) {
141 : case CIPHER_BLOCK:
142 : return 1;
143 0 : case CIPHER_STREAM:
144 : case CIPHER_AEAD:
145 : default:
146 0 : 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
169 226 : gnutls_range_split(gnutls_session_t session,
170 : const gnutls_range_st * orig,
171 : gnutls_range_st * next, gnutls_range_st * remainder)
172 : {
173 226 : int ret;
174 226 : ssize_t max_frag;
175 226 : ssize_t orig_low = (ssize_t) orig->low;
176 226 : ssize_t orig_high = (ssize_t) orig->high;
177 226 : record_parameters_st *record_params;
178 :
179 226 : ret =
180 226 : _gnutls_epoch_get(session, EPOCH_WRITE_CURRENT,
181 : &record_params);
182 226 : if (ret < 0)
183 0 : return gnutls_assert_val(ret);
184 :
185 226 : max_frag = max_record_send_size(session, record_params);
186 :
187 226 : if (orig_high == orig_low) {
188 0 : int length = MIN(orig_high, max_frag);
189 0 : int rem = orig_high - length;
190 0 : _gnutls_set_range(next, length, length);
191 0 : _gnutls_set_range(remainder, rem, rem);
192 :
193 0 : return 0;
194 : } else {
195 226 : if (orig_low >= max_frag) {
196 0 : _gnutls_set_range(next, max_frag, max_frag);
197 0 : _gnutls_set_range(remainder, orig_low - max_frag,
198 0 : orig_high - max_frag);
199 : } else {
200 452 : ret =
201 226 : _gnutls_range_max_lh_pad(session, orig_low,
202 : max_frag);
203 226 : if (ret < 0)
204 0 : return gnutls_assert_val(ret);
205 :
206 226 : ssize_t this_pad = MIN(ret, orig_high - orig_low);
207 :
208 226 : _gnutls_set_range(next, orig_low,
209 226 : orig_low + this_pad);
210 226 : _gnutls_set_range(remainder, 0,
211 226 : orig_high - (orig_low +
212 : this_pad));
213 : }
214 :
215 226 : return 0;
216 : }
217 : }
218 :
219 : static size_t
220 226 : _gnutls_range_fragment(size_t data_size, gnutls_range_st cur,
221 : gnutls_range_st next)
222 : {
223 226 : 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
248 11 : gnutls_record_send_range(gnutls_session_t session, const void *data,
249 : size_t data_size, const gnutls_range_st * range)
250 : {
251 11 : size_t sent = 0;
252 11 : size_t next_fragment_length;
253 11 : ssize_t ret;
254 11 : gnutls_range_st cur_range, next_range;
255 :
256 : /* sanity check on range and data size */
257 11 : if (range->low > range->high ||
258 11 : data_size < range->low || data_size > range->high) {
259 0 : return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
260 : }
261 :
262 11 : ret = gnutls_record_can_use_length_hiding(session);
263 11 : if (ret == 0)
264 0 : return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
265 :
266 11 : _gnutls_set_range(&cur_range, range->low, range->high);
267 :
268 11 : _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 237 : while (cur_range.high != 0) {
273 452 : ret =
274 226 : gnutls_range_split(session, &cur_range, &cur_range,
275 : &next_range);
276 226 : if (ret < 0) {
277 : return ret; /* already gnutls_assert_val'd */
278 : }
279 :
280 226 : next_fragment_length =
281 226 : _gnutls_range_fragment(data_size, cur_range,
282 : next_range);
283 :
284 226 : _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 226 : ret =
291 226 : _gnutls_send_tlen_int(session, GNUTLS_APPLICATION_DATA,
292 : -1, EPOCH_WRITE_CURRENT,
293 : &(((char *) data)[sent]),
294 : next_fragment_length,
295 226 : cur_range.high -
296 : next_fragment_length,
297 : MBUFFER_FLUSH);
298 :
299 226 : while (ret == GNUTLS_E_AGAIN
300 226 : || ret == GNUTLS_E_INTERRUPTED) {
301 0 : ret =
302 0 : _gnutls_send_tlen_int(session,
303 : GNUTLS_APPLICATION_DATA,
304 : -1, EPOCH_WRITE_CURRENT,
305 : NULL, 0, 0,
306 : MBUFFER_FLUSH);
307 : }
308 :
309 226 : if (ret < 0) {
310 0 : return gnutls_assert_val(ret);
311 : }
312 226 : if (ret != (ssize_t) next_fragment_length) {
313 0 : _gnutls_record_log
314 : ("RANGE: ERROR: ret = %d; next_fragment_length = %d\n",
315 : (int) ret, (int) next_fragment_length);
316 0 : return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
317 : }
318 226 : sent += next_fragment_length;
319 226 : data_size -= next_fragment_length;
320 226 : _gnutls_set_range(&cur_range, next_range.low,
321 : next_range.high);
322 : }
323 :
324 11 : return sent;
325 : }
|