// SPDX-FileCopyrightText: 2023 The Pion community <https://pion.ly>
// SPDX-License-Identifier: MIT

// Package replaydetector provides packet replay detection algorithm.
package replaydetector // ReplayDetector is the interface of sequence replay detector. type ReplayDetector interface { // Check returns true if given sequence number is not replayed. // Call accept() to mark the packet is received properly. Check(seq uint64) (accept func(), ok bool) } type slidingWindowDetector struct { latestSeq uint64 maxSeq uint64 windowSize uint mask *fixedBigInt } // New creates ReplayDetector. // Created ReplayDetector doesn't allow wrapping. // It can handle monotonically increasing sequence number up to // full 64bit number. It is suitable for DTLS replay protection. func ( uint, uint64) ReplayDetector { return &slidingWindowDetector{ maxSeq: , windowSize: , mask: newFixedBigInt(), } } func ( *slidingWindowDetector) ( uint64) ( func(), bool) { if > .maxSeq { // Exceeded upper limit. return func() {}, false } if <= .latestSeq { if .latestSeq >= uint64(.windowSize)+ { return func() {}, false } if .mask.Bit(uint(.latestSeq-)) != 0 { // The sequence number is duplicated. return func() {}, false } } return func() { if > .latestSeq { // Update the head of the window. .mask.Lsh(uint( - .latestSeq)) .latestSeq = } := (.latestSeq - ) % .maxSeq .mask.SetBit(uint()) }, true } // WithWrap creates ReplayDetector allowing sequence wrapping. // This is suitable for short bit width counter like SRTP and SRTCP. func ( uint, uint64) ReplayDetector { return &wrappedSlidingWindowDetector{ maxSeq: , windowSize: , mask: newFixedBigInt(), } } type wrappedSlidingWindowDetector struct { latestSeq uint64 maxSeq uint64 windowSize uint mask *fixedBigInt init bool } func ( *wrappedSlidingWindowDetector) ( uint64) ( func(), bool) { if > .maxSeq { // Exceeded upper limit. return func() {}, false } if !.init { if != 0 { .latestSeq = - 1 } else { .latestSeq = .maxSeq } .init = true } := int64(.latestSeq) - int64() // Wrap the number. if > int64(.maxSeq)/2 { -= int64(.maxSeq + 1) } else if <= -int64(.maxSeq)/2 { += int64(.maxSeq + 1) } if >= int64(.windowSize) { // Too old. return func() {}, false } if >= 0 { if .mask.Bit(uint()) != 0 { // The sequence number is duplicated. return func() {}, false } } return func() { if < 0 { // Update the head of the window. .mask.Lsh(uint(-)) .latestSeq = } .mask.SetBit(uint(.latestSeq - )) }, true }