|
Packit Service |
4684c1 |
/*
|
|
Packit Service |
4684c1 |
* Copyright (C) 2012 INRIA Paris-Rocquencourt
|
|
Packit Service |
4684c1 |
*
|
|
Packit Service |
4684c1 |
* Author: Alfredo Pironti
|
|
Packit Service |
4684c1 |
*
|
|
Packit Service |
4684c1 |
* This file is part of GnuTLS.
|
|
Packit Service |
4684c1 |
*
|
|
Packit Service |
4684c1 |
* The GnuTLS is free software; you can redistribute it and/or
|
|
Packit Service |
4684c1 |
* modify it under the terms of the GNU Lesser General Public License
|
|
Packit Service |
4684c1 |
* as published by the Free Software Foundation; either version 2.1 of
|
|
Packit Service |
4684c1 |
* the License, or (at your option) any later version.
|
|
Packit Service |
4684c1 |
*
|
|
Packit Service |
4684c1 |
* This library is distributed in the hope that it will be useful, but
|
|
Packit Service |
4684c1 |
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
Packit Service |
4684c1 |
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
Packit Service |
4684c1 |
* Lesser General Public License for more details.
|
|
Packit Service |
4684c1 |
*
|
|
Packit Service |
4684c1 |
* You should have received a copy of the GNU Lesser General Public License
|
|
Packit Service |
4684c1 |
* along with this program. If not, see <https://www.gnu.org/licenses/>
|
|
Packit Service |
4684c1 |
*
|
|
Packit Service |
4684c1 |
*/
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
#include "gnutls_int.h"
|
|
Packit Service |
4684c1 |
#include "errors.h"
|
|
Packit Service |
4684c1 |
#include "algorithms.h"
|
|
Packit Service |
4684c1 |
#include "constate.h"
|
|
Packit Service |
4684c1 |
#include "record.h"
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
static void
|
|
Packit Service |
4684c1 |
_gnutls_set_range(gnutls_range_st * dst, const size_t low,
|
|
Packit Service |
4684c1 |
const size_t high)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
dst->low = low;
|
|
Packit Service |
4684c1 |
dst->high = high;
|
|
Packit Service |
4684c1 |
return;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/*
|
|
Packit Service |
4684c1 |
* Returns how much LH pad we can put in this fragment, given we'll
|
|
Packit Service |
4684c1 |
* put at least data_length bytes of user data.
|
|
Packit Service |
4684c1 |
*/
|
|
Packit Service |
4684c1 |
static ssize_t
|
|
Packit Service |
4684c1 |
_gnutls_range_max_lh_pad(gnutls_session_t session, ssize_t data_length,
|
|
Packit Service |
4684c1 |
ssize_t max_frag)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
int ret;
|
|
Packit Service |
4684c1 |
ssize_t max_pad;
|
|
Packit Service |
4684c1 |
unsigned int fixed_pad;
|
|
Packit Service |
4684c1 |
record_parameters_st *record_params;
|
|
Packit Service |
4684c1 |
ssize_t this_pad;
|
|
Packit Service |
4684c1 |
ssize_t block_size;
|
|
Packit Service |
4684c1 |
ssize_t tag_size, overflow;
|
|
Packit Service |
4684c1 |
const version_entry_st *vers = get_version(session);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (unlikely(vers == NULL))
|
|
Packit Service |
4684c1 |
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
ret =
|
|
Packit Service |
4684c1 |
_gnutls_epoch_get(session, EPOCH_WRITE_CURRENT,
|
|
Packit Service |
4684c1 |
&record_params);
|
|
Packit Service |
4684c1 |
if (ret < 0) {
|
|
Packit Service |
4684c1 |
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (!vers->tls13_sem && record_params->write.is_aead) /* not yet ready */
|
|
Packit Service |
4684c1 |
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (vers->tls13_sem) {
|
|
Packit Service |
4684c1 |
max_pad = max_record_send_size(session, record_params);
|
|
Packit Service |
4684c1 |
fixed_pad = 2;
|
|
Packit Service |
4684c1 |
} else {
|
|
Packit Service |
4684c1 |
max_pad = MAX_PAD_SIZE;
|
|
Packit Service |
4684c1 |
fixed_pad = 1;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
this_pad = MIN(max_pad, max_frag - data_length);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
block_size = _gnutls_cipher_get_block_size(record_params->cipher);
|
|
Packit Service |
4684c1 |
tag_size =
|
|
Packit Service |
4684c1 |
_gnutls_auth_cipher_tag_len(&record_params->write.
|
|
Packit Service |
4684c1 |
ctx.tls12);
|
|
Packit Service |
4684c1 |
switch (_gnutls_cipher_type(record_params->cipher)) {
|
|
Packit Service |
4684c1 |
case CIPHER_AEAD:
|
|
Packit Service |
4684c1 |
case CIPHER_STREAM:
|
|
Packit Service |
4684c1 |
return this_pad;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
case CIPHER_BLOCK:
|
|
Packit Service |
4684c1 |
overflow =
|
|
Packit Service |
4684c1 |
(data_length + this_pad + tag_size +
|
|
Packit Service |
4684c1 |
fixed_pad) % block_size;
|
|
Packit Service |
4684c1 |
if (overflow > this_pad) {
|
|
Packit Service |
4684c1 |
return this_pad;
|
|
Packit Service |
4684c1 |
} else {
|
|
Packit Service |
4684c1 |
return this_pad - overflow;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
default:
|
|
Packit Service |
4684c1 |
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/**
|
|
Packit Service |
4684c1 |
* gnutls_record_can_use_length_hiding:
|
|
Packit Service |
4684c1 |
* @session: is a #gnutls_session_t type.
|
|
Packit Service |
4684c1 |
*
|
|
Packit Service |
4684c1 |
* If the session supports length-hiding padding, you can
|
|
Packit Service |
4684c1 |
* invoke gnutls_record_send_range() to send a message whose
|
|
Packit Service |
4684c1 |
* length is hidden in the given range. If the session does not
|
|
Packit Service |
4684c1 |
* support length hiding padding, you can use the standard
|
|
Packit Service |
4684c1 |
* gnutls_record_send() function, or gnutls_record_send_range()
|
|
Packit Service |
4684c1 |
* making sure that the range is the same as the length of the
|
|
Packit Service |
4684c1 |
* message you are trying to send.
|
|
Packit Service |
4684c1 |
*
|
|
Packit Service |
4684c1 |
* Returns: true (1) if the current session supports length-hiding
|
|
Packit Service |
4684c1 |
* padding, false (0) if the current session does not.
|
|
Packit Service |
4684c1 |
**/
|
|
Packit Service |
4684c1 |
unsigned gnutls_record_can_use_length_hiding(gnutls_session_t session)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
int ret;
|
|
Packit Service |
4684c1 |
record_parameters_st *record_params;
|
|
Packit Service |
4684c1 |
const version_entry_st *vers = get_version(session);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (unlikely(vers == NULL))
|
|
Packit Service |
4684c1 |
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (vers->tls13_sem)
|
|
Packit Service |
4684c1 |
return 1;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
#ifdef ENABLE_SSL3
|
|
Packit Service |
4684c1 |
if (vers->id == GNUTLS_SSL3)
|
|
Packit Service |
4684c1 |
return 0;
|
|
Packit Service |
4684c1 |
#endif
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
ret =
|
|
Packit Service |
4684c1 |
_gnutls_epoch_get(session, EPOCH_WRITE_CURRENT,
|
|
Packit Service |
4684c1 |
&record_params);
|
|
Packit Service |
4684c1 |
if (ret < 0) {
|
|
Packit Service |
4684c1 |
return 0;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
switch (_gnutls_cipher_type(record_params->cipher)) {
|
|
Packit Service |
4684c1 |
case CIPHER_BLOCK:
|
|
Packit Service |
4684c1 |
return 1;
|
|
Packit Service |
4684c1 |
case CIPHER_STREAM:
|
|
Packit Service |
4684c1 |
case CIPHER_AEAD:
|
|
Packit Service |
4684c1 |
default:
|
|
Packit Service |
4684c1 |
return 0;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/**
|
|
Packit Service |
4684c1 |
* gnutls_range_split:
|
|
Packit Service |
4684c1 |
* @session: is a #gnutls_session_t type
|
|
Packit Service |
4684c1 |
* @orig: is the original range provided by the user
|
|
Packit Service |
4684c1 |
* @next: is the returned range that can be conveyed in a TLS record
|
|
Packit Service |
4684c1 |
* @remainder: is the returned remaining range
|
|
Packit Service |
4684c1 |
*
|
|
Packit Service |
4684c1 |
* This function should be used when it is required to hide the length
|
|
Packit Service |
4684c1 |
* of very long data that cannot be directly provided to gnutls_record_send_range().
|
|
Packit Service |
4684c1 |
* In that case this function should be called with the desired length
|
|
Packit Service |
4684c1 |
* hiding range in @orig. The returned @next value should then be used in
|
|
Packit Service |
4684c1 |
* the next call to gnutls_record_send_range() with the partial data.
|
|
Packit Service |
4684c1 |
* That process should be repeated until @remainder is (0,0).
|
|
Packit Service |
4684c1 |
*
|
|
Packit Service |
4684c1 |
* Returns: 0 in case splitting succeeds, non zero in case of error.
|
|
Packit Service |
4684c1 |
* Note that @orig is not changed, while the values of @next
|
|
Packit Service |
4684c1 |
* and @remainder are modified to store the resulting values.
|
|
Packit Service |
4684c1 |
*/
|
|
Packit Service |
4684c1 |
int
|
|
Packit Service |
4684c1 |
gnutls_range_split(gnutls_session_t session,
|
|
Packit Service |
4684c1 |
const gnutls_range_st * orig,
|
|
Packit Service |
4684c1 |
gnutls_range_st * next, gnutls_range_st * remainder)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
int ret;
|
|
Packit Service |
4684c1 |
ssize_t max_frag;
|
|
Packit Service |
4684c1 |
ssize_t orig_low = (ssize_t) orig->low;
|
|
Packit Service |
4684c1 |
ssize_t orig_high = (ssize_t) orig->high;
|
|
Packit Service |
4684c1 |
record_parameters_st *record_params;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
ret =
|
|
Packit Service |
4684c1 |
_gnutls_epoch_get(session, EPOCH_WRITE_CURRENT,
|
|
Packit Service |
4684c1 |
&record_params);
|
|
Packit Service |
4684c1 |
if (ret < 0)
|
|
Packit Service |
4684c1 |
return gnutls_assert_val(ret);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
max_frag = max_record_send_size(session, record_params);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (orig_high == orig_low) {
|
|
Packit Service |
4684c1 |
int length = MIN(orig_high, max_frag);
|
|
Packit Service |
4684c1 |
int rem = orig_high - length;
|
|
Packit Service |
4684c1 |
_gnutls_set_range(next, length, length);
|
|
Packit Service |
4684c1 |
_gnutls_set_range(remainder, rem, rem);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
return 0;
|
|
Packit Service |
4684c1 |
} else {
|
|
Packit Service |
4684c1 |
if (orig_low >= max_frag) {
|
|
Packit Service |
4684c1 |
_gnutls_set_range(next, max_frag, max_frag);
|
|
Packit Service |
4684c1 |
_gnutls_set_range(remainder, orig_low - max_frag,
|
|
Packit Service |
4684c1 |
orig_high - max_frag);
|
|
Packit Service |
4684c1 |
} else {
|
|
Packit Service |
4684c1 |
ret =
|
|
Packit Service |
4684c1 |
_gnutls_range_max_lh_pad(session, orig_low,
|
|
Packit Service |
4684c1 |
max_frag);
|
|
Packit Service |
4684c1 |
if (ret < 0)
|
|
Packit Service |
4684c1 |
return gnutls_assert_val(ret);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
ssize_t this_pad = MIN(ret, orig_high - orig_low);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
_gnutls_set_range(next, orig_low,
|
|
Packit Service |
4684c1 |
orig_low + this_pad);
|
|
Packit Service |
4684c1 |
_gnutls_set_range(remainder, 0,
|
|
Packit Service |
4684c1 |
orig_high - (orig_low +
|
|
Packit Service |
4684c1 |
this_pad));
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
return 0;
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
static size_t
|
|
Packit Service |
4684c1 |
_gnutls_range_fragment(size_t data_size, gnutls_range_st cur,
|
|
Packit Service |
4684c1 |
gnutls_range_st next)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
return MIN(cur.high, data_size - next.low);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/**
|
|
Packit Service |
4684c1 |
* gnutls_record_send_range:
|
|
Packit Service |
4684c1 |
* @session: is a #gnutls_session_t type.
|
|
Packit Service |
4684c1 |
* @data: contains the data to send.
|
|
Packit Service |
4684c1 |
* @data_size: is the length of the data.
|
|
Packit Service |
4684c1 |
* @range: is the range of lengths in which the real data length must be hidden.
|
|
Packit Service |
4684c1 |
*
|
|
Packit Service |
4684c1 |
* This function operates like gnutls_record_send() but, while
|
|
Packit Service |
4684c1 |
* gnutls_record_send() adds minimal padding to each TLS record,
|
|
Packit Service |
4684c1 |
* this function uses the TLS extra-padding feature to conceal the real
|
|
Packit Service |
4684c1 |
* data size within the range of lengths provided.
|
|
Packit Service |
4684c1 |
* Some TLS sessions do not support extra padding (e.g. stream ciphers in standard
|
|
Packit Service |
4684c1 |
* TLS or SSL3 sessions). To know whether the current session supports extra
|
|
Packit Service |
4684c1 |
* padding, and hence length hiding, use the gnutls_record_can_use_length_hiding()
|
|
Packit Service |
4684c1 |
* function.
|
|
Packit Service |
4684c1 |
*
|
|
Packit Service |
4684c1 |
* Note: This function currently is limited to blocking sockets.
|
|
Packit Service |
4684c1 |
*
|
|
Packit Service |
4684c1 |
* Returns: The number of bytes sent (that is data_size in a successful invocation),
|
|
Packit Service |
4684c1 |
* or a negative error code.
|
|
Packit Service |
4684c1 |
**/
|
|
Packit Service |
4684c1 |
ssize_t
|
|
Packit Service |
4684c1 |
gnutls_record_send_range(gnutls_session_t session, const void *data,
|
|
Packit Service |
4684c1 |
size_t data_size, const gnutls_range_st * range)
|
|
Packit Service |
4684c1 |
{
|
|
Packit Service |
4684c1 |
size_t sent = 0;
|
|
Packit Service |
4684c1 |
size_t next_fragment_length;
|
|
Packit Service |
4684c1 |
ssize_t ret;
|
|
Packit Service |
4684c1 |
gnutls_range_st cur_range, next_range;
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
/* sanity check on range and data size */
|
|
Packit Service |
4684c1 |
if (range->low > range->high ||
|
|
Packit Service |
4684c1 |
data_size < range->low || data_size > range->high) {
|
|
Packit Service |
4684c1 |
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
ret = gnutls_record_can_use_length_hiding(session);
|
|
Packit Service |
4684c1 |
if (ret == 0)
|
|
Packit Service |
4684c1 |
return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
_gnutls_set_range(&cur_range, range->low, range->high);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
_gnutls_record_log
|
|
Packit Service |
4684c1 |
("RANGE: Preparing message with size %d, range (%d,%d)\n",
|
|
Packit Service |
4684c1 |
(int) data_size, (int) range->low, (int) range->high);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
while (cur_range.high != 0) {
|
|
Packit Service |
4684c1 |
ret =
|
|
Packit Service |
4684c1 |
gnutls_range_split(session, &cur_range, &cur_range,
|
|
Packit Service |
4684c1 |
&next_range);
|
|
Packit Service |
4684c1 |
if (ret < 0) {
|
|
Packit Service |
4684c1 |
return ret; /* already gnutls_assert_val'd */
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
next_fragment_length =
|
|
Packit Service |
4684c1 |
_gnutls_range_fragment(data_size, cur_range,
|
|
Packit Service |
4684c1 |
next_range);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
_gnutls_record_log
|
|
Packit Service |
4684c1 |
("RANGE: Next fragment size: %d (%d,%d); remaining range: (%d,%d)\n",
|
|
Packit Service |
4684c1 |
(int) next_fragment_length, (int) cur_range.low,
|
|
Packit Service |
4684c1 |
(int) cur_range.high, (int) next_range.low,
|
|
Packit Service |
4684c1 |
(int) next_range.high);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
ret =
|
|
Packit Service |
4684c1 |
_gnutls_send_tlen_int(session, GNUTLS_APPLICATION_DATA,
|
|
Packit Service |
4684c1 |
-1, EPOCH_WRITE_CURRENT,
|
|
Packit Service |
4684c1 |
&(((char *) data)[sent]),
|
|
Packit Service |
4684c1 |
next_fragment_length,
|
|
Packit Service |
4684c1 |
cur_range.high -
|
|
Packit Service |
4684c1 |
next_fragment_length,
|
|
Packit Service |
4684c1 |
MBUFFER_FLUSH);
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
while (ret == GNUTLS_E_AGAIN
|
|
Packit Service |
4684c1 |
|| ret == GNUTLS_E_INTERRUPTED) {
|
|
Packit Service |
4684c1 |
ret =
|
|
Packit Service |
4684c1 |
_gnutls_send_tlen_int(session,
|
|
Packit Service |
4684c1 |
GNUTLS_APPLICATION_DATA,
|
|
Packit Service |
4684c1 |
-1, EPOCH_WRITE_CURRENT,
|
|
Packit Service |
4684c1 |
NULL, 0, 0,
|
|
Packit Service |
4684c1 |
MBUFFER_FLUSH);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
if (ret < 0) {
|
|
Packit Service |
4684c1 |
return gnutls_assert_val(ret);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
if (ret != (ssize_t) next_fragment_length) {
|
|
Packit Service |
4684c1 |
_gnutls_record_log
|
|
Packit Service |
4684c1 |
("RANGE: ERROR: ret = %d; next_fragment_length = %d\n",
|
|
Packit Service |
4684c1 |
(int) ret, (int) next_fragment_length);
|
|
Packit Service |
4684c1 |
return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
sent += next_fragment_length;
|
|
Packit Service |
4684c1 |
data_size -= next_fragment_length;
|
|
Packit Service |
4684c1 |
_gnutls_set_range(&cur_range, next_range.low,
|
|
Packit Service |
4684c1 |
next_range.high);
|
|
Packit Service |
4684c1 |
}
|
|
Packit Service |
4684c1 |
|
|
Packit Service |
4684c1 |
return sent;
|
|
Packit Service |
4684c1 |
}
|