Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -63,8 +63,8 @@ holds the ruling. By the ``id()`` criterion,
TLS 1.3**, in the ARP shape (:ref:`protocol-family-shapes`): ``id()`` stays
``('TLS',)`` and ``alias`` names the version read. Only record version ``0x0300`` is
branded SSLv3, in ``name`` as well as ``alias``; every later version is TLS.
:class:`~pcapkit.protocols.application.tls.SSLv3` ``('SSLv3',)`` narrows ``id()`` and
nothing else, as :class:`~pcapkit.protocols.link.arp.InARP` does under ARP, and nothing
:class:`~pcapkit.protocols.application.sslv3.SSLv3` ``('SSLv3',)`` narrows ``id()`` and
nothing else, as :class:`~pcapkit.protocols.link.inarp.InARP` does under ARP, and nothing
dispatches to it: an SSLv3 record on TCP port 443 parses as TLS with alias ``SSLv3``.
:class:`~pcapkit.protocols.application.dtls.DTLS` ``('DTLS',)`` reads DTLS 1.0, 1.2 and
1.3 the same way.
Expand Down Expand Up @@ -345,6 +345,5 @@ holds :class:`~pcapkit.protocols.link.linux_sll1.LinuxSLL1`. **The** ``schema/``
``data/`` **modules follow the same layout**: the top-level class takes the module's
name, and sub-structure classes such as
:class:`~pcapkit.protocols.schema.internet.igmpv3.Query` share its module. Ruled on
:issue:`2529`, where the classes that do not match yet are pending:
:class:`tls.SSLv3 <pcapkit.protocols.application.tls.SSLv3>` and
:issue:`2529`, where the class that does not match yet is pending:
:class:`null.NoPayload <pcapkit.protocols.misc.null.NoPayload>`.
2 changes: 1 addition & 1 deletion docs/source/ext.rst
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ The protocol classes :mod:`pcapkit` ships:
+ + +-----------------------+-------------------------------------------------------------+
| | | :class:`pcapkit.protocols.application.tls.TLS` |
+ + +-----------------------+-------------------------------------------------------------+
| | SSL Family | :class:`pcapkit.protocols.application.tls.SSLv3` |
| | SSL Family | :class:`pcapkit.protocols.application.sslv3.SSLv3` |
+ + +-----------------------+-------------------------------------------------------------+
| | | :class:`pcapkit.protocols.application.dtls.DTLS` |
+ + +-----------------------+-------------------------------------------------------------+
Expand Down
1 change: 1 addition & 0 deletions docs/source/pcapkit/protocols/application/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ function rather than encapsulation, see
ssl
sslv2
tls
sslv3
dtls

.. todo::
Expand Down
2 changes: 1 addition & 1 deletion docs/source/pcapkit/protocols/application/ssl.rst
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ family [*]_ -- the SSL versions, Transport Layer Security and Datagram TLS:
* :class:`~pcapkit.protocols.application.sslv2.SSLv2` -- SSL 2.0, whose
record layer shares no wire format with the others;
* :class:`~pcapkit.protocols.application.tls.TLS` -- SSLv3 and TLS 1.0
through TLS 1.3, with :class:`~pcapkit.protocols.application.tls.SSLv3` as
through TLS 1.3, with :class:`~pcapkit.protocols.application.sslv3.SSLv3` as
its thin subclass for explicit construction;
* :class:`~pcapkit.protocols.application.dtls.DTLS` -- DTLS 1.0, 1.2 and 1.3.

Expand Down
26 changes: 26 additions & 0 deletions docs/source/pcapkit/protocols/application/sslv3.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
SSLv3 - Secure Sockets Layer version 3
======================================

.. module:: pcapkit.protocols.application.sslv3

:mod:`pcapkit.protocols.application.sslv3` contains
:class:`~pcapkit.protocols.application.sslv3.SSLv3` only,
which implements an extractor for Secure Sockets Layer
version 3 (SSLv3) [*]_, whose record is the one
:mod:`pcapkit.protocols.application.tls` describes.

Nothing dispatches to :class:`~pcapkit.protocols.application.sslv3.SSLv3`:
an SSLv3 record parses as :class:`~pcapkit.protocols.application.tls.TLS`
with alias ``SSLv3``.

.. versionadded:: 1.6.0

.. autoclass:: pcapkit.protocols.application.sslv3.SSLv3
:no-members:
:show-inheritance:

.. automethod:: id

.. rubric:: Footnotes

.. [*] https://datatracker.ietf.org/doc/html/rfc6101
13 changes: 3 additions & 10 deletions docs/source/pcapkit/protocols/application/tls.rst
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@ TLS/SSL - Transport Layer Security
.. module:: pcapkit.protocols.application.tls

:mod:`pcapkit.protocols.application.tls` contains
:class:`~pcapkit.protocols.application.tls.TLS` and
:class:`~pcapkit.protocols.application.tls.SSLv3`, which implement an
:class:`~pcapkit.protocols.application.tls.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`,
Expand Down Expand Up @@ -61,9 +60,9 @@ 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``.

:class:`~pcapkit.protocols.application.tls.SSLv3` is for explicit
:class:`~pcapkit.protocols.application.sslv3.SSLv3` is for explicit
construction only, mirroring :class:`~pcapkit.protocols.link.inarp.InARP`: it
narrows :meth:`~pcapkit.protocols.application.tls.SSLv3.id` to ``('SSLv3',)``
narrows :meth:`~pcapkit.protocols.application.sslv3.SSLv3.id` to ``('SSLv3',)``
and inherits :meth:`~pcapkit.protocols.application.tls.TLS.read` unchanged,
so the alias still follows the wire. Dispatch never constructs one.

Expand Down Expand Up @@ -133,12 +132,6 @@ this is routine.

.. automethod:: _make_data

.. autoclass:: pcapkit.protocols.application.tls.SSLv3
:no-members:
:show-inheritance:

.. automethod:: id

Header Schema
-------------

Expand Down
2 changes: 1 addition & 1 deletion docs/source/pcapkit/protocols/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ from given information. The class hierarchy of :mod:`pcapkit.protocols`:
click SSL "application/ssl.html#pcapkit.protocols.application.ssl.SSL"
click SSLv2 "application/sslv2.html#pcapkit.protocols.application.sslv2.SSLv2"
click TLS "application/tls.html#pcapkit.protocols.application.tls.TLS"
click SSLv3 "application/tls.html#pcapkit.protocols.application.tls.SSLv3"
click SSLv3 "application/sslv3.html#pcapkit.protocols.application.sslv3.SSLv3"
click DTLS "application/dtls.html#pcapkit.protocols.application.dtls.DTLS"

click Raw "misc/raw.html#pcapkit.protocols.misc.raw.Raw"
Expand Down
3 changes: 2 additions & 1 deletion pcapkit/protocols/application/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@
from pcapkit.protocols.application.rarp import RARP
from pcapkit.protocols.application.drarp import DRARP
from pcapkit.protocols.application.sslv2 import SSLv2
from pcapkit.protocols.application.tls import TLS, SSLv3
from pcapkit.protocols.application.tls import TLS
from pcapkit.protocols.application.sslv3 import SSLv3
from pcapkit.protocols.application.dtls import DTLS

# Base Classes
Expand Down
2 changes: 1 addition & 1 deletion pcapkit/protocols/application/ssl.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
which is the umbrella base class for the Secure Sockets Layer protocol
family [*]_ -- the SSL versions, Transport Layer Security
(:class:`~pcapkit.protocols.application.tls.TLS`, which also covers SSLv3
through :class:`~pcapkit.protocols.application.tls.SSLv3`) and Datagram TLS
through :class:`~pcapkit.protocols.application.sslv3.SSLv3`) and Datagram TLS
(:class:`~pcapkit.protocols.application.dtls.DTLS`).

The TLS record header (:rfc:`9846#section-5.1`) is a
Expand Down
52 changes: 52 additions & 0 deletions pcapkit/protocols/application/sslv3.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
# -*- coding: utf-8 -*-
"""SSLv3 - Secure Sockets Layer version 3
============================================

.. module:: pcapkit.protocols.application.sslv3

:mod:`pcapkit.protocols.application.sslv3` contains
:class:`~pcapkit.protocols.application.sslv3.SSLv3` only,
which implements an extractor for Secure Sockets Layer
version 3 (SSLv3) [*]_, whose record is the one
:mod:`pcapkit.protocols.application.tls` describes.

Nothing dispatches to :class:`~pcapkit.protocols.application.sslv3.SSLv3`:
an SSLv3 record parses as :class:`~pcapkit.protocols.application.tls.TLS`
with alias ``SSLv3``.

.. [*] https://datatracker.ietf.org/doc/html/rfc6101

.. versionadded:: 1.6.0

"""
from typing import TYPE_CHECKING

from pcapkit.protocols.application.tls import TLS
from pcapkit.protocols.data.application.tls import TLS as Data_TLS
from pcapkit.protocols.schema.application.tls import TLS as Schema_TLS

if TYPE_CHECKING:
from typing_extensions import Literal

__all__ = ['SSLv3']


class SSLv3(TLS, schema=Schema_TLS, data=Data_TLS):
"""This class implements Secure Sockets Layer version 3.

Dispatch never constructs it: an SSLv3 record parses as
:class:`~pcapkit.protocols.application.tls.TLS` with
:attr:`~pcapkit.protocols.application.tls.TLS.alias` ``'SSLv3'``. Like
:class:`~pcapkit.protocols.link.inarp.InARP` to
:class:`~pcapkit.protocols.link.arp.ARP`, it narrows :meth:`id` only.

"""

##########################################################################
# Methods.
##########################################################################

@classmethod
def id(cls) -> 'tuple[Literal["SSLv3"]]': # type: ignore[override]
"""Index ID of the protocol."""
return ('SSLv3',)
29 changes: 4 additions & 25 deletions pcapkit/protocols/application/tls.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,8 @@
.. module:: pcapkit.protocols.application.tls

:mod:`pcapkit.protocols.application.tls` contains
:class:`~pcapkit.protocols.application.tls.TLS` and
:class:`~pcapkit.protocols.application.tls.SSLv3`,
which implement an extractor for the TLS/SSLv3+ record-layer family --
:class:`~pcapkit.protocols.application.tls.TLS` only,
which implements an extractor for the TLS/SSLv3+ record-layer family --
SSLv3, TLS 1.0, TLS 1.1, 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`). :attr:`TCP.__proto__
Expand Down Expand Up @@ -88,7 +87,7 @@
from pcapkit.protocols.protocol import ProtocolBase
from pcapkit.protocols.schema.schema import Schema

__all__ = ['TLS', 'SSLv3']
__all__ = ['TLS']

#: Length of the record header: content type, legacy version and length
#: (:rfc:`9846#section-5.1`).
Expand Down Expand Up @@ -136,7 +135,7 @@ def _opens_whole_record(data: 'bytes') -> 'bool':
class TLS(SSL[Data_TLS, Schema_TLS], schema=Schema_TLS, data=Data_TLS):
"""This class implements all protocols in the TLS/SSLv3+ record-layer family.

- Secure Sockets Layer version 3 (:class:`SSLv3`)
- Secure Sockets Layer version 3 (:class:`~pcapkit.protocols.application.sslv3.SSLv3`)
- Transport Layer Security 1.0 [:rfc:`2246`]
- Transport Layer Security 1.1 [:rfc:`4346`]
- Transport Layer Security 1.2 [:rfc:`5246`]
Expand Down Expand Up @@ -526,23 +525,3 @@ def _import_next_record(self, proto: 'int', length: 'Optional[int]' = None, *,
return self._parse_next_layer(type(self), file_, len(file_) if length is None else length,
alias=proto, packet=packet, layer=self._exlayer,
protocol=self._exproto, __context__=self._exctx, chained=True)



class SSLv3(TLS, schema=Schema_TLS, data=Data_TLS):
"""This class implements Secure Sockets Layer version 3.

Dispatch never constructs it: an SSLv3 record parses as :class:`TLS` with
:attr:`~TLS.alias` ``'SSLv3'``. Like :class:`~pcapkit.protocols.link.inarp.InARP`
to :class:`~pcapkit.protocols.link.arp.ARP`, it narrows :meth:`id` only.

"""

##########################################################################
# Methods.
##########################################################################

@classmethod
def id(cls) -> 'tuple[Literal["SSLv3"]]': # type: ignore[override]
"""Index ID of the protocol."""
return ('SSLv3',)
7 changes: 4 additions & 3 deletions tests/protocols/application/test_ssl_tls_dtls_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -800,7 +800,7 @@ def test_sslv3_is_constructible_directly_and_narrows_id(self) -> None:
:class:`~pcapkit.protocols.application.tls.TLS` would.

"""
from pcapkit.protocols.application.tls import SSLv3
from pcapkit.protocols.application.sslv3 import SSLv3

tls_wire = b'\x17\x03\x00\x00\x05hello'
parsed = SSLv3(tls_wire, len(tls_wire))
Expand All @@ -816,7 +816,7 @@ def test_sslv3_class_does_not_force_its_own_alias(self) -> None:
it happened to be instantiated as.

"""
from pcapkit.protocols.application.tls import SSLv3
from pcapkit.protocols.application.sslv3 import SSLv3

tls_1_0_wire = b'\x17\x03\x01\x00\x05hello'
parsed = SSLv3(tls_1_0_wire, len(tls_1_0_wire))
Expand Down Expand Up @@ -1168,7 +1168,8 @@ def test_exported_from_every_namespace(self) -> None:
import pcapkit.protocols.application
from pcapkit.protocols.application.dtls import DTLS
from pcapkit.protocols.application.ssl import SSL
from pcapkit.protocols.application.tls import TLS, SSLv3
from pcapkit.protocols.application.sslv3 import SSLv3
from pcapkit.protocols.application.tls import TLS

classes = {'SSL': SSL, 'SSLv3': SSLv3, 'TLS': TLS, 'DTLS': DTLS}
for module in (pcapkit, pcapkit.all, pcapkit.protocols, pcapkit.protocols.application):
Expand Down
97 changes: 97 additions & 0 deletions tests/protocols/application/test_sslv3_unit.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# -*- coding: utf-8 -*-
"""Unit tests for :mod:`pcapkit.protocols.application.sslv3`.

#2529: a module holds one protocol class, named after it, so ``SSLv3`` has a
module of its own beside :mod:`pcapkit.protocols.application.tls`. Every case
builds its own octets.

"""
from __future__ import annotations

import importlib
import importlib.util
import unittest

from tests._support import reimport_once_per_class

RUNTIME_DEPS = ('tbtrim', 'aenum', 'chardet', 'dictdumper')
HAS_RUNTIME = all(importlib.util.find_spec(name) is not None for name in RUNTIME_DEPS)

#: An SSLv3 ``close_notify`` warning alert, :rfc:`6101#section-5.4.2`: content
#: type 21, version ``{3, 0}``, length 2, then level 1 and description 0.
SSLV3_ALERT = bytes.fromhex('15' '0300' '0002' '01' '00')


@unittest.skipUnless(HAS_RUNTIME, 'runtime dependencies not installed')
class SSLv3ModuleTests(unittest.TestCase):
"""``SSLv3`` lives in its own module and keeps TLS's behaviour."""

def setUp(self) -> None:
reimport_once_per_class(self)

def test_sslv3_is_defined_in_its_own_module(self) -> None:
from pcapkit.protocols.application.sslv3 import SSLv3
from pcapkit.protocols.application.tls import TLS

sslv3 = importlib.import_module('pcapkit.protocols.application.sslv3')
self.assertEqual(SSLv3.__module__, 'pcapkit.protocols.application.sslv3')
self.assertEqual(sslv3.__all__, ['SSLv3'])
self.assertEqual(SSLv3.__bases__, (TLS,))

def test_tls_module_holds_tls_alone(self) -> None:
"""#2529: one protocol class per module."""
from pcapkit.protocols.protocol import ProtocolBase

tls = importlib.import_module('pcapkit.protocols.application.tls')
defined = [name for name, obj in vars(tls).items()
if isinstance(obj, type) and issubclass(obj, ProtocolBase)
and obj.__module__ == tls.__name__]
self.assertEqual(tls.__all__, ['TLS'])
self.assertEqual(defined, ['TLS'])

def test_id_and_index(self) -> None:
"""``SSLv3`` narrows ``id()`` to itself and inherits the rest of TLS."""
from pcapkit.protocols.application.sslv3 import SSLv3
from pcapkit.protocols.application.tls import TLS

self.assertEqual(SSLv3.id(), ('SSLv3',))
self.assertEqual(TLS.id(), ('TLS',))
self.assertEqual([name for name in vars(SSLv3) if not name.startswith('_')], ['id'])
self.assertNotIn('__index__', vars(SSLv3))

def test_top_level_names_are_the_new_homes_object(self) -> None:
import pcapkit
import pcapkit.all
import pcapkit.protocols
import pcapkit.protocols.application
from pcapkit.protocols.application.sslv3 import SSLv3

for package in (pcapkit, pcapkit.all, pcapkit.protocols, pcapkit.protocols.application):
with self.subTest(package=package.__name__):
self.assertIs(package.SSLv3, SSLv3)
self.assertIn('SSLv3', package.__all__)
self.assertIs(pcapkit.protocols.__proto__['SSLV3'], SSLv3)

def test_parsed_tls_keeps_its_dynamic_sslv3_alias(self) -> None:
"""Dispatch binds TLS alone; legacy version ``0x0300`` reports ``SSLv3``."""
from pcapkit.protocols.application.sslv3 import SSLv3
from pcapkit.protocols.application.tls import TLS

parsed = TLS(SSLV3_ALERT, len(SSLV3_ALERT))
self.assertIs(type(parsed), TLS)
self.assertEqual(parsed.alias, 'SSLv3')

direct = SSLv3(SSLV3_ALERT, len(SSLV3_ALERT))
self.assertEqual(direct.alias, 'SSLv3')
self.assertEqual(direct.name, 'Secure Sockets Layer version 3')
self.assertEqual((direct.info.content_type, direct.info.version, direct.info.length),
(21, 0x0300, 2))
self.assertEqual((direct.info.content.level, direct.info.content.description), (1, 0))

rebuilt = SSLv3.from_data(direct.info)
self.assertIs(type(rebuilt), SSLv3)
self.assertEqual(bytes(rebuilt), SSLV3_ALERT)


if __name__ == '__main__':
unittest.main()
Loading