github.com/koomox/wireguard-go@v0.0.0-20230722134753-17a50b2f22a3/replay/replay.go (about) 1 /* SPDX-License-Identifier: MIT 2 * 3 * Copyright (C) 2017-2023 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 }