SSLv2 - Secure Sockets Layer Version 2

pcapkit.protocols.application.sslv2 contains SSLv2 only, which implements an extractor for Secure Sockets Layer version 2 (SSLv2) [*], whose record header is 2 or 3 octets, chosen by the high bit of its first octet (sslv2.indicator):

2-octet form (high bit set):

   0                   1                   2
   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |1|      sslv2.indicator/length_low (15)    |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |sslv2.msg_type |   handshake message body ...

3-octet form (high bit clear):

   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
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |0|E|   sslv2.indicator/length_low (14)  |  sslv2.padding (8)   |
  +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  |sslv2.msg_type |   handshake message body ...

E is the IS-ESCAPE flag, meaningful only in the 3-octet form. See sslv2_record_length() for the exact bit arithmetic.

SSLv2 is wire-incompatible with SSLv3 and TLS: it carries no content-type byte and no 0x03xx-style record version, and its handshake message type is the first octet of the record’s data rather than a separate record-layer field. This package parses the CLIENT-HELLO and SERVER-HELLO handshake message bodies; every other HandshakeType, and a hello-tagged body whose declared sub-field lengths do not fit the record (e.g. ciphertext), is kept as raw octets, header and message type decoded.

Records in One Segment

A whole record after the first in a TCP segment is dissected as a further SSLv2 layer, the previous record’s payload, whatever its message type (#2567): a CLIENT-HELLO followed by a CLIENT-MASTER-KEY reads SSLv2:SSLv2. The exceptions:

  • A whole record that fails to decode – a RECORD-LENGTH of 0, which leaves no MSG-TYPE – becomes Raw together with everything after it.

  • Octets that open no record – a header cut short, or a record the segment ends inside – stay raw, with no error.

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

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

Added in version 1.6.0.


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

Bases: SSL[SSLv2, SSLv2]

This class implements Secure Sockets Layer version 2.

Note

This class is not bound to TCP port 443 in TCP.__proto__: SSLv3 and TLS reuse that port. On 443 it is reached only as TLS’s fallback, after TLS has rejected the record. Callers that know a stream is SSLv2 construct this class directly.

property name: Literal['Secure Sockets Layer version 2']

Name of current protocol.

property alias: Literal['SSLv2']

Acronym of current protocol.

property length: int

Header length of current protocol – 2 or 3 octets, depending on the record’s own header form.

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

Read Secure Sockets Layer version 2.

Structure of an SSLv2 record, 2-octet header form:

+-+-----------------------------+---------------+
|1|      RECORD-LENGTH (15)     |  MSG-TYPE (8) |
+-+-----------------------------+---------------+
|                   Body (*)                  ...
+-----------------------------------------------+

3-octet header form:

+-+-+---------------------------+---------------+---------------+
|0|E|     RECORD-LENGTH (14)    |  PADDING (8)  |  MSG-TYPE (8) |
+-+-+---------------------------+---------------+---------------+
|                           Body (*)                          ...
+---------------------------------------------------------------+
Parameters:
  • length (int | None) – Length of packet data.

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

Return type:

SSLv2

Returns:

Parsed packet data.

make(msg_type=HandshakeType.CLIENT_HELLO, two_byte_header=True, is_escape=False, padding=0, body=None, payload=b'', **kwargs)[source]

Make (construct) packet data.

Parameters:
  • msg_type (HandshakeType | int) – Handshake message type.

  • two_byte_header (bool) – Whether to emit the 2-octet header form (the more common of the two on real traffic) rather than the 3-octet form.

  • is_escape (bool) – IS-ESCAPE flag; only meaningful – and only written – for the 3-octet header form.

  • padding (int) – The header’s third, padding octet; only meaningful – and only written – for the 3-octet header form.

  • body (bytes | ClientHello | ServerHello | ClientHello | ServerHello | UnknownHandshakeBody | dict[str, Any] | None) – Handshake message body. A dict or a ClientHello /ServerHello data model builds the body according to msg_type, which must then be CLIENT_HELLO or SERVER_HELLO respectively; omitting it, i.e. None, builds an empty one the same way, as {} does. A schema instance is taken as is. Raw bytes are written verbatim. A message type whose body this package does not parse is given its body as bytes or as an UnknownHandshakeBody.

  • payload (bytes | ProtocolBase | Schema) – Octets after the record, e.g. the next record.

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

Return type:

SSLv2

Returns:

Constructed packet data.

Raises:

ProtocolError – If msg_type is a name, a bool, or a member or code the 8-bit MSG-TYPE field cannot carry, or a hello body’s client_version or server_version is such a value for its 16-bit field; if body is a dict and msg_type is not CLIENT_HELLO/SERVER_HELLO; if body is a ClientHello/ ServerHello data model and msg_type does not match it (CLIENT_HELLO for the former, SERVER_HELLO for the latter); if body is of a type this maker does not recognise; if the resulting record is longer than the chosen header form allows; or if padding is not an int (a bool included) that fits the 3-octet header form’s single padding octet (0-255).

classmethod id()[source]

Index ID of the protocol.

Return type:

tuple[Literal['SSLv2']]

classmethod _make_data(data)[source]

Create key-value pairs from data for protocol construction.

Only the fields make() accepts are passed, so derived fields such as record_length raise no UnknownFieldWarning, and payload carries any chained next record through a from_data() round trip.

Parameters:

data (SSLv2) – protocol data

Return type:

dict[str, Any]

Returns:

Key-value pairs for protocol construction.

_make_sslv2_client_hello(client_version=Version.SSLv2, cipher_specs=b'', session_id=b'', challenge=b'\x00' * 16)[source]

Make a CLIENT-HELLO handshake message body.

Parameters:
  • client_version (Version | int) – Client’s maximum supported version.

  • cipher_specs (bytes) – Cipher kinds the client supports, as a concatenation of 3-octet CIPHER-KIND entries. The draft (§5.5.1) requires CIPHER-SPECS-LENGTH to be greater than zero, i.e. at least one entry – the default, b'', builds a CLIENT-HELLO the draft forbids, so a caller building a conformant one must supply this.

  • session_id (bytes) – Session identifier; 0 or 16 octets.

  • challenge (bytes) – Client’s challenge data; 16-32 octets.

Return type:

ClientHello

Returns:

CLIENT-HELLO message body schema.

Raises:

ProtocolError – If client_version is a name, a bool, or a member or code its 16-bit field cannot carry.

_make_sslv2_server_hello(session_id_hit=0, certificate_type=0, server_version=Version.SSLv2, certificate=b'', cipher_specs=b'', connection_id=b'\x00' * 16)[source]

Make a SERVER-HELLO handshake message body.

Parameters:
  • session_id_hit (int | bool) – SESSION-ID-HIT octet. Per the draft (§5.6.1) any non-zero value means “hit”, not specifically 1 – this is written through verbatim (so a caller reconstructing from parsed data reproduces the exact octet observed on the wire), with True/False taken as 1/0 for callers that only care about the boolean sense. Must fit the single octet the wire format has for it (0-255); anything else raises ProtocolError rather than wrapping mod 256.

  • certificate_type (int) – Certificate type.

  • server_version (Version | int) – Server’s selected version.

  • certificate (bytes) – Server’s certificate.

  • cipher_specs (bytes) – Cipher kinds the server supports, as a concatenation of 3-octet CIPHER-KIND entries – empty when session_id_hit is non-zero, but the default, b'', is also what this builds for a non-hit SERVER-HELLO unless a caller overrides it, which the draft does not sanction (the server picks from the client’s offered specs, so it always has at least one to report when it is not a hit).

  • connection_id (bytes) – Connection identifier; 16-32 octets.

Return type:

ServerHello

Returns:

SERVER-HELLO message body schema.

Raises:

ProtocolError – If server_version is a name, a bool, or a member or code its 16-bit field cannot carry, or if session_id_hit does not fit its octet.

Header Schemas

class pcapkit.protocols.schema.application.sslv2.SSLv2(indicator=NO_VALUE, length_low=NO_VALUE, padding=NO_VALUE, msg_type=NO_VALUE, probe=NO_VALUE, body=NO_VALUE, payload=NO_VALUE, *, __packet__=None, **kwargs)[source]

Bases: Schema

Header schema for SSLv2 packet.

The record header is two or three octets, chosen by the high bit of its first octet – see sslv2_record_length() – rather than a fixed width, so padding is read only for the 3-octet form. msg_type is the one octet the SSLv2 draft puts at the front of every record’s data rather than in the header proper, and it is read here, ahead of body, for the same reason type is read ahead of frame.

indicator: int = <UInt8Field indicator>

First octet of the record header. Its high bit (0x80) selects the header form, and (only for the 3-octet form) the next bit (0x40) is IS-ESCAPE; see sslv2_record_length().

length_low: int = <UInt8Field length_low>

Second octet of the record header – the low 8 bits of RECORD-LENGTH.

padding: int = <ConditionalField padding>

Third octet of the record header, present only for the 3-octet form.

msg_type: HandshakeType = <EnumField msg_type>

Handshake message type, the first octet of the record’s data.

probe: bytes = <ForwardMatchField probe>

The body’s first few octets, forward-matched (consuming nothing) so sslv2_body_selector() can sanity-check a CLIENT-HELLO/ SERVER-HELLO’s own declared sub-field lengths before committing to decoding it as one; see sslv2_hello_probe_is_plausible().

body: HandshakeBody = <SwitchField body>

Handshake message body.

payload: bytes = <PayloadField payload>

Octets after the record, e.g. the next record.

class pcapkit.protocols.schema.application.sslv2.HandshakeBody(*args, **kwargs)[source]

Bases: EnumSchema[HandshakeType]

Header schema for SSLv2 handshake message bodies.

Unlike Option and its siblings, this base class declares no field of its own for the discriminator: SSLv2.msg_type already consumes that octet in the enclosing schema, before SSLv2.body dispatches here, so every concrete subclass’s own first field starts at the body’s first octet – the same arrangement FrameType uses for HTTP/2 frame payloads, where type is consumed by the outer HTTPv2 schema.

class pcapkit.protocols.schema.application.sslv2.UnknownHandshakeBody(data=NO_VALUE, *, __packet__=None, **kwargs)[source]

Bases: HandshakeBody

Header schema for an SSLv2 handshake message body kept as raw octets: every HandshakeType other than CLIENT-HELLO and SERVER-HELLO, and a hello-tagged body that fails sslv2_hello_probe_is_plausible(), e.g. ciphertext.

Keeping the undissected body, rather than losing the whole record, matches UnassignedFrame.

data: bytes = <BytesField data>

Undissected message body.

class pcapkit.protocols.schema.application.sslv2.ClientHello(client_version=NO_VALUE, cipher_specs_length=NO_VALUE, session_id_length=NO_VALUE, challenge_length=NO_VALUE, cipher_specs=NO_VALUE, session_id=NO_VALUE, challenge=NO_VALUE, *, __packet__=None, **kwargs)[source]

Bases: HandshakeBody

Header schema for the SSLv2 CLIENT-HELLO handshake message.

client_version: Version = <EnumField client_version>

Client’s maximum supported version.

cipher_specs_length: int = <UInt16Field cipher_specs_length>

Length of cipher_specs.

session_id_length: int = <UInt16Field session_id_length>

Length of session_id.

challenge_length: int = <UInt16Field challenge_length>

Length of challenge.

cipher_specs: bytes = <BytesField cipher_specs>

Cipher kinds the client supports, as a concatenation of 3-octet CIPHER-KIND entries.

session_id: bytes = <BytesField session_id>

Session identifier, 0 or 16 octets.

challenge: bytes = <BytesField challenge>

Client’s challenge data, 16-32 octets.

class pcapkit.protocols.schema.application.sslv2.ServerHello(session_id_hit=NO_VALUE, certificate_type=NO_VALUE, server_version=NO_VALUE, certificate_length=NO_VALUE, cipher_specs_length=NO_VALUE, connection_id_length=NO_VALUE, certificate=NO_VALUE, cipher_specs=NO_VALUE, connection_id=NO_VALUE, *, __packet__=None, **kwargs)[source]

Bases: HandshakeBody

Header schema for the SSLv2 SERVER-HELLO handshake message.

session_id_hit: int = <UInt8Field session_id_hit>

Whether the client’s proposed ClientHello.session_id was accepted; when set, certificate_type, certificate and cipher_specs carry no data.

certificate_type: int = <UInt8Field certificate_type>

Certificate type, SSL_CT_X509_CERTIFICATE (0x01) being the only one the draft defines.

server_version: Version = <EnumField server_version>

Server’s selected version.

certificate_length: int = <UInt16Field certificate_length>

Length of certificate.

cipher_specs_length: int = <UInt16Field cipher_specs_length>

Length of cipher_specs.

connection_id_length: int = <UInt16Field connection_id_length>

Length of connection_id.

certificate: bytes = <BytesField certificate>

Server’s certificate.

cipher_specs: bytes = <BytesField cipher_specs>

Cipher kinds the server supports, as a concatenation of 3-octet CIPHER-KIND entries.

connection_id: bytes = <BytesField connection_id>

Connection identifier, 16-32 octets.

Auxiliary Functions

pcapkit.protocols.schema.application.sslv2.sslv2_record_length(packet)[source]

Compute the SSLv2 RECORD-LENGTH from the already-parsed header octets.

The record header carries the length split across two or three octets in a shape that depends on the high bit of its first octet, per the SSLv2 draft, §4.1 (SSL Record Header Format):

  • if indicator & 0x80 is set, the header is the 2-octet form and RECORD-LENGTH = ((indicator & 0x7f) << 8) | length_low;

  • otherwise, the header is the 3-octet form (a third, padding octet follows) and RECORD-LENGTH = ((indicator & 0x3f) << 8) | length_low.

Parameters:

packet (dict[str, Any]) – Packet data, with indicator and length_low already parsed (SSLv2.indicator and SSLv2.length_low).

Return type:

int

Returns:

The record’s declared length, header excluded.

pcapkit.protocols.schema.application.sslv2.sslv2_hello_probe_length(packet)[source]

Compute SSLv2.probe’s forward-match length.

Never more than the body actually declares (sslv2_record_length() minus the one octet SSLv2.msg_type already consumed), and never more than _HELLO_PROBE_LENGTH.

Parameters:

packet (dict[str, Any]) – Packet data, with indicator/length_low already parsed (see sslv2_record_length()).

Return type:

int

Returns:

The probe’s length, in octets.

pcapkit.protocols.schema.application.sslv2.sslv2_hello_probe_is_plausible(msg_type, probe, inner_length)[source]

Check a CLIENT-HELLO/SERVER-HELLO body’s declared sub-field lengths against the record’s own declared body length.

A hello-tagged record that is actually ciphertext would otherwise decode random octets as *-LENGTH sub-fields that overrun or underrun the body. sslv2_body_selector() parses a body failing this check as UnknownHandshakeBody, keeping its raw octets whole.

Parameters:
  • msg_type (HandshakeType) – The record’s handshake message type; only CLIENT_HELLO and SERVER_HELLO are checked, every other type is vacuously plausible.

  • probe (bytes) – The body’s first min(10, inner_length) octets, forward-matched by SSLv2.probe – not yet actually consumed.

  • inner_length (int) – The body’s declared length (RECORD-LENGTH minus the one octet SSLv2.msg_type already consumed).

Return type:

bool

Returns:

True if the declared sub-field lengths add up to inner_length exactly, or if msg_type is not one this check applies to.

pcapkit.protocols.schema.application.sslv2.sslv2_body_selector(packet)[source]

Selector function for SSLv2.body field.

Parameters:

packet (dict[str, Any]) – Packet data.

Return type:

FieldBase

Returns:

A SchemaField wrapped HandshakeBody instance, of whichever subclass HandshakeBody.registry maps packet['msg_type'] to, sized to the record’s remaining declared length – the record’s own RECORD-LENGTH, the one octet SSLv2.msg_type already consumed excluded. For CLIENT-HELLO/SERVER-HELLO, falls back to UnknownHandshakeBody when sslv2_hello_probe_is_plausible() rejects SSLv2.probe – see that function for why.

Note

A malformed RECORD-LENGTH = 0 sizes the body to 0, not to the rest of the buffer; read() reports it.

Data Models

class pcapkit.protocols.data.application.sslv2.SSLv2(header_len, two_byte_header, is_escape, padding, record_length, msg_type, body)[source]

Bases: Protocol

Data model for SSLv2 packet.

header_len: int

Header length (2 or 3 octets), chosen by the high bit of the record header’s first octet.

two_byte_header: bool

Whether the record uses the 2-octet header form.

is_escape: bool

IS-ESCAPE flag; always False for a 2-octet header, which has no bit for it.

padding: int

The header’s third, padding octet; 0 for a 2-octet header, which has none.

record_length: int

RECORD-LENGTH, the record’s data length, header excluded.

msg_type: HandshakeType

Handshake message type.

body: ClientHello | ServerHello | bytes

Handshake message body – a parsed data model for CLIENT-HELLO and SERVER-HELLO, or raw octets for every other HandshakeType and for a hello-tagged body whose declared sub-field lengths do not fit the record, e.g. ciphertext.

class pcapkit.protocols.data.application.sslv2.ClientHello(client_version, cipher_specs, session_id, challenge)[source]

Bases: Data

Data model for the SSLv2 CLIENT-HELLO handshake message.

client_version: Version

Client’s maximum supported version.

cipher_specs: bytes

Cipher kinds the client supports, as a concatenation of 3-octet CIPHER-KIND entries.

session_id: bytes

Session identifier, 0 or 16 octets.

challenge: bytes

Client’s challenge data, 16-32 octets.

class pcapkit.protocols.data.application.sslv2.ServerHello(session_id_hit, certificate_type, server_version, certificate, cipher_specs, connection_id)[source]

Bases: Data

Data model for the SSLv2 SERVER-HELLO handshake message.

session_id_hit: int

Raw SESSION-ID-HIT octet, kept as read rather than normalised to a bool: per draft-hickman-netscape-ssl-00 §5.6.1 any non-zero value is a hit. When non-zero, certificate_type, certificate and cipher_specs carry no data.

certificate_type: int

Certificate type, zero when session_id_hit is non-zero.

server_version: Version

Server’s selected version.

certificate: bytes

Server’s certificate, empty when session_id_hit is non-zero.

cipher_specs: bytes

Cipher kinds the server supports, as a concatenation of 3-octet CIPHER-KIND entries; empty when session_id_hit is non-zero.

connection_id: bytes

Connection identifier, 16-32 octets.

Footnotes