Skip to content

Commit

Permalink
Implement FALLBACK_SCSV.
Browse files Browse the repository at this point in the history
Adapted from a Chromium patch, with tests and client-side portions
added.
  • Loading branch information
davidben committed Mar 24, 2015
1 parent 6cc42d4 commit fa8db51
Show file tree
Hide file tree
Showing 5 changed files with 75 additions and 11 deletions.
59 changes: 50 additions & 9 deletions tests/tlstest.py
Original file line number Diff line number Diff line change
Expand Up @@ -423,8 +423,31 @@ def connect():
#print(" Next-Protocol Negotiated: %s" % connection.next_proto)
assert(connection.next_proto == b'spdy/2')
connection.close()

print('Test 25 - good standard XMLRPC https client')

print("Test 25.a - FALLBACK_SCSV")
synchro.recv(1)
connection = connect()
settings = HandshakeSettings()
settings.sendFallbackSCSV = True
connection.handshakeClientCert(settings=settings)
testConnClient(connection)
connection.close()

print("Test 25.b - FALLBACK_SCSV")
synchro.recv(1)
connection = connect()
settings = HandshakeSettings()
settings.sendFallbackSCSV = True
settings.maxVersion = (3, 2)
try:
connection.handshakeClientCert(settings=settings)
assert()
except TLSRemoteAlert as alert:
if alert.description != AlertDescription.inappropriate_fallback:
raise
connection.close()

print('Test 26 - good standard XMLRPC https client')
address = address[0], address[1]+1
synchro.recv(1)
try:
Expand All @@ -441,30 +464,30 @@ def connect():
synchro.recv(1)
assert server.pow(2,4) == 16

print('Test 26 - good tlslite XMLRPC client')
print('Test 27 - good tlslite XMLRPC client')
transport = XMLRPCTransport(ignoreAbruptClose=True)
server = xmlrpclib.Server('https://%s:%s' % address, transport)
synchro.recv(1)
assert server.add(1,2) == 3
synchro.recv(1)
assert server.pow(2,4) == 16

print('Test 27 - good XMLRPC ignored protocol')
print('Test 28 - good XMLRPC ignored protocol')
server = xmlrpclib.Server('http://%s:%s' % address, transport)
synchro.recv(1)
assert server.add(1,2) == 3
synchro.recv(1)
assert server.pow(2,4) == 16
print("Test 28 - Internet servers test")

print("Test 29 - Internet servers test")
try:
i = IMAP4_TLS("cyrus.andrew.cmu.edu")
i.login("anonymous", "[email protected]")
i.logout()
print("Test 28: IMAP4 good")
print("Test 30: IMAP4 good")
p = POP3_TLS("pop.gmail.com")
p.quit()
print("Test 29: POP3 good")
print("Test 31: POP3 good")
except socket.error as e:
print("Non-critical error: socket error trying to reach internet server: ", e)

Expand Down Expand Up @@ -843,7 +866,25 @@ def server_bind(self):
testConnServer(connection)
connection.close()

print("Tests 25-27 - XMLRPXC server")
print("Test 25.a - FALLBACK_SCSV")
synchro.send(b'R')
connection = connect()
connection.handshakeServer(certChain=x509Chain, privateKey=x509Key)
testConnServer(connection)
connection.close()

print("Test 25.b - FALLBACK_SCSV")
synchro.send(b'R')
connection = connect()
try:
connection.handshakeServer(certChain=x509Chain, privateKey=x509Key)
assert()
except TLSLocalAlert as alert:
if alert.description != AlertDescription.inappropriate_fallback:
raise
connection.close()

print("Tests 26-28 - XMLRPXC server")
address = address[0], address[1]+1
class Server(TLSXMLRPCServer):

Expand Down
4 changes: 4 additions & 0 deletions tlslite/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,7 @@ class AlertDescription:
protocol_version = 70
insufficient_security = 71
internal_error = 80
inappropriate_fallback = 86
user_canceled = 90
no_renegotiation = 100
unknown_psk_identity = 115
Expand All @@ -115,6 +116,9 @@ class CipherSuite:
# We actually don't do any renegotiation, but this
# prevents renegotiation attacks
TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF

# draft-ietf-tls-downgrade-scsv-03
TLS_FALLBACK_SCSV = 0x5600

TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = 0xC01A
TLS_SRP_SHA_WITH_AES_128_CBC_SHA = 0xC01D
Expand Down
1 change: 1 addition & 0 deletions tlslite/errors.py
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,7 @@ class TLSAlert(TLSError):
AlertDescription.protocol_version: "protocol_version",\
AlertDescription.insufficient_security: "insufficient_security",\
AlertDescription.internal_error: "internal_error",\
AlertDescription.inappropriate_fallback: "inappropriate_fallback",\
AlertDescription.user_canceled: "user_canceled",\
AlertDescription.no_renegotiation: "no_renegotiation",\
AlertDescription.unknown_psk_identity: "unknown_psk_identity"}
Expand Down
5 changes: 5 additions & 0 deletions tlslite/handshakesettings.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,9 @@ class HandshakeSettings(object):
Note that TACK support is not standardized by IETF and uses a temporary
TLS Extension number, so should NOT be used in production software.
@type sendFallbackSCSV: bool
@ivar sendFallbackSCSV: Whether to, as a client, send FALLBACK_SCSV.
"""
def __init__(self):
self.minKeySize = 1023
Expand All @@ -106,6 +109,7 @@ def __init__(self):
self.minVersion = (3,1)
self.maxVersion = (3,3)
self.useExperimentalTackExtension = False
self.sendFallbackSCSV = False

# Validates the min/max fields, and certificateTypes
# Filters out unsupported cipherNames and cipherImplementations
Expand All @@ -119,6 +123,7 @@ def _filter(self):
other.certificateTypes = self.certificateTypes
other.minVersion = self.minVersion
other.maxVersion = self.maxVersion
other.sendFallbackSCSV = self.sendFallbackSCSV

if not cipherfactory.tripleDESPresent:
other.cipherNames = [e for e in self.cipherNames if e != "3des"]
Expand Down
17 changes: 15 additions & 2 deletions tlslite/tlsconnection.py
Original file line number Diff line number Diff line change
Expand Up @@ -506,6 +506,12 @@ def _clientSendClientHello(self, settings, session, srpUsername,
else:
assert(False)

#Add any SCSVs. These are not real cipher suites, but signaling
#values which reuse the cipher suite field in the ClientHello.
wireCipherSuites = list(cipherSuites)
if settings.sendFallbackSCSV:
wireCipherSuites.append(CipherSuite.TLS_FALLBACK_SCSV)

#Initialize acceptable certificate types
certificateTypes = settings._getCertificateTypes()

Expand All @@ -519,7 +525,7 @@ def _clientSendClientHello(self, settings, session, srpUsername,
else:
clientHello = ClientHello()
clientHello.create(settings.maxVersion, getRandomBytes(32),
session.sessionID, cipherSuites,
session.sessionID, wireCipherSuites,
certificateTypes,
session.srpUsername,
reqTack, nextProtos is not None,
Expand All @@ -529,7 +535,7 @@ def _clientSendClientHello(self, settings, session, srpUsername,
else:
clientHello = ClientHello()
clientHello.create(settings.maxVersion, getRandomBytes(32),
bytearray(0), cipherSuites,
bytearray(0), wireCipherSuites,
certificateTypes,
srpUsername,
reqTack, nextProtos is not None,
Expand Down Expand Up @@ -1257,6 +1263,13 @@ def _serverGetClientHello(self, settings, certChain, verifierDB,
#Set the version to the client's version
self.version = clientHello.client_version

#Detect if the client performed an inappropriate fallback.
if clientHello.client_version < settings.maxVersion and \
CipherSuite.TLS_FALLBACK_SCSV in clientHello.cipher_suites:
for result in self._sendError(\
AlertDescription.inappropriate_fallback):
yield result

#If resumption was requested and we have a session cache...
if clientHello.session_id and sessionCache:
session = None
Expand Down

0 comments on commit fa8db51

Please sign in to comment.