2 * Copyright (C) 2012 INRIA Paris-Rocquencourt
4 * Author: Alfredo Pironti
6 * This file is part of GnuTLS.
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.
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.
18 * You should have received a copy of the GNU Lesser General Public License
19 * along with this program. If not, see <http://www.gnu.org/licenses/>
23 #include "gnutls_int.h"
24 #include "gnutls_errors.h"
25 #include "algorithms.h"
26 #include "gnutls_constate.h"
27 #include "gnutls_record.h"
30 _gnutls_set_range(gnutls_range_st * dst, const size_t low,
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.
43 _gnutls_range_max_lh_pad(gnutls_session_t session, ssize_t data_length,
48 unsigned int fixed_pad;
49 record_parameters_st *record_params;
52 ssize_t tag_size, overflow;
55 _gnutls_epoch_get(session, EPOCH_WRITE_CURRENT,
58 return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
61 max_pad = MAX_PAD_SIZE;
64 this_pad = MIN(max_pad, max_frag - data_length);
66 block_size = _gnutls_cipher_get_block_size(record_params->cipher);
68 _gnutls_auth_cipher_tag_len(&record_params->write.
70 switch (_gnutls_cipher_is_block(record_params->cipher)) {
76 (data_length + this_pad + tag_size +
77 fixed_pad) % block_size;
78 if (overflow > this_pad) {
81 return this_pad - overflow;
84 return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
89 * gnutls_record_can_use_length_hiding:
90 * @session: is a #gnutls_session_t structure.
92 * If the session supports length-hiding padding, you can
93 * invoke gnutls_range_send_message() to send a message whose
94 * length is hidden in the given range. If the session does not
95 * support length hiding padding, you can use the standard
96 * gnutls_record_send() function, or gnutls_range_send_message()
97 * making sure that the range is the same as the length of the
98 * message you are trying to send.
100 * Returns: true (1) if the current session supports length-hiding
101 * padding, false (0) if the current session does not.
103 int gnutls_record_can_use_length_hiding(gnutls_session_t session)
106 record_parameters_st *record_params;
108 if (get_num_version(session) == GNUTLS_SSL3)
112 _gnutls_epoch_get(session, EPOCH_WRITE_CURRENT,
118 switch (_gnutls_cipher_is_block(record_params->cipher)) {
128 * gnutls_range_split:
129 * @session: is a #gnutls_session_t structure
130 * @orig: is the original range provided by the user
131 * @next: is the returned range that can be conveyed in a TLS record
132 * @remainder: is the returned remaining range
134 * This function should be used when it is required to hide the length
135 * of very long data that cannot be directly provided to gnutls_record_send_range().
136 * In that case this function should be called with the desired length
137 * hiding range in @orig. The returned @next value should then be used in
138 * the next call to gnutls_record_send_range() with the partial data.
139 * That process should be repeated until @remainder is (0,0).
141 * Returns: 0 in case splitting succeeds, non zero in case of error.
142 * Note that @orig is not changed, while the values of @next
143 * and @remainder are modified to store the resulting values.
146 gnutls_range_split(gnutls_session_t session,
147 const gnutls_range_st * orig,
148 gnutls_range_st * next, gnutls_range_st * remainder)
152 ssize_t orig_low = (ssize_t) orig->low;
153 ssize_t orig_high = (ssize_t) orig->high;
154 record_parameters_st *record_params;
157 _gnutls_epoch_get(session, EPOCH_WRITE_CURRENT,
160 return gnutls_assert_val(ret);
162 max_frag = max_user_send_size(session, record_params);
164 if (orig_high == orig_low) {
165 int length = MIN(orig_high, max_frag);
166 int rem = orig_high - length;
167 _gnutls_set_range(next, length, length);
168 _gnutls_set_range(remainder, rem, rem);
172 if (orig_low >= max_frag) {
173 _gnutls_set_range(next, max_frag, max_frag);
174 _gnutls_set_range(remainder, orig_low - max_frag,
175 orig_high - max_frag);
178 _gnutls_range_max_lh_pad(session, orig_low,
181 return gnutls_assert_val(ret);
183 ssize_t this_pad = MIN(ret, orig_high - orig_low);
185 _gnutls_set_range(next, orig_low,
186 orig_low + this_pad);
187 _gnutls_set_range(remainder, 0,
188 orig_high - (orig_low +
197 _gnutls_range_fragment(size_t data_size, gnutls_range_st cur,
198 gnutls_range_st next)
200 return MIN(cur.high, data_size - next.low);
204 * gnutls_record_send_range:
205 * @session: is a #gnutls_session_t structure.
206 * @data: contains the data to send.
207 * @data_size: is the length of the data.
208 * @range: is the range of lengths in which the real data length must be hidden.
210 * This function operates like gnutls_record_send() but, while
211 * gnutls_record_send() adds minimal padding to each TLS record,
212 * this function uses the TLS extra-padding feature to conceal the real
213 * data size within the range of lengths provided.
214 * Some TLS sessions do not support extra padding (e.g. stream ciphers in standard
215 * TLS or SSL3 sessions). To know whether the current session supports extra
216 * padding, and hence length hiding, use the gnutls_record_can_use_length_hiding()
219 * Note: This function currently is only limited to blocking sockets.
221 * Returns: The number of bytes sent (that is data_size in a successful invocation),
222 * or a negative error code.
225 gnutls_record_send_range(gnutls_session_t session, const void *data,
226 size_t data_size, const gnutls_range_st * range)
229 size_t next_fragment_length;
231 gnutls_range_st cur_range, next_range;
233 /* sanity check on range and data size */
234 if (range->low > range->high ||
235 data_size < range->low || data_size > range->high) {
236 return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
239 ret = gnutls_record_can_use_length_hiding(session);
241 return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
243 _gnutls_set_range(&cur_range, range->low, range->high);
246 ("RANGE: Preparing message with size %d, range (%d,%d)\n",
247 (int) data_size, (int) range->low, (int) range->high);
249 while (cur_range.high != 0) {
251 gnutls_range_split(session, &cur_range, &cur_range,
254 return ret; /* already gnutls_assert_val'd */
257 next_fragment_length =
258 _gnutls_range_fragment(data_size, cur_range,
262 ("RANGE: Next fragment size: %d (%d,%d); remaining range: (%d,%d)\n",
263 (int) next_fragment_length, (int) cur_range.low,
264 (int) cur_range.high, (int) next_range.low,
265 (int) next_range.high);
268 _gnutls_send_tlen_int(session, GNUTLS_APPLICATION_DATA,
269 -1, EPOCH_WRITE_CURRENT,
270 &(((char *) data)[sent]),
271 next_fragment_length,
273 next_fragment_length,
276 while (ret == GNUTLS_E_AGAIN
277 || ret == GNUTLS_E_INTERRUPTED) {
279 _gnutls_send_tlen_int(session,
280 GNUTLS_APPLICATION_DATA,
281 -1, EPOCH_WRITE_CURRENT,
287 return gnutls_assert_val(ret);
289 if (ret != (ssize_t) next_fragment_length) {
291 ("RANGE: ERROR: ret = %d; next_fragment_length = %d\n",
292 (int) ret, (int) next_fragment_length);
293 return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
295 sent += next_fragment_length;
296 data_size -= next_fragment_length;
297 _gnutls_set_range(&cur_range, next_range.low,