Skip to content

Commit eaf9db4

Browse files
authored
Merge pull request #1269 from karpierz/fix_tls_aead_auth_decrypt
fix SyntaxError in layers/tls/tools/_tls_aead_auth_decrypt(). [PR is ready for merge to the master]
2 parents 869c336 + c2ff43a commit eaf9db4

File tree

2 files changed

+140
-46
lines changed

2 files changed

+140
-46
lines changed

scapy/layers/tls/tools.py

Lines changed: 50 additions & 46 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@
77
TLS helpers, provided as out-of-context methods.
88
"""
99

10+
from __future__ import absolute_import
11+
import struct
12+
13+
from scapy.compat import orb, chb
1014
from scapy.error import warning
1115
from scapy.fields import (ByteEnumField, ShortEnumField,
1216
FieldLenField, StrLenField)
@@ -19,10 +23,9 @@ class TLSPlaintext(Packet):
1923
name = "TLS Plaintext"
2024
fields_desc = [ ByteEnumField("type", None, _tls_type),
2125
ShortEnumField("version", None, _tls_version),
22-
FieldLenField("len", None, length_of="fragment",
23-
fmt="!H"),
24-
StrLenField("fragment", "",
25-
length_from = lambda pkt: pkt.length) ]
26+
FieldLenField("len", None, length_of="data", fmt="!H"),
27+
StrLenField("data", "",
28+
length_from = lambda pkt: pkt.len) ]
2629

2730
class TLSCompressed(TLSPlaintext):
2831
name = "TLS Compressed"
@@ -39,8 +42,8 @@ def _tls_compress(alg, p):
3942
c = TLSCompressed()
4043
c.type = p.type
4144
c.version = p.version
42-
c.fragment = alg.compress(p.fragment)
43-
c.len = len(c.fragment)
45+
c.data = alg.compress(p.data)
46+
c.len = len(c.data)
4447
return c
4548

4649
def _tls_decompress(alg, c):
@@ -51,21 +54,21 @@ def _tls_decompress(alg, c):
5154
p = TLSPlaintext()
5255
p.type = c.type
5356
p.version = c.version
54-
p.fragment = alg.decompress(c.fragment)
55-
p.len = len(p.fragment)
57+
p.data = alg.decompress(c.data)
58+
p.len = len(p.data)
5659
return p
5760

5861
def _tls_mac_add(alg, c, write_seq_num):
5962
"""
6063
Compute the MAC using provided MAC alg instance over TLSCiphertext c using
6164
current write sequence number write_seq_num. Computed MAC is then appended
62-
to c.fragment and c.length is updated to reflect that change. It is the
65+
to c.data and c.len is updated to reflect that change. It is the
6366
caller responsability to increment the sequence number after the operation.
6467
The function has no return value.
6568
"""
6669
write_seq_num = struct.pack("!Q", write_seq_num)
67-
h = alg.digest(write_seq_num + str(c))
68-
c.fragment += h
70+
h = alg.digest(write_seq_num + bytes(c))
71+
c.data += h
6972
c.len += alg.hash_len
7073

7174
def _tls_mac_verify(alg, p, read_seq_num):
@@ -77,7 +80,7 @@ def _tls_mac_verify(alg, p, read_seq_num):
7780
If the MAC is valid:
7881
- The function returns True
7982
- The packet p is updated in the following way: trailing MAC value is
80-
removed from p.fragment and length is updated accordingly.
83+
removed from p.data and length is updated accordingly.
8184
8285
In case of error, False is returned, and p may have been modified.
8386
@@ -87,52 +90,52 @@ def _tls_mac_verify(alg, p, read_seq_num):
8790
h_size = alg.hash_len
8891
if p.len < h_size:
8992
return False
90-
received_h = p.fragment[-h_size:]
93+
received_h = p.data[-h_size:]
9194
p.len -= h_size
92-
p.fragment = p.fragment[:-h_size]
95+
p.data = p.data[:-h_size]
9396

9497
read_seq_num = struct.pack("!Q", read_seq_num)
95-
h = alg.digest(read_seq_num + str(p))
98+
h = alg.digest(read_seq_num + bytes(p))
9699
return h == received_h
97100

98101
def _tls_add_pad(p, block_size):
99102
"""
100103
Provided with cipher block size parameter and current TLSCompressed packet
101104
p (after MAC addition), the function adds required, deterministic padding
102-
to p.fragment before encryption step, as it is defined for TLS (i.e. not
105+
to p.data before encryption step, as it is defined for TLS (i.e. not
103106
SSL and its allowed random padding). The function has no return value.
104107
"""
105-
padlen = block_size - ((p.len + 1) % block_size)
106-
if padlen == block_size:
107-
padlen = 0
108-
padding = chr(padlen) * (padlen + 1)
108+
padlen = -p.len % block_size
109+
padding = chb(padlen) * (padlen + 1)
109110
p.len += len(padding)
110-
p.fragment += padding
111+
p.data += padding
111112

112113
def _tls_del_pad(p):
113114
"""
114115
Provided with a just decrypted TLSCiphertext (now a TLSPlaintext instance)
115-
p, the function removes the trailing padding found in p.fragment. It also
116+
p, the function removes the trailing padding found in p.data. It also
116117
performs some sanity checks on the padding (length, content, ...). False
117118
is returned if one of the check fails. Otherwise, True is returned,
118-
indicating that p.fragment and p.len have been updated.
119+
indicating that p.data and p.len have been updated.
119120
"""
120121

121122
if p.len < 1:
122123
warning("Message format is invalid (padding)")
123124
return False
124125

125-
padlen = ord(p.fragment[-1]) + 1
126-
if (p.len < padlen):
126+
padlen = orb(p.data[-1])
127+
padsize = padlen + 1
128+
129+
if p.len < padsize:
127130
warning("Invalid padding length")
128131
return False
129132

130-
if (p.fragment[-padlen:] != p.fragment[-1] * padlen):
131-
warning("Padding content is invalid %s", repr(p.fragment[-padlen:]))
133+
if p.data[-padsize:] != chb(padlen) * padsize:
134+
warning("Padding content is invalid %s", repr(p.data[-padsize:]))
132135
return False
133136

134-
p.fragment = p.fragment[:-padlen]
135-
p.len -= padlen
137+
p.data = p.data[:-padsize]
138+
p.len -= padsize
136139

137140
return True
138141

@@ -146,66 +149,67 @@ def _tls_encrypt(alg, p):
146149
c = TLSCiphertext()
147150
c.type = p.type
148151
c.version = p.version
149-
c.fragment = alg.encrypt(p.fragment)
150-
c.len = len(c.fragment)
152+
c.data = alg.encrypt(p.data)
153+
c.len = len(c.data)
151154
return c
152155

153156
def _tls_decrypt(alg, c):
154157
"""
155158
Provided with a TLSCiphertext instance c, and a stream or block cipher alg,
156-
the function decrypts c.fragment and returns a newly created TLSPlaintext.
159+
the function decrypts c.data and returns a newly created TLSPlaintext.
157160
"""
158161
p = TLSPlaintext()
159162
p.type = c.type
160163
p.version = c.version
161-
p.fragment = alg.decrypt(c.fragment)
162-
p.len = len(p.fragment)
164+
p.data = alg.decrypt(c.data)
165+
p.len = len(p.data)
163166
return p
164167

165168
def _tls_aead_auth_encrypt(alg, p, write_seq_num):
166169
"""
167170
Provided with a TLSCompressed instance p, the function applies AEAD
168-
cipher alg to p.fragment and builds a new TLSCiphertext instance. Unlike
171+
cipher alg to p.data and builds a new TLSCiphertext instance. Unlike
169172
for block and stream ciphers, for which the authentication step is done
170173
separately, AEAD alg does it simultaneously: this is the reason why
171174
write_seq_num is passed to the function, to be incorporated in
172175
authenticated data. Note that it is the caller's responsibility to increment
173176
write_seq_num afterwards.
174177
"""
175-
P = str(p)
178+
P = bytes(p)
176179
write_seq_num = struct.pack("!Q", write_seq_num)
177180
A = write_seq_num + P[:5]
178181

179-
c = TLCCiphertext()
182+
c = TLSCiphertext()
180183
c.type = p.type
181184
c.version = p.version
182-
c.fragment = alg.auth_encrypt(P, A)
183-
c.len = len(c.fragment)
185+
c.data = alg.auth_encrypt(P, A, write_seq_num)
186+
c.len = len(c.data)
184187
return c
185188

186189
def _tls_aead_auth_decrypt(alg, c, read_seq_num):
187190
"""
188191
Provided with a TLSCiphertext instance c, the function applies AEAD
189-
cipher alg auth_decrypt function to c.fragment (and additional data)
190-
in order to authenticate the data and decrypt c.fragment. When those
192+
cipher alg auth_decrypt function to c.data (and additional data)
193+
in order to authenticate the data and decrypt c.data. When those
191194
steps succeed, the result is a newly created TLSCompressed instance.
192195
On error, None is returned. Note that it is the caller's responsibility to
193196
increment read_seq_num afterwards.
194197
"""
195198
# 'Deduce' TLSCompressed length from TLSCiphertext length
196199
# There is actually no guaranty of this equality, but this is defined as
197200
# such in TLS 1.2 specifications, and it works for GCM and CCM at least.
198-
l = p.len - alg.nonce_explicit_len - alg.tag_len
201+
#
202+
plen = c.len - getattr(alg, "nonce_explicit_len", 0) - alg.tag_len
199203
read_seq_num = struct.pack("!Q", read_seq_num)
200-
A = read_seq_num + struct.pack('!BHH', p.type, p.version, l)
204+
A = read_seq_num + struct.pack('!BHH', c.type, c.version, plen)
201205

202206
p = TLSCompressed()
203207
p.type = c.type
204208
p.version = c.version
205-
p.len = l
206-
p.fragment = alg.auth_decrypt(A, c.fragment)
209+
p.len = plen
210+
p.data = alg.auth_decrypt(A, c.data, read_seq_num)
207211

208-
if p.fragment is None: # Verification failed.
212+
if p.data is None: # Verification failed.
209213
return None
210214
return p
211215

test/tls.uts

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1126,6 +1126,96 @@ assert not s.consider_write_padding()
11261126
= Test connState
11271127
assert s.wcs.__repr__() == 'Connection end : SERVER\nCipher suite : TLS_NULL_WITH_NULL_NULL (0x0000)\nCompression : null (0x00)\n'
11281128

1129+
= Test tls.tools
1130+
def test_tls_tools():
1131+
from scapy.layers.tls.crypto.compression import Comp_Deflate
1132+
from scapy.layers.tls.crypto.ciphers import CipherError
1133+
from scapy.layers.tls.crypto.cipher_stream import Cipher_RC4_40
1134+
from scapy.layers.tls.crypto.cipher_aead import (Cipher_AES_128_GCM,
1135+
Cipher_AES_128_GCM_TLS13)
1136+
from scapy.layers.tls.crypto.hash import Hash_SHA256
1137+
from scapy.layers.tls.crypto.pkcs1 import pkcs_i2osp, pkcs_os2ip
1138+
from scapy.layers.tls.tools import TLSPlaintext, TLSCompressed, TLSCiphertext
1139+
from scapy.layers.tls.tools import _tls_compress, _tls_decompress
1140+
from scapy.layers.tls.tools import _tls_mac_add, _tls_mac_verify
1141+
from scapy.layers.tls.tools import _tls_add_pad, _tls_del_pad
1142+
from scapy.layers.tls.tools import _tls_encrypt, _tls_decrypt
1143+
from scapy.layers.tls.tools import _tls_aead_auth_encrypt, _tls_aead_auth_decrypt
1144+
plain = TLSPlaintext()
1145+
plain.type = 'application_data'
1146+
plain.version = 'TLS 1.2'
1147+
plain.data = b'X\xe1\xb1T\xaa\xb1\x0b\xa0zlg\xf8\xd14]%\xa9\x91d\x08\xc7t\xcd6\xd4"\x9f\xcf'
1148+
plain.len = len(plain.data)
1149+
# Compress/decompress test
1150+
alg = Comp_Deflate()
1151+
comp = _tls_compress(alg, plain)
1152+
assert isinstance(comp, TLSCompressed)
1153+
assert comp != plain
1154+
dcomp = _tls_decompress(alg, comp)
1155+
assert isinstance(dcomp, TLSPlaintext)
1156+
assert dcomp == plain
1157+
# Encrypt/decrypt test
1158+
ch = Cipher_RC4_40(_rc4_40_test.k)
1159+
encr = _tls_encrypt(ch, plain)
1160+
assert isinstance(encr, TLSCiphertext)
1161+
assert encr != plain
1162+
decr = _tls_decrypt(ch, encr)
1163+
assert isinstance(decr, TLSPlaintext)
1164+
assert decr == plain
1165+
encr = _tls_encrypt(ch, comp)
1166+
assert isinstance(encr, TLSCiphertext)
1167+
assert encr != comp
1168+
decr = _tls_decrypt(ch, encr)
1169+
assert isinstance(decr, TLSPlaintext)
1170+
assert (decr.version == comp.version and decr.type == comp.type
1171+
and decr.len == comp.len and decr.data == comp.data)
1172+
# MAC add/verify test
1173+
mac = Hash_SHA256()
1174+
save_encr = encr.copy()
1175+
assert save_encr is not encr
1176+
_tls_mac_add(mac, encr, 1)
1177+
assert isinstance(encr, TLSCiphertext)
1178+
had_mac = _tls_mac_verify(mac, encr, 1)
1179+
assert had_mac
1180+
assert encr == save_encr
1181+
# Pad add/delete test
1182+
save_comp = comp.copy()
1183+
assert save_comp is not comp
1184+
block_size = 8
1185+
_tls_add_pad(comp, block_size)
1186+
assert isinstance(comp, TLSCompressed)
1187+
assert comp.len == save_comp.len + -save_comp.len % block_size + 1
1188+
had_pad = _tls_del_pad(comp)
1189+
assert had_pad
1190+
assert comp == save_comp
1191+
block_size = save_comp.len // 2
1192+
_tls_add_pad(comp, block_size)
1193+
assert isinstance(comp, TLSCompressed)
1194+
assert comp.len == save_comp.len + -save_comp.len % block_size + 1
1195+
had_pad = _tls_del_pad(comp)
1196+
assert had_pad
1197+
assert comp == save_comp
1198+
# AEAD auth encrypt/decrypt test
1199+
ch_auth = Cipher_AES_128_GCM(key=_aes128gcm_test_1.k,
1200+
fixed_iv=_aes128gcm_test_1.n[:4],
1201+
nonce_explicit=pkcs_os2ip(_aes128gcm_test_1.n[4:]))
1202+
auth_encr = _tls_aead_auth_encrypt(ch_auth, comp, 1)
1203+
assert isinstance(auth_encr, TLSCiphertext)
1204+
assert auth_encr != comp
1205+
# auth_decr = _tls_aead_auth_decrypt(ch_auth, auth_encr, 1)
1206+
# assert isinstance(auth_decr, TLSCompressed)
1207+
# assert auth_decr == comp
1208+
ch_auth = Cipher_AES_128_GCM_TLS13(key=_aes128gcm_test_1.k,
1209+
fixed_iv=_aes128gcm_test_1.n)
1210+
auth_encr = _tls_aead_auth_encrypt(ch_auth, comp, 1)
1211+
assert isinstance(auth_encr, TLSCiphertext)
1212+
assert auth_encr != comp
1213+
# auth_decr = _tls_aead_auth_decrypt(ch_auth, auth_encr, 1)
1214+
# assert isinstance(auth_decr, TLSCompressed)
1215+
# assert auth_decr == comp
1216+
1217+
test_tls_tools()
1218+
11291219

11301220
###############################################################################
11311221
############################ Automaton behaviour ##############################

0 commit comments

Comments
 (0)