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
20 changes: 13 additions & 7 deletions scapy/layers/tls/crypto/prf.py
Original file line number Diff line number Diff line change
Expand Up @@ -203,23 +203,32 @@ def __init__(self, hash_name="SHA256", tls_version=0x0303):
elif hash_name == "SHA512":
self.prf = _tls12_SHA512PRF
else:
if hash_name in ["MD5", "SHA"]:
self.hash_name = "SHA256"
self.prf = _tls12_SHA256PRF
else:
warning("Unknown TLS version")

def compute_master_secret(self, pre_master_secret,
client_random, server_random):
def compute_master_secret(self, pre_master_secret, client_random,
server_random, extms=False, handshake_hash=None):
"""
Return the 48-byte master_secret, computed from pre_master_secret,
client_random and server_random. See RFC 5246, section 6.3.
Supports Extended Master Secret Derivation, see RFC 7627
"""
seed = client_random + server_random
label = b'master secret'

if extms is True and handshake_hash is not None:
seed = handshake_hash
label = b'extended master secret'

if self.tls_version < 0x0300:
return None
elif self.tls_version == 0x0300:
return self.prf(pre_master_secret, seed, 48)
else:
return self.prf(pre_master_secret, b"master secret", seed, 48)
return self.prf(pre_master_secret, label, seed, 48)

def derive_key_block(self, master_secret, server_random,
client_random, req_len):
Expand Down Expand Up @@ -286,10 +295,7 @@ def compute_verify_data(self, con_end, read_or_write,
s2 = _tls_hash_algs["SHA"]().digest(handshake_msg)
verify_data = self.prf(master_secret, label, s1 + s2, 12)
else:
if self.hash_name in ["MD5", "SHA"]:
h = _tls_hash_algs["SHA256"]()
else:
h = _tls_hash_algs[self.hash_name]()
h = _tls_hash_algs[self.hash_name]()
s = h.digest(handshake_msg)
verify_data = self.prf(master_secret, label, s, 12)

Expand Down
35 changes: 34 additions & 1 deletion scapy/layers/tls/handshake.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,8 @@
TLS_Ext_SignatureAlgorithms,
TLS_Ext_SupportedVersion_SH,
TLS_Ext_EarlyDataIndication,
_tls_hello_retry_magic)
_tls_hello_retry_magic,
TLS_Ext_ExtendedMasterSecret)
from scapy.layers.tls.keyexchange import (_TLSSignature, _TLSServerParamsField,
_TLSSignatureField, ServerRSAParams,
SigAndHashAlgsField, _tls_hash_sig,
Expand Down Expand Up @@ -111,6 +112,7 @@ 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)

Expand Down Expand Up @@ -540,6 +542,13 @@ def tls_session_update(self, msg_str):
s.server_random = self.random_bytes
s.sid = self.sid

# EXTMS
if self.ext:
for e in self.ext:
if isinstance(e, TLS_Ext_ExtendedMasterSecret):
self.tls_session.extms = True
break

cs_cls = None
if self.cipher:
cs_val = self.cipher
Expand Down Expand Up @@ -1296,6 +1305,30 @@ def build(self, *args, **kargs):
self.exchkeys = cls
return _TLSHandshake.build(self, *args, **kargs)

def tls_session_update(self, msg_str):
"""
Finalize the EXTMS messages and compute the hash
"""
super(TLSClientKeyExchange, self).tls_session_update(msg_str)

if self.tls_session.extms:
to_hash = b''.join(self.tls_session.handshake_messages)
# https://tools.ietf.org/html/rfc7627#section-3
if self.tls_session.tls_version >= 0x303:
# TLS 1.2 uses the same Hash as the PRF
from scapy.layers.tls.crypto.hash import _tls_hash_algs
hash_object = _tls_hash_algs.get(
self.tls_session.prcs.prf.hash_name
)()
self.tls_session.session_hash = hash_object.digest(to_hash)
else:
# Previous TLS version use concatenation of MD5 & SHA1
from scapy.layers.tls.crypto.hash import Hash_MD5, Hash_SHA
self.tls_session.session_hash = (
Hash_MD5().digest(to_hash) + Hash_SHA().digest(to_hash)
)
self.tls_session.compute_ms_and_derive_keys()


###############################################################################
# Finished #
Expand Down
21 changes: 15 additions & 6 deletions scapy/layers/tls/keyexchange.py
Original file line number Diff line number Diff line change
Expand Up @@ -743,7 +743,11 @@ def fill_missing(self):
if s.client_kx_privkey and s.server_kx_pubkey:
pms = s.client_kx_privkey.exchange(s.server_kx_pubkey)
s.pre_master_secret = pms
s.compute_ms_and_derive_keys()
if not s.extms or s.session_hash:
# If extms is set (extended master secret), the key will
# need the session hash to be computed. This is provided
# by the TLSClientKeyExchange. Same in all occurrences
s.compute_ms_and_derive_keys()

def post_build(self, pkt, pay):
if not self.dh_Yc:
Expand Down Expand Up @@ -773,7 +777,8 @@ def post_dissection(self, m):
if s.server_kx_privkey and s.client_kx_pubkey:
ZZ = s.server_kx_privkey.exchange(s.client_kx_pubkey)
s.pre_master_secret = ZZ
s.compute_ms_and_derive_keys()
if not s.extms or s.session_hash:
s.compute_ms_and_derive_keys()

def guess_payload_class(self, p):
return Padding
Expand Down Expand Up @@ -805,7 +810,8 @@ def fill_missing(self):
if s.client_kx_privkey and s.server_kx_pubkey:
pms = s.client_kx_privkey.exchange(ec.ECDH(), s.server_kx_pubkey)
s.pre_master_secret = pms
s.compute_ms_and_derive_keys()
if not s.extms or s.session_hash:
s.compute_ms_and_derive_keys()

def post_build(self, pkt, pay):
if not self.ecdh_Yc:
Expand All @@ -830,7 +836,8 @@ def post_dissection(self, m):
if s.server_kx_privkey and s.client_kx_pubkey:
ZZ = s.server_kx_privkey.exchange(ec.ECDH(), s.client_kx_pubkey)
s.pre_master_secret = ZZ
s.compute_ms_and_derive_keys()
if not s.extms or s.session_hash:
s.compute_ms_and_derive_keys()


# RSA Encryption (standard & export)
Expand Down Expand Up @@ -893,7 +900,8 @@ def pre_dissect(self, m):
warning(err)

s.pre_master_secret = pms
s.compute_ms_and_derive_keys()
if not s.extms or s.session_hash:
s.compute_ms_and_derive_keys()

return pms

Expand All @@ -908,7 +916,8 @@ def post_build(self, pkt, pay):

s = self.tls_session
s.pre_master_secret = enc
s.compute_ms_and_derive_keys()
if not s.extms or s.session_hash:
s.compute_ms_and_derive_keys()

if s.server_tmp_rsa_key is not None:
enc = s.server_tmp_rsa_key.encrypt(pkt, t="pkcs")
Expand Down
10 changes: 9 additions & 1 deletion scapy/layers/tls/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -453,6 +453,10 @@ def __init__(self,
self.handshake_messages = []
self.handshake_messages_parsed = []

# Flag, whether we derive the secret as Extended MS or not
self.extms = False
self.session_hash = None

# All exchanged TLS packets.
# XXX no support for now
# self.exchanged_pkts = []
Expand Down Expand Up @@ -526,10 +530,14 @@ def compute_master_secret(self):
warning("Missing client_random while computing master_secret!")
if self.server_random is None:
warning("Missing server_random while computing master_secret!")
if self.extms and self.session_hash is None:
warning("Missing session hash while computing master secret!")

ms = self.pwcs.prf.compute_master_secret(self.pre_master_secret,
self.client_random,
self.server_random)
self.server_random,
self.extms,
self.session_hash)
self.master_secret = ms
if conf.debug_tls:
log_runtime.debug("TLS: master secret: %s", repr_hex(ms))
Expand Down
51 changes: 51 additions & 0 deletions test/tls.uts
Original file line number Diff line number Diff line change
Expand Up @@ -979,6 +979,57 @@ assert(rec_fin.mac == b'\xecguD\xa8\x87$<7+\n\x94\x1e9\x96\xfa')
assert(isinstance(rec_fin.msg[0], _TLSEncryptedContent))
rec_fin.msg[0].load == b'7\\)`\xaa`\x7ff\xcd\x10\xa9v\xa3*\x17\x1a'

= Reading TLS test session - Extended master secret
~ test

# See https://github.com/secdev/scapy/issues/2784

from scapy.layers.tls.cert import PrivKey
from scapy.layers.tls.handshake import TLSFinished
from scapy.layers.tls.record import TLS

chello_extms = hex_bytes(b'1603010200010001fc0303f8b3dbcb70ed3804009c15af4a4298720619b70d1ad4f24d0e99de9e93ce3c3b201c3b2cf3266bcba19b29479ec66fe815f7db0a6b976111f70958395e7aeebaba003e130213031301c02cc030009fcca9cca8ccaac02bc02f009ec024c028006bc023c0270067c00ac0140039c009c0130033009d009c003d003c0035002f00ff01000175000b000403000102000a000c000a001d0017001e00190018337400000010000e000c02683208687474702f312e31001600000017000000310000000d002a0028040305030603080708080809080a080b080408050806040105010601030303010302040205020602002b00050403040303002d00020101003300260024001d0020e8410f5ab09d96b05f10183ccd9e93a057a73290b4c9e1c254cdfc299fc01d41001500d000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000')
shello_extms = hex_bytes(b'160303005502000051030320a54032477ea3a963b8a700090459f11f1f4ad1896e1d75745b7e2bdc51dde0200600f552db6c51b97a309717ff847bb6e8fef1ce2601544413fda7b66075b887009d000009ff0100010000170000160303036e0b00036a0003670003643082036030820248a003020102020900eb73b71c3e2f9fdc300d06092a864886f70d01010b05003045310b30090603550406130241553113301106035504080c0a536f6d652d53746174653121301f060355040a0c18496e7465726e6574205769646769747320507479204c7464301e170d3139303231353135313430335a170d3239303231323135313430335a3045310b30090603550406130241553113301106035504080c0a536f6d652d53746174653121301f060355040a0c18496e7465726e6574205769646769747320507479204c746430820122300d06092a864886f70d01010105000382010f003082010a0282010100d2f7d36b233a5619368fc3a7db0f2364dc71986dd4eebcbee85b783e139cfeb0a80de50147c936aa84230e2fa2eb91ef1737410387b932440ac7cfda3c5966eef88f688bcbee6a5d0dfaa075b77dfe836f2cb318375ff5be2b35f6d62dd7c4b147224f67d53d95c7fcc11cda7bc622369b4d4a685655169fb28f66e511724f0c9af2f74ea4cdf09b92f917246a582f67fade3eff7eca2c794d713c13f80cd53f847aa196d0adc04494790a628e327f4b53d05b83025c3ea541195f953ce6fc37edcc68a8fd6eca621f38bc08bc2d8d72cfcdf85c68f9f4f4485b32133c63299f85ffd62bde5a9d585e5a896f08319448277f19e86d5d6878bc53768b2ef9b3210203010001a3533051301d0603551d0e041604148297fb8cf3d9ddf2f60ec90f92815c28e3f3ff35301f0603551d230418301680148297fb8cf3d9ddf2f60ec90f92815c28e3f3ff35300f0603551d130101ff040530030101ff300d06092a864886f70d01010b050003820101003c001f0f5d106072e0beedfa47895329d4623422080e66caa4beb4c3d9b6868fa107467d6160e512ede7cbbefdeb17c09b9f594f86f1a9922c982ccf9655d4d67eda061f667eeb25fe7203bea00e40f150a490a78ca963d87c185ade8b7c294f5052fb1e1edb3403831e33e4026c8e56717cb77bc32321858be37a77fe7f5511ef8c2013c86e2730c5b2366875131cae0c7616fdc7c8605696133e7a685f20203c0db8e0ff1d1a5991d2f058f48f20b10a5fb0df27a1f9874cc0fe8d6ebf77e9a7ba38490e9d63241a0fb3fd7701ff3b130c9aa7aa77770280b7003c1bb5e0784c34aacb74ce8114960e50eee04602a7ab20e5c878028e4292e90e40fd631fee16030300040e000000')
finished_extms = hex_bytes(b'160303010610000102010007534dd8642e57edd33d156d8002f70562864c1dfe5d721763e8e4ef2c03fb14b4e4eac1864c41fcce57367f95798f04954ef957deb934536b0ac39a72c14f772d0f64b7cc0d8260e2019748fc65fd6f382da6d4f873afe6fc1fa17e786cf6c72b6a46950d2030c7b42ed10f2c4dba37282001132ddb151a44f6face6b049338217784cf2a5ac6a054a2a1d205fb7657d7affa14113c43314b54b28164423455174f57eb50f6eea0836ba1c68616db720641bf18f0cdf7bb729c9cc0b4cfeee8aeed94e00573210eb5328cbcca4ccb1aa29a910c5b5f2c96cf3a431e9677980400d574244ff6bfdabf36ba9dda84703f5760d607e4b731d4f1dc16372b0feac11403030001011603030028269118aa98b35c71e35034f35c23c78d55c04662cdb71c11b1ef862e3b4ebf8ace2aff053257bb08')
key = base64_bytes(b'LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1JSUV2UUlCQURBTkJna3Foa2lHOXcwQkFRRUZBQVNDQktjd2dnU2pBZ0VBQW9JQkFRRFM5OU5ySXpwV0dUYVAKdzZmYkR5TmszSEdZYmRUdXZMN29XM2crRTV6K3NLZ041UUZIeVRhcWhDTU9MNkxya2U4WE4wRURoN2t5UkFySAp6OW84V1didStJOW9pOHZ1YWwwTitxQjF0MzMrZzI4c3N4ZzNYL1crS3pYMjFpM1h4TEZISWs5bjFUMlZ4L3pCCkhOcDd4aUkybTAxS2FGWlZGcCt5ajJibEVYSlBESnJ5OTA2a3pmQ2JrdmtYSkdwWUwyZjYzajcvZnNvc2VVMXgKUEJQNEROVS9oSHFobHRDdHdFU1VlUXBpampKL1MxUFFXNE1DWEQ2bFFSbGZsVHptL0RmdHpHaW8vVzdLWWg4NAp2QWk4TFkxeXo4MzRYR2o1OVBSSVd6SVRQR01wbjRYLzFpdmVXcDFZWGxxSmJ3Z3hsRWduZnhub2JWMW9lTHhUCmRvc3UrYk1oQWdNQkFBRUNnZ0VBSGQ5NTBISHNrTVNCTlZvL0tvVzZQVTM1eDl2Rml3aXUvN2YwRHRZNEpOaGUKODVpNTFiQm9UVHpvdWRtRStGWnh4SmZPWFBHYkI4TWF3N0JxOXFDeU1xUi9xZzRoa21EOVREMXcrenBBWFFtLwpkRlRuMk85OW5MQUJ0RElmeTYzT2JJUXZPa1MzczczZHpIcUpkWDFZMnVLaXp5WjNFeFZoQjZmR3Fpa09ScU1BCmNYbjJSRzN1UXFNWk4yUkVUK1hFYWdsa1dkbGphVTdaTC9CbklRT2xGS0h2ZzVSeGFwWGpJbTM2NnFUVStreGEKWDJFZnllOUJycWxWK0o4cnYzODVjRDBQc3RkSVFTQzMxZFBzUHMrSnJMVlBKQVpGZTBLVk1lYkk2ODU1cERYZApGd1ZGcC9BOXhFa3NwRW1jS0tnL1ZkZ3JQZUxMQmxhVm9mMVhPeUhWQVFLQmdRRHhPdXFGaXJvNTNQNGZQUGlMCkFnTTNvRnpmY2xwdDFMdnduelprUmVMU1NvVFBvZSt1R2xMdTBpS3lMUHBjWm1DTCt0bldsSXBheHRYOU1CRmUKOWNvMlJpSU9WM2JZM0ZpOTBLYjlvN3NyZURhaWE5NElHNGlBYktyWjJJdktBZmFkWnBqb1hBTXZpWnBEYWxGYgprZWVCd29nV0sreTdic2EwU1RYTGVMdjF5d0tCZ1FEZjRwT2lUZ3RBNFdtMXo2WFB4Z0ZCa3A3OWVjaWhINTlICnF1cVJNNkhtQ2YzSnZqZzJCZnYyb2hYNTlTU2VnZTI5ek0yZEhmVGhSeW1vZlg5VkpyMnRYY2FhVWpkRnp1Ui8Kcm1EblJMTjVDTUFnUWNCU3M5UXFCaXdTM0hqVmpML1REcFMvblJwY2VCQnNZTFYvR1YvQkpvWDkxTlVodVRXcwpjQ0VvRmNVOVF3S0JnUUNjbCtGTHhTMTBpSGZTY1hMcVVla2l3QS9wNFVMQWoxdGRMUTFTOUdiMG1ma3pDKzBaCitPNmpKM2ZzYi9RcDdTOTVUdU1BUDdhOGpOeTJtZkI4MDFOci9nVDNpR0dYRHhyd1JUVlI2MnFDSW14YzdXYloKbm4zeTJCZmtpSVRlSW40ajJVa2pkUytBT1hRUmxUK3hFTHJXNmlBTFBJSlZmZWl4ZWVEWTc4d2NGd0tCZ0Z5aQoxcTFvbDNWd0Q1cGY0ZDdYc2Z0YzNKWkxCcjNNWk01MXBQc1JueUtjN2JyRkQyTWpGTDlYRDdyT09TbXczeHNTCm05MHY0UHc1d3IzcHQzOFhPWko3WThyRXpBUUJlRUJ3ZWI0WGloOUJoS1dVTHl6SkpiZUJ1RWpSbXRuWmxDR1QKUGU4TzVUSnZwM1FBaS9pY0dpZkVkZHF5YnNHMmJjUDgzV3RGbnNnYkFvR0FMOHF4VUx3bGlMck1ML3c3aEJNegpXSHdKM21PK0NXbzFWR3p4bi9lK3I2ejVTUW03M0VuYzlSZnVkN3RBWmU1QUhXYXVSR3RNaVNoY0J1bkl5Q0g1CnU2Q2laZU5UOTBRdElLRmVCS09QSk5WNDR1QzJtK0xKQkNGa0hzU085MHp0dHZzcmVyU0tiNG5oZ2tiZDhxQ24KbDVFZFBpZEx2NXdiY0tyc3dIVzZYSm89Ci0tLS0tRU5EIFBSSVZBVEUgS0VZLS0tLS0=')
# Load key
ssl_key = PrivKey(key)

# Load TLS session
r1 = TLS(chello_extms)
r1.tls_session.server_rsa_key = ssl_key
r2 = TLS(shello_extms, tls_session=r1.tls_session.mirror())
r3 = TLS(finished_extms, tls_session=r2.tls_session.mirror())

r3

assert r3.tls_session.extms
assert r3.tls_session.session_hash == b'\n\x8b\xe0\x08S\xb9f|\xd4\x1f\xc5\x8f\xdb\xfaj\xc6\xb4Aj\\j~B)Ep\x07\x90\xc6/\x18\x1e\x99\x1e\x8d.\xe2,B\xe1\x10ZJ\x10^\xect('

l3 = r3.getlayer(TLS, 3)
assert isinstance(l3.msg[0], TLSFinished)
assert l3.msg[0][TLSFinished].vdata == b'\x00\x1fG\xd8VD@\x0ctK\xeee'

# RC4 case

chello_extms = hex_bytes(b'160301008501000081030360037703ac90bb5e29ae0fca71b68dd8133b17b7060c13779d34f69d5c3255110000060005000400ff01000052337400000010000e000c02683208687474702f312e310016000000170000000d0030002e040305030603080708080809080a080b080408050806040105010601030302030301020103020202040205020602')
shello_extms = hex_bytes(b'1603030055020000510303c985430a03add71566a952a16249e471cd3226c0792ba42c444f574e4752440120e835d66cd3293b9fcb157d5c477848d654a2d3a42fc92bcf9c472171188f69610005000009ff0100010000170000160303036e0b00036a0003670003643082036030820248a003020102020900eb73b71c3e2f9fdc300d06092a864886f70d01010b05003045310b30090603550406130241553113301106035504080c0a536f6d652d53746174653121301f060355040a0c18496e7465726e6574205769646769747320507479204c7464301e170d3139303231353135313430335a170d3239303231323135313430335a3045310b30090603550406130241553113301106035504080c0a536f6d652d53746174653121301f060355040a0c18496e7465726e6574205769646769747320507479204c746430820122300d06092a864886f70d01010105000382010f003082010a0282010100d2f7d36b233a5619368fc3a7db0f2364dc71986dd4eebcbee85b783e139cfeb0a80de50147c936aa84230e2fa2eb91ef1737410387b932440ac7cfda3c5966eef88f688bcbee6a5d0dfaa075b77dfe836f2cb318375ff5be2b35f6d62dd7c4b147224f67d53d95c7fcc11cda7bc622369b4d4a685655169fb28f66e511724f0c9af2f74ea4cdf09b92f917246a582f67fade3eff7eca2c794d713c13f80cd53f847aa196d0adc04494790a628e327f4b53d05b83025c3ea541195f953ce6fc37edcc68a8fd6eca621f38bc08bc2d8d72cfcdf85c68f9f4f4485b32133c63299f85ffd62bde5a9d585e5a896f08319448277f19e86d5d6878bc53768b2ef9b3210203010001a3533051301d0603551d0e041604148297fb8cf3d9ddf2f60ec90f92815c28e3f3ff35301f0603551d230418301680148297fb8cf3d9ddf2f60ec90f92815c28e3f3ff35300f0603551d130101ff040530030101ff300d06092a864886f70d01010b050003820101003c001f0f5d106072e0beedfa47895329d4623422080e66caa4beb4c3d9b6868fa107467d6160e512ede7cbbefdeb17c09b9f594f86f1a9922c982ccf9655d4d67eda061f667eeb25fe7203bea00e40f150a490a78ca963d87c185ade8b7c294f5052fb1e1edb3403831e33e4026c8e56717cb77bc32321858be37a77fe7f5511ef8c2013c86e2730c5b2366875131cae0c7616fdc7c8605696133e7a685f20203c0db8e0ff1d1a5991d2f058f48f20b10a5fb0df27a1f9874cc0fe8d6ebf77e9a7ba38490e9d63241a0fb3fd7701ff3b130c9aa7aa77770280b7003c1bb5e0784c34aacb74ce8114960e50eee04602a7ab20e5c878028e4292e90e40fd631fee16030300040e000000')
finished_extms = hex_bytes(b'16030301061000010201004971b89ae4355a001c49ccb49ed0664a9090a2dc0c14c97563b6dd98f13004ac5327c97abf10617b1f5d19b1f6e1091ccf159693497ebda262aedba2f3b76ae217d56477cad45e2ea129c324083701c2e99e65b6d63f916f963de8d98c5357d22272c032a30acccd673d1556d01e22e206186bcda3a5845d6dacee260ab66f47ea86a4c0081faa082b398f2c65da35264428f320c354b97cd96c986da43c8510e914ffb7f8bb73baee2530c4533ae2d6a922771af689c15b42c53428978510a3e3e90a3806f77fc1cb35c2c3f34dd7e3f831a79bc59b333f0c9e8be49390cd2a8e1c88dafbb9e3e24d1e0530703dbff7cd1c516fcc21a7d484f2111f985f03f8140303000101160303002457ed5c62171e4720a5890cf9ef09323f6e2db063aeebea776a54b879ffb6a69182d15cae')

# Load TLS session
r1 = TLS(chello_extms)
r1.tls_session.server_rsa_key = ssl_key
r2 = TLS(shello_extms, tls_session=r1.tls_session.mirror())
r3 = TLS(finished_extms, tls_session=r2.tls_session.mirror())

assert r3.tls_session.extms
assert r3.tls_session.pwcs.prf.hash_name == "SHA256"
assert r3.tls_session.session_hash == b'2\xdc\xf5\xcb\xbc\x99\xc6IV\xba\x0f.\x0bdq\x1f=\xef\xdaW\xfc*A\x9b\xe2?b\xccKW\xe9\xb7'

l3 = r3.getlayer(TLS, 3)
assert isinstance(l3.msg[0], TLSFinished)
assert l3.msg[0][TLSFinished].vdata == b'\x15\xd6\xd5\xea\x84\xee\xb3\xdd\xd6\x10\xd8\x11'

###
### Other/bug tests
###
Expand Down