Blame lib/dtls-sw.c

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