skip to content

How does an SRTP receiver's sliding replay window decide whether an arriving packet's 48-bit index is accepted or discarded?

level: middleimportance: must knowfreq 30%

answer

  1. highest authenticated index is the edge
  2. ahead, inside-unseen, inside-seen, behind
  3. a bitmap of at least 64
  4. mark only after the tag verifies

basics

~20 s

An SRTP receiver accepts an index ahead of its highest authenticated index, or inside the window and unseen; it discards one already marked or too far behind. The window, at least 64 packets, is marked only after the tag verifies.

solid answer

~50 s

The receiver keeps the highest authenticated 48-bit index and a bitmap covering the `SRTP-WINDOW-SIZE` indices behind it; RFC 3711 makes that size a receiver-side choice that MUST be at least 64. An arriving index falls in one of four zones. **Ahead** of the highest: accept, and slide the window forward. **Inside and unmarked**: a late or reordered packet, accept. **Inside and marked**: a replay or network duplicate, discard. **Behind the window**: treated as already received, discard, even if it is genuine and simply very late. The bitmap and the highest index are updated only after the packet's authentication tag verifies, so a forged packet cannot move the window. Each SSRC has its own list, and SRTCP keeps a separate one. A 64-packet window is tight for video, where a keyframe spans many packets and reordering across a burst can exceed 64, so receivers often choose a larger window.

code

pseudocode · 17 lines
pseudocode
// H = highest authenticated index, W = window size (>= 64)
// seen[d] = 1 when index H - d was accepted, d in 0..W-1
replay_check(i):
    if i > H: return PASS            // ahead of the window
    d = H - i
    if d >= W: return REPLAY         // behind: assumed received
    if seen[d] == 1: return REPLAY   // already accepted
    return PASS

after_tag_verified(i):
    if i > H:
        k = i - H
        move every bit k places back; clear seen[0..k-1]
        H = i
        seen[0] = 1
    else:
        seen[H - i] = 1

go deeper

for a junior

Recall that the SRTP receiver remembers which packet indices it has already accepted and refuses repeats, and that the window must be at least 64 packets.

for a middle

Walk the four zones against the highest authenticated index: ahead, inside unmarked, inside marked, behind. Explain the bitmap and why marking waits until the tag verifies.

for a senior

Size the window for the media: video bursts and multi-path reordering push genuine packets behind a 64-slot window. Read replay-discard counters as duplication or attack, and say how to tell them apart.

for a principal

Weigh window size against loss of late genuine packets and memory per stream across a large media fleet, and set the policy for when replay-discard counts should page someone.

## What the window is protecting Every SRTP packet has a **48-bit packet index**, `i = 2^16 × ROC + SEQ`: the 32-bit **rollover counter** (ROC) the endpoints keep, times 65,536, plus the 16-bit RTP sequence number from the header. An honest sender never sends the same index twice on a stream, so an index the receiver has already accepted can only be a **replay** (an attacker re-injecting a captured packet) or a **network duplicate**. RFC 3711 defines a **Replay List**: conceptually, the set of every index received and authenticated. Storing all of them is pointless, so the RFC allows a **sliding window**: indices that lag the highest one by more than `SRTP-WINDOW-SIZE` "can be assumed to have been received". The window size is a receiver-side, implementation-dependent parameter that **MUST be at least 64** and MAY be larger. RFC 3711 suggests a **bitmap**, the technique the IPsec architecture uses for its own anti-replay window. ## The four zones Call the highest authenticated index `H` and the window size `W`. An arriving index `i` lands in one of these: | Zone | Condition | Decision | |---|---|---| | **Ahead** | `i > H` | Pass the replay check; after the tag verifies, slide the window so `i` becomes the new edge | | **Inside, unmarked** | fewer than `W` behind `H`, bit clear | Pass; a late or reordered packet. Mark it after the tag verifies | | **Inside, marked** | fewer than `W` behind `H`, bit set | Discard as a replay | | **Behind** | outside the window (`W` or more behind `H` in the sketch below) | Discard; assumed already received | RFC 3711 puts it in one line: only packets with an index ahead of the window, or inside the window but not already received, are accepted. ## A worked trace Take `W = 64` and a receiver whose highest authenticated index is `H = 200,000`. Each row starts from that same state: 1. Index **200,010** arrives: ahead by 10. It passes; once its tag verifies, `H` becomes 200,010 and every existing bit moves 10 places further back. 2. Index **199,990** arrives: 10 behind, bit clear. It passes; once its tag verifies, its bit is set. 3. Index **199,990** arrives again: bit set. Discarded as a replay — whether an attacker re-sent it or the network duplicated it. 4. Index **199,900** arrives: 100 behind, outside a 64-packet window. Discarded, even if it is a genuine packet delayed in a queue. Now the attacker from the reserved scenario: a captured burst of a video stream re-sent seconds later. Every index in it is either still in the window and marked, or already behind it. Every packet is discarded, and with RFC 3711's order the tag never needs computing for them. ## Why the update waits for the tag The order matters. The replay check is a cheap lookup; the bitmap and `H` are written only **after** the authentication tag verifies (RFC 3711: after the packet has been authenticated, the replay list is updated, moving the window ahead first if needed). If an unauthenticated packet could move the window, a single forged packet claiming an index far ahead would slide `H` forward, and every genuine packet after it would land "behind the window" and be thrown away. Writing state only on a verified packet means a forger can cost the receiver a lookup and a tag computation, nothing more. ## Choosing the size - **Too small** discards genuine packets: anything reordered by more than `W` positions is treated as already received. Video sends bursts of many packets per frame, and a keyframe can easily exceed 64 packets, so reordering across paths can push a real packet out. - **Larger** costs one bit per extra slot, which is cheap. RFC 4568 (SDES) even defines a **WSH** (Window Size Hint) parameter so a sender can suggest a size, noting that 64 "may be considered too low for some applications (e.g., video)"; the receiver MAY ignore it. - **One list per stream.** Streams that share keys still keep separate replay lists and counters per SSRC, and SRTCP keeps a replay list of its own. ## What the window does not do - It does not stop **delay**: a packet held back and released inside the window is accepted. SRTP gives at-most-once acceptance per index, not freshness. - It does not stop **dropping**; nothing in SRTP can. - It does not protect without **authentication**: with no tag, an attacker rewrites the sequence number and walks a recorded packet past the check.

  • A video receiver logs a steady trickle of SRTP replay discards but no authentication failures. Is it under attack?
    Not necessarily. Network duplicates land on marked bits and are discarded exactly like replays, and genuine packets reordered by more than the window size are discarded as already received. A trickle that tracks bursty video or a multi-path link points at duplication or a too-small window; widening the window is cheap. A sudden block of discards replaying an earlier stretch of indices is the pattern worth investigating.
  • Why does the SRTP replay window not need to remember indices older than the window?
    RFC 3711 lets the receiver assume that any index lagging the highest by more than the window size has already been received, and refuse it. That trades a small chance of dropping a very late genuine packet for constant memory: a bitmap of the window size, instead of a record of every index in the 48-bit space.

A ticket gate with a punch card for the last 64 ticket numbers: a new number is punched, a punched number is refused, and a number older than the card is refused even if that ticket was never used.

saying these in an interview costs you the question

  • The replay window is negotiated by both ends and is always exactly 64 packets.
  • A packet older than the window is accepted if its bit was never set.
  • The receiver marks the index as soon as the replay check passes, before the tag.
  • Discarded duplicates always mean an attacker is replaying traffic.
  • The replay window guarantees packets are fresh, not merely accepted once.