replay.go raw
1 /* SPDX-License-Identifier: MIT
2 *
3 * Copyright (C) 2017-2025 WireGuard LLC. All Rights Reserved.
4 */
5
6 // Package replay implements an efficient anti-replay algorithm as specified in RFC 6479.
7 package replay
8
9 type block uint64
10
11 const (
12 blockBitLog = 6 // 1<<6 == 64 bits
13 blockBits = 1 << blockBitLog // must be power of 2
14 ringBlocks = 1 << 7 // must be power of 2
15 windowSize = (ringBlocks - 1) * blockBits
16 blockMask = ringBlocks - 1
17 bitMask = blockBits - 1
18 )
19
20 // A Filter rejects replayed messages by checking if message counter value is
21 // within a sliding window of previously received messages.
22 // The zero value for Filter is an empty filter ready to use.
23 // Filters are unsafe for concurrent use.
24 type Filter struct {
25 last uint64
26 ring [ringBlocks]block
27 }
28
29 // Reset resets the filter to empty state.
30 func (f *Filter) Reset() {
31 f.last = 0
32 f.ring[0] = 0
33 }
34
35 // ValidateCounter checks if the counter should be accepted.
36 // Overlimit counters (>= limit) are always rejected.
37 func (f *Filter) ValidateCounter(counter, limit uint64) bool {
38 if counter >= limit {
39 return false
40 }
41 indexBlock := counter >> blockBitLog
42 if counter > f.last { // move window forward
43 current := f.last >> blockBitLog
44 diff := indexBlock - current
45 if diff > ringBlocks {
46 diff = ringBlocks // cap diff to clear the whole ring
47 }
48 for i := current + 1; i <= current+diff; i++ {
49 f.ring[i&blockMask] = 0
50 }
51 f.last = counter
52 } else if f.last-counter > windowSize { // behind current window
53 return false
54 }
55 // check and set bit
56 indexBlock &= blockMask
57 indexBit := counter & bitMask
58 old := f.ring[indexBlock]
59 new := old | 1<<indexBit
60 f.ring[indexBlock] = new
61 return old != new
62 }
63