p2p-swift / Sources / IrdP2PCrypto / ReplayWindow.swift
314 words
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import Foundation

/// Best-effort in-session replay suppression for one remote sender and key.
/// IVs are epoch ‖ counter. Within an epoch a sliding window of the last 64
/// counters is tracked (as in DTLS/IPsec anti-replay): a counter is accepted if
/// it is new and not more than 64 behind the highest seen — so frames that were
/// sealed concurrently and delivered out of order still pass, while duplicates
/// and stale replays do not. A retired epoch never comes back.
public struct ReplayWindow: Sendable {
    public static let retiredEpochsKept = 8
    public static let windowSize: UInt64 = 64
    private var epoch: Data?
    private var highest: UInt64 = 0
    /// Bit i set ⇔ counter `highest - i` has been seen (bit 0 is `highest` itself).
    private var seen: UInt64 = 0
    private var retired: [Data] = []

    public init() {}

    /// Returns `true` if the IV is fresh (and records it), `false` for a replay or a stale counter.
    public mutating func accept(iv: Data) -> Bool {
        guard iv.count == 12 else { return false }
        let e = Data(iv.prefix(4))
        let c = iv.suffix(8).reduce(UInt64(0)) { ($0 << 8) | UInt64($1) }
        if let current = epoch {
            if e != current {
                if retired.contains(e) { return false }
                retired.append(current)
                if retired.count > ReplayWindow.retiredEpochsKept { retired.removeFirst() }
                epoch = e
                highest = c
                seen = 1
                return true
            }
            if c > highest {
                let shift = c - highest
                seen = shift >= 64 ? 1 : (seen << shift) | 1
                highest = c
                return true
            }
            let behind = highest - c
            guard behind < ReplayWindow.windowSize else { return false }
            let bit: UInt64 = 1 << behind
            guard seen & bit == 0 else { return false }
            seen |= bit
            return true
        }
        epoch = e
        highest = c
        seen = 1
        return true
    }
}

static mirror of HEAD · about · clone: git clone https://ird.cod-sursa.ro/p2p-swift.git