1665 lines
70 KiB
Python
1665 lines
70 KiB
Python
|
# This file is part of Scapy
|
||
|
# Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
|
||
|
# 2015, 2016, 2017 Maxence Tury
|
||
|
# 2019 Romain Perez
|
||
|
# This program is published under a GPLv2 license
|
||
|
|
||
|
"""
|
||
|
TLS handshake fields & logic.
|
||
|
|
||
|
This module covers the handshake TLS subprotocol, except for the key exchange
|
||
|
mechanisms which are addressed with keyexchange.py.
|
||
|
"""
|
||
|
|
||
|
from __future__ import absolute_import
|
||
|
import math
|
||
|
import os
|
||
|
import struct
|
||
|
|
||
|
from scapy.error import log_runtime, warning
|
||
|
from scapy.fields import ByteEnumField, ByteField, EnumField, Field, \
|
||
|
FieldLenField, IntField, PacketField, PacketListField, ShortField, \
|
||
|
StrFixedLenField, StrLenField, ThreeBytesField, UTCTimeField
|
||
|
|
||
|
from scapy.compat import hex_bytes, orb, raw
|
||
|
from scapy.config import conf
|
||
|
from scapy.modules import six
|
||
|
from scapy.packet import Packet, Raw, Padding
|
||
|
from scapy.utils import randstring, repr_hex
|
||
|
from scapy.layers.x509 import OCSP_Response
|
||
|
from scapy.layers.tls.cert import Cert
|
||
|
from scapy.layers.tls.basefields import (_tls_version, _TLSVersionField,
|
||
|
_TLSClientVersionField)
|
||
|
from scapy.layers.tls.extensions import (_ExtensionsLenField, _ExtensionsField,
|
||
|
_cert_status_type,
|
||
|
TLS_Ext_SupportedVersion_CH,
|
||
|
TLS_Ext_SignatureAlgorithms,
|
||
|
TLS_Ext_SupportedVersion_SH,
|
||
|
TLS_Ext_EarlyDataIndication,
|
||
|
_tls_hello_retry_magic)
|
||
|
from scapy.layers.tls.keyexchange import (_TLSSignature, _TLSServerParamsField,
|
||
|
_TLSSignatureField, ServerRSAParams,
|
||
|
SigAndHashAlgsField, _tls_hash_sig,
|
||
|
SigAndHashAlgsLenField)
|
||
|
from scapy.layers.tls.session import (_GenericTLSSessionInheritance,
|
||
|
readConnState, writeConnState)
|
||
|
from scapy.layers.tls.keyexchange_tls13 import TLS_Ext_PreSharedKey_CH
|
||
|
from scapy.layers.tls.crypto.compression import (_tls_compression_algs,
|
||
|
_tls_compression_algs_cls,
|
||
|
Comp_NULL, _GenericComp,
|
||
|
_GenericCompMetaclass)
|
||
|
from scapy.layers.tls.crypto.suites import (_tls_cipher_suites,
|
||
|
_tls_cipher_suites_cls,
|
||
|
_GenericCipherSuite,
|
||
|
_GenericCipherSuiteMetaclass)
|
||
|
from scapy.layers.tls.crypto.hkdf import TLS13_HKDF
|
||
|
|
||
|
if conf.crypto_valid:
|
||
|
from cryptography.hazmat.backends import default_backend
|
||
|
from cryptography.hazmat.primitives import hashes
|
||
|
|
||
|
|
||
|
###############################################################################
|
||
|
# Generic TLS Handshake message #
|
||
|
###############################################################################
|
||
|
|
||
|
_tls_handshake_type = {0: "hello_request", 1: "client_hello",
|
||
|
2: "server_hello", 3: "hello_verify_request",
|
||
|
4: "session_ticket", 6: "hello_retry_request",
|
||
|
8: "encrypted_extensions", 11: "certificate",
|
||
|
12: "server_key_exchange", 13: "certificate_request",
|
||
|
14: "server_hello_done", 15: "certificate_verify",
|
||
|
16: "client_key_exchange", 20: "finished",
|
||
|
21: "certificate_url", 22: "certificate_status",
|
||
|
23: "supplemental_data", 24: "key_update"}
|
||
|
|
||
|
|
||
|
class _TLSHandshake(_GenericTLSSessionInheritance):
|
||
|
"""
|
||
|
Inherited by other Handshake classes to get post_build().
|
||
|
Also used as a fallback for unknown TLS Handshake packets.
|
||
|
"""
|
||
|
name = "TLS Handshake Generic message"
|
||
|
fields_desc = [ByteEnumField("msgtype", None, _tls_handshake_type),
|
||
|
ThreeBytesField("msglen", None),
|
||
|
StrLenField("msg", "",
|
||
|
length_from=lambda pkt: pkt.msglen)]
|
||
|
|
||
|
def post_build(self, p, pay):
|
||
|
tmp_len = len(p)
|
||
|
if self.msglen is None:
|
||
|
l2 = tmp_len - 4
|
||
|
p = struct.pack("!I", (orb(p[0]) << 24) | l2) + p[4:]
|
||
|
return p + pay
|
||
|
|
||
|
def guess_payload_class(self, p):
|
||
|
return conf.padding_layer
|
||
|
|
||
|
def tls_session_update(self, msg_str):
|
||
|
"""
|
||
|
Covers both post_build- and post_dissection- context updates.
|
||
|
"""
|
||
|
self.tls_session.handshake_messages.append(msg_str)
|
||
|
self.tls_session.handshake_messages_parsed.append(self)
|
||
|
|
||
|
|
||
|
###############################################################################
|
||
|
# HelloRequest #
|
||
|
###############################################################################
|
||
|
|
||
|
class TLSHelloRequest(_TLSHandshake):
|
||
|
name = "TLS Handshake - Hello Request"
|
||
|
fields_desc = [ByteEnumField("msgtype", 0, _tls_handshake_type),
|
||
|
ThreeBytesField("msglen", None)]
|
||
|
|
||
|
def tls_session_update(self, msg_str):
|
||
|
"""
|
||
|
Message should not be added to the list of handshake messages
|
||
|
that will be hashed in the finished and certificate verify messages.
|
||
|
"""
|
||
|
return
|
||
|
|
||
|
|
||
|
###############################################################################
|
||
|
# ClientHello fields #
|
||
|
###############################################################################
|
||
|
|
||
|
class _GMTUnixTimeField(UTCTimeField):
|
||
|
"""
|
||
|
"The current time and date in standard UNIX 32-bit format (seconds since
|
||
|
the midnight starting Jan 1, 1970, GMT, ignoring leap seconds)."
|
||
|
"""
|
||
|
|
||
|
def i2h(self, pkt, x):
|
||
|
if x is not None:
|
||
|
return x
|
||
|
return 0
|
||
|
|
||
|
|
||
|
class _TLSRandomBytesField(StrFixedLenField):
|
||
|
def i2repr(self, pkt, x):
|
||
|
if x is None:
|
||
|
return repr(x)
|
||
|
return repr_hex(self.i2h(pkt, x))
|
||
|
|
||
|
|
||
|
class _SessionIDField(StrLenField):
|
||
|
"""
|
||
|
opaque SessionID<0..32>; section 7.4.1.2 of RFC 4346
|
||
|
"""
|
||
|
pass
|
||
|
|
||
|
|
||
|
class _CipherSuitesField(StrLenField):
|
||
|
__slots__ = ["itemfmt", "itemsize", "i2s", "s2i"]
|
||
|
islist = 1
|
||
|
|
||
|
def __init__(self, name, default, dico, length_from=None, itemfmt="!H"):
|
||
|
StrLenField.__init__(self, name, default, length_from=length_from)
|
||
|
self.itemfmt = itemfmt
|
||
|
self.itemsize = struct.calcsize(itemfmt)
|
||
|
i2s = self.i2s = {}
|
||
|
s2i = self.s2i = {}
|
||
|
for k in six.iterkeys(dico):
|
||
|
i2s[k] = dico[k]
|
||
|
s2i[dico[k]] = k
|
||
|
|
||
|
def any2i_one(self, pkt, x):
|
||
|
if (isinstance(x, _GenericCipherSuite) or
|
||
|
isinstance(x, _GenericCipherSuiteMetaclass)):
|
||
|
x = x.val
|
||
|
if isinstance(x, bytes):
|
||
|
x = self.s2i[x]
|
||
|
return x
|
||
|
|
||
|
def i2repr_one(self, pkt, x):
|
||
|
fmt = "0x%%0%dx" % self.itemsize
|
||
|
return self.i2s.get(x, fmt % x)
|
||
|
|
||
|
def any2i(self, pkt, x):
|
||
|
if x is None:
|
||
|
return None
|
||
|
if not isinstance(x, list):
|
||
|
x = [x]
|
||
|
return [self.any2i_one(pkt, z) for z in x]
|
||
|
|
||
|
def i2repr(self, pkt, x):
|
||
|
if x is None:
|
||
|
return "None"
|
||
|
tmp_len = [self.i2repr_one(pkt, z) for z in x]
|
||
|
if len(tmp_len) == 1:
|
||
|
tmp_len = tmp_len[0]
|
||
|
else:
|
||
|
tmp_len = "[%s]" % ", ".join(tmp_len)
|
||
|
return tmp_len
|
||
|
|
||
|
def i2m(self, pkt, val):
|
||
|
if val is None:
|
||
|
val = []
|
||
|
return b"".join(struct.pack(self.itemfmt, x) for x in val)
|
||
|
|
||
|
def m2i(self, pkt, m):
|
||
|
res = []
|
||
|
itemlen = struct.calcsize(self.itemfmt)
|
||
|
while m:
|
||
|
res.append(struct.unpack(self.itemfmt, m[:itemlen])[0])
|
||
|
m = m[itemlen:]
|
||
|
return res
|
||
|
|
||
|
def i2len(self, pkt, i):
|
||
|
if i is None:
|
||
|
return 0
|
||
|
return len(i) * self.itemsize
|
||
|
|
||
|
|
||
|
class _CompressionMethodsField(_CipherSuitesField):
|
||
|
|
||
|
def any2i_one(self, pkt, x):
|
||
|
if (isinstance(x, _GenericComp) or
|
||
|
isinstance(x, _GenericCompMetaclass)):
|
||
|
x = x.val
|
||
|
if isinstance(x, str):
|
||
|
x = self.s2i[x]
|
||
|
return x
|
||
|
|
||
|
|
||
|
###############################################################################
|
||
|
# ClientHello #
|
||
|
###############################################################################
|
||
|
|
||
|
class TLSClientHello(_TLSHandshake):
|
||
|
"""
|
||
|
TLS ClientHello, with abilities to handle extensions.
|
||
|
|
||
|
The Random structure follows the RFC 5246: while it is 32-byte long,
|
||
|
many implementations use the first 4 bytes as a gmt_unix_time, and then
|
||
|
the remaining 28 byts should be completely random. This was designed in
|
||
|
order to (sort of) mitigate broken RNGs. If you prefer to show the full
|
||
|
32 random bytes without any GMT time, just comment in/out the lines below.
|
||
|
"""
|
||
|
name = "TLS Handshake - Client Hello"
|
||
|
fields_desc = [ByteEnumField("msgtype", 1, _tls_handshake_type),
|
||
|
ThreeBytesField("msglen", None),
|
||
|
_TLSClientVersionField("version", None, _tls_version),
|
||
|
|
||
|
# _TLSRandomBytesField("random_bytes", None, 32),
|
||
|
_GMTUnixTimeField("gmt_unix_time", None),
|
||
|
_TLSRandomBytesField("random_bytes", None, 28),
|
||
|
|
||
|
FieldLenField("sidlen", None, fmt="B", length_of="sid"),
|
||
|
_SessionIDField("sid", "",
|
||
|
length_from=lambda pkt: pkt.sidlen),
|
||
|
|
||
|
FieldLenField("cipherslen", None, fmt="!H",
|
||
|
length_of="ciphers"),
|
||
|
_CipherSuitesField("ciphers", None,
|
||
|
_tls_cipher_suites, itemfmt="!H",
|
||
|
length_from=lambda pkt: pkt.cipherslen),
|
||
|
|
||
|
FieldLenField("complen", None, fmt="B", length_of="comp"),
|
||
|
_CompressionMethodsField("comp", [0],
|
||
|
_tls_compression_algs,
|
||
|
itemfmt="B",
|
||
|
length_from=lambda pkt: pkt.complen), # noqa: E501
|
||
|
|
||
|
_ExtensionsLenField("extlen", None, length_of="ext"),
|
||
|
_ExtensionsField("ext", None,
|
||
|
length_from=lambda pkt: (pkt.msglen -
|
||
|
(pkt.sidlen or 0) - # noqa: E501
|
||
|
(pkt.cipherslen or 0) - # noqa: E501
|
||
|
(pkt.complen or 0) - # noqa: E501
|
||
|
40))]
|
||
|
|
||
|
def post_build(self, p, pay):
|
||
|
if self.random_bytes is None:
|
||
|
p = p[:10] + randstring(28) + p[10 + 28:]
|
||
|
|
||
|
# if no ciphersuites were provided, we add a few usual, supported
|
||
|
# ciphersuites along with the appropriate extensions
|
||
|
if self.ciphers is None:
|
||
|
cipherstart = 39 + (self.sidlen or 0)
|
||
|
s = b"001ac02bc023c02fc027009e0067009c003cc009c0130033002f000a"
|
||
|
p = p[:cipherstart] + hex_bytes(s) + p[cipherstart + 2:]
|
||
|
if self.ext is None:
|
||
|
ext_len = b'\x00\x2c'
|
||
|
ext_reneg = b'\xff\x01\x00\x01\x00'
|
||
|
ext_sn = b'\x00\x00\x00\x0f\x00\r\x00\x00\nsecdev.org'
|
||
|
ext_sigalg = b'\x00\r\x00\x08\x00\x06\x04\x03\x04\x01\x02\x01'
|
||
|
ext_supgroups = b'\x00\n\x00\x04\x00\x02\x00\x17'
|
||
|
p += ext_len + ext_reneg + ext_sn + ext_sigalg + ext_supgroups
|
||
|
|
||
|
return super(TLSClientHello, self).post_build(p, pay)
|
||
|
|
||
|
def tls_session_update(self, msg_str):
|
||
|
"""
|
||
|
Either for parsing or building, we store the client_random
|
||
|
along with the raw string representing this handshake message.
|
||
|
"""
|
||
|
super(TLSClientHello, self).tls_session_update(msg_str)
|
||
|
s = self.tls_session
|
||
|
s.advertised_tls_version = self.version
|
||
|
# This ClientHello could be a 1.3 one. Let's store the sid
|
||
|
# in all cases
|
||
|
if self.sidlen and self.sidlen > 0:
|
||
|
s.sid = self.sid
|
||
|
self.random_bytes = msg_str[10:38]
|
||
|
s.client_random = (struct.pack('!I', self.gmt_unix_time) +
|
||
|
self.random_bytes)
|
||
|
|
||
|
# No distinction between a TLS 1.2 ClientHello and a TLS
|
||
|
# 1.3 ClientHello when dissecting : TLS 1.3 CH will be
|
||
|
# parsed as TLSClientHello
|
||
|
if self.ext:
|
||
|
for e in self.ext:
|
||
|
if isinstance(e, TLS_Ext_SupportedVersion_CH):
|
||
|
for ver in e.versions:
|
||
|
# RFC 8701: GREASE of TLS will send unknown versions
|
||
|
# here. We have to ignore them
|
||
|
if ver in _tls_version:
|
||
|
s.advertised_tls_version = ver
|
||
|
break
|
||
|
if s.sid:
|
||
|
s.middlebox_compatibility = True
|
||
|
|
||
|
if isinstance(e, TLS_Ext_SignatureAlgorithms):
|
||
|
s.advertised_sig_algs = e.sig_algs
|
||
|
|
||
|
|
||
|
class TLS13ClientHello(_TLSHandshake):
|
||
|
"""
|
||
|
TLS 1.3 ClientHello, with abilities to handle extensions.
|
||
|
|
||
|
The Random structure is 32 random bytes without any GMT time
|
||
|
"""
|
||
|
name = "TLS 1.3 Handshake - Client Hello"
|
||
|
fields_desc = [ByteEnumField("msgtype", 1, _tls_handshake_type),
|
||
|
ThreeBytesField("msglen", None),
|
||
|
_TLSClientVersionField("version", None, _tls_version),
|
||
|
|
||
|
_TLSRandomBytesField("random_bytes", None, 32),
|
||
|
|
||
|
FieldLenField("sidlen", None, fmt="B", length_of="sid"),
|
||
|
_SessionIDField("sid", "",
|
||
|
length_from=lambda pkt: pkt.sidlen),
|
||
|
|
||
|
FieldLenField("cipherslen", None, fmt="!H",
|
||
|
length_of="ciphers"),
|
||
|
_CipherSuitesField("ciphers", None,
|
||
|
_tls_cipher_suites, itemfmt="!H",
|
||
|
length_from=lambda pkt: pkt.cipherslen),
|
||
|
|
||
|
FieldLenField("complen", None, fmt="B", length_of="comp"),
|
||
|
_CompressionMethodsField("comp", [0],
|
||
|
_tls_compression_algs,
|
||
|
itemfmt="B",
|
||
|
length_from=lambda pkt: pkt.complen), # noqa: E501
|
||
|
|
||
|
_ExtensionsLenField("extlen", None, length_of="ext"),
|
||
|
_ExtensionsField("ext", None,
|
||
|
length_from=lambda pkt: (pkt.msglen -
|
||
|
(pkt.sidlen or 0) - # noqa: E501
|
||
|
(pkt.cipherslen or 0) - # noqa: E501
|
||
|
(pkt.complen or 0) - # noqa: E501
|
||
|
40))]
|
||
|
|
||
|
def post_build(self, p, pay):
|
||
|
if self.random_bytes is None:
|
||
|
p = p[:6] + randstring(32) + p[6 + 32:]
|
||
|
# We don't call the post_build function from class _TLSHandshake
|
||
|
# to compute the message length because we need that value now
|
||
|
# for the HMAC in binder
|
||
|
tmp_len = len(p)
|
||
|
if self.msglen is None:
|
||
|
sz = tmp_len - 4
|
||
|
p = struct.pack("!I", (orb(p[0]) << 24) | sz) + p[4:]
|
||
|
s = self.tls_session
|
||
|
if self.ext:
|
||
|
for e in self.ext:
|
||
|
if isinstance(e, TLS_Ext_PreSharedKey_CH):
|
||
|
if s.client_session_ticket:
|
||
|
# For a resumed PSK, the hash function use
|
||
|
# to compute the binder must be the same
|
||
|
# as the one used to establish the original
|
||
|
# conntection. For that, we assume that
|
||
|
# the ciphersuite associate with the ticket
|
||
|
# is given as argument to tlsSession
|
||
|
# (see layers/tls/automaton_cli.py for an
|
||
|
# example)
|
||
|
res_suite = s.tls13_ticket_ciphersuite
|
||
|
cs_cls = _tls_cipher_suites_cls[res_suite]
|
||
|
hkdf = TLS13_HKDF(cs_cls.hash_alg.name.lower())
|
||
|
hash_len = hkdf.hash.digest_size
|
||
|
s.compute_tls13_early_secrets(external=False)
|
||
|
else:
|
||
|
# For out of band PSK, SHA-256 is used as default
|
||
|
# hash functions for HKDF
|
||
|
hkdf = TLS13_HKDF("sha256")
|
||
|
hash_len = hkdf.hash.digest_size
|
||
|
s.compute_tls13_early_secrets(external=True)
|
||
|
|
||
|
# RFC8446 4.2.11.2
|
||
|
# "Each entry in the binders list is computed as an HMAC
|
||
|
# over a transcript hash (see Section 4.4.1) containing a
|
||
|
# partial ClientHello up to and including the
|
||
|
# PreSharedKeyExtension.identities field."
|
||
|
# PSK Binders field is :
|
||
|
# - PSK Binders length (2 bytes)
|
||
|
# - First PSK Binder length (1 byte) +
|
||
|
# HMAC (hash_len bytes)
|
||
|
# The PSK Binder is computed in the same way as the
|
||
|
# Finished message with binder_key as BaseKey
|
||
|
|
||
|
handshake_context = b""
|
||
|
if s.tls13_retry:
|
||
|
for m in s.handshake_messages:
|
||
|
handshake_context += m
|
||
|
handshake_context += p[:-hash_len - 3]
|
||
|
|
||
|
binder_key = s.tls13_derived_secrets["binder_key"]
|
||
|
psk_binder = hkdf.compute_verify_data(binder_key,
|
||
|
handshake_context)
|
||
|
|
||
|
# Here, we replaced the last 32 bytes of the packet by the
|
||
|
# new HMAC values computed over the ClientHello (without
|
||
|
# the binders)
|
||
|
p = p[:-hash_len] + psk_binder
|
||
|
|
||
|
return p + pay
|
||
|
|
||
|
def tls_session_update(self, msg_str):
|
||
|
"""
|
||
|
Either for parsing or building, we store the client_random
|
||
|
along with the raw string representing this handshake message.
|
||
|
"""
|
||
|
super(TLS13ClientHello, self).tls_session_update(msg_str)
|
||
|
s = self.tls_session
|
||
|
|
||
|
if self.sidlen and self.sidlen > 0:
|
||
|
s.sid = self.sid
|
||
|
s.middlebox_compatibility = True
|
||
|
|
||
|
self.random_bytes = msg_str[10:38]
|
||
|
s.client_random = self.random_bytes
|
||
|
if self.ext:
|
||
|
for e in self.ext:
|
||
|
if isinstance(e, TLS_Ext_SupportedVersion_CH):
|
||
|
for ver in e.versions:
|
||
|
# RFC 8701: GREASE of TLS will send unknown versions
|
||
|
# here. We have to ignore them
|
||
|
if ver in _tls_version:
|
||
|
self.tls_session.advertised_tls_version = ver
|
||
|
break
|
||
|
if isinstance(e, TLS_Ext_SignatureAlgorithms):
|
||
|
s.advertised_sig_algs = e.sig_algs
|
||
|
|
||
|
|
||
|
###############################################################################
|
||
|
# ServerHello #
|
||
|
###############################################################################
|
||
|
|
||
|
|
||
|
class TLSServerHello(_TLSHandshake):
|
||
|
"""
|
||
|
TLS ServerHello, with abilities to handle extensions.
|
||
|
|
||
|
The Random structure follows the RFC 5246: while it is 32-byte long,
|
||
|
many implementations use the first 4 bytes as a gmt_unix_time, and then
|
||
|
the remaining 28 byts should be completely random. This was designed in
|
||
|
order to (sort of) mitigate broken RNGs. If you prefer to show the full
|
||
|
32 random bytes without any GMT time, just comment in/out the lines below.
|
||
|
"""
|
||
|
name = "TLS Handshake - Server Hello"
|
||
|
fields_desc = [ByteEnumField("msgtype", 2, _tls_handshake_type),
|
||
|
ThreeBytesField("msglen", None),
|
||
|
_TLSVersionField("version", None, _tls_version),
|
||
|
|
||
|
# _TLSRandomBytesField("random_bytes", None, 32),
|
||
|
_GMTUnixTimeField("gmt_unix_time", None),
|
||
|
_TLSRandomBytesField("random_bytes", None, 28),
|
||
|
|
||
|
FieldLenField("sidlen", None, length_of="sid", fmt="B"),
|
||
|
_SessionIDField("sid", "",
|
||
|
length_from=lambda pkt: pkt.sidlen),
|
||
|
|
||
|
EnumField("cipher", None, _tls_cipher_suites),
|
||
|
_CompressionMethodsField("comp", [0],
|
||
|
_tls_compression_algs,
|
||
|
itemfmt="B",
|
||
|
length_from=lambda pkt: 1),
|
||
|
|
||
|
_ExtensionsLenField("extlen", None, length_of="ext"),
|
||
|
_ExtensionsField("ext", None,
|
||
|
length_from=lambda pkt: (pkt.msglen -
|
||
|
(pkt.sidlen or 0) - # noqa: E501
|
||
|
38))]
|
||
|
# 40)) ]
|
||
|
|
||
|
@classmethod
|
||
|
def dispatch_hook(cls, _pkt=None, *args, **kargs):
|
||
|
if _pkt and len(_pkt) >= 6:
|
||
|
version = struct.unpack("!H", _pkt[4:6])[0]
|
||
|
if version == 0x0304 or version > 0x7f00:
|
||
|
return TLS13ServerHello
|
||
|
return TLSServerHello
|
||
|
|
||
|
def post_build(self, p, pay):
|
||
|
if self.random_bytes is None:
|
||
|
p = p[:10] + randstring(28) + p[10 + 28:]
|
||
|
return super(TLSServerHello, self).post_build(p, pay)
|
||
|
|
||
|
def tls_session_update(self, msg_str):
|
||
|
"""
|
||
|
Either for parsing or building, we store the server_random
|
||
|
along with the raw string representing this handshake message.
|
||
|
We also store the session_id, the cipher suite (if recognized),
|
||
|
the compression method, and finally we instantiate the pending write
|
||
|
and read connection states. Usually they get updated later on in the
|
||
|
negotiation when we learn the session keys, and eventually they
|
||
|
are committed once a ChangeCipherSpec has been sent/received.
|
||
|
"""
|
||
|
super(TLSServerHello, self).tls_session_update(msg_str)
|
||
|
|
||
|
self.tls_session.tls_version = self.version
|
||
|
self.random_bytes = msg_str[10:38]
|
||
|
self.tls_session.server_random = (struct.pack('!I',
|
||
|
self.gmt_unix_time) +
|
||
|
self.random_bytes)
|
||
|
self.tls_session.sid = self.sid
|
||
|
|
||
|
cs_cls = None
|
||
|
if self.cipher:
|
||
|
cs_val = self.cipher
|
||
|
if cs_val not in _tls_cipher_suites_cls:
|
||
|
warning("Unknown cipher suite %d from ServerHello" % cs_val)
|
||
|
# we do not try to set a default nor stop the execution
|
||
|
else:
|
||
|
cs_cls = _tls_cipher_suites_cls[cs_val]
|
||
|
|
||
|
comp_cls = Comp_NULL
|
||
|
if self.comp:
|
||
|
comp_val = self.comp[0]
|
||
|
if comp_val not in _tls_compression_algs_cls:
|
||
|
err = "Unknown compression alg %d from ServerHello" % comp_val
|
||
|
warning(err)
|
||
|
comp_val = 0
|
||
|
comp_cls = _tls_compression_algs_cls[comp_val]
|
||
|
|
||
|
connection_end = self.tls_session.connection_end
|
||
|
self.tls_session.pwcs = writeConnState(ciphersuite=cs_cls,
|
||
|
compression_alg=comp_cls,
|
||
|
connection_end=connection_end,
|
||
|
tls_version=self.version)
|
||
|
self.tls_session.prcs = readConnState(ciphersuite=cs_cls,
|
||
|
compression_alg=comp_cls,
|
||
|
connection_end=connection_end,
|
||
|
tls_version=self.version)
|
||
|
|
||
|
|
||
|
_tls_13_server_hello_fields = [
|
||
|
ByteEnumField("msgtype", 2, _tls_handshake_type),
|
||
|
ThreeBytesField("msglen", None),
|
||
|
_TLSVersionField("version", 0x0303, _tls_version),
|
||
|
_TLSRandomBytesField("random_bytes", None, 32),
|
||
|
FieldLenField("sidlen", None, length_of="sid", fmt="B"),
|
||
|
_SessionIDField("sid", "",
|
||
|
length_from=lambda pkt: pkt.sidlen),
|
||
|
EnumField("cipher", None, _tls_cipher_suites),
|
||
|
_CompressionMethodsField("comp", [0],
|
||
|
_tls_compression_algs,
|
||
|
itemfmt="B",
|
||
|
length_from=lambda pkt: 1),
|
||
|
_ExtensionsLenField("extlen", None, length_of="ext"),
|
||
|
_ExtensionsField("ext", None,
|
||
|
length_from=lambda pkt: (pkt.msglen -
|
||
|
38))
|
||
|
]
|
||
|
|
||
|
|
||
|
class TLS13ServerHello(_TLSHandshake):
|
||
|
""" TLS 1.3 ServerHello """
|
||
|
name = "TLS 1.3 Handshake - Server Hello"
|
||
|
fields_desc = _tls_13_server_hello_fields
|
||
|
|
||
|
# ServerHello and HelloRetryRequest has the same structure and the same
|
||
|
# msgId. We need to check the server_random value to determine which it is.
|
||
|
@classmethod
|
||
|
def dispatch_hook(cls, _pkt=None, *args, **kargs):
|
||
|
if _pkt and len(_pkt) >= 38:
|
||
|
# If SHA-256("HelloRetryRequest") == server_random,
|
||
|
# this message is a HelloRetryRequest
|
||
|
random_bytes = _pkt[6:38]
|
||
|
if random_bytes == _tls_hello_retry_magic:
|
||
|
return TLS13HelloRetryRequest
|
||
|
return TLS13ServerHello
|
||
|
|
||
|
def post_build(self, p, pay):
|
||
|
if self.random_bytes is None:
|
||
|
p = p[:6] + randstring(32) + p[6 + 32:]
|
||
|
return super(TLS13ServerHello, self).post_build(p, pay)
|
||
|
|
||
|
def tls_session_update(self, msg_str):
|
||
|
"""
|
||
|
Either for parsing or building, we store the server_random along with
|
||
|
the raw string representing this handshake message. We also store the
|
||
|
cipher suite (if recognized), and finally we instantiate the write and
|
||
|
read connection states.
|
||
|
"""
|
||
|
super(TLS13ServerHello, self).tls_session_update(msg_str)
|
||
|
|
||
|
s = self.tls_session
|
||
|
if self.ext:
|
||
|
for e in self.ext:
|
||
|
if isinstance(e, TLS_Ext_SupportedVersion_SH):
|
||
|
s.tls_version = e.version
|
||
|
break
|
||
|
s.server_random = self.random_bytes
|
||
|
s.ciphersuite = self.cipher
|
||
|
|
||
|
cs_cls = None
|
||
|
if self.cipher:
|
||
|
cs_val = self.cipher
|
||
|
if cs_val not in _tls_cipher_suites_cls:
|
||
|
warning("Unknown cipher suite %d from ServerHello" % cs_val)
|
||
|
# we do not try to set a default nor stop the execution
|
||
|
else:
|
||
|
cs_cls = _tls_cipher_suites_cls[cs_val]
|
||
|
|
||
|
connection_end = s.connection_end
|
||
|
if connection_end == "server":
|
||
|
s.pwcs = writeConnState(ciphersuite=cs_cls,
|
||
|
connection_end=connection_end,
|
||
|
tls_version=s.tls_version)
|
||
|
|
||
|
if not s.middlebox_compatibility:
|
||
|
s.triggered_pwcs_commit = True
|
||
|
elif connection_end == "client":
|
||
|
|
||
|
s.prcs = readConnState(ciphersuite=cs_cls,
|
||
|
connection_end=connection_end,
|
||
|
tls_version=s.tls_version)
|
||
|
if not s.middlebox_compatibility:
|
||
|
s.triggered_prcs_commit = True
|
||
|
|
||
|
if s.tls13_early_secret is None:
|
||
|
# In case the connState was not pre-initialized, we could not
|
||
|
# compute the early secrets at the ClientHello, so we do it here.
|
||
|
s.compute_tls13_early_secrets()
|
||
|
s.compute_tls13_handshake_secrets()
|
||
|
if connection_end == "server":
|
||
|
shts = s.tls13_derived_secrets["server_handshake_traffic_secret"]
|
||
|
s.pwcs.tls13_derive_keys(shts)
|
||
|
elif connection_end == "client":
|
||
|
shts = s.tls13_derived_secrets["server_handshake_traffic_secret"]
|
||
|
s.prcs.tls13_derive_keys(shts)
|
||
|
|
||
|
|
||
|
###############################################################################
|
||
|
# HelloRetryRequest #
|
||
|
###############################################################################
|
||
|
|
||
|
class TLS13HelloRetryRequest(_TLSHandshake):
|
||
|
name = "TLS 1.3 Handshake - Hello Retry Request"
|
||
|
|
||
|
fields_desc = _tls_13_server_hello_fields
|
||
|
|
||
|
def build(self):
|
||
|
fval = self.getfieldval("random_bytes")
|
||
|
if fval is None:
|
||
|
self.random_bytes = _tls_hello_retry_magic
|
||
|
return _TLSHandshake.build(self)
|
||
|
|
||
|
def tls_session_update(self, msg_str):
|
||
|
s = self.tls_session
|
||
|
s.tls13_retry = True
|
||
|
s.tls13_client_pubshares = {}
|
||
|
# If the server responds to a ClientHello with a HelloRetryRequest
|
||
|
# The value of the first ClientHello is replaced by a message_hash
|
||
|
if s.client_session_ticket:
|
||
|
cs_cls = _tls_cipher_suites_cls[s.tls13_ticket_ciphersuite]
|
||
|
hkdf = TLS13_HKDF(cs_cls.hash_alg.name.lower())
|
||
|
hash_len = hkdf.hash.digest_size
|
||
|
else:
|
||
|
cs_cls = _tls_cipher_suites_cls[self.cipher]
|
||
|
hkdf = TLS13_HKDF(cs_cls.hash_alg.name.lower())
|
||
|
hash_len = hkdf.hash.digest_size
|
||
|
|
||
|
handshake_context = struct.pack("B", 254)
|
||
|
handshake_context += struct.pack("B", 0)
|
||
|
handshake_context += struct.pack("B", 0)
|
||
|
handshake_context += struct.pack("B", hash_len)
|
||
|
digest = hashes.Hash(hkdf.hash, backend=default_backend())
|
||
|
digest.update(s.handshake_messages[0])
|
||
|
handshake_context += digest.finalize()
|
||
|
s.handshake_messages[0] = handshake_context
|
||
|
super(TLS13HelloRetryRequest, self).tls_session_update(msg_str)
|
||
|
|
||
|
|
||
|
###############################################################################
|
||
|
# EncryptedExtensions #
|
||
|
###############################################################################
|
||
|
|
||
|
|
||
|
class TLSEncryptedExtensions(_TLSHandshake):
|
||
|
name = "TLS 1.3 Handshake - Encrypted Extensions"
|
||
|
fields_desc = [ByteEnumField("msgtype", 8, _tls_handshake_type),
|
||
|
ThreeBytesField("msglen", None),
|
||
|
_ExtensionsLenField("extlen", None, length_of="ext"),
|
||
|
_ExtensionsField("ext", None,
|
||
|
length_from=lambda pkt: pkt.msglen - 2)]
|
||
|
|
||
|
def post_build_tls_session_update(self, msg_str):
|
||
|
self.tls_session_update(msg_str)
|
||
|
|
||
|
s = self.tls_session
|
||
|
connection_end = s.connection_end
|
||
|
|
||
|
# Check if the server early_data extension is present in
|
||
|
# EncryptedExtensions message (if so, early data was accepted by the
|
||
|
# server)
|
||
|
early_data_accepted = False
|
||
|
if self.ext:
|
||
|
for e in self.ext:
|
||
|
if isinstance(e, TLS_Ext_EarlyDataIndication):
|
||
|
early_data_accepted = True
|
||
|
|
||
|
# If the serveur did not accept early_data, we change prcs traffic
|
||
|
# encryption keys. Otherwise, the the keys will be updated after the
|
||
|
# EndOfEarlyData message
|
||
|
if connection_end == "server":
|
||
|
if not early_data_accepted:
|
||
|
s.prcs = readConnState(ciphersuite=type(s.wcs.ciphersuite),
|
||
|
connection_end=connection_end,
|
||
|
tls_version=s.tls_version)
|
||
|
|
||
|
chts = s.tls13_derived_secrets["client_handshake_traffic_secret"] # noqa: E501
|
||
|
s.prcs.tls13_derive_keys(chts)
|
||
|
|
||
|
if not s.middlebox_compatibility:
|
||
|
s.rcs = self.tls_session.prcs
|
||
|
s.triggered_prcs_commit = False
|
||
|
else:
|
||
|
s.triggered_prcs_commit = True
|
||
|
|
||
|
def post_dissection_tls_session_update(self, msg_str):
|
||
|
self.tls_session_update(msg_str)
|
||
|
s = self.tls_session
|
||
|
connection_end = s.connection_end
|
||
|
|
||
|
# Check if the server early_data extension is present in
|
||
|
# EncryptedExtensions message (if so, early data was accepted by the
|
||
|
# server)
|
||
|
early_data_accepted = False
|
||
|
if self.ext:
|
||
|
for e in self.ext:
|
||
|
if isinstance(e, TLS_Ext_EarlyDataIndication):
|
||
|
early_data_accepted = True
|
||
|
|
||
|
# If the serveur did not accept early_data, we change pwcs traffic
|
||
|
# encryption key. Otherwise, the the keys will be updated after the
|
||
|
# EndOfEarlyData message
|
||
|
if connection_end == "client":
|
||
|
if not early_data_accepted:
|
||
|
s.pwcs = writeConnState(ciphersuite=type(s.rcs.ciphersuite),
|
||
|
connection_end=connection_end,
|
||
|
tls_version=s.tls_version)
|
||
|
chts = s.tls13_derived_secrets["client_handshake_traffic_secret"] # noqa: E501
|
||
|
s.pwcs.tls13_derive_keys(chts)
|
||
|
if not s.middlebox_compatibility:
|
||
|
s.wcs = self.tls_session.pwcs
|
||
|
s.triggered_pwcs_commit = False
|
||
|
else:
|
||
|
s.triggered_prcs_commit = True
|
||
|
###############################################################################
|
||
|
# Certificate #
|
||
|
###############################################################################
|
||
|
|
||
|
# XXX It might be appropriate to rewrite this mess with basic 3-byte FieldLenField. # noqa: E501
|
||
|
|
||
|
|
||
|
class _ASN1CertLenField(FieldLenField):
|
||
|
"""
|
||
|
This is mostly a 3-byte FieldLenField.
|
||
|
"""
|
||
|
def __init__(self, name, default, length_of=None, adjust=lambda pkt, x: x):
|
||
|
self.length_of = length_of
|
||
|
self.adjust = adjust
|
||
|
Field.__init__(self, name, default, fmt="!I")
|
||
|
|
||
|
def i2m(self, pkt, x):
|
||
|
if x is None:
|
||
|
if self.length_of is not None:
|
||
|
fld, fval = pkt.getfield_and_val(self.length_of)
|
||
|
f = fld.i2len(pkt, fval)
|
||
|
x = self.adjust(pkt, f)
|
||
|
return x
|
||
|
|
||
|
def addfield(self, pkt, s, val):
|
||
|
return s + struct.pack(self.fmt, self.i2m(pkt, val))[1:4]
|
||
|
|
||
|
def getfield(self, pkt, s):
|
||
|
return s[3:], self.m2i(pkt, struct.unpack(self.fmt, b"\x00" + s[:3])[0]) # noqa: E501
|
||
|
|
||
|
|
||
|
class _ASN1CertListField(StrLenField):
|
||
|
islist = 1
|
||
|
|
||
|
def i2len(self, pkt, i):
|
||
|
if i is None:
|
||
|
return 0
|
||
|
return len(self.i2m(pkt, i))
|
||
|
|
||
|
def getfield(self, pkt, s):
|
||
|
"""
|
||
|
Extract Certs in a loop.
|
||
|
XXX We should provide safeguards when trying to parse a Cert.
|
||
|
"""
|
||
|
tmp_len = None
|
||
|
if self.length_from is not None:
|
||
|
tmp_len = self.length_from(pkt)
|
||
|
|
||
|
lst = []
|
||
|
ret = b""
|
||
|
m = s
|
||
|
if tmp_len is not None:
|
||
|
m, ret = s[:tmp_len], s[tmp_len:]
|
||
|
while m:
|
||
|
clen = struct.unpack("!I", b'\x00' + m[:3])[0]
|
||
|
lst.append((clen, Cert(m[3:3 + clen])))
|
||
|
m = m[3 + clen:]
|
||
|
return m + ret, lst
|
||
|
|
||
|
def i2m(self, pkt, i):
|
||
|
def i2m_one(i):
|
||
|
if isinstance(i, str):
|
||
|
return i
|
||
|
if isinstance(i, Cert):
|
||
|
s = i.der
|
||
|
tmp_len = struct.pack("!I", len(s))[1:4]
|
||
|
return tmp_len + s
|
||
|
|
||
|
(tmp_len, s) = i
|
||
|
if isinstance(s, Cert):
|
||
|
s = s.der
|
||
|
return struct.pack("!I", tmp_len)[1:4] + s
|
||
|
|
||
|
if i is None:
|
||
|
return b""
|
||
|
if isinstance(i, str):
|
||
|
return i
|
||
|
if isinstance(i, Cert):
|
||
|
i = [i]
|
||
|
return b"".join(i2m_one(x) for x in i)
|
||
|
|
||
|
def any2i(self, pkt, x):
|
||
|
return x
|
||
|
|
||
|
|
||
|
class _ASN1CertField(StrLenField):
|
||
|
def i2len(self, pkt, i):
|
||
|
if i is None:
|
||
|
return 0
|
||
|
return len(self.i2m(pkt, i))
|
||
|
|
||
|
def getfield(self, pkt, s):
|
||
|
tmp_len = None
|
||
|
if self.length_from is not None:
|
||
|
tmp_len = self.length_from(pkt)
|
||
|
ret = b""
|
||
|
m = s
|
||
|
if tmp_len is not None:
|
||
|
m, ret = s[:tmp_len], s[tmp_len:]
|
||
|
clen = struct.unpack("!I", b'\x00' + m[:3])[0]
|
||
|
len_cert = (clen, Cert(m[3:3 + clen]))
|
||
|
m = m[3 + clen:]
|
||
|
return m + ret, len_cert
|
||
|
|
||
|
def i2m(self, pkt, i):
|
||
|
def i2m_one(i):
|
||
|
if isinstance(i, str):
|
||
|
return i
|
||
|
if isinstance(i, Cert):
|
||
|
s = i.der
|
||
|
tmp_len = struct.pack("!I", len(s))[1:4]
|
||
|
return tmp_len + s
|
||
|
|
||
|
(tmp_len, s) = i
|
||
|
if isinstance(s, Cert):
|
||
|
s = s.der
|
||
|
return struct.pack("!I", tmp_len)[1:4] + s
|
||
|
|
||
|
if i is None:
|
||
|
return b""
|
||
|
return i2m_one(i)
|
||
|
|
||
|
def any2i(self, pkt, x):
|
||
|
return x
|
||
|
|
||
|
|
||
|
class TLSCertificate(_TLSHandshake):
|
||
|
"""
|
||
|
XXX We do not support RFC 5081, i.e. OpenPGP certificates.
|
||
|
"""
|
||
|
name = "TLS Handshake - Certificate"
|
||
|
fields_desc = [ByteEnumField("msgtype", 11, _tls_handshake_type),
|
||
|
ThreeBytesField("msglen", None),
|
||
|
_ASN1CertLenField("certslen", None, length_of="certs"),
|
||
|
_ASN1CertListField("certs", [],
|
||
|
length_from=lambda pkt: pkt.certslen)]
|
||
|
|
||
|
@classmethod
|
||
|
def dispatch_hook(cls, _pkt=None, *args, **kargs):
|
||
|
if _pkt:
|
||
|
tls_session = kargs.get("tls_session", None)
|
||
|
if tls_session and (tls_session.tls_version or 0) >= 0x0304:
|
||
|
return TLS13Certificate
|
||
|
return TLSCertificate
|
||
|
|
||
|
def post_dissection_tls_session_update(self, msg_str):
|
||
|
self.tls_session_update(msg_str)
|
||
|
connection_end = self.tls_session.connection_end
|
||
|
if connection_end == "client":
|
||
|
self.tls_session.server_certs = [x[1] for x in self.certs]
|
||
|
else:
|
||
|
self.tls_session.client_certs = [x[1] for x in self.certs]
|
||
|
|
||
|
|
||
|
class _ASN1CertAndExt(_GenericTLSSessionInheritance):
|
||
|
name = "Certificate and Extensions"
|
||
|
fields_desc = [_ASN1CertField("cert", ""),
|
||
|
FieldLenField("extlen", None, length_of="ext"),
|
||
|
_ExtensionsField("ext", [],
|
||
|
length_from=lambda pkt: pkt.extlen)]
|
||
|
|
||
|
def extract_padding(self, s):
|
||
|
return b"", s
|
||
|
|
||
|
|
||
|
class _ASN1CertAndExtListField(PacketListField):
|
||
|
def m2i(self, pkt, m):
|
||
|
return self.cls(m, tls_session=pkt.tls_session)
|
||
|
|
||
|
|
||
|
class TLS13Certificate(_TLSHandshake):
|
||
|
name = "TLS 1.3 Handshake - Certificate"
|
||
|
fields_desc = [ByteEnumField("msgtype", 11, _tls_handshake_type),
|
||
|
ThreeBytesField("msglen", None),
|
||
|
FieldLenField("cert_req_ctxt_len", None, fmt="B",
|
||
|
length_of="cert_req_ctxt"),
|
||
|
StrLenField("cert_req_ctxt", "",
|
||
|
length_from=lambda pkt: pkt.cert_req_ctxt_len),
|
||
|
_ASN1CertLenField("certslen", None, length_of="certs"),
|
||
|
_ASN1CertAndExtListField("certs", [], _ASN1CertAndExt,
|
||
|
length_from=lambda pkt: pkt.certslen)] # noqa: E501
|
||
|
|
||
|
def post_dissection_tls_session_update(self, msg_str):
|
||
|
self.tls_session_update(msg_str)
|
||
|
connection_end = self.tls_session.connection_end
|
||
|
if connection_end == "client":
|
||
|
if self.certs:
|
||
|
sc = [x.cert[1] for x in self.certs]
|
||
|
self.tls_session.server_certs = sc
|
||
|
else:
|
||
|
if self.certs:
|
||
|
cc = [x.cert[1] for x in self.certs]
|
||
|
self.tls_session.client_certs = cc
|
||
|
|
||
|
|
||
|
###############################################################################
|
||
|
# ServerKeyExchange #
|
||
|
###############################################################################
|
||
|
|
||
|
class TLSServerKeyExchange(_TLSHandshake):
|
||
|
name = "TLS Handshake - Server Key Exchange"
|
||
|
fields_desc = [ByteEnumField("msgtype", 12, _tls_handshake_type),
|
||
|
ThreeBytesField("msglen", None),
|
||
|
_TLSServerParamsField("params", None,
|
||
|
length_from=lambda pkt: pkt.msglen),
|
||
|
_TLSSignatureField("sig", None,
|
||
|
length_from=lambda pkt: pkt.msglen - len(pkt.params))] # noqa: E501
|
||
|
|
||
|
def build(self, *args, **kargs):
|
||
|
r"""
|
||
|
We overload build() method in order to provide a valid default value
|
||
|
for params based on TLS session if not provided. This cannot be done by
|
||
|
overriding i2m() because the method is called on a copy of the packet.
|
||
|
|
||
|
The 'params' field is built according to key_exchange.server_kx_msg_cls
|
||
|
which should have been set after receiving a cipher suite in a
|
||
|
previous ServerHello. Usual cases are:
|
||
|
|
||
|
- None: for RSA encryption or fixed FF/ECDH. This should never happen,
|
||
|
as no ServerKeyExchange should be generated in the first place.
|
||
|
- ServerDHParams: for ephemeral FFDH. In that case, the parameter to
|
||
|
server_kx_msg_cls does not matter.
|
||
|
- ServerECDH\*Params: for ephemeral ECDH. There are actually three
|
||
|
classes, which are dispatched by _tls_server_ecdh_cls_guess on
|
||
|
the first byte retrieved. The default here is b"\03", which
|
||
|
corresponds to ServerECDHNamedCurveParams (implicit curves).
|
||
|
|
||
|
When the Server\*DHParams are built via .fill_missing(), the session
|
||
|
server_kx_privkey will be updated accordingly.
|
||
|
"""
|
||
|
fval = self.getfieldval("params")
|
||
|
if fval is None:
|
||
|
s = self.tls_session
|
||
|
if s.pwcs:
|
||
|
if s.pwcs.key_exchange.export:
|
||
|
cls = ServerRSAParams(tls_session=s)
|
||
|
else:
|
||
|
cls = s.pwcs.key_exchange.server_kx_msg_cls(b"\x03")
|
||
|
cls = cls(tls_session=s)
|
||
|
try:
|
||
|
cls.fill_missing()
|
||
|
except Exception:
|
||
|
if conf.debug_dissector:
|
||
|
raise
|
||
|
else:
|
||
|
cls = Raw()
|
||
|
self.params = cls
|
||
|
|
||
|
fval = self.getfieldval("sig")
|
||
|
if fval is None:
|
||
|
s = self.tls_session
|
||
|
if s.pwcs:
|
||
|
if not s.pwcs.key_exchange.anonymous:
|
||
|
p = self.params
|
||
|
if p is None:
|
||
|
p = b""
|
||
|
m = s.client_random + s.server_random + raw(p)
|
||
|
cls = _TLSSignature(tls_session=s)
|
||
|
cls._update_sig(m, s.server_key)
|
||
|
else:
|
||
|
cls = Raw()
|
||
|
else:
|
||
|
cls = Raw()
|
||
|
self.sig = cls
|
||
|
|
||
|
return _TLSHandshake.build(self, *args, **kargs)
|
||
|
|
||
|
def post_dissection(self, pkt):
|
||
|
"""
|
||
|
While previously dissecting Server*DHParams, the session
|
||
|
server_kx_pubkey should have been updated.
|
||
|
|
||
|
XXX Add a 'fixed_dh' OR condition to the 'anonymous' test.
|
||
|
"""
|
||
|
s = self.tls_session
|
||
|
if s.prcs and s.prcs.key_exchange.no_ske:
|
||
|
pkt_info = pkt.firstlayer().summary()
|
||
|
log_runtime.info("TLS: useless ServerKeyExchange [%s]", pkt_info)
|
||
|
if (s.prcs and
|
||
|
not s.prcs.key_exchange.anonymous and
|
||
|
s.client_random and s.server_random and
|
||
|
s.server_certs and len(s.server_certs) > 0):
|
||
|
m = s.client_random + s.server_random + raw(self.params)
|
||
|
sig_test = self.sig._verify_sig(m, s.server_certs[0])
|
||
|
if not sig_test:
|
||
|
pkt_info = pkt.firstlayer().summary()
|
||
|
log_runtime.info("TLS: invalid ServerKeyExchange signature [%s]", pkt_info) # noqa: E501
|
||
|
|
||
|
|
||
|
###############################################################################
|
||
|
# CertificateRequest #
|
||
|
###############################################################################
|
||
|
|
||
|
_tls_client_certificate_types = {1: "rsa_sign",
|
||
|
2: "dss_sign",
|
||
|
3: "rsa_fixed_dh",
|
||
|
4: "dss_fixed_dh",
|
||
|
5: "rsa_ephemeral_dh_RESERVED",
|
||
|
6: "dss_ephemeral_dh_RESERVED",
|
||
|
20: "fortezza_dms_RESERVED",
|
||
|
64: "ecdsa_sign",
|
||
|
65: "rsa_fixed_ecdh",
|
||
|
66: "ecdsa_fixed_ecdh"}
|
||
|
|
||
|
|
||
|
class _CertTypesField(_CipherSuitesField):
|
||
|
pass
|
||
|
|
||
|
|
||
|
class _CertAuthoritiesField(StrLenField):
|
||
|
"""
|
||
|
XXX Rework this with proper ASN.1 parsing.
|
||
|
"""
|
||
|
islist = 1
|
||
|
|
||
|
def getfield(self, pkt, s):
|
||
|
tmp_len = self.length_from(pkt)
|
||
|
return s[tmp_len:], self.m2i(pkt, s[:tmp_len])
|
||
|
|
||
|
def m2i(self, pkt, m):
|
||
|
res = []
|
||
|
while len(m) > 1:
|
||
|
tmp_len = struct.unpack("!H", m[:2])[0]
|
||
|
if len(m) < tmp_len + 2:
|
||
|
res.append((tmp_len, m[2:]))
|
||
|
break
|
||
|
dn = m[2:2 + tmp_len]
|
||
|
res.append((tmp_len, dn))
|
||
|
m = m[2 + tmp_len:]
|
||
|
return res
|
||
|
|
||
|
def i2m(self, pkt, i):
|
||
|
return b"".join(map(lambda x_y: struct.pack("!H", x_y[0]) + x_y[1], i))
|
||
|
|
||
|
def addfield(self, pkt, s, val):
|
||
|
return s + self.i2m(pkt, val)
|
||
|
|
||
|
def i2len(self, pkt, val):
|
||
|
if val is None:
|
||
|
return 0
|
||
|
else:
|
||
|
return len(self.i2m(pkt, val))
|
||
|
|
||
|
|
||
|
class TLSCertificateRequest(_TLSHandshake):
|
||
|
name = "TLS Handshake - Certificate Request"
|
||
|
fields_desc = [ByteEnumField("msgtype", 13, _tls_handshake_type),
|
||
|
ThreeBytesField("msglen", None),
|
||
|
FieldLenField("ctypeslen", None, fmt="B",
|
||
|
length_of="ctypes"),
|
||
|
_CertTypesField("ctypes", [1, 64],
|
||
|
_tls_client_certificate_types,
|
||
|
itemfmt="!B",
|
||
|
length_from=lambda pkt: pkt.ctypeslen),
|
||
|
SigAndHashAlgsLenField("sig_algs_len", None,
|
||
|
length_of="sig_algs"),
|
||
|
SigAndHashAlgsField("sig_algs", [0x0403, 0x0401, 0x0201],
|
||
|
EnumField("hash_sig", None, _tls_hash_sig), # noqa: E501
|
||
|
length_from=lambda pkt: pkt.sig_algs_len), # noqa: E501
|
||
|
FieldLenField("certauthlen", None, fmt="!H",
|
||
|
length_of="certauth"),
|
||
|
_CertAuthoritiesField("certauth", [],
|
||
|
length_from=lambda pkt: pkt.certauthlen)] # noqa: E501
|
||
|
|
||
|
|
||
|
class TLS13CertificateRequest(_TLSHandshake):
|
||
|
name = "TLS 1.3 Handshake - Certificate Request"
|
||
|
fields_desc = [ByteEnumField("msgtype", 13, _tls_handshake_type),
|
||
|
ThreeBytesField("msglen", None),
|
||
|
FieldLenField("cert_req_ctxt_len", None, fmt="B",
|
||
|
length_of="cert_req_ctxt"),
|
||
|
StrLenField("cert_req_ctxt", "",
|
||
|
length_from=lambda pkt: pkt.cert_req_ctxt_len),
|
||
|
_ExtensionsLenField("extlen", None, length_of="ext"),
|
||
|
_ExtensionsField("ext", None,
|
||
|
length_from=lambda pkt: pkt.msglen -
|
||
|
pkt.cert_req_ctxt_len - 3)]
|
||
|
|
||
|
###############################################################################
|
||
|
# ServerHelloDone #
|
||
|
###############################################################################
|
||
|
|
||
|
|
||
|
class TLSServerHelloDone(_TLSHandshake):
|
||
|
name = "TLS Handshake - Server Hello Done"
|
||
|
fields_desc = [ByteEnumField("msgtype", 14, _tls_handshake_type),
|
||
|
ThreeBytesField("msglen", None)]
|
||
|
|
||
|
|
||
|
###############################################################################
|
||
|
# CertificateVerify #
|
||
|
###############################################################################
|
||
|
|
||
|
class TLSCertificateVerify(_TLSHandshake):
|
||
|
name = "TLS Handshake - Certificate Verify"
|
||
|
fields_desc = [ByteEnumField("msgtype", 15, _tls_handshake_type),
|
||
|
ThreeBytesField("msglen", None),
|
||
|
_TLSSignatureField("sig", None,
|
||
|
length_from=lambda pkt: pkt.msglen)]
|
||
|
|
||
|
def build(self, *args, **kargs):
|
||
|
sig = self.getfieldval("sig")
|
||
|
if sig is None:
|
||
|
s = self.tls_session
|
||
|
m = b"".join(s.handshake_messages)
|
||
|
tls_version = s.tls_version
|
||
|
if tls_version is None:
|
||
|
tls_version = s.advertised_tls_version
|
||
|
if tls_version >= 0x0304:
|
||
|
if s.connection_end == "client":
|
||
|
context_string = b"TLS 1.3, client CertificateVerify"
|
||
|
elif s.connection_end == "server":
|
||
|
context_string = b"TLS 1.3, server CertificateVerify"
|
||
|
m = b"\x20" * 64 + context_string + b"\x00" + s.wcs.hash.digest(m) # noqa: E501
|
||
|
self.sig = _TLSSignature(tls_session=s)
|
||
|
if s.connection_end == "client":
|
||
|
self.sig._update_sig(m, s.client_key)
|
||
|
elif s.connection_end == "server":
|
||
|
# should be TLS 1.3 only
|
||
|
self.sig._update_sig(m, s.server_key)
|
||
|
return _TLSHandshake.build(self, *args, **kargs)
|
||
|
|
||
|
def post_dissection(self, pkt):
|
||
|
s = self.tls_session
|
||
|
m = b"".join(s.handshake_messages)
|
||
|
tls_version = s.tls_version
|
||
|
if tls_version is None:
|
||
|
tls_version = s.advertised_tls_version
|
||
|
if tls_version >= 0x0304:
|
||
|
if s.connection_end == "client":
|
||
|
context_string = b"TLS 1.3, server CertificateVerify"
|
||
|
elif s.connection_end == "server":
|
||
|
context_string = b"TLS 1.3, client CertificateVerify"
|
||
|
m = b"\x20" * 64 + context_string + b"\x00" + s.rcs.hash.digest(m)
|
||
|
|
||
|
if s.connection_end == "server":
|
||
|
if s.client_certs and len(s.client_certs) > 0:
|
||
|
sig_test = self.sig._verify_sig(m, s.client_certs[0])
|
||
|
if not sig_test:
|
||
|
pkt_info = pkt.firstlayer().summary()
|
||
|
log_runtime.info("TLS: invalid CertificateVerify signature [%s]", pkt_info) # noqa: E501
|
||
|
elif s.connection_end == "client":
|
||
|
# should be TLS 1.3 only
|
||
|
if s.server_certs and len(s.server_certs) > 0:
|
||
|
sig_test = self.sig._verify_sig(m, s.server_certs[0])
|
||
|
if not sig_test:
|
||
|
pkt_info = pkt.firstlayer().summary()
|
||
|
log_runtime.info("TLS: invalid CertificateVerify signature [%s]", pkt_info) # noqa: E501
|
||
|
|
||
|
|
||
|
###############################################################################
|
||
|
# ClientKeyExchange #
|
||
|
###############################################################################
|
||
|
|
||
|
class _TLSCKExchKeysField(PacketField):
|
||
|
__slots__ = ["length_from"]
|
||
|
holds_packet = 1
|
||
|
|
||
|
def __init__(self, name, length_from=None, remain=0):
|
||
|
self.length_from = length_from
|
||
|
PacketField.__init__(self, name, None, None, remain=remain)
|
||
|
|
||
|
def m2i(self, pkt, m):
|
||
|
"""
|
||
|
The client_kx_msg may be either None, EncryptedPreMasterSecret
|
||
|
(for RSA encryption key exchange), ClientDiffieHellmanPublic,
|
||
|
or ClientECDiffieHellmanPublic. When either one of them gets
|
||
|
dissected, the session context is updated accordingly.
|
||
|
"""
|
||
|
tmp_len = self.length_from(pkt)
|
||
|
tbd, rem = m[:tmp_len], m[tmp_len:]
|
||
|
|
||
|
s = pkt.tls_session
|
||
|
cls = None
|
||
|
|
||
|
if s.prcs and s.prcs.key_exchange:
|
||
|
cls = s.prcs.key_exchange.client_kx_msg_cls
|
||
|
|
||
|
if cls is None:
|
||
|
return Raw(tbd) / Padding(rem)
|
||
|
|
||
|
return cls(tbd, tls_session=s) / Padding(rem)
|
||
|
|
||
|
|
||
|
class TLSClientKeyExchange(_TLSHandshake):
|
||
|
"""
|
||
|
This class mostly works like TLSServerKeyExchange and its 'params' field.
|
||
|
"""
|
||
|
name = "TLS Handshake - Client Key Exchange"
|
||
|
fields_desc = [ByteEnumField("msgtype", 16, _tls_handshake_type),
|
||
|
ThreeBytesField("msglen", None),
|
||
|
_TLSCKExchKeysField("exchkeys",
|
||
|
length_from=lambda pkt: pkt.msglen)]
|
||
|
|
||
|
def build(self, *args, **kargs):
|
||
|
fval = self.getfieldval("exchkeys")
|
||
|
if fval is None:
|
||
|
s = self.tls_session
|
||
|
if s.prcs:
|
||
|
cls = s.prcs.key_exchange.client_kx_msg_cls
|
||
|
cls = cls(tls_session=s)
|
||
|
else:
|
||
|
cls = Raw()
|
||
|
self.exchkeys = cls
|
||
|
return _TLSHandshake.build(self, *args, **kargs)
|
||
|
|
||
|
|
||
|
###############################################################################
|
||
|
# Finished #
|
||
|
###############################################################################
|
||
|
|
||
|
class _VerifyDataField(StrLenField):
|
||
|
def getfield(self, pkt, s):
|
||
|
if pkt.tls_session.tls_version == 0x0300:
|
||
|
sep = 36
|
||
|
elif pkt.tls_session.tls_version >= 0x0304:
|
||
|
sep = pkt.tls_session.rcs.hash.hash_len
|
||
|
else:
|
||
|
sep = 12
|
||
|
return s[sep:], s[:sep]
|
||
|
|
||
|
|
||
|
class TLSFinished(_TLSHandshake):
|
||
|
name = "TLS Handshake - Finished"
|
||
|
fields_desc = [ByteEnumField("msgtype", 20, _tls_handshake_type),
|
||
|
ThreeBytesField("msglen", None),
|
||
|
_VerifyDataField("vdata", None)]
|
||
|
|
||
|
def build(self, *args, **kargs):
|
||
|
fval = self.getfieldval("vdata")
|
||
|
if fval is None:
|
||
|
s = self.tls_session
|
||
|
handshake_msg = b"".join(s.handshake_messages)
|
||
|
con_end = s.connection_end
|
||
|
tls_version = s.tls_version
|
||
|
if tls_version is None:
|
||
|
tls_version = s.advertised_tls_version
|
||
|
if tls_version < 0x0304:
|
||
|
ms = s.master_secret
|
||
|
self.vdata = s.wcs.prf.compute_verify_data(con_end, "write",
|
||
|
handshake_msg, ms)
|
||
|
else:
|
||
|
self.vdata = s.compute_tls13_verify_data(con_end, "write")
|
||
|
return _TLSHandshake.build(self, *args, **kargs)
|
||
|
|
||
|
def post_dissection(self, pkt):
|
||
|
s = self.tls_session
|
||
|
if not s.frozen:
|
||
|
handshake_msg = b"".join(s.handshake_messages)
|
||
|
tls_version = s.tls_version
|
||
|
if tls_version is None:
|
||
|
tls_version = s.advertised_tls_version
|
||
|
if tls_version < 0x0304 and s.master_secret is not None:
|
||
|
ms = s.master_secret
|
||
|
con_end = s.connection_end
|
||
|
verify_data = s.rcs.prf.compute_verify_data(con_end, "read",
|
||
|
handshake_msg, ms)
|
||
|
if self.vdata != verify_data:
|
||
|
pkt_info = pkt.firstlayer().summary()
|
||
|
log_runtime.info("TLS: invalid Finished received [%s]", pkt_info) # noqa: E501
|
||
|
elif tls_version >= 0x0304:
|
||
|
con_end = s.connection_end
|
||
|
verify_data = s.compute_tls13_verify_data(con_end, "read")
|
||
|
if self.vdata != verify_data:
|
||
|
pkt_info = pkt.firstlayer().summary()
|
||
|
log_runtime.info("TLS: invalid Finished received [%s]", pkt_info) # noqa: E501
|
||
|
|
||
|
def post_build_tls_session_update(self, msg_str):
|
||
|
self.tls_session_update(msg_str)
|
||
|
s = self.tls_session
|
||
|
tls_version = s.tls_version
|
||
|
if tls_version is None:
|
||
|
tls_version = s.advertised_tls_version
|
||
|
if tls_version >= 0x0304:
|
||
|
s.pwcs = writeConnState(ciphersuite=type(s.wcs.ciphersuite),
|
||
|
connection_end=s.connection_end,
|
||
|
tls_version=s.tls_version)
|
||
|
s.triggered_pwcs_commit = True
|
||
|
if s.connection_end == "server":
|
||
|
s.compute_tls13_traffic_secrets()
|
||
|
elif s.connection_end == "client":
|
||
|
s.compute_tls13_traffic_secrets_end()
|
||
|
s.compute_tls13_resumption_secret()
|
||
|
|
||
|
def post_dissection_tls_session_update(self, msg_str):
|
||
|
self.tls_session_update(msg_str)
|
||
|
s = self.tls_session
|
||
|
tls_version = s.tls_version
|
||
|
if tls_version is None:
|
||
|
tls_version = s.advertised_tls_version
|
||
|
if tls_version >= 0x0304:
|
||
|
s.prcs = readConnState(ciphersuite=type(s.rcs.ciphersuite),
|
||
|
connection_end=s.connection_end,
|
||
|
tls_version=s.tls_version)
|
||
|
s.triggered_prcs_commit = True
|
||
|
if s.connection_end == "client":
|
||
|
s.compute_tls13_traffic_secrets()
|
||
|
elif s.connection_end == "server":
|
||
|
s.compute_tls13_traffic_secrets_end()
|
||
|
s.compute_tls13_resumption_secret()
|
||
|
|
||
|
|
||
|
# Additional handshake messages
|
||
|
|
||
|
###############################################################################
|
||
|
# HelloVerifyRequest #
|
||
|
###############################################################################
|
||
|
|
||
|
class TLSHelloVerifyRequest(_TLSHandshake):
|
||
|
"""
|
||
|
Defined for DTLS, see RFC 6347.
|
||
|
"""
|
||
|
name = "TLS Handshake - Hello Verify Request"
|
||
|
fields_desc = [ByteEnumField("msgtype", 21, _tls_handshake_type),
|
||
|
ThreeBytesField("msglen", None),
|
||
|
FieldLenField("cookielen", None,
|
||
|
fmt="B", length_of="cookie"),
|
||
|
StrLenField("cookie", "",
|
||
|
length_from=lambda pkt: pkt.cookielen)]
|
||
|
|
||
|
|
||
|
###############################################################################
|
||
|
# CertificateURL #
|
||
|
###############################################################################
|
||
|
|
||
|
_tls_cert_chain_types = {0: "individual_certs",
|
||
|
1: "pkipath"}
|
||
|
|
||
|
|
||
|
class URLAndOptionalHash(Packet):
|
||
|
name = "URLAndOptionHash structure for TLSCertificateURL"
|
||
|
fields_desc = [FieldLenField("urllen", None, length_of="url"),
|
||
|
StrLenField("url", "",
|
||
|
length_from=lambda pkt: pkt.urllen),
|
||
|
FieldLenField("hash_present", None,
|
||
|
fmt="B", length_of="hash",
|
||
|
adjust=lambda pkt, x: int(math.ceil(x / 20.))), # noqa: E501
|
||
|
StrLenField("hash", "",
|
||
|
length_from=lambda pkt: 20 * pkt.hash_present)]
|
||
|
|
||
|
def guess_payload_class(self, p):
|
||
|
return Padding
|
||
|
|
||
|
|
||
|
class TLSCertificateURL(_TLSHandshake):
|
||
|
"""
|
||
|
Defined in RFC 4366. PkiPath structure of section 8 is not implemented yet.
|
||
|
"""
|
||
|
name = "TLS Handshake - Certificate URL"
|
||
|
fields_desc = [ByteEnumField("msgtype", 21, _tls_handshake_type),
|
||
|
ThreeBytesField("msglen", None),
|
||
|
ByteEnumField("certchaintype", None, _tls_cert_chain_types),
|
||
|
FieldLenField("uahlen", None, length_of="uah"),
|
||
|
PacketListField("uah", [], URLAndOptionalHash,
|
||
|
length_from=lambda pkt: pkt.uahlen)]
|
||
|
|
||
|
|
||
|
###############################################################################
|
||
|
# CertificateStatus #
|
||
|
###############################################################################
|
||
|
|
||
|
class ThreeBytesLenField(FieldLenField):
|
||
|
def __init__(self, name, default, length_of=None, adjust=lambda pkt, x: x):
|
||
|
FieldLenField.__init__(self, name, default, length_of=length_of,
|
||
|
fmt='!I', adjust=adjust)
|
||
|
|
||
|
def i2repr(self, pkt, x):
|
||
|
if x is None:
|
||
|
return 0
|
||
|
return repr(self.i2h(pkt, x))
|
||
|
|
||
|
def addfield(self, pkt, s, val):
|
||
|
return s + struct.pack(self.fmt, self.i2m(pkt, val))[1:4]
|
||
|
|
||
|
def getfield(self, pkt, s):
|
||
|
return s[3:], self.m2i(pkt, struct.unpack(self.fmt, b"\x00" + s[:3])[0]) # noqa: E501
|
||
|
|
||
|
|
||
|
_cert_status_cls = {1: OCSP_Response}
|
||
|
|
||
|
|
||
|
class _StatusField(PacketField):
|
||
|
def m2i(self, pkt, m):
|
||
|
idtype = pkt.status_type
|
||
|
cls = self.cls
|
||
|
if idtype in _cert_status_cls:
|
||
|
cls = _cert_status_cls[idtype]
|
||
|
return cls(m)
|
||
|
|
||
|
|
||
|
class TLSCertificateStatus(_TLSHandshake):
|
||
|
name = "TLS Handshake - Certificate Status"
|
||
|
fields_desc = [ByteEnumField("msgtype", 22, _tls_handshake_type),
|
||
|
ThreeBytesField("msglen", None),
|
||
|
ByteEnumField("status_type", 1, _cert_status_type),
|
||
|
ThreeBytesLenField("responselen", None,
|
||
|
length_of="response"),
|
||
|
_StatusField("response", None, Raw)]
|
||
|
|
||
|
|
||
|
###############################################################################
|
||
|
# SupplementalData #
|
||
|
###############################################################################
|
||
|
|
||
|
class SupDataEntry(Packet):
|
||
|
name = "Supplemental Data Entry - Generic"
|
||
|
fields_desc = [ShortField("sdtype", None),
|
||
|
FieldLenField("len", None, length_of="data"),
|
||
|
StrLenField("data", "",
|
||
|
length_from=lambda pkt:pkt.len)]
|
||
|
|
||
|
def guess_payload_class(self, p):
|
||
|
return Padding
|
||
|
|
||
|
|
||
|
class UserMappingData(Packet):
|
||
|
name = "User Mapping Data"
|
||
|
fields_desc = [ByteField("version", None),
|
||
|
FieldLenField("len", None, length_of="data"),
|
||
|
StrLenField("data", "",
|
||
|
length_from=lambda pkt: pkt.len)]
|
||
|
|
||
|
def guess_payload_class(self, p):
|
||
|
return Padding
|
||
|
|
||
|
|
||
|
class SupDataEntryUM(Packet):
|
||
|
name = "Supplemental Data Entry - User Mapping"
|
||
|
fields_desc = [ShortField("sdtype", None),
|
||
|
FieldLenField("len", None, length_of="data",
|
||
|
adjust=lambda pkt, x: x + 2),
|
||
|
FieldLenField("dlen", None, length_of="data"),
|
||
|
PacketListField("data", [], UserMappingData,
|
||
|
length_from=lambda pkt:pkt.dlen)]
|
||
|
|
||
|
def guess_payload_class(self, p):
|
||
|
return Padding
|
||
|
|
||
|
|
||
|
class TLSSupplementalData(_TLSHandshake):
|
||
|
name = "TLS Handshake - Supplemental Data"
|
||
|
fields_desc = [ByteEnumField("msgtype", 23, _tls_handshake_type),
|
||
|
ThreeBytesField("msglen", None),
|
||
|
ThreeBytesLenField("sdatalen", None, length_of="sdata"),
|
||
|
PacketListField("sdata", [], SupDataEntry,
|
||
|
length_from=lambda pkt: pkt.sdatalen)]
|
||
|
|
||
|
|
||
|
###############################################################################
|
||
|
# NewSessionTicket #
|
||
|
###############################################################################
|
||
|
|
||
|
class TLSNewSessionTicket(_TLSHandshake):
|
||
|
"""
|
||
|
XXX When knowing the right secret, we should be able to read the ticket.
|
||
|
"""
|
||
|
name = "TLS Handshake - New Session Ticket"
|
||
|
fields_desc = [ByteEnumField("msgtype", 4, _tls_handshake_type),
|
||
|
ThreeBytesField("msglen", None),
|
||
|
IntField("lifetime", 0xffffffff),
|
||
|
FieldLenField("ticketlen", None, length_of="ticket"),
|
||
|
StrLenField("ticket", "",
|
||
|
length_from=lambda pkt: pkt.ticketlen)]
|
||
|
|
||
|
@classmethod
|
||
|
def dispatch_hook(cls, _pkt=None, *args, **kargs):
|
||
|
s = kargs.get("tls_session", None)
|
||
|
if s and s.tls_version and s.tls_version >= 0x0304:
|
||
|
return TLS13NewSessionTicket
|
||
|
return TLSNewSessionTicket
|
||
|
|
||
|
def post_dissection_tls_session_update(self, msg_str):
|
||
|
self.tls_session_update(msg_str)
|
||
|
if self.tls_session.connection_end == "client":
|
||
|
self.tls_session.client_session_ticket = self.ticket
|
||
|
|
||
|
|
||
|
class TLS13NewSessionTicket(_TLSHandshake):
|
||
|
"""
|
||
|
Uncomment the TicketField line for parsing a RFC 5077 ticket.
|
||
|
"""
|
||
|
name = "TLS 1.3 Handshake - New Session Ticket"
|
||
|
fields_desc = [ByteEnumField("msgtype", 4, _tls_handshake_type),
|
||
|
ThreeBytesField("msglen", None),
|
||
|
IntField("ticket_lifetime", 0xffffffff),
|
||
|
IntField("ticket_age_add", 0),
|
||
|
FieldLenField("noncelen", None, fmt="B",
|
||
|
length_of="ticket_nonce"),
|
||
|
StrLenField("ticket_nonce", "",
|
||
|
length_from=lambda pkt: pkt.noncelen),
|
||
|
FieldLenField("ticketlen", None, length_of="ticket"),
|
||
|
# TicketField("ticket", "",
|
||
|
StrLenField("ticket", "",
|
||
|
length_from=lambda pkt: pkt.ticketlen),
|
||
|
_ExtensionsLenField("extlen", None, length_of="ext"),
|
||
|
_ExtensionsField("ext", None,
|
||
|
length_from=lambda pkt: (pkt.msglen -
|
||
|
(pkt.ticketlen or 0) - # noqa: E501
|
||
|
pkt.noncelen or 0) - 13)] # noqa: E501
|
||
|
|
||
|
def build(self):
|
||
|
fval = self.getfieldval("ticket")
|
||
|
if fval == b"":
|
||
|
# Here, the ticket is just a random 48-byte label
|
||
|
# The ticket may also be a self-encrypted and self-authenticated
|
||
|
# value
|
||
|
self.ticket = os.urandom(48)
|
||
|
|
||
|
fval = self.getfieldval("ticket_nonce")
|
||
|
if fval == b"":
|
||
|
# Nonce is randomly chosen
|
||
|
self.ticket_nonce = os.urandom(32)
|
||
|
|
||
|
fval = self.getfieldval("ticket_lifetime")
|
||
|
if fval == 0xffffffff:
|
||
|
# ticket_lifetime is set to 12 hours
|
||
|
self.ticket_lifetime = 43200
|
||
|
|
||
|
fval = self.getfieldval("ticket_age_add")
|
||
|
if fval == 0:
|
||
|
# ticket_age_add is a random 32-bit value
|
||
|
self.ticket_age_add = struct.unpack("!I", os.urandom(4))[0]
|
||
|
|
||
|
return _TLSHandshake.build(self)
|
||
|
|
||
|
def post_dissection_tls_session_update(self, msg_str):
|
||
|
self.tls_session_update(msg_str)
|
||
|
if self.tls_session.connection_end == "client":
|
||
|
self.tls_session.client_session_ticket = self.ticket
|
||
|
|
||
|
|
||
|
###############################################################################
|
||
|
# EndOfEarlyData #
|
||
|
###############################################################################
|
||
|
|
||
|
class TLS13EndOfEarlyData(_TLSHandshake):
|
||
|
name = "TLS 1.3 Handshake - End Of Early Data"
|
||
|
|
||
|
fields_desc = [ByteEnumField("msgtype", 5, _tls_handshake_type),
|
||
|
ThreeBytesField("msglen", None)]
|
||
|
|
||
|
|
||
|
###############################################################################
|
||
|
# KeyUpdate #
|
||
|
###############################################################################
|
||
|
_key_update_request = {0: "update_not_requested", 1: "update_requested"}
|
||
|
|
||
|
|
||
|
class TLS13KeyUpdate(_TLSHandshake):
|
||
|
name = "TLS 1.3 Handshake - Key Update"
|
||
|
fields_desc = [ByteEnumField("msgtype", 24, _tls_handshake_type),
|
||
|
ThreeBytesField("msglen", None),
|
||
|
ByteEnumField("request_update", 0, _key_update_request)]
|
||
|
|
||
|
def post_build_tls_session_update(self, msg_str):
|
||
|
s = self.tls_session
|
||
|
s.pwcs = writeConnState(ciphersuite=type(s.wcs.ciphersuite),
|
||
|
connection_end=s.connection_end,
|
||
|
tls_version=s.tls_version)
|
||
|
s.triggered_pwcs_commit = True
|
||
|
s.compute_tls13_next_traffic_secrets(s.connection_end, "write")
|
||
|
|
||
|
def post_dissection_tls_session_update(self, msg_str):
|
||
|
s = self.tls_session
|
||
|
s.prcs = writeConnState(ciphersuite=type(s.rcs.ciphersuite),
|
||
|
connection_end=s.connection_end,
|
||
|
tls_version=s.tls_version)
|
||
|
s.triggered_prcs_commit = True
|
||
|
if s.connection_end == "server":
|
||
|
s.compute_tls13_next_traffic_secrets("client", "read")
|
||
|
elif s.connection_end == "client":
|
||
|
s.compute_tls13_next_traffic_secrets("server", "read")
|
||
|
|
||
|
|
||
|
###############################################################################
|
||
|
# All handshake messages defined in this module #
|
||
|
###############################################################################
|
||
|
|
||
|
_tls_handshake_cls = {0: TLSHelloRequest, 1: TLSClientHello,
|
||
|
2: TLSServerHello, 3: TLSHelloVerifyRequest,
|
||
|
4: TLSNewSessionTicket,
|
||
|
8: TLSEncryptedExtensions, 11: TLSCertificate,
|
||
|
12: TLSServerKeyExchange, 13: TLSCertificateRequest,
|
||
|
14: TLSServerHelloDone, 15: TLSCertificateVerify,
|
||
|
16: TLSClientKeyExchange, 20: TLSFinished,
|
||
|
21: TLSCertificateURL, 22: TLSCertificateStatus,
|
||
|
23: TLSSupplementalData}
|
||
|
|
||
|
_tls13_handshake_cls = {1: TLS13ClientHello, 2: TLS13ServerHello,
|
||
|
4: TLS13NewSessionTicket, 5: TLS13EndOfEarlyData,
|
||
|
8: TLSEncryptedExtensions, 11: TLS13Certificate,
|
||
|
13: TLS13CertificateRequest, 15: TLSCertificateVerify,
|
||
|
20: TLSFinished, 24: TLS13KeyUpdate}
|