Combining Security Associations
Chapter Seventy-Five
Syllabus topic Module 2, "IP Security: Combining Security Associations"
Pages 496 to 502 of 678
In one line
One security association does one job with one protocol between two points, so when a packet needs two jobs, or protection between two different pairs of points, it gets two SAs, one wrapped around the other.
In the words an answer should use: because a single SA applies either AH or ESP but not both, and runs between one pair of endpoints, a security policy that needs a combination of services, or protection over different segments of a path, is met by a security association bundle, a sequence of SAs through which traffic is processed; SAs are combined by transport adjacency, applying more than one protocol to the same IP datagram without tunnelling, or by iterated tunnelling, applying several layers of protocol through nested IP tunnels.
Why one SA is sometimes not enough
Two limits of a single SA force combinations.
One protocol per SA. RFC 4301: "Security services are afforded to an SA by the use of AH, or ESP, but not both." A policy wanting AH's protection of the IP header and ESP's encryption needs two SAs.
One pair of endpoints per SA. A teacher working from home may need protection to the campus gateway, which checks who may enter the campus network, and to the server itself, so that even staff and machines inside the campus cannot read the data. Those are two different pairs of endpoints, and so two SAs.
The SA bundle, and its two forms
RFC 2401 called a sequence of SAs that traffic must pass through, to satisfy a policy, an SA bundle. The SAs in a bundle may end at different points: one at a gateway, another at a host behind it.
Transport adjacency. More than one security protocol is applied to the same IP datagram, without tunnelling. Both SAs run between the same two hosts. RFC 2401 notes that only one level of this is useful: the processing all happens at the one destination, so nesting deeper adds nothing.
Iterated tunnelling. Security protocols are applied in layers, through IP tunnels, and each tunnel can begin and end at a different place along the path. This allows many levels of nesting. RFC 2401 describes three arrangements:
- Both endpoints the same. An inner and an outer tunnel between the same two hosts, which is legal but of little use.
- One endpoint the same. A host tunnels to a remote gateway, and inside that to a host behind it.
- Neither endpoint the same. Two gateways tunnel between themselves, and two hosts behind them tunnel end to end inside it.
Authentication plus confidentiality: three ways
A policy wanting both services can be met in three ways, and textbooks compare them.
Combining Security Associations
ESP with its integrity option. One SA does both jobs. The ICV covers the ESP packet, but not the IP header in front of it. This is the arrangement in use today, usually with AES-GCM, which does both jobs in one operation.
Transport adjacency: ESP inside, AH outside. An inner ESP SA encrypts the payload, and an outer AH SA authenticates the result. The headers read [IP][AH][ESP][upper]. AH's check covers the ESP packet and the unchanging parts of the IP header, so the addresses are authenticated too, which ESP alone cannot do. RFC 2401 fixes the order: "first ESP, then AH are applied to the packet". The receiver then checks AH first, and so rejects a forged or altered packet before spending any effort decrypting it.
A transport-tunnel bundle: AH inside, ESP outside. An inner AH SA in transport mode authenticates the original packet, and an outer ESP SA in tunnel mode encrypts the whole authenticated packet. Authentication comes before encryption, which has two consequences: the authentication data is itself hidden and protected by the encryption, and the destination can keep the authenticated packet, with its authentication, after the encryption is removed, to check later.
The four basic combinations
RFC 2401 s.4.5 listed four combinations that every compliant host or gateway had to be able to generate and process.
Figure 75.1 The four basic combinations of RFC 2401 section 4.5.
Case 1: end-to-end security between two hosts. Both hosts implement IPsec and share one or more SAs. The possible header stacks are, in transport mode, [IP1][AH][upper], [IP1][ESP][upper] or [IP1][AH][ESP][upper], and in tunnel mode [IP2][AH][IP1][upper] or [IP2][ESP][IP1][upper].
Case 2: a virtual private network between gateways. Only the two security gateways implement IPsec, and only tunnel mode is needed: [IP2][AH][IP1][upper] or [IP2][ESP][IP1][upper]. The hosts behind them need nothing.
Case 3: case 2 plus end-to-end security. The gateways keep their tunnel, and the hosts add their own SA inside it. Nothing new is required of anyone, except that the gateways must let the hosts' own IPsec and key-management traffic through.
Case 4: a remote host reaching a host behind a gateway. The remote host, H1, sets up a tunnel to the gateway, SG2, and an end-to-end SA to the server, H2. RFC 2401: "the sender MUST apply the transport header before the tunnel header". The end-to-end SA is applied first, innermost; the tunnel second, outermost.
The run: case 4, and transport adjacency
The listing builds case 4 with real nesting: an ESP SA from the remote host H1 to the server H2, carried inside an ESP tunnel from H1 to the gateway SG2. Each node has its own key ring, holding only the SAs it is party to. Then it builds transport adjacency in the order RFC 2401 mandates, and attacks it.
Combining Security Associations
# Two SAs on one packet: an end-to-end SA inside a tunnel (RFC 2401 case 4), and AH over ESP.
import hashlib, hmac, random
rng = random.Random(750)
def ip(src, dst, proto, payload):
return {'src': src, 'dst': dst, 'proto': proto, 'payload': payload}
def size(p):
inner = p['payload']
return 20 + (size(inner) if isinstance(inner, dict) else len(inner))
def stream(key, iv, n):
return b''.join(hashlib.sha256(key + iv + i.to_bytes(4, 'big')).digest()
for i in range(n // 32 + 1))[:n]
def serial(x): # bytes of whatever an ESP layer carries
if isinstance(x, dict):
return repr(sorted(x.items())).encode()
return x
class ESP: # one SA: ESP with an HMAC-SHA2-256-128 ICV
def __init__(self, spi, key):
self.spi, self.key, self.opened = spi, key, 0
def seal(self, inner, next_header):
iv = rng.randbytes(16)
data = serial(inner)
ct = bytes(a ^ b for a, b in zip(data, stream(self.key, iv, len(data))))
body = self.spi.to_bytes(4, 'big') + iv + ct
return {'esp': self.spi, 'nh': next_header, 'inner': inner, 'bytes': body,
'icv': hmac.new(self.key, body, hashlib.sha256).digest()[:16]}
def unseal(sa, e):
if not hmac.compare_digest(e['icv'], hmac.new(sa.key, e['bytes'], hashlib.sha256)
.digest()[:16]):
return None
sa.opened += 1
return e['inner']
def describe(p):
parts, x = [], p
while True:
parts.append('[IP %s->%s]' % (x['src'], x['dst']))
e = x['payload']
parts.append('[ESP 0x%X]' % e['esp'])
if isinstance(e['inner'], dict) and 'src' in e['inner']:
x = e['inner']
continue
parts.append('[TCP + data, sealed]')
return ' '.join(parts)
def length(p): # IP 20 + ESP (8 + IV 16 + data + pad + 2 + ICV 16)
inner = p['payload']['inner']
n = length(inner) if isinstance(inner, dict) else len(inner)
return 20 + 8 + 16 + n + (-(n + 2)) % 16 + 2 + 16
# ---- RFC 2401 case 4: remote host H1, gateway SG2, server H2 behind it --------------------
H1, SG2, H2 = '203.0.113.50', '198.51.100.1', '10.2.8.1'
tunnel = ESP(0x7001, rng.randbytes(32)) # shared by H1 and SG2 only
e2e = ESP(0x8001, rng.randbytes(32)) # shared by H1 and H2 only
data = b'\x1f\x90\x00\x50' + bytes(16) + b'POST /internal-marks'
inner = ip(H1, H2, 50, e2e.seal(data, 6)) # 1. the end-to-end SA, transport mode
outer = ip(H1, SG2, 50, tunnel.seal(inner, 4)) # 2. then the tunnel SA around it
print('CASE 4: A REMOTE HOST, THROUGH A GATEWAY, TO A SERVER (RFC 2401 s.4.5)')
print(' the sender applies the end-to-end SA FIRST, then the tunnel SA')
print(' on the Internet %3d bytes %s' % (length(outer), describe(outer)))
KEYS = {'SG2': {0x7001: tunnel}, 'H2': {0x8001: e2e}} # each node's own SAs
def open_layer(node, packet):
sa = KEYS[node].get(packet['payload']['esp'])
return unseal(sa, packet['payload']) if sa else None
at_gateway = open_layer('SG2', outer)
print(' SG2 removes the tunnel SA 0x7001 :', at_gateway is not None)
print(' SG2 has an SA for the inner 0x8001 :', 0x8001 in KEYS['SG2'])
print(' so SG2 can read the TCP data :', open_layer('SG2', at_gateway) is not None)
print(' on the intranet %3d bytes %s' % (length(at_gateway), describe(at_gateway)))
print(' H2 removes the end-to-end SA 0x8001:', open_layer('H2', at_gateway) == data)
print(' so the data was sealed on the Internet AND on the intranet, and the gateway')
print(' checked who H1 was without ever seeing what H1 sent')
# ---- transport adjacency: ESP first, then AH over it (RFC 2401 case 1, header stack 3) ----
print()
print('TRANSPORT ADJACENCY: [IP][AH][ESP][TCP + data] (RFC 2401: first ESP, then AH)')
AH_KEY = rng.randbytes(32)
esp_only = ESP(0x9001, rng.randbytes(32))
sealed = esp_only.seal(data, 6)
header = (H1 + '>' + H2).encode() # the immutable parts of the IP header
ah_icv = hmac.new(AH_KEY, header + sealed['bytes'] + sealed['icv'],
hashlib.sha256).digest()[:16]
def receive(header, sealed, ah_icv):
expect = hmac.new(AH_KEY, header + sealed['bytes'] + sealed['icv'], hashlib.sha256)
good = hmac.compare_digest(ah_icv, expect.digest()[:16])
if not good:
return 'AH fails: dropped before any decryption'
return 'AH verifies, then ESP decrypts: %r' % unseal(esp_only, sealed)[-20:]
print(' genuine packet :', receive(header, sealed, ah_icv))
forged = dict(sealed, bytes=sealed['bytes'][:-1] + b'X')
print(' forged packet :', receive(header, forged, ah_icv))
print(' spoofed source :', receive(('203.0.113.99>' + H2).encode(), sealed, ah_icv))
print(' decryptions performed by this receiver: %d' % esp_only.opened)Combining Security Associations
CASE 4: A REMOTE HOST, THROUGH A GATEWAY, TO A SERVER (RFC 2401 s.4.5)
the sender applies the end-to-end SA FIRST, then the tunnel SA
on the Internet 172 bytes [IP 203.0.113.50->198.51.100.1] [ESP 0x7001] [IP 203.0.113.50->10.2.8.1] [ESP 0x8001] [TCP + data, sealed]
SG2 removes the tunnel SA 0x7001 : True
SG2 has an SA for the inner 0x8001 : False
so SG2 can read the TCP data : False
on the intranet 108 bytes [IP 203.0.113.50->10.2.8.1] [ESP 0x8001] [TCP + data, sealed]
H2 removes the end-to-end SA 0x8001: True
so the data was sealed on the Internet AND on the intranet, and the gateway
checked who H1 was without ever seeing what H1 sent
TRANSPORT ADJACENCY: [IP][AH][ESP][TCP + data] (RFC 2401: first ESP, then AH)
genuine packet : AH verifies, then ESP decrypts: b'POST /internal-marks'
forged packet : AH fails: dropped before any decryption
spoofed source : AH fails: dropped before any decryption
decryptions performed by this receiver: 1What the run establishes, in order.
The sender applies the SAs inside out. First the end-to-end SA 0x8001, then the tunnel SA 0x7001. On the Internet the packet is 172 bytes, and an observer sees only that H1 is talking to the gateway.
The gateway removes exactly one layer. SG2 holds the tunnel SA, so it verifies and removes the tunnel; it holds no SA for 0x8001, so it cannot open what is inside. The packet it forwards, 108 bytes, is still sealed on the campus intranet.
Combining Security Associations
The server removes the other. H2 opens SA 0x8001 and recovers the data. The gateway checked that H1 belonged, without ever seeing what H1 sent, which is precisely what case 4 is for.
Transport adjacency lets the receiver refuse forgeries cheaply. With AH outside ESP, a genuine packet verifies and decrypts. A packet with one byte of the ESP data forged, and a packet whose source address is spoofed, both fail AH and are dropped before any decryption: the receiver performed exactly one decryption, for the one genuine packet.
What the current architecture says
RFC 4301 no longer requires any of this. Its s.4.3: "This document does not require support for nested security associations or for what RFC 2401 called 'SA bundles'." Such arrangements "still can be effected by appropriate configuration of both the SPD and the local forwarding functions", but management of them is now "potentially more complex and less assured". RFC 4301's Appendix E gives an example of configuring nested SAs.
Why the change is reasonable. ESP with integrity, and especially ESP with a combined algorithm such as AES-GCM, gives confidentiality and integrity in one SA, and AH, the usual reason for bundling, is now optional. The one combination still common in practice is the one the run shows: an end-to-end SA inside a VPN tunnel.
Distinctions that carry marks
| Transport adjacency | Iterated tunnelling | |
|---|---|---|
| How | several protocols on the same datagram, no new IP header | protocols applied through nested tunnels, each with a new IP header |
| Endpoints | the same two hosts for every SA | each tunnel may begin and end at a different point |
| Levels of nesting | one is useful | many |
| Example | [IP][AH][ESP][upper] | [IP2][ESP][IP1][ESP][upper], a tunnel with an end-to-end SA inside |
| ESP with integrity | AH over ESP (transport adjacency) | AH inside an ESP tunnel | |
|---|---|---|---|
| SAs | one | two | two |
| IP header authenticated | no | yes, the immutable fields | the inner header, yes |
| Authentication data hidden | no | no | yes, it is encrypted |
| Forgeries rejected before decryption | yes: the ICV is checked before, or alongside, decryption (RFC 4303) | yes, AH is checked first | no: the outer ESP is decrypted first |
What beginners get wrong here
Saying one SA can apply both AH and ESP. It cannot; that is the reason bundles exist.
Applying the tunnel before the end-to-end SA in case 4. The inner, end-to-end SA is applied first and the tunnel around it second; applied the other way, the gateway would have to decrypt data meant only for the server.
Putting AH inside ESP when the rule says ESP inside AH. In transport adjacency RFC 2401 requires ESP first, then AH, giving [IP][AH][ESP][upper].
Presenting SA bundles as current requirements. They are RFC 2401's; RFC 4301 no longer requires implementations to support them.
Combining Security Associations
Quick revision
- One SA = one protocol (AH or ESP) between one pair of endpoints; combinations need several SAs.
- SA bundle (RFC 2401): a sequence of SAs traffic must pass through; SAs may end at different points.
- Transport adjacency: several protocols on the same datagram, no tunnelling, one useful level; ESP first, then AH:
[IP][AH][ESP][upper]. - Iterated tunnelling: layers through nested tunnels; endpoints the same, one the same, or neither the same.
- Authentication plus confidentiality: ESP with integrity; AH over ESP (authenticates addresses, rejects forgeries before decrypting); AH inside an ESP tunnel (authentication hidden, kept after decryption).
- Four basic combinations (RFC 2401 s.4.5): host to host; gateway to gateway (VPN); both together; remote host to gateway plus end to end. In case 4 the end-to-end SA is applied first.
- RFC 4301 s.4.3 no longer requires nested SAs or SA bundles; they can still be configured.
Test yourself
1. Why is it sometimes necessary to combine security associations? Because a single SA applies only one protocol, AH or ESP, and runs between one pair of endpoints. A policy needing both protocols' services, or protection over two different segments of a path, such as to a gateway and also end to end to a server behind it, needs more than one SA applied to the same traffic.
2. Distinguish transport adjacency from iterated tunnelling. Transport adjacency applies more than one security protocol to the same IP datagram without tunnelling, with every SA between the same two hosts, and only one level of it is useful. Iterated tunnelling applies security protocols in layers through nested IP tunnels, each tunnel able to start and end at a different point along the path, and allows many levels of nesting.
3. In transport adjacency, in which order are ESP and AH applied, and why is that order sensible? ESP is applied first and AH second, giving the header order IP, AH, ESP, upper layer. AH then authenticates the ESP packet together with the immutable fields of the IP header, so the addresses are authenticated as well, and the receiver checks AH first, rejecting a forged or altered packet before spending any effort decrypting it.
4. Describe the four basic combinations of security associations. Case 1: end-to-end security between two IPsec hosts, by one or more SAs in transport or tunnel mode. Case 2: a virtual private network in which two security gateways share a tunnel-mode SA and the hosts behind them need no IPsec. Case 3: case 2 together with an end-to-end SA between the two hosts inside the gateways' tunnel. Case 4: a remote host that sets up a tunnel to an organisation's gateway and, inside it, an end-to-end SA to a host behind the gateway.
Combining Security Associations
5. In case 4, what can the gateway see of the traffic, and why? It can verify and remove the tunnel SA, because it shares that SA's keys with the remote host, and so it can check that the remote host is entitled to enter the network. It cannot read the data, because the data is also protected by the end-to-end SA between the remote host and the server, whose keys the gateway does not have.
6. What does RFC 4301 say about SA bundles? That it does not require support for nested security associations or for what RFC 2401 called SA bundles; such arrangements can still be achieved by configuring the Security Policy Database and the forwarding functions appropriately, but their management is potentially more complex and less assured than under RFC 2401.
The rest of this subject
These notes are cut from the University's printed syllabus. Open the syllabus itself for the same subject.