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