Blame lib/dtls-sw.c

Packit 549fdc
/*
Packit 549fdc
 * Copyright (C) 2016 Red Hat, Inc.
Packit 549fdc
 *
Packit 549fdc
 * Authors: Fridolin Pokorny
Packit 549fdc
 *	  Nikos Mavrogiannopoulos
Packit 549fdc
 *
Packit 549fdc
 * This file is part of GNUTLS.
Packit 549fdc
 *
Packit 549fdc
 * The GNUTLS library is free software; you can redistribute it and/or
Packit 549fdc
 * modify it under the terms of the GNU Lesser General Public License
Packit 549fdc
 * as published by the Free Software Foundation; either version 2.1 of
Packit 549fdc
 * the License, or (at your option) any later version.
Packit 549fdc
 *
Packit 549fdc
 * This library is distributed in the hope that it will be useful, but
Packit 549fdc
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 549fdc
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 549fdc
 * Lesser General Public License for more details.
Packit 549fdc
 *
Packit 549fdc
 * You should have received a copy of the GNU Lesser General Public License
Packit 549fdc
 * along with this program.  If not, see <http://www.gnu.org/licenses/>
Packit 549fdc
 *
Packit 549fdc
 */
Packit 549fdc
Packit 549fdc
/* Functions that relate to DTLS sliding window handling.
Packit 549fdc
 */
Packit 549fdc
Packit 549fdc
#ifndef DTLS_SW_NO_INCLUDES
Packit 549fdc
#include "gnutls_int.h"
Packit 549fdc
#include "errors.h"
Packit 549fdc
#include "debug.h"
Packit 549fdc
#include "dtls.h"
Packit 549fdc
#include "record.h"
Packit 549fdc
#endif
Packit 549fdc
Packit 549fdc
/*
Packit 549fdc
 * DTLS sliding window handling
Packit 549fdc
 */
Packit 549fdc
#define DTLS_EPOCH_SHIFT		(6*CHAR_BIT)
Packit 549fdc
#define DTLS_SEQ_NUM_MASK		0x0000FFFFFFFFFFFF
Packit 549fdc
Packit 549fdc
#define DTLS_EMPTY_BITMAP		(0xFFFFFFFFFFFFFFFFULL)
Packit 549fdc
Packit 549fdc
/* We expect the compiler to be able to spot that this is a byteswapping
Packit 549fdc
 * load, and emit instructions like 'movbe' on x86_64 where appropriate.
Packit 549fdc
*/
Packit 549fdc
#define LOAD_UINT64(out, ubytes)  \
Packit 549fdc
	out = (((uint64_t)ubytes[0] << 56) |	\
Packit 549fdc
	       ((uint64_t)ubytes[1] << 48) |	\
Packit 549fdc
	       ((uint64_t)ubytes[2] << 40) |	\
Packit 549fdc
	       ((uint64_t)ubytes[3] << 32) |	\
Packit 549fdc
	       ((uint64_t)ubytes[4] << 24) |	\
Packit 549fdc
	       ((uint64_t)ubytes[5] << 16) |	\
Packit 549fdc
	       ((uint64_t)ubytes[6] << 8) |	\
Packit 549fdc
	       ((uint64_t)ubytes[7] << 0) )
Packit 549fdc
Packit 549fdc
Packit 549fdc
void _dtls_reset_window(struct record_parameters_st *rp)
Packit 549fdc
{
Packit 549fdc
	rp->dtls_sw_have_recv = 0;
Packit 549fdc
}
Packit 549fdc
Packit 549fdc
/* Checks if a sequence number is not replayed. If a replayed
Packit 549fdc
 * packet is detected it returns a negative value (but no sensible error code).
Packit 549fdc
 * Otherwise zero.
Packit 549fdc
 */
Packit 549fdc
int _dtls_record_check(struct record_parameters_st *rp, gnutls_uint64 * _seq)
Packit 549fdc
{
Packit 549fdc
	uint64_t seq_num = 0;
Packit 549fdc
Packit 549fdc
	LOAD_UINT64(seq_num, _seq->i);
Packit 549fdc
Packit 549fdc
	if ((seq_num >> DTLS_EPOCH_SHIFT) != rp->epoch) {
Packit 549fdc
		return gnutls_assert_val(-1);
Packit 549fdc
	}
Packit 549fdc
Packit 549fdc
	seq_num &= DTLS_SEQ_NUM_MASK;
Packit 549fdc
Packit 549fdc
	/*
Packit 549fdc
	 * rp->dtls_sw_next is the next *expected* packet (N), being
Packit 549fdc
	 * the sequence number *after* the latest we have received.
Packit 549fdc
	 *
Packit 549fdc
	 * By definition, therefore, packet N-1 *has* been received.
Packit 549fdc
	 * And thus there's no point wasting a bit in the bitmap for it.
Packit 549fdc
	 *
Packit 549fdc
	 * So the backlog bitmap covers the 64 packets prior to that,
Packit 549fdc
	 * with the LSB representing packet (N - 2), and the MSB
Packit 549fdc
	 * representing (N - 65). A received packet is represented
Packit 549fdc
	 * by a zero bit, and a missing packet is represented by a one.
Packit 549fdc
	 *
Packit 549fdc
	 * Thus we can allow out-of-order reception of packets that are
Packit 549fdc
	 * within a reasonable interval of the latest packet received.
Packit 549fdc
	 */
Packit 549fdc
	if (!rp->dtls_sw_have_recv) {
Packit 549fdc
		rp->dtls_sw_next = seq_num + 1;
Packit 549fdc
		rp->dtls_sw_bits = DTLS_EMPTY_BITMAP;
Packit 549fdc
		rp->dtls_sw_have_recv = 1;
Packit 549fdc
		return 0;
Packit 549fdc
	} else if (seq_num == rp->dtls_sw_next) {
Packit 549fdc
		/* The common case. This is the packet we expected next. */
Packit 549fdc
Packit 549fdc
		rp->dtls_sw_bits <<= 1;
Packit 549fdc
Packit 549fdc
		/* This might reach a value higher than 48-bit DTLS sequence
Packit 549fdc
		 * numbers can actually reach. Which is fine. When that
Packit 549fdc
		 * happens, we'll do the right thing and just not accept
Packit 549fdc
		 * any newer packets. Someone needs to start a new epoch. */
Packit 549fdc
		rp->dtls_sw_next++;
Packit 549fdc
		return 0;
Packit 549fdc
	} else if (seq_num > rp->dtls_sw_next) {
Packit 549fdc
		/* The packet we were expecting has gone missing; this one is newer.
Packit 549fdc
		 * We always advance the window to accommodate it. */
Packit 549fdc
		uint64_t delta = seq_num - rp->dtls_sw_next;
Packit 549fdc
Packit 549fdc
		if (delta >= 64) {
Packit 549fdc
			/* We jumped a long way into the future. We have not seen
Packit 549fdc
			 * any of the previous 32 packets so set the backlog bitmap
Packit 549fdc
			 * to all ones. */
Packit 549fdc
			rp->dtls_sw_bits = DTLS_EMPTY_BITMAP;
Packit 549fdc
		} else if (delta == 63) {
Packit 549fdc
			/* Avoid undefined behaviour that shifting by 64 would incur.
Packit 549fdc
			 * The (clear) top bit represents the packet which is currently
Packit 549fdc
			 * rp->dtls_sw_next, which we know was already received. */
Packit 549fdc
			rp->dtls_sw_bits = DTLS_EMPTY_BITMAP >> 1;
Packit 549fdc
		} else {
Packit 549fdc
			/* We have missed (delta) packets. Shift the backlog by that
Packit 549fdc
			 * amount *plus* the one we would have shifted it anyway if
Packit 549fdc
			 * we'd received the packet we were expecting. The zero bit
Packit 549fdc
			 * representing the packet which is currently rp->dtls_sw_next-1,
Packit 549fdc
			 * which we know has been received, ends up at bit position
Packit 549fdc
			 * (1<
Packit 549fdc
			 * represent the missing packets. */
Packit 549fdc
			rp->dtls_sw_bits <<= delta + 1;
Packit 549fdc
			rp->dtls_sw_bits |= (1ULL << delta) - 1;
Packit 549fdc
		}
Packit 549fdc
		rp->dtls_sw_next = seq_num + 1;
Packit 549fdc
		return 0;
Packit 549fdc
	} else {
Packit 549fdc
		/* This packet is older than the one we were expecting. By how much...? */
Packit 549fdc
		uint64_t delta = rp->dtls_sw_next - seq_num;
Packit 549fdc
Packit 549fdc
		if (delta > 65) {
Packit 549fdc
			/* Too old. We can't know if it's a replay */
Packit 549fdc
			return gnutls_assert_val(-2);
Packit 549fdc
		} else if (delta == 1) {
Packit 549fdc
			/* Not in the bitmask since it is by definition already received. */
Packit 549fdc
			return gnutls_assert_val(-3);
Packit 549fdc
		} else {
Packit 549fdc
			/* Within the sliding window, so we remember whether we've seen it or not */
Packit 549fdc
			uint64_t mask = 1ULL << (rp->dtls_sw_next - seq_num - 2);
Packit 549fdc
Packit 549fdc
			if (!(rp->dtls_sw_bits & mask))
Packit 549fdc
				return gnutls_assert_val(-3);
Packit 549fdc
Packit 549fdc
			rp->dtls_sw_bits &= ~mask;
Packit 549fdc
			return 0;
Packit 549fdc
		}
Packit 549fdc
	}
Packit 549fdc
}