Blame lib/range.c

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
}