ESP - Encapsulating Security Payload

ESP - Encapsulating Security Payload

pcapkit.protocols.internet.esp contains ESP only, which implements extractor for Encapsulating Security Payload (ESP) [*], whose structure is described as below:

Octets

Bits

Name

Description

0

0

esp.spi

Security Parameters Index (SPI)

4

32

esp.seq

Sequence Number

8

64

esp.payload_data

Payload Data (variable, encrypted)

?

?

Padding (0-255 bytes, encrypted)

?

?

esp.pad_len

Pad Length (encrypted)

?

?

esp.next

Next Header (encrypted)

?

?

esp.icv

Integrity Check Value (ICV, variable)

Unlike every other protocol in pcapkit, ESP is not self describing. RFC 4303 places the Pad Length and Next Header fields inside the ciphertext, and leaves the length of the Integrity Check Value to be determined by the Security Association (SA), which is negotiated out of band. Therefore:

  • Without SA context, ESP parses the SPI and Sequence Number, reports the remainder as an opaque encrypted payload, and says so through esp.status. It does not guess at the trailer, and it does not raise.

  • With SA context, ESP splits off the ICV, verifies integrity, decrypts, strips the padding using Pad Length, and dispatches the recovered plaintext to the next layer using Next Header – so an ESP tunnelled TCP segment decodes as TCP.

SA context is supplied through the generic, protocol keyed context channel in pcapkit.corekit.context:

import pcapkit
from pcapkit.protocols.internet.esp import (Cipher, ESPContext, Integrity,
                                            SecurityAssociation)

sa = SecurityAssociation(
    spi=0x4321,
    encryption=Cipher.AES_CBC,
    encryption_key=bytes.fromhex('90d382b410eeba7ad938c46cec1a82bf'),
    integrity=Integrity.HMAC_SHA2_256_128,
    integrity_key=bytes.fromhex('00' * 32),
    destination='192.168.123.100',   # optional, disambiguates several tunnels
)
extraction = pcapkit.extract('esp.pcap', context=ESPContext(sa))

Registered algorithms, and supported ones

ESP has no algorithm registry of its own – an SA’s algorithms are negotiated by IKEv2 – so Cipher and Integrity are generated from the IKEv2 transform ID sub-registries and enumerate everything IANA has registered: 3DES, AES-CTR, the AES-CCM and Camellia families, ChaCha20-Poly1305, the implicit IV variants of RFC 8750, the RFC 9227 MGM suites, and the transforms long since deprecated.

Registration is not support. A member of either enumeration says only that IANA assigned the transform an ID; what pcapkit can actually apply is the separate, explicit CIPHER_SUITES and INTEGRITY_SUITES tables, and CipherSuite.get() / IntegritySuite.get() refuse anything outside them rather than half-working:

>>> Cipher.get('ENCR_3DES')          # registered, so the enum has it
<Cipher.ENCR_3DES: 3>
>>> CipherSuite.get('ENCR_3DES')     # but ESP cannot apply it
Traceback (most recent call last):
  ...
pcapkit.utilities.exceptions.ProtocolError: unsupported ESP encryption
algorithm: ENCR_3DES; pcapkit implements ENCR_NULL, ENCR_AES_CBC,
ENCR_AES_GCM_8, ENCR_AES_GCM_12, ENCR_AES_GCM_16

Decryption requires the optional cryptography dependency (pip install pypcapkit[crypto]). pcapkit imports and works without it; an SA that names an AES suite simply degrades to the opaque payload path, with a warning.

The supported set is anchored on the mandatory to implement algorithms of RFC 8221. The rows marked yes are exactly the keys of CIPHER_SUITES; everything else the registry lists is enumerated and rejected.

Encryption

RFC 8221 status

Implemented

Notes

ENCR_NULL

MUST

yes

RFC 2410; needs no cryptography

ENCR_AES_CBC

MUST

yes

RFC 3602; 128/192/256-bit keys

ENCR_AES_GCM_16

MUST

yes

RFC 4106; 8-octet explicit IV

ENCR_AES_GCM_8

yes

RFC 4106, 8-octet ICV

ENCR_AES_GCM_12

yes

RFC 4106, 12-octet ICV

ENCR_AES_CCM_8

SHOULD

no

registered, not implemented

ENCR_CHACHA20_POLY1305

SHOULD

no

registered, not implemented

ENCR_3DES

SHOULD NOT

no

registered, deliberately omitted

DES, Blowfish, 3IDEA

MUST NOT

no

registered, deliberately omitted

“DES, Blowfish, 3IDEA” above covers ENCR_DES, ENCR_DES_IV64, ENCR_DES_IV32, ENCR_BLOWFISH and ENCR_3IDEA.

Likewise, the rows marked yes below are exactly the keys of INTEGRITY_SUITES:

Integrity

RFC 8221 status

Implemented

Notes

NONE

MUST (AEAD only)

yes

for AEAD suites

AUTH_HMAC_SHA2_256_128

MUST

yes

RFC 4868

AUTH_HMAC_SHA2_512_256

SHOULD

yes

RFC 4868

AUTH_HMAC_SHA2_384_192

yes

RFC 4868

AUTH_HMAC_SHA1_96

MUST-

yes

RFC 2404; still widely captured

AUTH_AES_XCBC_96

SHOULD / MAY

no

registered, not implemented

AUTH_AES_*_GMAC

MAY

no

registered, not implemented

MD5, DES-MAC, KPDK-MD5

MUST NOT

no

registered, deliberately omitted

“MD5, DES-MAC, KPDK-MD5” above covers AUTH_HMAC_MD5_96, AUTH_HMAC_MD5_128, AUTH_DES_MAC and AUTH_KPDK_MD5. The registry spells the “no integrity algorithm” transform NONE rather than AUTH_NONE, and Integrity.NONE follows it.

Both enumerations additionally carry each transform’s prefix-stripped spelling as an alias, since that is how ESP and RFC 8221 name the algorithms, so Cipher.AES_CBC and Cipher.ENCR_AES_CBC are the same member.

Known limitations

  • Extended Sequence Numbers (ESN, RFC 4303 §2.2.1) are not supported. The high-order 32 bits of an ESN are not transmitted, and a stateless parser cannot recover them; they are required both for the ICV computation and for the AEAD associated data. An ESN protected packet therefore fails the integrity check cleanly rather than being decoded.

  • Traffic Flow Confidentiality (TFC) padding (§2.4) is not detected. TFC padding is indistinguishable from real payload without inspecting the inner protocol’s own length field, so it is handed to the next layer as part of the plaintext.

  • Anti-replay is not performed. pcapkit is an analyser, not a receiver; the sequence number is reported, never checked.

  • The ICV is verified but a failure is reported rather than raised, so that one bad packet does not abort a capture.

class pcapkit.protocols.internet.esp.ESP(file=None, length=None, **kwargs)[source]

Bases: IPsec[ESP, ESP]

This class implements Encapsulating Security Payload.

property name: Literal['Encapsulating Security Payload']

Name of corresponding protocol.

property length: int

Length of the ESP header, payload, trailer and ICV.

Note

Unlike most protocols, this is not just the fixed header: RFC 4303 puts the trailer and the ICV at the end of the packet, and the next layer is recovered from inside the ciphertext rather than from the bytes that follow. Every byte ESP owns is therefore counted here.

classmethod id()[source]

Index ID of the protocol.

Return type:

tuple[Literal['ESP']]

Returns:

Index ID of the protocol.

read(length=None, *, version=4, extension=False, **kwargs)[source]

Read Encapsulating Security Payload.

Structure of ESP header [RFC 4303]:

 0                   1                   2                   3
 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ----
|               Security Parameters Index (SPI)                 | ^Int.
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |Cov-
|                      Sequence Number                          | |ered
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | ----
|                    Payload Data* (variable)                   | |   ^
~                                                               ~ |   |
|                                                               | |Conf.
+               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |Cov-
|               |     Padding (0-255 bytes)                     | |ered*
+-+-+-+-+-+-+-+-+               +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |   |
|                               |  Pad Length   | Next Header   | v   v
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ ------
|         Integrity Check Value-ICV   (variable)                |
~                                                               ~
|                                                               |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
Parameters:
  • length (int | None) – Length of packet data.

  • version (Literal[4, 6]) – IP protocol version.

  • extension (bool) – If the protocol is used as an IPv6 extension header. Unlike the other extension headers, ESP terminates the header chain – everything after it is encrypted – so it decodes its own next layer either way.

  • **kwargs (Any) – Arbitrary keyword arguments.

Return type:

ESP

Returns:

Parsed packet data.

Notes

The outer destination address, used to disambiguate Security Associations that share an SPI, is taken from the packet information the enclosing IP layer passes down. It is not available for every encapsulation, and an SA that names a destination still matches when it cannot be confirmed – see SecurityAssociation.matches().

make(spi=0, seq=0, next=<TransType.UDP: 17>, next_default=None, next_namespace=None, next_reversed=False, encrypt=False, iv=None, pad_len=None, icv=b'', payload=b'', **kwargs)[source]

Make (construct) packet data.

There are two modes, chosen by encrypt:

  • encrypt=False (the default) writes payload after the SPI and sequence number verbatim, followed by icv. This is the mode used to reproduce a captured packet byte for byte, and is what _make_data() drives – re-encrypting a packet that was only ever read would change its bytes.

  • encrypt=True treats payload as the inner plaintext: it appends RFC 4303 §2.4 padding, the pad length and next, encrypts the result under the Security Association matching spi, and appends the resulting ICV.

Parameters:
  • spi (int) – Security Parameters Index.

  • seq (int) – Sequence number.

  • next (TransType | IntEnum | IntEnum | str | int) – Next header type, written into the ESP trailer. Only used when encrypt is True.

  • next_default (int | None) – Default value of next header type.

  • next_namespace (dict[str, int] | dict[int, str] | Type[IntEnum] | Type[IntEnum] | None) – Namespace of next header type.

  • next_reversed (bool) – If the namespace is reversed.

  • encrypt (bool) – Whether to protect payload, as described above.

  • iv (bytes | None) – Explicit IV to use; a random one is generated when omitted. Only used when encrypt is True.

  • pad_len (int | None) – Pad length to use; the smallest value that satisfies the alignment requirement is chosen when omitted. Only used when encrypt is True.

  • icv (bytes) – Integrity check value. Ignored when encrypt is True, where it is computed instead.

  • payload (bytes | ProtocolBase | Schema) – Payload of current instance.

  • **kwargs (Any) – Arbitrary keyword arguments.

Return type:

ESP

Returns:

Constructed packet data.

Raises:

ProtocolError – If encrypt is True and no Security Association is available for spi, or if pad_len does not satisfy the algorithm’s alignment requirement.

classmethod _make_data(data)[source]

Create key-value pairs from data for protocol construction.

The payload is reproduced verbatim rather than re-encrypted, so that reconstruction is byte exact and does not need the keys.

Parameters:

data (ESP) – protocol data

Return type:

dict[str, Any]

Returns:

Key-value pairs for protocol construction.

static _payload_bytes(payload)[source]

Render payload as bytes.

Parameters:

payload (bytes | ProtocolBase | Schema) – Payload as supplied to make().

Return type:

bytes

Returns:

Packed payload.

Raises:

ProtocolUnbound – If payload is of an unsupported type. This mirrors Schema.pack, which rejects the same set.

static _read_trailer(plaintext, association, spi)[source]

Split the ESP trailer off the decrypted plaintext.

Parameters:
  • plaintext (bytes) – Decrypted payload data, i.e. the inner payload followed by the ESP trailer.

  • association (SecurityAssociation) – Security Association the packet was decrypted with.

  • spi (int) – Security Parameters Index, for the diagnostic message.

Return type:

tuple[bytes, bytes, int, int] | str

Returns:

A 4-tuple of the inner payload, the padding, the pad length and the next header, or a str explaining why the trailer is not self consistent.

_make_opaque(spi, seq, total, payload_data, status, error, *, icv=b'', version=4, packet=None, warning=True)[source]

Report an ESP packet whose payload was not decrypted.

The payload is surfaced as Raw – which is what a next header of None resolves to – so that self.payload and the protocol chain behave as they do for any other protocol, and the trailer fields are left None rather than guessed at.

Parameters:
  • spi (int) – Security Parameters Index.

  • seq (int) – Sequence number.

  • total (int) – Total length of the ESP portion of the packet.

  • payload_data (bytes) – Payload data, excluding the ICV.

  • status (ESPStatus) – Why the payload was not decrypted.

  • error (str) – Human readable form of status.

  • icv (bytes) – Integrity check value, when its length is known.

  • version (Literal[4, 6]) – IP protocol version.

  • packet (dict[str, Any] | None) – Packet information from the enclosing layer.

  • warning (bool) – Whether to warn; a capture taken without keys is the expected case and does not warrant one.

Return type:

ESP

Returns:

Parsed packet data.

__post_init__(file=None, length=None, *, version=4, extension=False, **kwargs)[source]

Post initialisation hook.

Overloads:
  • self, file (IO[bytes] | bytes), length (Optional[int]), version (Literal[4, 6]), extension (bool), kwargs (Any) → None

  • self, kwargs (Any) → None

Parameters:
  • file (IO[bytes] | bytes | None) – Source packet stream.

  • length (int | None) – Length of packet data.

  • version (Literal[4, 6]) – IP protocol version.

  • extension (bool) – If the protocol is used as an IPv6 extension header.

  • **kwargs (Any) – Arbitrary keyword arguments.

See also

For construction argument, please refer to self.make.

classmethod __index__()[source]

Numeral registry index of the protocol.

Return type:

TransType

Returns:

Numeral registry index of the protocol in IANA.

Security Associations

SA context is supplied through the generic, protocol keyed channel of pcapkit.corekit.context.

class pcapkit.protocols.internet.esp.SecurityAssociation(spi=None, *, encryption=<Cipher.ENCR_NULL: 11>, encryption_key=b'', salt=None, integrity=<Integrity.NONE: 0>, integrity_key=b'', icv_length=None, destination=None, strict=True)[source]

Bases: object

An inbound IPsec Security Association, as far as ESP parsing needs one.

Parameters:
  • spi (int | None) – Security Parameters Index the SA applies to; None matches any SPI, which is convenient for a capture holding a single tunnel.

  • encryption (Cipher | str | int) – Encryption algorithm, c.f. CipherSuite.get(). Must be one pcapkit implements; being in the IANA registry is not enough.

  • encryption_key (bytes) – Encryption keying material. For an AEAD suite this is the AES key followed by the 4-octet salt [RFC 4106 §8.1], unless salt is given separately.

  • salt (bytes | None) – AEAD salt, when not appended to encryption_key.

  • integrity (Integrity | str | int) – Integrity algorithm, c.f. IntegritySuite.get(). Must be Integrity.NONE for an AEAD suite, which provides its own.

  • integrity_key (bytes) – Integrity key.

  • icv_length (int | None) – Override for the ICV length, in octets. Needed for the long standing implementation bug noted in RFC 8221 §6, where AUTH_HMAC_SHA2_256_128 is truncated to 96 rather than 128 bits.

  • destination (IPv4Address | IPv6Address | str | int | bytes | None) – Outer destination address the SA applies to. IPsec keys an SA by (SPI, destination, protocol), and supplying the address is what lets several tunnels sharing an SPI be told apart. Matched only when the outer destination is known to pcapkit; see ESP.read().

  • strict (bool) – Whether a padding pattern that does not follow the monotonically increasing sequence of RFC 4303 §2.4 should be treated as a decryption failure. Only applied when nothing else authenticated the packet, since a verified ICV or AEAD tag is a far better signal than the padding is. Some implementations pad with zeros; set to False for those.

Raises:

ProtocolError – If the algorithms or key lengths are inconsistent.

Important

Key material is held in private attributes of this object, and is exposed only through encryption_key / integrity_key. It is deliberately absent from __repr__(), and it is never copied into the ESP data model, which is the only thing that reaches Info.to_dict and hence the output dumpers.

property encryption_key: bytes

Encryption key, excluding any AEAD salt.

property salt: bytes

AEAD salt.

property integrity_key: bytes

Integrity key.

property icv_length: int

Length of the ICV field carried on the wire, in octets.

property authenticated: bool

Whether the SA provides any integrity protection at all.

matches(spi, destination=None)[source]

Score how well the SA matches a packet.

Parameters:
Return type:

int

Returns:

A non-negative score, where a higher score is a better match, or -1 when the SA does not apply at all. An SA pinned to this exact SPI outranks a wildcard one, and an SA whose destination was confirmed outranks one whose destination is unconstrained.

unavailable()[source]

Say whether the SA’s algorithms can be applied at all.

This is checked before a packet is processed, so that a missing optional dependency is reported as a configuration problem once per packet rather than raised as an error from decrypt() – it is not a defect in the packet.

Return type:

str | None

Returns:

A reason the SA cannot be applied, or None when it can.

compute_icv(spi, seq, body)[source]

Compute the ICV over the integrity protected part of the packet.

The integrity computation of RFC 4303 §2.8 covers the SPI, the Sequence Number, the payload data (including any explicit IV) and the explicit ESP trailer – that is, everything transmitted except the ICV itself.

Parameters:
  • spi (int) – Security Parameters Index.

  • seq (int) – Sequence number.

  • body (bytes) – Payload data and ESP trailer, as transmitted.

Return type:

bytes

Returns:

The truncated ICV.

Raises:

ProtocolError – If the SA has no separate integrity algorithm.

decrypt(spi, seq, body, icv)[source]

Decrypt the payload data of an ESP packet.

Parameters:
  • spi (int) – Security Parameters Index.

  • seq (int) – Sequence number.

  • body (bytes) – Payload data as transmitted, i.e. the explicit IV (if the algorithm uses one) followed by the ciphertext.

  • icv (bytes) – ICV as transmitted; for an AEAD algorithm this is the authentication tag and is an input to the decryption.

Return type:

bytes

Returns:

The plaintext, i.e. the inner payload followed by the ESP trailer (padding, pad length, next header).

Raises:
  • ProtocolError – If the payload is malformed for the algorithm, or if cryptography is needed and unavailable.

  • cryptography.exceptions.InvalidTag – If an AEAD tag fails to verify – typically a wrong key.

encrypt(spi, seq, plaintext, iv=None)[source]

Encrypt the payload data of an ESP packet.

This is the inverse of decrypt(), used by ESP.make().

Parameters:
  • spi (int) – Security Parameters Index.

  • seq (int) – Sequence number.

  • plaintext (bytes) – Inner payload followed by the ESP trailer.

  • iv (bytes | None) – Explicit IV; a random one is generated when not given.

Return type:

tuple[bytes, bytes]

Returns:

A 2-tuple of the payload data as it goes on the wire (explicit IV followed by ciphertext) and the AEAD tag, which is empty for a non-AEAD algorithm.

Raises:

ProtocolError – If iv is of the wrong length, or if cryptography is needed and unavailable.

static _split_key(suite, material, salt)[source]

Split keying material into the key and the AEAD salt.

Parameters:
  • suite (CipherSuite) – Encryption algorithm parameters.

  • material (bytes) – Keying material as supplied by the caller.

  • salt (bytes | None) – Explicit salt, if the caller kept it separate.

Return type:

tuple[bytes, bytes]

Returns:

A 2-tuple of the key and the salt.

Raises:

ProtocolError – If the lengths do not match the algorithm.

__repr__()[source]

Representation of the SA, free of any key material.

Return type:

str

class pcapkit.protocols.internet.esp.ESPContext(*associations)[source]

Bases: ProtocolContext

Caller supplied Security Association context for ESP.

Parameters:

*associations (SecurityAssociation) – Security Associations to make available to the parser, in order of preference for otherwise equal matches.

property associations: tuple[SecurityAssociation, ...]

Registered Security Associations.

classmethod protocol()[source]

Index ID of the protocol this context applies to.

Return type:

tuple[Literal['ESP']]

register(association)[source]

Add a Security Association to the context.

Parameters:

association (SecurityAssociation) – Security Association to add.

Raises:

ProtocolError – If association is not a SecurityAssociation.

Return type:

None

match(spi, destination=None)[source]

Find the Security Association that best fits a packet.

Parameters:
Return type:

SecurityAssociation | None

Returns:

The best matching SA, or None when none applies.

__repr__()[source]

Representation of the context, free of any key material.

Return type:

str

Algorithm Registries

The algorithm enumerations are the IANA IKEv2 transform ID registries, generated into pcapkit.const.esp and re-exported here for convenience: Cipher is pcapkit.const.esp.cipher.Cipher and Integrity is pcapkit.const.esp.integrity.Integrity.

Algorithm Support

A registry enumerates what IANA assigned an ID to, which is far more than pcapkit implements. The tables below are the authority on what an SA may actually name, and the two get methods refuse anything outside them.

class pcapkit.protocols.internet.esp.CipherSuite(cipher: Cipher, is_aead: bool, iv_length: int, block_size: int, icv_length: int, key_sizes: tuple[int, ...], salt_length: int, requires_cryptography: bool)[source]

Bases: NamedTuple

Parameters of an ESP encryption algorithm pcapkit implements.

A member of Cipher records only that IANA registered the transform. This records how to apply it, and membership of CIPHER_SUITES is what “supported” means – see the module docstring.

cipher: Cipher

Encryption algorithm the suite describes.

is_aead: bool

Whether the algorithm is a combined mode (AEAD) algorithm, i.e. one that provides its own integrity protection.

iv_length: int

Length of the explicit IV carried at the head of the payload data.

block_size: int

Cipher block size, in octets. RFC 4303 §2.4 additionally requires the ciphertext to be a multiple of 4 octets, which is why ESP.make() aligns to max(block_size, 4) rather than to this value alone.

icv_length: int

Length of the ICV the algorithm itself produces (AEAD only).

key_sizes: tuple[int, ...]

Permitted lengths of the key, in octets, excluding any salt.

salt_length: int

Length of the salt taken from the keying material [RFC 4106 §8.1].

requires_cryptography: bool

Whether the algorithm needs the optional cryptography dependency.

classmethod get(value)[source]

Look up how to apply an encryption algorithm.

Parameters:

value (Cipher | str | int) – A Cipher member, an IKEv2 transform ID, or a name such as 'AES-CBC', 'aes_cbc' or 'ENCR_AES_CBC'.

Return type:

CipherSuite

Returns:

The suite describing how to apply the algorithm.

Raises:

ProtocolError – If value names no registered algorithm, or names one pcapkit does not implement. The registry is far larger than the set of suites here, so the second case is the common one.

class pcapkit.protocols.internet.esp.IntegritySuite(integrity: Integrity, digest: str | None, icv_length: int, key_size: int)[source]

Bases: NamedTuple

Parameters of an ESP integrity algorithm pcapkit implements.

As with CipherSuite, membership of INTEGRITY_SUITES – not membership of the IANA registry – is what makes an algorithm supported.

integrity: Integrity

Integrity algorithm the suite describes.

digest: str | None

Name of the underlying hash, for hmac.new(), or None when the algorithm computes no ICV of its own.

icv_length: int

Length of the truncated ICV, in octets.

key_size: int

Key length required by the specification, in octets.

classmethod get(value)[source]

Look up how to apply an integrity algorithm.

Parameters:

value (Integrity | str | int) – An Integrity member, an IKEv2 transform ID, or a name such as 'HMAC-SHA-256-128', 'hmac_sha2_256_128' or 'AUTH_HMAC_SHA2_256_128'.

Return type:

IntegritySuite

Returns:

The suite describing how to apply the algorithm.

Raises:

ProtocolError – If value names no registered algorithm, or names one pcapkit does not implement.

pcapkit.protocols.internet.esp.CIPHER_SUITES: dict[Cipher, CipherSuite] = {<Cipher.ENCR_NULL: 11>: (<Cipher.ENCR_NULL: 11>, False, 0, 1, 0, (0,), 0, False), <Cipher.ENCR_AES_CBC: 12>: (<Cipher.ENCR_AES_CBC: 12>, False, 16, 16, 0, (16, 24, 32), 0, True), <Cipher.ENCR_AES_GCM_8: 18>: (<Cipher.ENCR_AES_GCM_8: 18>, True, 8, 1, 8, (16, 24, 32), 4, True), <Cipher.ENCR_AES_GCM_12: 19>: (<Cipher.ENCR_AES_GCM_12: 19>, True, 8, 1, 12, (16, 24, 32), 4, True), <Cipher.ENCR_AES_GCM_16: 20>: (<Cipher.ENCR_AES_GCM_16: 20>, True, 8, 1, 16, (16, 24, 32), 4, True)}

Encryption algorithms pcapkit implements, keyed by IKEv2 transform. This table – not Cipher, which is the whole IANA registry – defines what an SA may name.

pcapkit.protocols.internet.esp.INTEGRITY_SUITES: dict[Integrity, IntegritySuite] = {<Integrity.NONE: 0>: (<Integrity.NONE: 0>, None, 0, 0), <Integrity.AUTH_HMAC_SHA1_96: 2>: (<Integrity.AUTH_HMAC_SHA1_96: 2>, 'sha1', 12, 20), <Integrity.AUTH_HMAC_SHA2_256_128: 12>: (<Integrity.AUTH_HMAC_SHA2_256_128: 12>, 'sha256', 16, 32), <Integrity.AUTH_HMAC_SHA2_384_192: 13>: (<Integrity.AUTH_HMAC_SHA2_384_192: 13>, 'sha384', 24, 48), <Integrity.AUTH_HMAC_SHA2_512_256: 14>: (<Integrity.AUTH_HMAC_SHA2_512_256: 14>, 'sha512', 32, 64)}

Integrity algorithms pcapkit implements, keyed by IKEv2 transform. Integrity.NONE is here because “no separate integrity algorithm” is a supported configuration – it is what an AEAD suite, and an unprotected SA, use.

pcapkit.protocols.internet.esp._resolve(registry, value, prefix, kind)[source]

Resolve value to a member of an IKEv2 transform registry.

Parameters:
  • registry (Type[Cipher] | Type[Integrity]) – Cipher or Integrity.

  • value (Any) – A member of registry, an IKEv2 transform ID, or a transform name.

  • prefix (str) – Prefix the registry spells its names with, i.e. 'ENCR_' or 'AUTH_'.

  • kind (str) – Word naming the registry for the error message, i.e. 'encryption' or 'integrity'.

Return type:

Any

Returns:

The corresponding member of registry.

Raises:

ProtocolError – If value names nothing the registry holds.

Notes

Both enumerations carry each transform’s prefix-stripped spelling as an alias, so a plain lookup already accepts 'AES_CBC' as well as 'ENCR_AES_CBC'. prefix is still needed for the few names that are no Python identifier once stripped, and so have no alias – ENCR_3DES and ENCR_3IDEA.

Resolution is deliberately separate from support: it answers only whether IANA registered the transform. See CipherSuite.get().

Processing Status

class pcapkit.protocols.internet.esp.ESPStatus(*values)[source]

Bases: IntEnum

Outcome of ESP payload processing.

DECRYPTED = 0

The payload was decrypted and its trailer recovered.

NO_SA = 1

No Security Association matched the packet’s SPI, so the payload is reported as opaque ciphertext. This is the expected state for a capture taken without keys, and is not an error.

AUTH_FAILED = 2

A Security Association matched, but the ICV did not verify.

DECRYPT_FAILED = 3

A Security Association matched and the packet was authentic (or unauthenticated), but decryption did not yield a self consistent RFC 4303 trailer – most commonly a wrong encryption key.

TRUNCATED = 4

The packet is shorter than the Security Association says it must be.

UNSUPPORTED = 5

A Security Association matched but its algorithms cannot be applied, e.g. because the optional cryptography dependency is missing.

pcapkit.protocols.internet.esp.load_cryptography()[source]

Load the optional cryptography primitives.

Return type:

tuple[Any, Any, Any, Type[Exception]] | None

Returns:

A 4-tuple of (Cipher, algorithms, modes, InvalidTag) taken from cryptography.hazmat.primitives.ciphers and cryptography.exceptions, or None when cryptography is not installed.

Notes

The import is attempted at most once and the outcome is cached, so that a capture full of ESP packets does not pay for a failing import on every frame.

Header Schemas

class pcapkit.protocols.schema.internet.esp.ESP(dict_=None, **kwargs)[source]

Bases: Schema

Header schema for ESP packet.

Notes

Only the two fixed fields of RFC 4303SPI and Sequence Number – can be described declaratively. Everything after them (the payload data, including any cryptographic synchronisation such as an IV, the ESP trailer and the optional Integrity Check Value) is of a length that is a property of the Security Association rather than of the packet, so it is captured verbatim as payload and split by ESP.read.

This also means payload is not the next layer’s data: the next layer lives inside the ciphertext, and is handed to Protocol._decode_next_layer explicitly once decrypted.

spi: int = <UInt32Field spi>

Security parameters index.

seq: int = <UInt32Field seq>

Sequence number field.

payload: bytes = <PayloadField payload>

Payload data, ESP trailer and integrity check value, verbatim.

Data Models

class pcapkit.protocols.data.internet.esp.ESP(*args: VT, **kwargs: VT)[source]

Bases: Protocol

Data model for ESP protocol.

The trailer fields (next, pad_len, padding) and plaintext are recovered from the decrypted payload, and are therefore None whenever the payload could not be decrypted – RFC 4303 places them inside the ciphertext, so guessing them from an encrypted payload is not possible. status says which of those two cases applies, and error says why.

Important

No key material is recorded here, by design. This data model is what Info.to_dict returns and hence what reaches the output dumpers, so the Security Association – and the keys it holds – is deliberately kept out of it.

spi: int

Security parameters index.

seq: int

Sequence number field.

length: int

Total length of the ESP header, payload, trailer and ICV, i.e. every byte of the packet that ESP owns.

payload_data: bytes

Payload data exactly as transmitted – ciphertext, prefixed by any cryptographic synchronisation data (IV) – excluding the ICV.

icv: bytes

Integrity check value as transmitted; empty when absent, or when no Security Association was available to say how long it is.

status: ESPStatus

Outcome of the decryption and integrity check.

error: str | None

Reason the payload was not decrypted, if it was not.

next: TransType | None

Next header, from the decrypted ESP trailer.

pad_len: int | None

Pad length, from the decrypted ESP trailer.

padding: bytes | None

Padding bytes, from the decrypted ESP trailer.

plaintext: bytes | None

Decrypted payload, with the ESP trailer stripped, i.e. the next layer’s data.