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
7 changes: 4 additions & 3 deletions scapy/layers/tls/handshake.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@
TLS_Ext_SupportedVersion_SH,
TLS_Ext_EarlyDataIndication,
_tls_hello_retry_magic,
TLS_Ext_ExtendedMasterSecret)
TLS_Ext_ExtendedMasterSecret,
TLS_Ext_EncryptThenMAC)
from scapy.layers.tls.keyexchange import (_TLSSignature, _TLSServerParamsField,
_TLSSignatureField, ServerRSAParams,
SigAndHashAlgsField, _tls_hash_sig,
Expand Down Expand Up @@ -542,12 +543,12 @@ 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
if isinstance(e, TLS_Ext_EncryptThenMAC):
self.tls_session.encrypt_then_mac = True

cs_cls = None
if self.cipher:
Expand Down
64 changes: 35 additions & 29 deletions scapy/layers/tls/record.py
Original file line number Diff line number Diff line change
Expand Up @@ -445,9 +445,32 @@ def pre_dissect(self, s):

cipher_type = self.tls_session.rcs.cipher.type

def extract_mac(data):
"""Extract MAC."""
tmp_len = self.tls_session.rcs.mac_len
if tmp_len != 0:
frag, mac = data[:-tmp_len], data[-tmp_len:]
else:
frag, mac = data, b""
return frag, mac

def verify_mac(hdr, cfrag, mac):
"""Verify integrity."""
chdr = hdr[:3] + struct.pack('!H', len(cfrag))
is_mac_ok = self._tls_hmac_verify(chdr, cfrag, mac)
if not is_mac_ok:
pkt_info = self.firstlayer().summary()
log_runtime.info(
"TLS: record integrity check failed [%s]", pkt_info,
)

if cipher_type == 'block':
version = struct.unpack("!H", s[1:3])[0]

if self.tls_session.encrypt_then_mac:
efrag, mac = extract_mac(efrag)
verify_mac(hdr, efrag, mac)

# Decrypt
try:
if version >= 0x0302:
Expand Down Expand Up @@ -481,19 +504,11 @@ def pre_dissect(self, s):
mfrag, pad = pfrag[:-padlen], pfrag[-padlen:]
self.padlen = padlen

# Extract MAC
tmp_len = self.tls_session.rcs.mac_len
if tmp_len != 0:
cfrag, mac = mfrag[:-tmp_len], mfrag[-tmp_len:]
if self.tls_session.encrypt_then_mac:
cfrag = mfrag
else:
cfrag, mac = mfrag, b""

# Verify integrity
chdr = hdr[:3] + struct.pack('!H', len(cfrag))
is_mac_ok = self._tls_hmac_verify(chdr, cfrag, mac)
if not is_mac_ok:
pkt_info = self.firstlayer().summary()
log_runtime.info("TLS: record integrity check failed [%s]", pkt_info) # noqa: E501
cfrag, mac = extract_mac(mfrag)
verify_mac(hdr, cfrag, mac)

elif cipher_type == 'stream':
# Decrypt
Expand All @@ -504,21 +519,8 @@ def pre_dissect(self, s):
cfrag = e.args[0]
else:
decryption_success = True
mfrag = pfrag

# Extract MAC
tmp_len = self.tls_session.rcs.mac_len
if tmp_len != 0:
cfrag, mac = mfrag[:-tmp_len], mfrag[-tmp_len:]
else:
cfrag, mac = mfrag, b""

# Verify integrity
chdr = hdr[:3] + struct.pack('!H', len(cfrag))
is_mac_ok = self._tls_hmac_verify(chdr, cfrag, mac)
if not is_mac_ok:
pkt_info = self.firstlayer().summary()
log_runtime.info("TLS: record integrity check failed [%s]", pkt_info) # noqa: E501
cfrag, mac = extract_mac(pfrag)
verify_mac(hdr, cfrag, mac)

elif cipher_type == 'aead':
# Authenticated encryption
Expand Down Expand Up @@ -671,7 +673,8 @@ def post_build(self, pkt, pay):

if cipher_type == 'block':
# Integrity
mfrag = self._tls_hmac_add(hdr, cfrag)
if not self.tls_session.encrypt_then_mac:
cfrag = self._tls_hmac_add(hdr, cfrag)

# Excerpt below better corresponds to TLS 1.1 IV definition,
# but the result is the same as with TLS 1.2 anyway.
Expand All @@ -681,7 +684,7 @@ def post_build(self, pkt, pay):
# mfrag = iv + mfrag

# Add padding
pfrag = self._tls_pad(mfrag)
pfrag = self._tls_pad(cfrag)

# Encryption
if self.version >= 0x0302:
Expand All @@ -695,6 +698,9 @@ def post_build(self, pkt, pay):
# Implicit IV for SSLv3 and TLS 1.0
efrag = self._tls_encrypt(pfrag)

if self.tls_session.encrypt_then_mac:
efrag = self._tls_hmac_add(hdr, efrag)

elif cipher_type == "stream":
# Integrity
mfrag = self._tls_hmac_add(hdr, cfrag)
Expand Down
2 changes: 2 additions & 0 deletions scapy/layers/tls/session.py
Original file line number Diff line number Diff line change
Expand Up @@ -457,6 +457,8 @@ def __init__(self,
self.extms = False
self.session_hash = None

self.encrypt_then_mac = False

# All exchanged TLS packets.
# XXX no support for now
# self.exchanged_pkts = []
Expand Down
39 changes: 39 additions & 0 deletions test/tls.uts
Original file line number Diff line number Diff line change
Expand Up @@ -1030,6 +1030,32 @@ 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'

= Reading TLS test session - Encrypt-then-MAC extension
from scapy.layers.tls.cert import PrivKey
from scapy.layers.tls.handshake import TLSFinished
from scapy.layers.tls.record import TLS

client_hello = hex_bytes(b'16030100c9010000c50303611a2f42b70345cfbc5c5c4da1929bea8a2cb8b1fd10ab1341e43ffaa8856a63000038c02cc030009fcca9cca8ccaac02bc02f009ec024c028006bc023c0270067c00ac0140039c009c0130033009d009c003d003c0035002f00ff01000064000b000403000102000a000c000a001d0017001e00190018337400000010000e000c02683208687474702f312e310016000000170000000d002a0028040305030603080708080809080a080b080408050806040105010601030303010302040205020602')
server_hello = hex_bytes(b'1603030059020000550303a22c975875df69bea936cbd28b083cde754693b4f34a15a036e5e57b7f4755cf20226e6386f90e3751723beea9196640d5bbe6c7c9f314568fa3645cb7218e9159003d00000dff010001000016000000170000160303036e0b00036a0003670003643082036030820248a003020102020900eb73b71c3e2f9fdc300d06092a864886f70d01010b05003045310b30090603550406130241553113301106035504080c0a536f6d652d53746174653121301f060355040a0c18496e7465726e6574205769646769747320507479204c7464301e170d3139303231353135313430335a170d3239303231323135313430335a3045310b30090603550406130241553113301106035504080c0a536f6d652d53746174653121301f060355040a0c18496e7465726e6574205769646769747320507479204c746430820122300d06092a864886f70d01010105000382010f003082010a0282010100d2f7d36b233a5619368fc3a7db0f2364dc71986dd4eebcbee85b783e139cfeb0a80de50147c936aa84230e2fa2eb91ef1737410387b932440ac7cfda3c5966eef88f688bcbee6a5d0dfaa075b77dfe836f2cb318375ff5be2b35f6d62dd7c4b147224f67d53d95c7fcc11cda7bc622369b4d4a685655169fb28f66e511724f0c9af2f74ea4cdf09b92f917246a582f67fade3eff7eca2c794d713c13f80cd53f847aa196d0adc04494790a628e327f4b53d05b83025c3ea541195f953ce6fc37edcc68a8fd6eca621f38bc08bc2d8d72cfcdf85c68f9f4f4485b32133c63299f85ffd62bde5a9d585e5a896f08319448277f19e86d5d6878bc53768b2ef9b3210203010001a3533051301d0603551d0e041604148297fb8cf3d9ddf2f60ec90f92815c28e3f3ff35301f0603551d230418301680148297fb8cf3d9ddf2f60ec90f92815c28e3f3ff35300f0603551d130101ff040530030101ff300d06092a864886f70d01010b050003820101003c001f0f5d106072e0beedfa47895329d4623422080e66caa4beb4c3d9b6868fa107467d6160e512ede7cbbefdeb17c09b9f594f86f1a9922c982ccf9655d4d67eda061f667eeb25fe7203bea00e40f150a490a78ca963d87c185ade8b7c294f5052fb1e1edb3403831e33e4026c8e56717cb77bc32321858be37a77fe7f5511ef8c2013c86e2730c5b2366875131cae0c7616fdc7c8605696133e7a685f20203c0db8e0ff1d1a5991d2f058f48f20b10a5fb0df27a1f9874cc0fe8d6ebf77e9a7ba38490e9d63241a0fb3fd7701ff3b130c9aa7aa77770280b7003c1bb5e0784c34aacb74ce8114960e50eee04602a7ab20e5c878028e4292e90e40fd631fee16030300040e000000')
client_finished = hex_bytes(b'1603030106100001020100482bf86fa7047c767ecc5f46e971f2349232d57d4c40b04856b6ea2b5645b5b233c0cd2ad7b05101d6a3fcbd2698b25064501ba4f0cde40c8189abc29aebfffcb87413d4590cae7cf3589fa371ad5e0d161da9c275a4b8ca1aa9a400a3d76021f92b872403a72a22bad6368276010209ca1344971adf7d7a9cdeefd534cd933ec3d2852ea1dfff217f7cd55eac7d2b18f7c5600c56f28746389d1d6c33cd2ac24817632fc0fbd81ffcf528b1c2a5b328a0105e88513e6b2f95b51ca3adf390146662115a721bfd718eae3033388aaa5cb37e2c16428a6f7c994f961137f6a7f933327ed300f15621500d427d261f39970bbf40f4ba303963609439007d34e6bc1403030001011603030050f4b7962d5455e9244efe886bbd4156ca20936e4b8868d80c82b06ceac7cff6d69f130a610f2aa4c4fd8cb2681f84e3ebecad1b563bcd258255aa509ba2b6388f90ac5f1c1f84f1569dc3809667b86ba4')
server_finished = hex_bytes(b'14030300010116030300509e8e5fd6aebaa98263e98266fffcf7fd21eb50fb0510b8598660afb65c57a025374c1e63aff3e260dd5d027180e8aa0d85d43e0c0b54e8783e4ce51a71ef0ae555ab81404020342ca1a34643ce713688')

# Load TLS session
r1 = TLS(client_hello)
r1.tls_session.server_rsa_key = ssl_key
r2 = TLS(server_hello, tls_session=r1.tls_session.mirror())
r3 = TLS(client_finished, tls_session=r2.tls_session.mirror())
r4 = TLS(server_finished, tls_session=r3.tls_session.mirror())

client_finished = r3.getlayer(TLS, 3).msg[0]
server_finished = r4.getlayer(TLS, 2).msg[0]

assert r4.tls_session.encrypt_then_mac
assert isinstance(client_finished, TLSFinished)
assert isinstance(server_finished, TLSFinished)
assert client_finished.vdata == hex_bytes(b'771049b4ff714ac71253f84f')
assert server_finished.vdata == hex_bytes(b'42c9765e833997b6714fec75')

###
### Other/bug tests
###
Expand Down Expand Up @@ -1230,6 +1256,19 @@ ch.ext = [ext1, ext2, ext3, ext4, ext5, ext6, ext7, ext8, ext9]
t = TLS(type='handshake', version='TLS 1.0', msg=ch)
raw(t) == b'\x16\x03\x01\x00\xc7\x01\x00\x00\xc3\x03\x03&\xee-\xddX\xe1\xb1T\xaa\xb1\x0b\xa0zlg\xf8\xd14]%\xa9\x91d\x08\xc7t\xcd6\xd4"\x9f\xcf\x00\x00\x16\xc0+\xc0/\xc0\n\xc0\t\xc0\x13\xc0\x14\x003\x009\x00/\x005\x00\n\x01\x00\x00\x84\x00\x00\x00\x11\x00\x0f\x00\x00\x0cmn.scapy.wtv\xff\x01\x00\x01\x00\x00\n\x00\x08\x00\x06\x00\x17\x00\x18\x00\x19\x00\x0b\x00\x02\x01\x00\x00#\x00\x003t\x00\x00\x00\x10\x00)\x00\'\x05h2-16\x05h2-15\x05h2-14\x02h2\x08spdy/3.1\x08http/1.1\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00\r\x00\x16\x00\x14\x04\x01\x05\x01\x06\x01\x02\x01\x04\x03\x05\x03\x06\x03\x02\x03\x04\x02\x02\x02'

= Building packets - application data with Encrypt-then-MAC
session = tlsSession(
rcs=connState(ciphersuite=TLS_RSA_WITH_AES_256_CBC_SHA256),
wcs=connState(ciphersuite=TLS_RSA_WITH_AES_256_CBC_SHA256),
)
session.encrypt_then_mac = True
session.tls_version = 0x0303
session.rcs.cipher.key = b'A' * 32
session.wcs.cipher.key = b'A' * 32
payload = b'PAYLOAD'
tlsdata = TLS(msg=TLSApplicationData(data=payload), tls_session=session)
t = TLS(raw(tlsdata), tls_session=session.mirror())
assert t[0].msg[0].data == payload

= Building packets - ServerHello context linking
from scapy.layers.tls.crypto.kx_algs import KX_ECDHE_RSA
Expand Down