86 lines
1.6 KiB
Go
86 lines
1.6 KiB
Go
package lafsession
|
|
|
|
// replayWindow is a sliding bit-window anti-replay tracker for a single direction.
|
|
type replayWindow struct {
|
|
maxSeq uint64
|
|
size uint64 // bits
|
|
bits []uint64
|
|
}
|
|
|
|
func newReplayWindow(size uint64) *replayWindow {
|
|
if size < 64 {
|
|
size = 64
|
|
}
|
|
words := (size + 63) / 64
|
|
return &replayWindow{
|
|
size: size,
|
|
bits: make([]uint64, words),
|
|
}
|
|
}
|
|
|
|
// accept returns true if seq is new and within window; marks it seen.
|
|
func (w *replayWindow) accept(seq uint64) bool {
|
|
if seq+w.size <= w.maxSeq {
|
|
// too old
|
|
return false
|
|
}
|
|
if seq > w.maxSeq {
|
|
shift := seq - w.maxSeq
|
|
if shift >= w.size {
|
|
// clear entire window
|
|
for i := range w.bits {
|
|
w.bits[i] = 0
|
|
}
|
|
} else {
|
|
w.shift(shift)
|
|
}
|
|
w.maxSeq = seq
|
|
}
|
|
idx := w.maxSeq - seq
|
|
word := idx / 64
|
|
bit := idx % 64
|
|
if word >= uint64(len(w.bits)) {
|
|
return false
|
|
}
|
|
mask := uint64(1) << bit
|
|
if w.bits[word]&mask != 0 {
|
|
return false // already seen
|
|
}
|
|
w.bits[word] |= mask
|
|
return true
|
|
}
|
|
|
|
func (w *replayWindow) shift(n uint64) {
|
|
words := n / 64
|
|
bits := n % 64
|
|
if words > 0 {
|
|
copy(w.bits[words:], w.bits)
|
|
for i := uint64(0); i < words && i < uint64(len(w.bits)); i++ {
|
|
w.bits[i] = 0
|
|
}
|
|
// zero the tail that was shifted out conceptually
|
|
for i := uint64(len(w.bits)); i > words; i-- {
|
|
// already copied
|
|
}
|
|
if words < uint64(len(w.bits)) {
|
|
for i := 0; i < int(words); i++ {
|
|
w.bits[i] = 0
|
|
}
|
|
} else {
|
|
for i := range w.bits {
|
|
w.bits[i] = 0
|
|
}
|
|
return
|
|
}
|
|
}
|
|
if bits == 0 {
|
|
return
|
|
}
|
|
var carry uint64
|
|
for i := len(w.bits) - 1; i >= 0; i-- {
|
|
newCarry := w.bits[i] >> (64 - bits)
|
|
w.bits[i] = (w.bits[i] << bits) | carry
|
|
carry = newCarry
|
|
}
|
|
}
|