Skip to content
Closed
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
20 changes: 12 additions & 8 deletions scapy/layers/tls/record_tls13.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,14 +47,18 @@ def pre_dissect(self, s):
if len(s) < 1:
raise Exception("Invalid InnerPlaintext (too short).")

tmp_len = len(s) - 1
if s[-1] != b"\x00":
msg_len = tmp_len
else:
n = 1
while s[-n] != b"\x00" and n < tmp_len:
n += 1
msg_len = tmp_len - n
# RFC 8446 sect. 5.2: an InnerPlaintext is
# content || content_type || zeros(padding)
# so the content type is the last non-zero byte. Scan back over the
# padding to find it; with no padding the loop stops immediately and
# msg_len is len(s) - 1, as before.
n = len(s)
while n > 0 and s[n - 1] == 0:
n -= 1
# All-zero input carries no valid content type (0 is not a ContentType).
# Keep the previous behaviour there rather than raising, so that
# default-constructed packets still round-trip.
msg_len = n - 1 if n > 0 else len(s) - 1
self.fields_desc[0].length_from = lambda pkt: msg_len

self.type = struct.unpack("B", s[msg_len:msg_len + 1])[0]
Expand Down
36 changes: 36 additions & 0 deletions test/scapy/layers/tls/tls13.uts
Original file line number Diff line number Diff line change
Expand Up @@ -1355,3 +1355,39 @@ def parses_share(value):

assert not parses_share(1)
assert parses_share(2)

+ TLS 1.3 InnerPlaintext padding

= InnerPlaintext - the content type is the last non-zero byte

% RFC 8446 sect. 5.2 lays an InnerPlaintext out as
% content || content_type || zeros(padding)
% The type was previously read from the very last byte, so any padded record
% reported content type 0 -- not a valid ContentType -- and the padding was
% swallowed into the message list instead of landing in `pad`.

from scapy.layers.tls.record_tls13 import TLSInnerPlaintext

for pad_len in [1, 3, 16]:
for content_type in [0x15, 0x16, 0x17]:
pkt = TLSInnerPlaintext(b"HELLO" + bytes([content_type]) + b"\x00" * pad_len)
assert pkt.type == content_type, (pad_len, content_type, pkt.type)
assert bytes(pkt.pad) == b"\x00" * pad_len, (pad_len, bytes(pkt.pad))

= InnerPlaintext - an unpadded record is unaffected

from scapy.layers.tls.record_tls13 import TLSInnerPlaintext

pkt = TLSInnerPlaintext(b"HELLO" + b"\x16")
assert pkt.type == 0x16
assert bytes(pkt.pad) == b""

= InnerPlaintext - an all-zero payload still dissects without raising

% Nothing in it is a valid content type, so there is nothing to find; this only
% pins that the scan does not walk off the front of the buffer.

from scapy.layers.tls.record_tls13 import TLSInnerPlaintext

pkt = TLSInnerPlaintext(b"\x00\x00")
assert pkt.type == 0
Loading