Blame lib/dtls-sw.c

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