Skip to content
Lesson 3 of 10

Step 1 of 5 · Reading · ~4 min

Read

VPN Fundamentals

Tunnel Encapsulation and Replay Protection

After this lesson your program takes apart WireGuard transport messages byte by byte, reads the session index and the packet counter, and refuses any counter it has already accepted.

The outer packet

Everything your program sends to a peer is one UDP datagram whose payload is a WireGuard message. The transport data message (whitepaper section 5.4.6) is laid out like this, every integer little-endian:

OffsetSizeField
01type, always 4 for transport data
13reserved, zero
44receiver index
88counter
16nencrypted padded inner packet, then a 16 byte tag

The first four bytes are really one 32 bit integer whose value is 4. A keepalive, the smallest valid message, carries no inner packet and is 32 bytes: the header and the 16 byte tag. One as it appears on the wire:

04 00 00 00 | 2a 00 00 00 | 07 00 00 00 00 00 00 00 | a54dca182530bb1d6d132cded6237b2e
 type 4       index 42      counter 7                  16 byte tag

Little-endian means the least significant byte comes first, so 2a 00 00 00 is 42, not 704643072. Three other types exist: 1 is the handshake initiation (148 bytes), 2 the response (92 bytes) and 3 a cookie reply (64 bytes). This stage handles transport data only; anything else is dropped here.

The receiver index is a number the receiving side picked when the session was created and told the sender during the handshake. It lets the receiver find the right session and key without trying them all. The counter is a per-direction packet number that starts at 0 and goes up by one for each packet. Soon it will also be the encryption nonce, which is why it must never repeat.

Replay protection

UDP may deliver a datagram twice, and an attacker on the path can record a valid message and send it again later. Both must fail. The receiver keeps the highest counter accepted so far and a bitmap of which of the last N counters below it were already seen. For each message:

  1. A counter above the highest is new. Accept it, slide the window up to it, and mark it as seen.
  2. A counter at or below highest - N is too old to judge. Reject it.
  3. Otherwise it is inside the window: reject it if its bit is already set, else set the bit and accept.

Worked trace with a window of N = 5:

CounterHighest beforeDecisionHighest after
1nonenew1
21new2
52new5
45in window (distance 1), unseen: accept5
45in window, already seen: replay5
05distance 5 is not below N: too old5

Real WireGuard uses a window of about 8,000 counters. This exercise limits N to 64 so the whole bitmap fits in one 64 bit integer; the logic is identical. Packets legitimately arrive out of order, which is why a plain "must be larger than the last" rule would drop good traffic.

In a real implementation the window is only updated after the message authenticates. If a forged message could move the window, an attacker could push it far ahead and make every genuine packet look too old. Authentication arrives in the AEAD lesson; here every well-formed message counts as authentic.

Common mistakes

  • Big-endian reads. The index comes out as 704643072 instead of 42, and the counter is garbage.
  • Counters in a 32 bit or floating point number. The counter uses all 64 bits. A value such as 2^53 + 1 is silently rounded in a double.
  • Off by one at the edge. With N = 64 and highest 100, counter 37 is still inside, counter 36 is not.
  • Not clearing the bitmap on a big jump. When the new counter is N or more above the highest, nothing old stays in the window. Shifting a 64 bit integer by 64 is undefined in some languages.
  • Calling a short or foreign message good. Fewer than 32 bytes, or type other than 4, is dropped.

Your exercise

The starter reads the lines and decodes the hex. You validate the header, decode the two fields and keep the window. The most likely mistake is mixing up which end of the window is inclusive.

Up nextNoise Protocol FrameworkCryptography & Handshake

Discussion

Ask a question, share an insight, or help someone who’s stuck.

Sign in to post a comment or reply.

Loading…