Skip to content

Commit 4b5efc2

Browse files
authored
Merge pull request #462 from tlsfuzzer/middlebox-ccs
Fixes for CCS and DSA
2 parents 5960393 + 1ad38b9 commit 4b5efc2

File tree

3 files changed

+132
-9
lines changed

3 files changed

+132
-9
lines changed

tlslite/tlsconnection.py

Lines changed: 42 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -734,7 +734,9 @@ def _clientSendClientHello(self, settings, session, srpUsername,
734734
create(bytearray(b'')))
735735
self._client_keypair = certParams
736736

737+
# fake session_id for middlebox compatibility mode
737738
session_id = getRandomBytes(32)
739+
738740
extensions.append(SupportedVersionsExtension().
739741
create(settings.versions))
740742

@@ -964,6 +966,13 @@ def _clientGetServerHello(self, settings, session, clientHello):
964966
"update to Client Hello"):
965967
yield result
966968

969+
if clientHello.session_id != hello_retry.session_id:
970+
for result in self._sendError(
971+
AlertDescription.illegal_parameter,
972+
"Received HRR session_id does not match the one in "
973+
"ClientHello"):
974+
yield result
975+
967976
ext = clientHello.getExtension(ExtensionType.pre_shared_key)
968977
if ext:
969978
# move the extension to end (in case extension like cookie was
@@ -979,8 +988,12 @@ def _clientGetServerHello(self, settings, session, clientHello):
979988
if session else None)
980989

981990
# resend the client hello with performed changes
982-
ccs = ChangeCipherSpec().create()
983-
for result in self._sendMsgs([ccs, clientHello]):
991+
msgs = []
992+
if clientHello.session_id:
993+
ccs = ChangeCipherSpec().create()
994+
msgs.append(ccs)
995+
msgs.append(clientHello)
996+
for result in self._sendMsgs(msgs):
984997
yield result
985998
self._ccs_sent = True
986999

@@ -1023,6 +1036,13 @@ def _clientGetServerHello(self, settings, session, clientHello):
10231036
"Too new version: {0} (max: {1})"
10241037
.format(real_version, settings.maxVersion)):
10251038
yield result
1039+
if real_version > (3, 3) and \
1040+
serverHello.session_id != clientHello.session_id:
1041+
for result in self._sendError(
1042+
AlertDescription.illegal_parameter,
1043+
"Received ServerHello session_id does not match the one "
1044+
"in ClientHello"):
1045+
yield result
10261046
cipherSuites = CipherSuite.filterForVersion(clientHello.cipher_suites,
10271047
minVersion=real_version,
10281048
maxVersion=real_version)
@@ -1460,7 +1480,7 @@ def _clientTLS13Handshake(self, settings, session, clientHello,
14601480
cl_finished = Finished(self.version, prf_size)
14611481
cl_finished.create(cl_verify_data)
14621482

1463-
if not self._ccs_sent:
1483+
if not self._ccs_sent and clientHello.session_id:
14641484
ccs = ChangeCipherSpec().create()
14651485
msgs = [ccs, cl_finished]
14661486
else:
@@ -1469,6 +1489,8 @@ def _clientTLS13Handshake(self, settings, session, clientHello,
14691489
for result in self._sendMsgs(msgs):
14701490
yield result
14711491

1492+
# CCS messages are not allowed in post handshake authentication
1493+
self._middlebox_compat_mode = False
14721494

14731495
# fully switch to application data
14741496
self._changeWriteState()
@@ -2637,7 +2659,7 @@ def _serverTLS13Handshake(self, settings, clientHello, cipherSuite,
26372659

26382660
msgs = []
26392661
msgs.append(serverHello)
2640-
if not self._ccs_sent:
2662+
if not self._ccs_sent and clientHello.session_id:
26412663
ccs = ChangeCipherSpec().create()
26422664
msgs.append(ccs)
26432665
for result in self._sendMsgs(msgs):
@@ -2714,7 +2736,10 @@ def _serverTLS13Handshake(self, settings, clientHello, cipherSuite,
27142736
ctx = b''
27152737

27162738
# Get list of valid Signing Algorithms
2717-
valid_sig_algs = self._sigHashesToList(settings)
2739+
# we don't support DSA for client certificates yet
2740+
cr_settings = settings.validate()
2741+
cr_settings.dsaSigHashes = []
2742+
valid_sig_algs = self._sigHashesToList(cr_settings)
27182743
assert valid_sig_algs
27192744

27202745
certificate_request = CertificateRequest(self.version)
@@ -2885,6 +2910,9 @@ def _serverTLS13Handshake(self, settings, clientHello, cipherSuite,
28852910
"Finished value is not valid"):
28862911
yield result
28872912

2913+
# disallow CCS messages after handshake
2914+
self._middlebox_compat_mode = False
2915+
28882916
resumption_master_secret = derive_secret(secret,
28892917
bytearray(b'res master'),
28902918
self._handshake_hash,
@@ -3644,8 +3672,11 @@ def _serverGetClientHello(self, settings, private_key, cert_chain,
36443672
hrr.create((3, 3), TLS_1_3_HRR, clientHello.session_id,
36453673
cipherSuite, extensions=hrr_ext)
36463674

3647-
ccs = ChangeCipherSpec().create()
3648-
for result in self._sendMsgs([hrr, ccs]):
3675+
msgs = [hrr]
3676+
if clientHello.session_id:
3677+
ccs = ChangeCipherSpec().create()
3678+
msgs.append(ccs)
3679+
for result in self._sendMsgs(msgs):
36493680
yield result
36503681
self._ccs_sent = True
36513682

@@ -4009,7 +4040,10 @@ def _serverCertKeyExchange(self, clientHello, serverHello, sigHashAlg,
40094040
certificateRequest = CertificateRequest(self.version)
40104041
if not reqCAs:
40114042
reqCAs = []
4012-
valid_sig_algs = self._sigHashesToList(settings)
4043+
cr_settings = settings.validate()
4044+
# we don't support DSA in client certificates yet
4045+
cr_settings.dsaSigHashes = []
4046+
valid_sig_algs = self._sigHashesToList(cr_settings)
40134047
certificateRequest.create([ClientCertificateType.rsa_sign,
40144048
ClientCertificateType.ecdsa_sign],
40154049
reqCAs,

tlslite/tlsrecordlayer.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,11 @@ def __init__(self, sock):
202202
# doesn't provide a certificate
203203
self.client_cert_required = False
204204

205+
# boolean to control if ChangeCipherSpec in TLS1.3 is allowed or not
206+
# we start with True, as peers MUST always processe the messages
207+
# before the handshake is done, only then we disable it (for PHA)
208+
self._middlebox_compat_mode = True
209+
205210
@property
206211
def _send_record_limit(self):
207212
"""Maximum size of payload that can be sent."""
@@ -1006,6 +1011,7 @@ def _getMsg(self, expectedType, secondaryType=None, constructorType=None):
10061011
# continue
10071012
if self.version > (3, 3) and \
10081013
ContentType.handshake in expectedType and \
1014+
self._middlebox_compat_mode and \
10091015
recordHeader.type == ContentType.change_cipher_spec:
10101016
ccs = ChangeCipherSpec().parse(p)
10111017
if ccs.type != 1:

unit_tests/test_tlslite_tlsconnection.py

Lines changed: 84 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,17 @@
1414

1515
from tlslite.recordlayer import RecordLayer
1616
from tlslite.messages import ServerHello, ClientHello, Alert, RecordHeader3
17-
from tlslite.constants import CipherSuite, AlertDescription, ContentType
17+
from tlslite.constants import CipherSuite, AlertDescription, ContentType, \
18+
GroupName, TLS_1_3_HRR
1819
from tlslite.tlsconnection import TLSConnection
1920
from tlslite.errors import TLSLocalAlert, TLSRemoteAlert
2021
from tlslite.x509 import X509
2122
from tlslite.x509certchain import X509CertChain
2223
from tlslite.utils.keyfactory import parsePEMKey
2324
from tlslite.handshakesettings import HandshakeSettings
2425
from tlslite.session import Session
26+
from tlslite.extensions import SrvSupportedVersionsExtension, \
27+
ServerKeyShareExtension, KeyShareEntry, HRRKeyShareExtension
2528

2629
from unit_tests.mocksock import MockSocket
2730

@@ -205,6 +208,86 @@ def test_server_with_client_not_using_required_EMS(self):
205208
self.assertEqual(err.exception.description,
206209
AlertDescription.insufficient_security)
207210

211+
def test_client_with_server_responing_with_wrong_session_id_in_TLS1_3(self):
212+
# socket to generate the faux response
213+
gen_sock = MockSocket(bytearray(0))
214+
215+
gen_record_layer = RecordLayer(gen_sock)
216+
gen_record_layer.version = (3, 3)
217+
218+
srv_ext = []
219+
srv_ext.append(SrvSupportedVersionsExtension().create((3, 4)))
220+
srv_ext.append(ServerKeyShareExtension().create(
221+
KeyShareEntry().create(
222+
GroupName.secp256r1, bytearray(b'\x03' + b'\x01' * 32))))
223+
224+
server_hello = ServerHello().create(
225+
version=(3, 3),
226+
random=bytearray(32),
227+
session_id=bytearray(b"test"),
228+
cipher_suite=CipherSuite.TLS_AES_128_GCM_SHA256,
229+
certificate_type=None,
230+
tackExt=None,
231+
next_protos_advertised=None,
232+
extensions=srv_ext)
233+
234+
for res in gen_record_layer.sendRecord(server_hello):
235+
if res in (0, 1):
236+
self.assertTrue(False, "Blocking socket")
237+
else:
238+
break
239+
240+
# test proper
241+
sock = MockSocket(gen_sock.sent[0])
242+
243+
conn = TLSConnection(sock)
244+
245+
with self.assertRaises(TLSLocalAlert) as err:
246+
conn.handshakeClientCert()
247+
248+
self.assertEqual(err.exception.description,
249+
AlertDescription.illegal_parameter)
250+
251+
def test_client_with_server_responing_with_wrong_session_id_in_TLS1_3_HRR(self):
252+
# socket to generate the faux response
253+
gen_sock = MockSocket(bytearray(0))
254+
255+
gen_record_layer = RecordLayer(gen_sock)
256+
gen_record_layer.version = (3, 3)
257+
258+
srv_ext = []
259+
srv_ext.append(SrvSupportedVersionsExtension().create((3, 4)))
260+
srv_ext.append(HRRKeyShareExtension().create(
261+
GroupName.secp521r1))
262+
263+
server_hello = ServerHello().create(
264+
version=(3, 3),
265+
random=TLS_1_3_HRR,
266+
session_id=bytearray(b"test"),
267+
cipher_suite=CipherSuite.TLS_AES_128_GCM_SHA256,
268+
certificate_type=None,
269+
tackExt=None,
270+
next_protos_advertised=None,
271+
extensions=srv_ext)
272+
273+
for res in gen_record_layer.sendRecord(server_hello):
274+
if res in (0, 1):
275+
self.assertTrue(False, "Blocking socket")
276+
else:
277+
break
278+
279+
# test proper
280+
sock = MockSocket(gen_sock.sent[0])
281+
282+
conn = TLSConnection(sock)
283+
284+
with self.assertRaises(TLSLocalAlert) as err:
285+
conn.handshakeClientCert()
286+
287+
self.assertEqual(err.exception.description,
288+
AlertDescription.illegal_parameter)
289+
290+
208291
def prepare_mock_socket_with_handshake_failure(self):
209292
alertObj = Alert().create(AlertDescription.handshake_failure)
210293
alert = alertObj.write()

0 commit comments

Comments
 (0)