munotes®

The Authentication Header

Get access to whole semester resourcesSemester Pass

Chapter Seventy-Three

Syllabus topic Module 2, "IP Security: Authentication Header"

Pages 483 to 489 of 678

In one line

The Authentication Header proves that an IP packet came from the holder of a shared key and was not changed on the way, including its source and destination addresses, but it hides nothing.

In the words an answer should use: the IP Authentication Header (AH), specified in RFC 4302, provides connectionless integrity and data origin authentication for IP datagrams, and optionally protection against replays, by carrying an Integrity Check Value computed with a keyed algorithm over the packet's payload and over those fields of the IP header that do not change in transit; it provides no confidentiality.

Why a header that only authenticates

Encryption is not always wanted or allowed. Network managers may need to read traffic; some settings do not permit encryption; and some traffic is public but must not be forged, such as a routing update. For these, what matters is who sent the packet and whether it was changed, and AH answers exactly that.

AH's distinctive promise is that it covers the IP header too. An attacker who rewrites the source address of an AH-protected packet breaks its integrity check. That is also why AH has fallen out of use, as the end of this chapter explains.

The header

AH sits after the IP header (in transport mode) or after a new outer IP header (in tunnel mode), and the IP header's protocol field is set to 51 to announce it. RFC 4302 s.2 defines six fields:

FieldSizeWhat it is
Next Header8 bitswhat comes after AH: 6 for TCP, 17 for UDP, 4 for an IPv4 packet in tunnel mode
Payload Length8 bitsthe length of AH in 32-bit words, minus 2
Reserved16 bitsset to zero, but still covered by the check
Security Parameters Index32 bitsidentifies the SA, and so the key and algorithm
Sequence Number32 bitsa counter that goes up by one with every packet on the SA, for anti-replay
Integrity Check Value (ICV)variable, a multiple of 32 bitsthe keyed hash over the packet

The Payload Length rule catches students. The three fixed words (next header to reserved, SPI, sequence number) plus the ICV, counted in 32-bit words, minus two. With a 128-bit ICV that is 3 + 4 - 2 = 5, and the header is (5 + 2) × 4 = 28 bytes. RFC 4302's own example is a 96-bit ICV, for which the field is 4.

What the ICV covers, and the mutable fields

The ICV is computed over the whole packet: the IP header, AH itself (with the ICV field set to zero), and everything after AH. But routers change some IP header fields on the way, and a check over those would fail on every packet.

munotes.in483

The Authentication Header

RFC 4302 sorts the IPv4 header's fields into three kinds:

KindIPv4 fieldsTreatment
Immutableversion, header length, total length, identification, protocol, source address, destination addresscovered as they are
Mutable but predictablethe destination address when source routing is usedcovered at the value it will have on arrival
MutableDSCP and ECN (the old type-of-service byte), flags, fragment offset, time to live, header checksumset to zero before the ICV is computed

Zeroed, not left out. RFC 4302 explains the choice: replacing each mutable field with zeros rather than omitting it keeps the alignment the same and means even the length of those fields cannot change unnoticed.

Why each mutable field changes. Routers decrement the time to live at every hop and so must recompute the header checksum; they may rewrite DSCP to give traffic a class of service and set ECN to signal congestion; and fragmentation along the path sets flags and fragment offset.

Anti-replay: the sequence number and the window

The sender starts each SA's counter at zero and puts the next value, beginning with 1, in every packet. It must never let the counter wrap round on an SA that uses anti-replay: before that happens, a new SA is set up.

The receiver keeps a window: a record of the highest sequence number accepted so far, N, and of which numbers in the range from N - W + 1 to N have arrived, where W is the window's width. RFC 4302 requires a window of at least 32 and prefers 64. For each arriving packet:

  1. If its number is left of the window, too old to judge, it is discarded.
  2. If its number is inside the window and has already been seen, it is a duplicate and is discarded.
  3. If it is inside the window and new, or right of the window, the ICV is checked.
  4. Only if the ICV verifies is the packet accepted and the window updated: a new highest number moves the window to the right.

Step 4 is the one that matters most. If the window moved before the ICV was checked, an attacker could send a forged packet with a huge sequence number and push the window past every genuine packet still on its way, which would then all be rejected as too old.

AH in the two modes

Two packet diagrams. In transport mode: the original IP header, then AH, then TCP, then data, with a bracket beneath the whole packet reading authenticated except the mutable fields of the IP header. In tunnel mode: a new IP header, AH, the original IP header, TCP and data, with a bracket beneath the whole packet reading authenticated except the mutable fields of the new IP header. A note says nothing is encrypted.

Figure 73.1 AH authenticates the entire packet it sits in, apart from the header fields routers are allowed to change.

Transport mode. AH follows the original IP header. The ICV covers the upper-layer data and the unchanging parts of the original header, source and destination addresses included.

munotes.in484

The Authentication Header

Tunnel mode. A new IP header is put in front, AH follows it, and the whole original packet comes after. The ICV covers the entire inner packet, its header in full, and the unchanging parts of the new outer header.

The run: AH on a real packet, and the window at work

The listing builds a real IPv4 packet carrying a TCP segment, adds AH in transport mode with HMAC-SHA2-256-128, the algorithm RFC 8221 requires, and prints each field. Then it plays the path: two things routers legitimately do, and four things an attacker or a NAT might do. Finally it runs a 32-packet anti-replay window over fourteen arrivals.

# The Authentication Header over a real IPv4 packet, and the anti-replay window worked through.
import hashlib, hmac

print('HMAC-SHA-256 reproduces RFC 4231 test case 2:',
      hmac.new(b'Jefe', b'what do ya want for nothing?', hashlib.sha256).hexdigest()
      .startswith('5bdcc146bf60754e'))

def checksum(header):                        # RFC 1071
    total = sum(int.from_bytes(header[i:i + 2], 'big') for i in range(0, len(header), 2))
    while total >> 16:
        total = (total & 0xFFFF) + (total >> 16)
    return ~total & 0xFFFF

def ipv4(src, dst, protocol, payload, ttl=64, tos=0):
    h = bytearray(20)
    h[0], h[1], h[8], h[9] = 0x45, tos, ttl, protocol
    h[2:4] = (20 + len(payload)).to_bytes(2, 'big')
    h[4:6] = (0x1C46).to_bytes(2, 'big')                  # identification
    h[6:8] = (0x4000).to_bytes(2, 'big')                  # flags: don't fragment
    h[12:16], h[16:20] = bytes(map(int, src.split('.'))), bytes(map(int, dst.split('.')))
    h[10:12] = checksum(bytes(h)).to_bytes(2, 'big')
    return bytearray(h) + payload

KEY, SPI, ICV_LEN = bytes(range(32)), 0x0000A001, 16    # HMAC-SHA2-256-128, RFC 8221 MUST

def ah_view(packet):                          # RFC 4302 s.3.3.3.1: zero the mutable fields
    v = bytearray(packet)
    v[1] = 0                                  # DSCP and ECN
    v[6:8] = b'\x00\x00'                      # flags and fragment offset
    v[8] = 0                                  # time to live
    v[10:12] = b'\x00\x00'                    # header checksum
    v[20 + 12:20 + 12 + ICV_LEN] = bytes(ICV_LEN)   # the ICV itself
    return bytes(v)

def add_ah(src, dst, next_header, payload, seq):
    ah = bytearray()
    ah += bytes([next_header, (12 + ICV_LEN) // 4 - 2]) + b'\x00\x00'
    ah += SPI.to_bytes(4, 'big') + seq.to_bytes(4, 'big') + bytes(ICV_LEN)
    packet = ipv4(src, dst, 51, bytes(ah) + payload)       # 51: AH
    icv = hmac.new(KEY, ah_view(packet), hashlib.sha256).digest()[:ICV_LEN]
    packet[20 + 12:20 + 12 + ICV_LEN] = icv
    return packet

def ah_ok(packet):
    icv = bytes(packet[32:32 + ICV_LEN])
    return hmac.compare_digest(icv, hmac.new(KEY, ah_view(packet), hashlib.sha256)
                               .digest()[:ICV_LEN])

tcp = b'\x1f\x90\x00\x50' + bytes(16) + b'GET /marks HTTP/1.1\r\n'
p = add_ah('10.1.4.7', '10.2.8.1', 6, tcp, seq=1)
print()
print('THE AH HEADER, AS BUILT (transport mode, after the 20-byte IPv4 header)')
print('  next header  %d (TCP)' % p[20])
print('  payload len  %d, so AH is (%d + 2) x 4 = %d bytes' % (p[21], p[21], (p[21] + 2) * 4))
print('  reserved     %s' % p[22:24].hex())
print('  SPI          0x%08X' % int.from_bytes(p[24:28], 'big'))
print('  sequence     %d' % int.from_bytes(p[28:32], 'big'))
print('  ICV          %s (%d bits)' % (p[32:48].hex(), ICV_LEN * 8))
print('  IP protocol field says %d, AH; total length %d' % (p[9], int.from_bytes(p[2:4], 'big')))
print('  ICV verifies:', ah_ok(p))

def tamper(label, change):
    q = bytearray(p)
    change(q)
    q[10:12] = b'\x00\x00'
    q[10:12] = checksum(bytes(q[:20])).to_bytes(2, 'big')    # a valid IPv4 header again
    print('  %-46s ICV verifies: %s' % (label, ah_ok(q)))

print()
print('WHAT HAPPENS ON THE WAY')
tamper('a router decrements TTL (mutable, zeroed)', lambda q: q.__setitem__(8, q[8] - 1))
tamper('a router re-marks DSCP (mutable, zeroed)', lambda q: q.__setitem__(1, 0xB8))
tamper('someone changes the source address',
       lambda q: q.__setitem__(slice(12, 16), bytes([10, 1, 4, 9])))
tamper('a NAT rewrites the source to 198.51.100.1',
       lambda q: q.__setitem__(slice(12, 16), bytes([198, 51, 100, 1])))
tamper('someone changes one byte of the request', lambda q: q.__setitem__(-5, ord('X')))
tamper('someone changes the SPI', lambda q: q.__setitem__(27, q[27] ^ 1))

# ---- the anti-replay window: RFC 4302 s.3.4.3, window of 32 --------------------------------
class Window:
    def __init__(self, size=32):
        self.size, self.right, self.seen = size, 0, set()

    def check(self, seq, icv_good=True):
        if seq == 0:
            return 'reject: sequence numbers start at 1'
        if seq <= self.right - self.size:
            return 'reject: left of the window (window is %d to %d)' % (
                self.right - self.size + 1, self.right)
        if seq in self.seen:
            return 'reject: duplicate'
        if not icv_good:
            return 'reject: ICV fails, window NOT updated'
        if seq > self.right:
            self.right = seq
        self.seen = {s for s in self.seen | {seq} if s > self.right - self.size}
        return 'accept (window now %d to %d)' % (max(1, self.right - self.size + 1), self.right)

print()
print('THE ANTI-REPLAY WINDOW, 32 PACKETS WIDE, AS PACKETS ARRIVE')
w = Window()
for seq, good in ((1, True), (2, True), (5, True), (3, True), (3, True), (40, True),
                  (7, True), (9, True), (20, True), (20, True), (41, False), (41, True),
                  (38, True), (0, True)):
    print('  sequence %2d %-9s -> %s' % (seq, '' if good else '(forged)', w.check(seq, good)))
munotes.in485

The Authentication Header

HMAC-SHA-256 reproduces RFC 4231 test case 2: True

THE AH HEADER, AS BUILT (transport mode, after the 20-byte IPv4 header)
  next header  6 (TCP)
  payload len  5, so AH is (5 + 2) x 4 = 28 bytes
  reserved     0000
  SPI          0x0000A001
  sequence     1
  ICV          f66cdf5054061ed402ec781e4f96a4a6 (128 bits)
  IP protocol field says 51, AH; total length 89
  ICV verifies: True

WHAT HAPPENS ON THE WAY
  a router decrements TTL (mutable, zeroed)      ICV verifies: True
  a router re-marks DSCP (mutable, zeroed)       ICV verifies: True
  someone changes the source address             ICV verifies: False
  a NAT rewrites the source to 198.51.100.1      ICV verifies: False
  someone changes one byte of the request        ICV verifies: False
  someone changes the SPI                        ICV verifies: False

THE ANTI-REPLAY WINDOW, 32 PACKETS WIDE, AS PACKETS ARRIVE
  sequence  1           -> accept (window now 1 to 1)
  sequence  2           -> accept (window now 1 to 2)
  sequence  5           -> accept (window now 1 to 5)
  sequence  3           -> accept (window now 1 to 5)
  sequence  3           -> reject: duplicate
  sequence 40           -> accept (window now 9 to 40)
  sequence  7           -> reject: left of the window (window is 9 to 40)
  sequence  9           -> accept (window now 9 to 40)
  sequence 20           -> accept (window now 9 to 40)
  sequence 20           -> reject: duplicate
  sequence 41 (forged)  -> reject: ICV fails, window NOT updated
  sequence 41           -> accept (window now 10 to 41)
  sequence 38           -> accept (window now 10 to 41)
  sequence  0           -> reject: sequence numbers start at 1
munotes.in486

The Authentication Header

What the run establishes, in order.

The header is exactly RFC 4302's. Next header 6, TCP; payload length 5, which means (5 + 2) × 4 = 28 bytes; reserved zero; the SPI; sequence number 1; and a 128-bit ICV. The IP header's protocol field is now 51.

What routers may change survives. Decrementing the time to live and re-marking DSCP, each followed by a correct new header checksum, leave the ICV valid, because all three fields were zeroed when it was computed.

Everything else is caught. A changed source address fails. A NAT rewriting the source address fails in exactly the same way, which is the whole story of AH's decline. A single changed byte of the web request fails, and so does a changed SPI.

The window rejects what it should and only what it should. Packets 1, 2, 5 and a late 3 are accepted; the second 3 is a duplicate. Packet 40 moves the window to 9 to 40, so packet 7 is now too old, while 9, at the window's left edge, and 20, inside it, are accepted. A forged 41 fails its ICV and does not move the window; the genuine 41 then does. A late 38 is still accepted, and 0, which no sender ever uses, is refused.

Why AH is fading

AH cannot pass through NAT. RFC 3715, on the incompatibilities between IPsec and network address translation, puts it first: because AH includes the source and destination addresses in its keyed check, "NAT or reverse NAT devices making changes to address fields will invalidate the message integrity check". ESP does not include the outer addresses, so it does not have this problem. Almost every home and campus network uses NAT.

ESP can do AH's job. ESP with its integrity service and the NULL cipher gives integrity and origin authentication without encryption. RFC 8221 keeps NULL encryption as a MUST "to enable the use of ESP with only authentication, which is preferred over AH due to NAT traversal". And ESP can cross a NAT by travelling inside UDP, on port 4500, as RFC 3948 specifies.

munotes.in487

The Authentication Header

So the standards demoted it. RFC 4301 requires ESP and only permits AH, because "there are very few contexts in which ESP cannot provide the requisite security services". What AH still offers that ESP does not is protection of the IP header itself, which matters only where the addresses must be proved and no NAT is in the path.

Algorithms today, from RFC 8221: HMAC-SHA2-256-128 MUST be implemented; HMAC-SHA2-512-256 SHOULD; HMAC-SHA1-96 is MUST-, required now but on its way out; HMAC-MD5-96 MUST NOT be used.

Distinctions that carry marks

AHESP
IP protocol number5150
Confidentialitynoneyes
Integrity and originyesyes, if selected
Covers the IP headeryes, apart from mutable fieldsno, only what follows the ESP header
Works through NATnoyes, inside UDP (RFC 3948)
Status (RFC 4301)MAYMUST
Mutable fieldImmutable field
In the ICVas zerosas it is
IPv4 examplesTTL, header checksum, DSCP, ECN, flags, fragment offsetsource and destination addresses, protocol, total length, identification
If changed in transitICV still verifiesICV fails

What beginners get wrong here

Saying AH encrypts. It encrypts nothing; the whole packet travels readable.

Saying AH covers the entire IP header. It covers the header except the mutable fields, which are zeroed for the computation.

Getting Payload Length wrong. It is the length of AH in 32-bit words minus 2, not the length of the data.

Updating the window before checking the ICV. The window moves only for packets whose ICV verifies; otherwise forged packets could push it past genuine ones.

Treating AH as a current choice. It is optional, it fails through NAT, and ESP with NULL encryption does its job.

Quick revision

  • AH (RFC 4302): IP protocol 51; integrity, data origin authentication, anti-replay; no confidentiality.
  • Fields: Next Header (8), Payload Length (8, in 32-bit words minus 2), Reserved (16), SPI (32), Sequence Number (32), ICV (variable).
  • ICV covers the whole packet; mutable fields zeroed: DSCP and ECN, flags, fragment offset, TTL, header checksum.
  • Anti-replay: sequence starts at 1; window at least 32, 64 preferred; left of window, discard; duplicate, discard; otherwise check the ICV and only then update the window.
  • Transport mode: covers the payload and the original header's immutable fields. Tunnel mode: covers the whole inner packet and the new header's immutable fields.
  • Decline: breaks through NAT (RFC 3715); ESP with NULL encryption does the same job; RFC 4301 makes AH a MAY.
  • Algorithms (RFC 8221): HMAC-SHA2-256-128 MUST; HMAC-SHA1-96 MUST-; HMAC-MD5-96 MUST NOT.

Test yourself

1. What services does AH provide, and which does it not? It provides connectionless integrity and data origin authentication for IP packets, covering the IP header's unchanging fields as well as the payload, and optionally rejection of replayed packets. It provides no confidentiality: nothing is encrypted.

munotes.in488

The Authentication Header

2. Describe the fields of the Authentication Header. Next Header, 8 bits, identifying what follows AH; Payload Length, 8 bits, giving the length of AH in 32-bit words minus 2; Reserved, 16 bits, set to zero; the Security Parameters Index, 32 bits, identifying the SA; the Sequence Number, 32 bits, incremented for every packet and used against replays; and the Integrity Check Value, of variable length in whole 32-bit words, computed over the packet.

3. Why are some IP header fields set to zero before the ICV is computed? Name them. Because routers are allowed to change them in transit, so a check over their original values would fail on every packet. In IPv4 they are the DSCP and ECN bits, the flags, the fragment offset, the time to live and the header checksum. They are replaced with zeros rather than omitted, which keeps the alignment and the length of the header unchanged for the computation.

4. An AH ICV is 96 bits long. What is the value of the Payload Length field? The fixed fields occupy three 32-bit words and the ICV three more, six in all; subtracting 2 gives a Payload Length of 4.

5. Explain the anti-replay window, and why it is updated only after the ICV verifies. The receiver records the highest sequence number accepted, N, and which numbers from N - W + 1 to N have arrived, W being the window size of at least 32. A packet numbered left of the window is discarded as too old, one already seen is discarded as a duplicate, and any other has its ICV checked; if the ICV verifies, the packet is accepted and a new highest number moves the window right. The window moves only after verification because otherwise an attacker could send a forged packet with a very high sequence number, push the window forward, and cause every genuine packet still in transit to be rejected as too old.

6. Why does AH not work through network address translation, and what is used instead? Because AH's integrity check covers the IP source and destination addresses, and a NAT device rewrites them, so every translated packet fails verification. ESP does not cover the outer IP header, so it passes through NAT, and ESP with the NULL cipher provides integrity and origin authentication without encryption, doing AH's job; RFC 4301 therefore requires ESP and makes AH optional.

munotes.in489

The rest of this subject

These notes are cut from the University's printed syllabus. Open the syllabus itself for the same subject.

Issue
Done!