You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 1b2290f
Browse filesBrowse the repository at this point in the historyBrowse files
feat(application): implement TLS/SSL and DTLS record layer + handshake framing
pcapkit.protocols.application.ssl.SSL is the shared record-layer base over
the part TLS and DTLS genuinely share (RFC 9846 §5.1 / RFC 9147 §4):
ContentType/legacy-version/length and content-type dispatch. tls.py and
dtls.py are the two concrete subclasses; DTLS alone carries the epoch +
sequence number in its record header and the message_seq/fragment_offset/
fragment_length header each of its handshake messages adds (RFC 6347
§4.2.2), plus a cookie field in its ClientHello that TLS's does not carry
(RFC 6347 §4.2.1 / RFC 9147 §5.3).
- ClientHello/ServerHello decode to their version, random, session ID,
cipher suite(s), compression method(s) and extension list (TYPE+LENGTH
framed, extension bodies undecoded); every other handshake type is
message-TYPE-level framing only. Alert and ChangeCipherSpec decode in
full only when cleartext (see below); application_data, heartbeat, ack
and return_routability_check always stay opaque bytes. tls12_cid (25,
RFC 9146) is excluded from the wire allowlist entirely and falls back to
Raw: its record format puts the connection ID before length, at an
offset only the out-of-band negotiated CID length can resolve, which
this dissector's stateless, per-packet schema read has no way to track.
- SSL._validate_tls_record rejects a content type outside IANA's assigned
20-27, a legacy version outside TLS's/DTLS's range, or a declared length
that runs past the buffer, so dispatch falls back to Raw instead of
fabricating a record from unrelated bytes. The content-type test is an
allowlist rather than "did the registry name this value": invalid(0) is
named -- RFC 9846 §5.1 defines it so a record carrying it can be
rejected -- and a name-based test accepted `00 03 03 00 04 deadbeef` on
port 443 as a TLS record.
- handshake, alert and change_cipher_spec records are decoded structurally
only when cleartext -- the same discipline for all three, generalised in
SSL._tls_content_is_cleartext. Past the handshake, TLS 1.2's Finished and
any renegotiation handshake/alert/ChangeCipherSpec travel as ciphertext
under the same content type, with nothing in the cleartext header to say
so (an encrypted alert includes the close_notify that ends nearly every
TLS 1.2 connection); TLS 1.3's post-handshake NewSessionTicket/KeyUpdate
are not under content type 22 at all, since TLS 1.3's outer record type
is always application_data once encryption starts. DTLS decodes all
three only at epoch 0 (the epoch counts cipher changes, RFC 9147 §4.2);
TLS has no such field, so handshake falls back to whether the 4-octet
message headers tile the fragment exactly, and alert/change_cipher_spec
to whether the fragment is exactly 2/1 octets -- an encrypted one can
never land on either exactly, given MAC/tag overhead. Otherwise the
octets are kept as-is rather than mis-decoded or dropped to Raw.
- New const registries pcapkit.const.tls.{content_type,handshake_type},
following the generated-shape convention (EnumRegistry + IntEnum). Both
name every value IANA assigns, ContentType through RFC 9853's
return_routability_check(27) and HandshakeType through RFC 9147's
connection-ID messages (9, 10), RFC 8879's compressed_certificate and
RFC 8870's ekt_key.
- TCP port 443 (https) and UDP port 5684 (coaps) are bound to TLS/DTLS;
5684 is not CoAP's only fixed DTLS-borne port, just the one bound here.
- Out of scope, each documented where it bites: reassembling a DTLS
handshake message fragmented across records (#2440), reassembling a TLS
record split across TCP segments (decodes as Raw until the record
arrives whole), and CID-aware offset parsing for tls12_cid records.
Every message shape implemented has a hand-crafted byte fixture proven
byte-exact both ways (parse-then-rebuild and make-then-parse), including a
genuine DTLS ClientHello with a cookie, both encrypted-handshake cases, and
encrypted-alert cases for both protocols (a 26-octet AES-GCM-shaped
close_notify, a 48-octet CBC alert, and a DTLS epoch-1 alert). tls.py and
dtls.py are at 100% statement and branch coverage; ssl.py is at 99%, the
one miss being an unreachable defensive branch. Build/test: mypy reports
nothing on any changed file and isort is clean; pylint shows only the
pre-existing schema-DSL and duplicate-code classes. tests/protocols,
tests/project and tests/const all pass.
Refs #2437
Copy file name to clipboardExpand all lines: CHANGELOG.md
+6Lines changed: 6 additions & 0 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -2,6 +2,12 @@
2
2
3
3
Wave 2 of the roadmap, per the contributing docs' enhancement-proposal page: the protocol pairs that wanted a shared abstract base, and the two that simply had no dissector at all.
4
4
5
+
### pcapkit.protocols
6
+
7
+
#### Added
8
+
9
+
- TLS and DTLS record-layer and handshake-message framing [[RFC 5246](https://datatracker.ietf.org/doc/html/rfc5246), [RFC 9846](https://datatracker.ietf.org/doc/html/rfc9846), [RFC 6347](https://datatracker.ietf.org/doc/html/rfc6347), [RFC 9147](https://datatracker.ietf.org/doc/html/rfc9147)] ([#2437](https://github.com/JarryShaw/PyPCAPKit/issues/2437), [#2441](https://github.com/JarryShaw/PyPCAPKit/pull/2441)). `SSL` is a shared base over the record header and content-type dispatch TLS and DTLS hold in common; `TLS` and `DTLS` are the two concrete subclasses, DTLS alone carrying the epoch/sequence-number pair and the fragment header each handshake message adds. `ClientHello`/`ServerHello` decode to their version, random, session ID, DTLS's `cookie`, cipher suite(s), compression method(s) and extension list (TYPE+LENGTH framed, extension bodies undecoded); every other handshake message is message-TYPE-level framing only. TCP port 443 (`https`) and UDP port 5684 (`coaps`) are bound to TLS and DTLS respectively. Reassembling a handshake message DTLS has fragmented across several records is left to its own follow-up ([#2440](https://github.com/JarryShaw/PyPCAPKit/issues/2440)).
10
+
5
11
---
6
12
7
13
Full changelog: <https://jarryshaw.github.io/PyPCAPKit/changelog.html>
0 commit comments