TLS/SSL - Transport Layer Security

pcapkit.protocols.application.tls contains TLS only, which implements an extractor for the TLS/SSLv3+ record-layer family [*] – SSLv3 (RFC 6101), TLS 1.0 (RFC 2246), TLS 1.1 (RFC 4346), TLS 1.2 (RFC 5246) and TLS 1.3 (RFC 9846, which obsoletes RFC 5077, RFC 5246, RFC 6961, RFC 7627, RFC 8422 and RFC 8446) – at the record layer, plus handshake message framing and the bodies Decoded Fields lists.

Version Identification

The record layer is wire-identical across the family, so one TLS class parses all of it and reports the version it identified through alias and name, set by read() – the same shape ARP uses for RARP/DRARP/InARP. id() stays ('TLS',) whatever the version.

Legacy record version

alias

name

0x0300

SSLv3

Secure Sockets Layer version 3

0x0301

TLS 1.0

Transport Layer Security

0x0302

TLS 1.1

Transport Layer Security

0x0303

TLS 1.2

Transport Layer Security

0x0301 or 0x0303, on a ClientHello or ServerHello whose supported_versions extension lists 0x0304

TLS 1.3

Transport Layer Security

any other 0x03xx

TLS

Transport Layer Security

TLS 1.3 never puts its own 0x0304 on the record layer (RFC 9846 Section 5.1), so it is told apart only by the supported_versions peek. A version the table does not name keeps the generic TLS alias rather than claiming a specific one.

Each record is branded by its own legacy version, not by the connection’s negotiated one. So in a TLS 1.3 connection, every record after the hellos carries 0x0303 and reads TLS 1.2; and a TLS 1.2 ClientHello sent at record version 0x0301 reads TLS 1.0.

SSLv3 is for explicit construction only, mirroring InARP: it narrows id() to ('SSLv3',) and inherits read() unchanged, so the alias still follows the wire. Dispatch never constructs one.

Decoded Fields

  • The record header, always: it is never encrypted.

  • A cleartext handshake, alert or change_cipher_spec record (SSL._tls_content_is_cleartext decides which are):

    • ClientHello/ServerHello down to their cipher suites, compression methods and extensions, and EncryptedExtensions down to its extensions; an extension’s body decodes where SSL.__extension__ registers a decoder for its type.

    • Every other handshake message at type and length only.

    • Alert and ChangeCipherSpec in full.

  • Everything else – the same types once encrypted, application_data and every other content type – as opaque bytes.

Records in One Segment

A whole record after the first in a TCP segment is dissected as a further TLS layer, the previous record’s payload (#2517): a ServerHello followed by a ChangeCipherSpec reads TCP:TLS 1.3:TLS 1.2. The exceptions:

  • A whole record that fails to decode becomes Raw together with everything after it, with no SSLv2 trial.

  • Octets that open no record – an unsupported content type, a legacy version outside 0x03xx, or a header cut short – stay raw, with no error.

  • A parse told to stop at TLS (protocol='TLS' or layer='Application') keeps the rest of the segment raw.

  • Each record is a layer, so FRAME_LAYER_LIMIT bounds how many are dissected.

Known Limitation

A record split across two TCP segments is not reassembled, and decodes as Raw, as does the partial record a segment ends inside. A record may be up to 16 KiB against a typical 1460-octet MSS, so this is routine.

Added in version 1.6.0.


class pcapkit.protocols.application.tls.TLS(file=None, length=None, **kwargs)[source]

Bases: SSL[TLS, TLS]

This class implements all protocols in the TLS/SSLv3+ record-layer family.

  • Secure Sockets Layer version 3 (SSLv3)

  • Transport Layer Security 1.0 [RFC 2246]

  • Transport Layer Security 1.1 [RFC 4346]

  • Transport Layer Security 1.2 [RFC 5246]

  • Transport Layer Security 1.3 [RFC 9846]

Note

The subpackage does not track the dispatch tier: TLS is dispatched from TCP.__proto__ at port 443, the https service name IANA assigns there. See pcapkit.foundation.registry.protocols.transport.register_tcp().

property name: Literal['Transport Layer Security', 'Secure Sockets Layer version 3']

Name of current protocol.

property alias: Literal['TLS', 'SSLv3', 'TLS 1.0', 'TLS 1.1', 'TLS 1.2', 'TLS 1.3']

Acronym of current protocol – the concrete tier read() identified.

property length: int

Header length of current protocol – the fixed record header, length’s fragment excluded; or, once unpack() has adopted an SSLv2 record, that record’s own 2- or 3-octet header.

classmethod id()[source]

Index ID of the protocol.

Return type:

tuple[Literal['TLS']]

Returns:

Index ID of the protocol. Fixed whatever tier read() identifies, so a lookup by ID matches every record; inspect alias for the tier.

read(length=None, **kwargs)[source]

Read Transport Layer Security (TLS).

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

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

Return type:

TLS

Returns:

Parsed packet data.

Raises:

ProtocolError – If the payload is not a TLS record (see _read_tls_record()), which unpack() then offers to SSLv2.

make(content_type=ContentType.application_data, version=0x0303, content=b'', data=None, payload=b'', **kwargs)[source]

Make (construct) packet data, for every tier: version is written as is.

Parameters:
  • content_type (ContentType | int) – Record content type.

  • version (int) – Legacy record version (major << 8 | minor), e.g. 0x0303 for TLS 1.2 and (the legacy value TLS 1.3 keeps here) TLS 1.3.

  • content (Any) – Record content: a tuple of Handshake for handshake, an Alert, a ChangeCipherSpec, or raw bytes, as read() would decode it.

  • data (bytes | None) – Pre-encoded record fragment. When given, it is used verbatim in place of content and length is computed from it.

  • payload (bytes | ProtocolBase | Schema) – Octets after the record, e.g. a further record coalesced in the same buffer.

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

Return type:

TLS

Returns:

Constructed packet data.

Raises:
  • ProtocolError – If content_type is a name, a bool, or a member or code the 8-bit content type field cannot carry, or an extension of content given as a (type, kwargs) pair has such a type for its 16-bit field.

  • StructError – If version or the record length, or a field of content, does not fit its width.

unpack(length=None, *, chained=False, **kwargs)[source]

Unpack (parse) packet data: TLS, or the SSLv2 record port 443 may carry.

TLS is tried first: the record header and read(). Only when that trial rejects the first record of a segment is the payload offered to SSLv2, whose own header checks are strict, so a genuine TLS record is never taken as SSLv2.

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

  • chained (bool) – Whether the record follows another in its segment; such a record gets no SSLv2 trial, since SSLv2 only opens a connection (#2517).

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

Return type:

Any

Returns:

Parsed packet data.

Raises:

ProtocolError – If the payload is neither a TLS record (see _read_tls_record()) nor an SSLv2 record.

Note

The TLS trial’s warnings are dropped when SSLv2 parses the record (#2525), and the SSLv2 trial’s when it fails. When the record is not parsed, the TLS trial’s are dropped only under beholder() (#2308).

__length_hint__()[source]

Return an estimated length for the object.

Return type:

Literal[5]

classmethod __index__()[source]

Numeral registry index of the protocol.

Return type:

int

Returns:

Numeral registry index of the protocol in IANA service names and port numbers: TCP port 443, the port of TCP.https_443 as an int, which TCP.__proto__ dispatches here (The Registry Index). SSLv3 inherits it.

classmethod _make_data(data)[source]

Create key-value pairs from data for protocol construction.

Parameters:

data (TLS) – protocol data

Return type:

dict[str, Any]

Returns:

Key-value pairs for protocol construction.

Header Schema

class pcapkit.protocols.schema.application.tls.TLS(content_type=NO_VALUE, version=NO_VALUE, length=NO_VALUE, fragment=NO_VALUE, payload=NO_VALUE, *, __packet__=None, **kwargs)[source]

Bases: Schema

Header schema for TLS record layer packet.

The record header has DTLS’s fields minus the epoch and sequence number (RFC 9846 Section 5.1). fragment is decoded by SSL, which DTLS shares.

content_type: ContentType = <EnumField content_type>

Content type.

version: int = <UInt16Field version>

Legacy record version (major << 8 | minor).

length: int = <UInt16Field length>

Length of fragment.

fragment: bytes = <BytesField fragment>

Record fragment, undecoded.

payload: bytes = <PayloadField payload>

Octets after the record, e.g. a further record coalesced in the same buffer.

Data Model

class pcapkit.protocols.data.application.tls.TLS(content_type, version, length, content)[source]

Bases: Protocol

Data model for TLS protocol.

content_type: ContentType

Content type.

version: int

Legacy record version (major << 8 | minor).

length: int

Length of the record fragment.

content: Any

Decoded record content. For handshake, alert, and change_cipher_spec, this is a tuple of Handshake, an Alert, or a ChangeCipherSpec respectively only when the record is cleartext (as decided by SSL._tls_content_is_cleartext) – an encrypted record of any of those three types, which is the common case for TLS 1.2 post-handshake traffic, stays raw bytes instead, same as every other content type.

See SSL - Secure Sockets Layer for ClientHello, ServerHello, EncryptedExtensions, Handshake, Extension and its decoded bodies, Alert and ChangeCipherSpec, shared verbatim with DTLS.

Footnotes