Skip to content

Commit 5f9123c

Browse files
committed
Select ciphers based of PRF of PSKs we can negotiate
1 parent 4263b0b commit 5f9123c

File tree

4 files changed

+123
-3
lines changed

4 files changed

+123
-3
lines changed

tests/tlstest.py

Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -728,6 +728,38 @@ def connect():
728728
connection.handshakeClientCert(settings=settings)
729729
assert connection.session.serverCertChain is None
730730
assert connection.ecdhCurve is not None
731+
assert connection.session.cipherSuite in \
732+
constants.CipherSuite.sha384PrfSuites
733+
testConnClient(connection)
734+
connection.close()
735+
736+
test_no += 1
737+
738+
print("Test {0} - good PSK SHA-256 PRF".format(test_no))
739+
synchro.recv(1)
740+
connection = connect()
741+
settings = HandshakeSettings()
742+
settings.pskConfigs = [(b'test', b'\x00secret', 'sha256')]
743+
connection.handshakeClientCert(settings=settings)
744+
assert connection.session.serverCertChain is None
745+
assert connection.ecdhCurve is not None
746+
assert connection.session.cipherSuite in \
747+
constants.CipherSuite.sha256PrfSuites
748+
testConnClient(connection)
749+
connection.close()
750+
751+
test_no += 1
752+
753+
print("Test {0} - good PSK default PRF".format(test_no))
754+
synchro.recv(1)
755+
connection = connect()
756+
settings = HandshakeSettings()
757+
settings.pskConfigs = [(b'test', b'\x00secret', 'sha256')]
758+
connection.handshakeClientCert(settings=settings)
759+
assert connection.session.serverCertChain is None
760+
assert connection.ecdhCurve is not None
761+
assert connection.session.cipherSuite in \
762+
constants.CipherSuite.sha256PrfSuites
731763
testConnClient(connection)
732764
connection.close()
733765

@@ -761,6 +793,21 @@ def connect():
761793

762794
test_no += 1
763795

796+
print("Test {0} - bad PSK X.509 fallback".format(test_no))
797+
synchro.recv(1)
798+
connection = connect()
799+
settings = HandshakeSettings()
800+
settings.pskConfigs = [(b'bad identity', b'\x00secret', 'sha256')]
801+
connection.handshakeClientCert(settings=settings)
802+
assert connection.session.serverCertChain
803+
assert connection.ecdhCurve is not None
804+
assert connection.session.cipherSuite in \
805+
constants.CipherSuite.sha384PrfSuites
806+
testConnClient(connection)
807+
connection.close()
808+
809+
test_no += 1
810+
764811
print("Test {0} - good SRP (db)".format(test_no))
765812
print("client {0} - waiting for synchro".format(time.time()))
766813
try:
@@ -2353,6 +2400,30 @@ def connect():
23532400

23542401
test_no += 1
23552402

2403+
print("Test {0} - good PSK SHA-256 PRF".format(test_no))
2404+
synchro.send(b'R')
2405+
settings = HandshakeSettings()
2406+
settings.pskConfigs = [(b'test', b'\x00secret', 'sha256')]
2407+
connection = connect()
2408+
connection.handshakeServer(certChain=x509Chain, privateKey=x509Key,
2409+
settings=settings)
2410+
testConnServer(connection)
2411+
connection.close()
2412+
2413+
test_no += 1
2414+
2415+
print("Test {0} - good PSK default PRF".format(test_no))
2416+
synchro.send(b'R')
2417+
settings = HandshakeSettings()
2418+
settings.pskConfigs = [(b'test', b'\x00secret')]
2419+
connection = connect()
2420+
connection.handshakeServer(certChain=x509Chain, privateKey=x509Key,
2421+
settings=settings)
2422+
testConnServer(connection)
2423+
connection.close()
2424+
2425+
test_no += 1
2426+
23562427
print("Test {0} - good PSK, no DH".format(test_no))
23572428
synchro.send(b'R')
23582429
settings = HandshakeSettings()
@@ -2378,6 +2449,18 @@ def connect():
23782449

23792450
test_no += 1
23802451

2452+
print("Test {0} - bad PSK X.509 fallback".format(test_no))
2453+
synchro.send(b'R')
2454+
settings = HandshakeSettings()
2455+
settings.pskConfigs = [(b'test', b'\x00secret', 'sha256')]
2456+
connection = connect()
2457+
connection.handshakeServer(certChain=x509Chain, privateKey=x509Key,
2458+
settings=settings)
2459+
testConnServer(connection)
2460+
connection.close()
2461+
2462+
test_no += 1
2463+
23812464
print("Test {0} - good SRP (db)".format(test_no))
23822465
try:
23832466
import logging

tlslite/constants.py

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1205,7 +1205,7 @@ class CipherSuite:
12051205
aeadSuites.extend(chacha20Suites)
12061206
aeadSuites.extend(chacha20draft00Suites)
12071207

1208-
#: TLS1.2 with SHA384 PRF
1208+
#: any with SHA384 PRF
12091209
sha384PrfSuites = []
12101210
sha384PrfSuites.extend(sha384Suites)
12111211
sha384PrfSuites.extend(aes256GcmSuites)
@@ -1227,6 +1227,12 @@ class CipherSuite:
12271227
tls12Suites.extend(sha384Suites)
12281228
tls12Suites.extend(aeadSuites)
12291229

1230+
#: any that will end up using SHA256 PRF in TLS 1.2 or later
1231+
sha256PrfSuites = []
1232+
sha256PrfSuites.extend(tls12Suites)
1233+
for i in sha384PrfSuites:
1234+
sha256PrfSuites.remove(i)
1235+
12301236
#: TLS1.3 specific ciphersuites
12311237
tls13Suites = []
12321238

@@ -1280,6 +1286,23 @@ def filter_for_certificate(suites, cert_chain):
12801286
includeSuites.update(CipherSuite.ecdhAnonSuites)
12811287
return [s for s in suites if s in includeSuites]
12821288

1289+
@staticmethod
1290+
def filter_for_prfs(suites, prfs):
1291+
"""Return a copy of suites without ciphers incompatible with the
1292+
specified prfs (sha256 or sha384)"""
1293+
includeSuites = set()
1294+
prfs = set(prfs)
1295+
if None in prfs:
1296+
prfs.update(["sha256"])
1297+
prfs.remove(None)
1298+
assert len(prfs) <= 2, prfs
1299+
1300+
if "sha256" in prfs:
1301+
includeSuites.update(CipherSuite.sha256PrfSuites)
1302+
if "sha384" in prfs:
1303+
includeSuites.update(CipherSuite.sha384PrfSuites)
1304+
return [s for s in suites if s in includeSuites]
1305+
12831306
@staticmethod
12841307
def _filterSuites(suites, settings, version=None):
12851308
if version is None:

tlslite/handshakesettings.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -298,7 +298,7 @@ class HandshakeSettings(object):
298298
(bytearray, can be empty for TLS 1.2 and earlier), second element is
299299
the binary secret (bytearray), third is an optional parameter
300300
specifying the PRF hash to be used in TLS 1.3 (``sha256`` or
301-
``sha384``)
301+
``sha384``, with ``sha256`` being the default)
302302
303303
:vartype ticketKeys: list(bytearray)
304304
:ivar ticketKeys: keys to be used for encrypting and decrypting session

tlslite/tlsconnection.py

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3960,14 +3960,15 @@ def _server_select_certificate(self, settings, client_hello,
39603960
else:
39613961
client_sigalgs = []
39623962

3963+
client_psks = client_hello.getExtension(ExtensionType.pre_shared_key)
3964+
39633965
# Get all the certificates we can offer
39643966
alt_certs = ((X509CertChain(i.certificates), i.key) for vh in
39653967
settings.virtual_hosts for i in vh.keys)
39663968
certs = [(cert, key)
39673969
for cert, key in chain([(cert_chain, private_key)], alt_certs)]
39683970

39693971
for cert, key in certs:
3970-
39713972
# Check if this is the last (cert, key) pair we have to check
39723973
if (cert, key) == certs[-1]:
39733974
last_cert = True
@@ -3977,10 +3978,23 @@ def _server_select_certificate(self, settings, client_hello,
39773978
try:
39783979
# Find a suitable ciphersuite based on the certificate
39793980
ciphers = CipherSuite.filter_for_certificate(cipher_suites, cert)
3981+
# but if we have matching PSKs, prefer those
3982+
if settings.pskConfigs and client_psks:
3983+
client_identities = [
3984+
i.identity for i in client_psks.identities]
3985+
psks_prfs = [i[2] if len(i) == 3 else None for i in
3986+
settings.pskConfigs if
3987+
i[0] in client_identities]
3988+
if psks_prfs:
3989+
ciphers = CipherSuite.filter_for_prfs(ciphers,
3990+
psks_prfs)
39803991
for cipher in ciphers:
3992+
# select first mutually supported
39813993
if cipher in client_hello.cipher_suites:
39823994
break
39833995
else:
3996+
# abort with context-specific alert if client indicated
3997+
# support for FFDHE groups
39843998
if client_groups and \
39853999
any(i in range(256, 512) for i in client_groups) and \
39864000
any(i in CipherSuite.dhAllSuites

0 commit comments

Comments
 (0)