Skip to content

Commit 38a2b76

Browse files
committed
TLSChangeCipherSpec middlebox in TLS1.3 & tests
1 parent 3ac1272 commit 38a2b76

File tree

9 files changed

+93
-31
lines changed

9 files changed

+93
-31
lines changed

scapy/automaton.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -698,7 +698,10 @@ class CommandMessage(AutomatonException):
698698
# Services
699699
def debug(self, lvl, msg):
700700
if self.debug_level >= lvl:
701-
log_interactive.debug(msg)
701+
if conf.interactive:
702+
log_interactive.debug(msg)
703+
else:
704+
print(msg)
702705

703706
def send(self, pkt):
704707
if self.state.state in self.interception_points:

scapy/layers/tls/automaton_cli.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -879,7 +879,7 @@ def tls13_should_add_ClientHello(self):
879879
c = 0x1301
880880
else:
881881
c = self.ciphersuite
882-
p = TLS13ClientHello(ciphers=self.ciphersuite, ext=ext)
882+
p = TLS13ClientHello(ciphers=c, ext=ext)
883883
self.add_msg(p)
884884
raise self.TLS13_ADDED_CLIENTHELLO()
885885

@@ -1097,7 +1097,7 @@ def tls13_should_add_ClientCertificateVerify(self):
10971097
def TLS13_ADDED_CERTIFICATEVERIFY(self):
10981098
return self.tls13_should_add_ClientFinished()
10991099

1100-
@ATMT.condition(TLS13_PREPARE_CLIENTFLIGHT2)
1100+
@ATMT.condition(TLS13_PREPARE_CLIENTFLIGHT2, prio=2)
11011101
def tls13_should_add_ClientFinished(self):
11021102
self.add_msg(TLSFinished())
11031103
raise self.TLS13_ADDED_CLIENTFINISHED()

scapy/layers/tls/automaton_srv.py

Lines changed: 36 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -641,6 +641,27 @@ def tls13_ADDED_SERVERFINISHED(self):
641641
@ATMT.condition(tls13_ADDED_SERVERFINISHED)
642642
def tls13_should_send_ServerFlight1(self):
643643
self.flush_records()
644+
raise self.tls13_HANDLED_SERVERFLIGHT1()
645+
646+
@ATMT.state()
647+
def tls13_HANDLED_SERVERFLIGHT1(self):
648+
pass
649+
650+
@ATMT.condition(tls13_HANDLED_SERVERFLIGHT1, prio=1)
651+
def tls13_should_handle_ChangeCipherSpec(self):
652+
self.raise_on_packet(TLSChangeCipherSpec,
653+
self.tls13_HANDLED_CHANGECIPHERSPEC)
654+
655+
@ATMT.state()
656+
def tls13_HANDLED_CHANGECIPHERSPEC(self):
657+
pass
658+
659+
@ATMT.condition(tls13_HANDLED_SERVERFLIGHT1, prio=2)
660+
def tls13_missing_ChangeCipherSpec(self):
661+
raise self.tls13_WAITING_CLIENTFLIGHT2()
662+
663+
@ATMT.condition(tls13_HANDLED_CHANGECIPHERSPEC)
664+
def tls13_should_wait_ClientFlight2(self):
644665
raise self.tls13_WAITING_CLIENTFLIGHT2()
645666

646667
@ATMT.state()
@@ -650,19 +671,19 @@ def tls13_WAITING_CLIENTFLIGHT2(self):
650671

651672
@ATMT.state()
652673
def tls13_RECEIVED_CLIENTFLIGHT2(self):
653-
print("tls13_RECEIVED_CLIENTFLIGHT2")
654674
pass
655675

656676
@ATMT.condition(tls13_RECEIVED_CLIENTFLIGHT2, prio=1)
657677
def tls13_should_handle_ClientFlight2(self):
658-
print("tls13_should_handle_ClientFinished")
678+
self.raise_on_packet(TLS13Certificate,
679+
self.TLS13_HANDLED_CLIENTCERTIFICATE)
680+
681+
@ATMT.condition(tls13_RECEIVED_CLIENTFLIGHT2, prio=2)
682+
def tls13_no_ClientCertificate(self):
659683
if self.client_auth:
660-
print("raise_on_packet(TLS13Certificate")
661-
self.raise_on_packet(TLS13Certificate,
662-
self.TLS13_HANDLED_CLIENTCERTIFICATE)
663-
else:
664-
self.raise_on_packet(TLSFinished,
665-
self.TLS13_HANDLED_CLIENTFINISHED)
684+
raise self.TLS13_MISSING_CLIENTCERTIFICATE()
685+
self.raise_on_packet(TLSFinished,
686+
self.TLS13_HANDLED_CLIENTFINISHED)
666687

667688
# RFC8446, section 4.4.2.4 :
668689
# "If the client does not send any certificates (i.e., it sends an empty
@@ -674,9 +695,9 @@ def tls13_should_handle_ClientFlight2(self):
674695
def TLS13_HANDLED_CLIENTCERTIFICATE(self):
675696
if self.client_auth:
676697
self.vprint("Received client certificate chain...")
677-
self.vprint(self.cur_pkt.show())
678698
if isinstance(self.cur_pkt, TLS13Certificate):
679699
if self.cur_pkt.certslen == 0:
700+
self.vprint("but it's empty !")
680701
raise self.TLS13_MISSING_CLIENTCERTIFICATE()
681702

682703
@ATMT.condition(TLS13_HANDLED_CLIENTCERTIFICATE)
@@ -699,11 +720,15 @@ def tls13_should_handle_ClientFinished(self):
699720
self.raise_on_packet(TLSFinished,
700721
self.TLS13_HANDLED_CLIENTFINISHED)
701722

702-
# TODO : change alert code
703723
@ATMT.state()
704724
def TLS13_MISSING_CLIENTCERTIFICATE(self):
705725
self.vprint("Missing ClientCertificate!")
706-
raise self.CLOSE_NOTIFY()
726+
self.add_record()
727+
self.add_msg(TLSAlert(level=2, descr=0x74))
728+
self.flush_records()
729+
self.vprint("Sending TLSAlert 116")
730+
self.socket.close()
731+
raise self.WAITING_CLIENT()
707732

708733
@ATMT.state()
709734
def TLS13_HANDLED_CLIENTFINISHED(self):

scapy/layers/tls/handshake.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@
5555
from cryptography.hazmat.backends import default_backend
5656
from cryptography.hazmat.primitives import hashes
5757

58+
5859
###############################################################################
5960
# Generic TLS Handshake message #
6061
###############################################################################
@@ -295,6 +296,10 @@ def tls_session_update(self, msg_str):
295296

296297
s = self.tls_session
297298
s.advertised_tls_version = self.version
299+
# This ClientHello could be a 1.3 one. Let's store the sid
300+
# in all cases
301+
if self.sidlen and self.sidlen > 0:
302+
s.sid = self.sid
298303
self.random_bytes = msg_str[10:38]
299304
s.client_random = (struct.pack('!I', self.gmt_unix_time) +
300305
self.random_bytes)
@@ -306,7 +311,6 @@ def tls_session_update(self, msg_str):
306311
for e in self.ext:
307312
if isinstance(e, TLS_Ext_SupportedVersion_CH):
308313
s.advertised_tls_version = e.versions[0]
309-
310314
if isinstance(e, TLS_Ext_SignatureAlgorithms):
311315
s.advertised_sig_algs = e.sig_algs
312316

@@ -1060,6 +1064,7 @@ class TLS13CertificateRequest(_TLSHandshake):
10601064
# ServerHelloDone #
10611065
###############################################################################
10621066

1067+
10631068
class TLSServerHelloDone(_TLSHandshake):
10641069
name = "TLS Handshake - Server Hello Done"
10651070
fields_desc = [ByteEnumField("msgtype", 14, _tls_handshake_type),

scapy/layers/tls/record.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,8 @@ def getfield(self, pkt, s):
140140
if (((pkt.tls_session.tls_version or 0x0303) > 0x0200) and
141141
hasattr(pkt, "type") and pkt.type == 23):
142142
return ret, [TLSApplicationData(data=b"")]
143+
elif hasattr(pkt, "type") and pkt.type == 20:
144+
return ret, [TLSChangeCipherSpec()]
143145
else:
144146
return ret, [Raw(load=b"")]
145147

scapy/layers/tls/record_tls13.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,16 +61,30 @@ def pre_dissect(self, s):
6161
return s
6262

6363

64+
class TLSInnerChangeCipherSpec(_GenericTLSSessionInheritance):
65+
__slots__ = ["type"]
66+
name = "TLS Inner Plaintext (CCS)"
67+
fields_desc = [_TLSMsgListField("msg", [], length_from=lambda x: 1)]
68+
69+
def __init__(self, _pkt=None, *args, **kwargs):
70+
self.type = 0x14
71+
super(TLSInnerChangeCipherSpec, self).__init__(_pkt, *args, **kwargs)
72+
73+
6474
class _TLSInnerPlaintextField(PacketField):
6575
def __init__(self, name, default, *args, **kargs):
6676
super(_TLSInnerPlaintextField, self).__init__(name,
6777
default,
6878
TLSInnerPlaintext)
6979

7080
def m2i(self, pkt, m):
81+
if pkt.type == 0x14:
82+
return TLSInnerChangeCipherSpec(m, tls_session=pkt.tls_session)
7183
return self.cls(m, tls_session=pkt.tls_session)
7284

7385
def getfield(self, pkt, s):
86+
if pkt.type == 0x14:
87+
return super(_TLSInnerPlaintextField, self).getfield(pkt, s)
7488
tag_len = pkt.tls_session.rcs.mac_len
7589
frag_len = pkt.len - tag_len
7690
if frag_len < 1:

test/run_tests_py2.bat

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,3 @@ if [%1]==[] (
99
) else (
1010
python "%MYDIR%\scapy\tools\UTscapy.py" %*
1111
)
12-
PAUSE

test/run_tests_py3.bat

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,4 +9,3 @@ if [%1]==[] (
99
) else (
1010
python3 "%MYDIR%\scapy\tools\UTscapy.py" %*
1111
)
12-
PAUSE

test/tls/tests_tls_netaccess.uts

Lines changed: 29 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
############
88
############
99
+ TLS server automaton tests
10+
~ server
1011

1112
### DISCLAIMER: Those tests are slow ###
1213

@@ -60,15 +61,17 @@ def check_output_for_data(out, err, expected_data):
6061
else:
6162
return (False, None)
6263

64+
def get_file(filename):
65+
return os.getenv("SCAPY_ROOT_DIR")+filename if not os.path.exists(filename) else filename
66+
67+
6368
def run_tls_test_server(expected_data, q, curve=None, cookie=False, client_auth=False):
6469
correct = False
6570
print("Server started !")
6671
with captured_output() as (out, err):
6772
# Prepare automaton
68-
filename = "/test/tls/pki/srv_cert.pem"
69-
mycert = os.getenv("SCAPY_ROOT_DIR")+filename if not os.path.exists(filename) else filename
70-
filename = "/test/tls/pki/srv_key.pem"
71-
mykey = os.getenv("SCAPY_ROOT_DIR")+filename if not os.path.exists(filename) else filename
73+
mycert = get_file("/test/tls/pki/srv_cert.pem")
74+
mykey = get_file("/test/tls/pki/srv_key.pem")
7275
print(os.environ["SCAPY_ROOT_DIR"])
7376
print(mykey)
7477
print(mycert)
@@ -100,18 +103,27 @@ def test_tls_server(suite="", version="", tls13=False, client_auth=False):
100103
q_.get()
101104
time.sleep(1)
102105
# Run client
103-
filename = "/test/tls/pki/ca_cert.pem"
104-
filename = os.getenv("SCAPY_ROOT_DIR")+filename if not os.path.exists(filename) else filename
105-
CA_f = os.path.abspath(filename)
106+
CA_f = get_file("/test/tls/pki/ca_cert.pem")
107+
mycert = get_file("/test/tls/pki/cli_cert.pem")
108+
mykey = get_file("/test/tls/pki/cli_key.pem")
109+
args = [
110+
"openssl", "s_client",
111+
"-connect", "127.0.0.1:4433", "-debug",
112+
"-ciphersuites" if tls13 else "-cipher", suite,
113+
version,
114+
"-CAfile", CA_f
115+
]
116+
if client_auth:
117+
args.extend(["-cert", mycert, "-key", mykey])
106118
p = subprocess.Popen(
107-
["openssl", "s_client", "-connect", "127.0.0.1:4433", "-debug", "-ciphersuites" if tls13 else "-cipher", suite, version, "-CAfile", CA_f],
119+
args,
108120
stdin=subprocess.PIPE, stdout=subprocess.PIPE, stderr=subprocess.STDOUT
109121
)
110122
msg += b"\nstop_server\n"
111123
out = p.communicate(input=msg)[0]
112124
print(out.decode())
113125
if p.returncode != 0:
114-
raise RuntimeError("OpenSSL returned with error code")
126+
raise RuntimeError("OpenSSL returned with error code %s" % p.returncode)
115127
else:
116128
p = re.compile(br'verify return:(\d+)')
117129
_failed = False
@@ -165,6 +177,7 @@ test_tls_server("TLS_AES_256_GCM_SHA384", "-tls1_3", tls13=True)
165177
test_tls_server("TLS_AES_256_GCM_SHA384", "-tls1_3", tls13=True, client_auth=True)
166178

167179
+ TLS client automaton tests
180+
~ client
168181

169182
= Load client utils functions
170183

@@ -177,16 +190,18 @@ from scapy.modules.six.moves.queue import Queue
177190

178191
send_data = cipher_suite_code = version = None
179192

180-
def run_tls_test_client(send_data=None, cipher_suite_code=None, version=None):
193+
def run_tls_test_client(send_data=None, cipher_suite_code=None, version=None, client_auth=False):
181194
print("Loading client...")
195+
mycert = get_file("/test/tls/pki/cli_cert.pem") if client_auth else None
196+
mykey = get_file("/test/tls/pki/cli_key.pem") if client_auth else None
182197
if version == "0002":
183-
t = TLSClientAutomaton(data=[send_data, b"stop_server", b"quit"], version="sslv2", debug=5)
198+
t = TLSClientAutomaton(data=[send_data, b"stop_server", b"quit"], version="sslv2", debug=5, mycert=mycert, mykey=mykey)
184199
elif version == "0304":
185200
ch = TLS13ClientHello(ciphers=int(cipher_suite_code, 16))
186-
t = TLSClientAutomaton(client_hello=ch, data=[send_data, b"stop_server", b"quit"], version="tls13", debug=5)
201+
t = TLSClientAutomaton(client_hello=ch, data=[send_data, b"stop_server", b"quit"], version="tls13", debug=5, mycert=mycert, mykey=mykey)
187202
else:
188203
ch = TLSClientHello(version=int(version, 16), ciphers=int(cipher_suite_code, 16))
189-
t = TLSClientAutomaton(client_hello=ch, data=[send_data, b"stop_server", b"quit"], debug=5)
204+
t = TLSClientAutomaton(client_hello=ch, data=[send_data, b"stop_server", b"quit"], debug=5, mycert=mycert, mykey=mykey)
190205
print("Running client...")
191206
t.run()
192207

@@ -204,7 +219,7 @@ def test_tls_client(suite, version, curve=None, cookie=False, client_auth=False)
204219
time.sleep(1)
205220
print("Thread synchronised")
206221
# Run client
207-
run_tls_test_client(msg, suite, version)
222+
run_tls_test_client(msg, suite, version, client_auth)
208223
# Wait for server
209224
print("Client running, waiting...")
210225
th_.join(5)

0 commit comments

Comments
 (0)