Index: third_party/tlslite/tlslite/tlsconnection.py |
diff --git a/third_party/tlslite/tlslite/tlsconnection.py b/third_party/tlslite/tlslite/tlsconnection.py |
index 90449afe13faf78390ad67acfbb0848f68147f8b..20cd85bc90f31b456ec45c5d23a746123b962346 100644 |
--- a/third_party/tlslite/tlslite/tlsconnection.py |
+++ b/third_party/tlslite/tlslite/tlsconnection.py |
@@ -1,18 +1,27 @@ |
+# Authors: |
+# Trevor Perrin |
+# Google - added reqCAs parameter |
+# Google (adapted by Sam Rushing and Marcelo Fernandez) - NPN support |
+# Dimitris Moraitis - Anon ciphersuites |
+# Martin von Loewis - python 3 port |
+# |
+# See the LICENSE file for legal information regarding use of this file. |
+ |
""" |
MAIN CLASS FOR TLS LITE (START HERE!). |
""" |
-from __future__ import generators |
import socket |
-from utils.compat import formatExceptionTrace |
-from tlsrecordlayer import TLSRecordLayer |
-from session import Session |
-from constants import * |
-from utils.cryptomath import getRandomBytes |
-from errors import * |
-from messages import * |
-from mathtls import * |
-from handshakesettings import HandshakeSettings |
+from .utils.compat import formatExceptionTrace |
+from .tlsrecordlayer import TLSRecordLayer |
+from .session import Session |
+from .constants import * |
+from .utils.cryptomath import getRandomBytes |
+from .errors import * |
+from .messages import * |
+from .mathtls import * |
+from .handshakesettings import HandshakeSettings |
+from .utils.tackwrapper import * |
class TLSConnection(TLSRecordLayer): |
@@ -36,11 +45,9 @@ class TLSConnection(TLSRecordLayer): |
not use the asynchronous functions directly, but should use some |
framework like asyncore or Twisted which TLS Lite integrates with |
(see |
- L{tlslite.integration.TLSAsyncDispatcherMixIn.TLSAsyncDispatcherMixIn} or |
- L{tlslite.integration.TLSTwistedProtocolWrapper.TLSTwistedProtocolWrapper}). |
+ L{tlslite.integration.tlsasyncdispatchermixin.TLSAsyncDispatcherMixIn}). |
""" |
- |
def __init__(self, sock): |
"""Create a new TLSConnection instance. |
@@ -52,23 +59,18 @@ class TLSConnection(TLSRecordLayer): |
""" |
TLSRecordLayer.__init__(self, sock) |
- def handshakeClientSRP(self, username, password, session=None, |
- settings=None, checker=None, async=False): |
- """Perform an SRP handshake in the role of client. |
- |
- This function performs a TLS/SRP handshake. SRP mutually |
- authenticates both parties to each other using only a |
- username and password. This function may also perform a |
- combined SRP and server-certificate handshake, if the server |
- chooses to authenticate itself with a certificate chain in |
- addition to doing SRP. |
+ #********************************************************* |
+ # Client Handshake Functions |
+ #********************************************************* |
- TLS/SRP is non-standard. Most TLS implementations don't |
- support it. See |
- U{http://www.ietf.org/html.charters/tls-charter.html} or |
- U{http://trevp.net/tlssrp/} for the latest information on |
- TLS/SRP. |
+ def handshakeClientAnonymous(self, session=None, settings=None, |
+ checker=None, serverName="", |
+ async=False): |
+ """Perform an anonymous handshake in the role of client. |
+ This function performs an SSL or TLS handshake using an |
+ anonymous Diffie Hellman ciphersuite. |
+ |
Like any handshake function, this can be called on a closed |
TLS connection, or on a TLS connection that is already open. |
If called on an open connection it performs a re-handshake. |
@@ -79,17 +81,10 @@ class TLSConnection(TLSRecordLayer): |
If an exception is raised, the connection will have been |
automatically closed (if it was ever open). |
- @type username: str |
- @param username: The SRP username. |
- |
- @type password: str |
- @param password: The SRP password. |
- |
@type session: L{tlslite.Session.Session} |
- @param session: A TLS session to attempt to resume. This |
- session must be an SRP session performed with the same username |
- and password as were passed in. If the resumption does not |
- succeed, a full SRP handshake will be performed. |
+ @param session: A TLS session to attempt to resume. If the |
+ resumption does not succeed, a full handshake will be |
+ performed. |
@type settings: L{tlslite.HandshakeSettings.HandshakeSettings} |
@param settings: Various settings which can be used to control |
@@ -100,6 +95,9 @@ class TLSConnection(TLSRecordLayer): |
@param checker: A Checker instance. This instance will be |
invoked to examine the other party's authentication |
credentials, if the handshake completes succesfully. |
+ |
+ @type serverName: string |
+ @param serverName: The ServerNameIndication TLS Extension. |
@type async: bool |
@param async: If False, this function will block until the |
@@ -120,35 +118,28 @@ class TLSConnection(TLSRecordLayer): |
@raise tlslite.errors.TLSAuthenticationError: If the checker |
doesn't like the other party's authentication credentials. |
""" |
- handshaker = self._handshakeClientAsync(srpParams=(username, password), |
- session=session, settings=settings, checker=checker) |
+ handshaker = self._handshakeClientAsync(anonParams=(True), |
+ session=session, |
+ settings=settings, |
+ checker=checker, |
+ serverName=serverName) |
if async: |
return handshaker |
for result in handshaker: |
pass |
- def handshakeClientCert(self, certChain=None, privateKey=None, |
- session=None, settings=None, checker=None, |
- async=False): |
- """Perform a certificate-based handshake in the role of client. |
- |
- This function performs an SSL or TLS handshake. The server |
- will authenticate itself using an X.509 or cryptoID certificate |
- chain. If the handshake succeeds, the server's certificate |
- chain will be stored in the session's serverCertChain attribute. |
- Unless a checker object is passed in, this function does no |
- validation or checking of the server's certificate chain. |
- |
- If the server requests client authentication, the |
- client will send the passed-in certificate chain, and use the |
- passed-in private key to authenticate itself. If no |
- certificate chain and private key were passed in, the client |
- will attempt to proceed without client authentication. The |
- server may or may not allow this. |
+ def handshakeClientSRP(self, username, password, session=None, |
+ settings=None, checker=None, |
+ reqTack=True, serverName="", |
+ async=False): |
+ """Perform an SRP handshake in the role of client. |
- Like any handshake function, this can be called on a closed |
- TLS connection, or on a TLS connection that is already open. |
- If called on an open connection it performs a re-handshake. |
+ This function performs a TLS/SRP handshake. SRP mutually |
+ authenticates both parties to each other using only a |
+ username and password. This function may also perform a |
+ combined SRP and server-certificate handshake, if the server |
+ chooses to authenticate itself with a certificate chain in |
+ addition to doing SRP. |
If the function completes without raising an exception, the |
TLS connection will be open and available for data transfer. |
@@ -156,30 +147,35 @@ class TLSConnection(TLSRecordLayer): |
If an exception is raised, the connection will have been |
automatically closed (if it was ever open). |
- @type certChain: L{tlslite.X509CertChain.X509CertChain} or |
- L{cryptoIDlib.CertChain.CertChain} |
- @param certChain: The certificate chain to be used if the |
- server requests client authentication. |
+ @type username: str |
+ @param username: The SRP username. |
- @type privateKey: L{tlslite.utils.RSAKey.RSAKey} |
- @param privateKey: The private key to be used if the server |
- requests client authentication. |
+ @type password: str |
+ @param password: The SRP password. |
- @type session: L{tlslite.Session.Session} |
- @param session: A TLS session to attempt to resume. If the |
- resumption does not succeed, a full handshake will be |
- performed. |
+ @type session: L{tlslite.session.Session} |
+ @param session: A TLS session to attempt to resume. This |
+ session must be an SRP session performed with the same username |
+ and password as were passed in. If the resumption does not |
+ succeed, a full SRP handshake will be performed. |
- @type settings: L{tlslite.HandshakeSettings.HandshakeSettings} |
+ @type settings: L{tlslite.handshakesettings.HandshakeSettings} |
@param settings: Various settings which can be used to control |
the ciphersuites, certificate types, and SSL/TLS versions |
offered by the client. |
- @type checker: L{tlslite.Checker.Checker} |
+ @type checker: L{tlslite.checker.Checker} |
@param checker: A Checker instance. This instance will be |
invoked to examine the other party's authentication |
credentials, if the handshake completes succesfully. |
+ @type reqTack: bool |
+ @param reqTack: Whether or not to send a "tack" TLS Extension, |
+ requesting the server return a TackExtension if it has one. |
+ |
+ @type serverName: string |
+ @param serverName: The ServerNameIndication TLS Extension. |
+ |
@type async: bool |
@param async: If False, this function will block until the |
handshake is completed. If True, this function will return a |
@@ -199,68 +195,80 @@ class TLSConnection(TLSRecordLayer): |
@raise tlslite.errors.TLSAuthenticationError: If the checker |
doesn't like the other party's authentication credentials. |
""" |
- handshaker = self._handshakeClientAsync(certParams=(certChain, |
- privateKey), session=session, settings=settings, |
- checker=checker) |
+ handshaker = self._handshakeClientAsync(srpParams=(username, password), |
+ session=session, settings=settings, checker=checker, |
+ reqTack=reqTack, serverName=serverName) |
+ # The handshaker is a Python Generator which executes the handshake. |
+ # It allows the handshake to be run in a "piecewise", asynchronous |
+ # fashion, returning 1 when it is waiting to able to write, 0 when |
+ # it is waiting to read. |
+ # |
+ # If 'async' is True, the generator is returned to the caller, |
+ # otherwise it is executed to completion here. |
if async: |
return handshaker |
for result in handshaker: |
pass |
- def handshakeClientUnknown(self, srpCallback=None, certCallback=None, |
- session=None, settings=None, checker=None, |
- async=False): |
- """Perform a to-be-determined type of handshake in the role of client. |
+ def handshakeClientCert(self, certChain=None, privateKey=None, |
+ session=None, settings=None, checker=None, |
+ nextProtos=None, reqTack=True, serverName="", |
+ async=False): |
+ """Perform a certificate-based handshake in the role of client. |
+ |
+ This function performs an SSL or TLS handshake. The server |
+ will authenticate itself using an X.509 certificate |
+ chain. If the handshake succeeds, the server's certificate |
+ chain will be stored in the session's serverCertChain attribute. |
+ Unless a checker object is passed in, this function does no |
+ validation or checking of the server's certificate chain. |
- This function performs an SSL or TLS handshake. If the server |
- requests client certificate authentication, the |
- certCallback will be invoked and should return a (certChain, |
- privateKey) pair. If the callback returns None, the library |
+ If the server requests client authentication, the |
+ client will send the passed-in certificate chain, and use the |
+ passed-in private key to authenticate itself. If no |
+ certificate chain and private key were passed in, the client |
will attempt to proceed without client authentication. The |
server may or may not allow this. |
- If the server requests SRP authentication, the srpCallback |
- will be invoked and should return a (username, password) pair. |
- If the callback returns None, the local implementation will |
- signal a user_canceled error alert. |
- |
- After the handshake completes, the client can inspect the |
- connection's session attribute to determine what type of |
- authentication was performed. |
- |
- Like any handshake function, this can be called on a closed |
- TLS connection, or on a TLS connection that is already open. |
- If called on an open connection it performs a re-handshake. |
- |
If the function completes without raising an exception, the |
TLS connection will be open and available for data transfer. |
If an exception is raised, the connection will have been |
automatically closed (if it was ever open). |
- @type srpCallback: callable |
- @param srpCallback: The callback to be used if the server |
- requests SRP authentication. If None, the client will not |
- offer support for SRP ciphersuites. |
+ @type certChain: L{tlslite.x509certchain.X509CertChain} |
+ @param certChain: The certificate chain to be used if the |
+ server requests client authentication. |
- @type certCallback: callable |
- @param certCallback: The callback to be used if the server |
- requests client certificate authentication. |
+ @type privateKey: L{tlslite.utils.rsakey.RSAKey} |
+ @param privateKey: The private key to be used if the server |
+ requests client authentication. |
- @type session: L{tlslite.Session.Session} |
+ @type session: L{tlslite.session.Session} |
@param session: A TLS session to attempt to resume. If the |
resumption does not succeed, a full handshake will be |
performed. |
- @type settings: L{tlslite.HandshakeSettings.HandshakeSettings} |
+ @type settings: L{tlslite.handshakesettings.HandshakeSettings} |
@param settings: Various settings which can be used to control |
the ciphersuites, certificate types, and SSL/TLS versions |
offered by the client. |
- @type checker: L{tlslite.Checker.Checker} |
+ @type checker: L{tlslite.checker.Checker} |
@param checker: A Checker instance. This instance will be |
invoked to examine the other party's authentication |
credentials, if the handshake completes succesfully. |
+ |
+ @type nextProtos: list of strings. |
+ @param nextProtos: A list of upper layer protocols ordered by |
+ preference, to use in the Next-Protocol Negotiation Extension. |
+ |
+ @type reqTack: bool |
+ @param reqTack: Whether or not to send a "tack" TLS Extension, |
+ requesting the server return a TackExtension if it has one. |
+ |
+ @type serverName: string |
+ @param serverName: The ServerNameIndication TLS Extension. |
@type async: bool |
@param async: If False, this function will block until the |
@@ -281,276 +289,261 @@ class TLSConnection(TLSRecordLayer): |
@raise tlslite.errors.TLSAuthenticationError: If the checker |
doesn't like the other party's authentication credentials. |
""" |
- handshaker = self._handshakeClientAsync(unknownParams=(srpCallback, |
- certCallback), session=session, settings=settings, |
- checker=checker) |
+ handshaker = self._handshakeClientAsync(certParams=(certChain, |
+ privateKey), session=session, settings=settings, |
+ checker=checker, serverName=serverName, |
+ nextProtos=nextProtos, reqTack=reqTack) |
+ # The handshaker is a Python Generator which executes the handshake. |
+ # It allows the handshake to be run in a "piecewise", asynchronous |
+ # fashion, returning 1 when it is waiting to able to write, 0 when |
+ # it is waiting to read. |
+ # |
+ # If 'async' is True, the generator is returned to the caller, |
+ # otherwise it is executed to completion here. |
if async: |
return handshaker |
for result in handshaker: |
pass |
- def handshakeClientSharedKey(self, username, sharedKey, settings=None, |
- checker=None, async=False): |
- """Perform a shared-key handshake in the role of client. |
- |
- This function performs a shared-key handshake. Using shared |
- symmetric keys of high entropy (128 bits or greater) mutually |
- authenticates both parties to each other. |
- |
- TLS with shared-keys is non-standard. Most TLS |
- implementations don't support it. See |
- U{http://www.ietf.org/html.charters/tls-charter.html} for the |
- latest information on TLS with shared-keys. If the shared-keys |
- Internet-Draft changes or is superceded, TLS Lite will track |
- those changes, so the shared-key support in later versions of |
- TLS Lite may become incompatible with this version. |
- |
- Like any handshake function, this can be called on a closed |
- TLS connection, or on a TLS connection that is already open. |
- If called on an open connection it performs a re-handshake. |
- |
- If the function completes without raising an exception, the |
- TLS connection will be open and available for data transfer. |
- |
- If an exception is raised, the connection will have been |
- automatically closed (if it was ever open). |
- |
- @type username: str |
- @param username: The shared-key username. |
- |
- @type sharedKey: str |
- @param sharedKey: The shared key. |
- |
- @type settings: L{tlslite.HandshakeSettings.HandshakeSettings} |
- @param settings: Various settings which can be used to control |
- the ciphersuites, certificate types, and SSL/TLS versions |
- offered by the client. |
- |
- @type checker: L{tlslite.Checker.Checker} |
- @param checker: A Checker instance. This instance will be |
- invoked to examine the other party's authentication |
- credentials, if the handshake completes succesfully. |
- |
- @type async: bool |
- @param async: If False, this function will block until the |
- handshake is completed. If True, this function will return a |
- generator. Successive invocations of the generator will |
- return 0 if it is waiting to read from the socket, 1 if it is |
- waiting to write to the socket, or will raise StopIteration if |
- the handshake operation is completed. |
- |
- @rtype: None or an iterable |
- @return: If 'async' is True, a generator object will be |
- returned. |
- @raise socket.error: If a socket error occurs. |
- @raise tlslite.errors.TLSAbruptCloseError: If the socket is closed |
- without a preceding alert. |
- @raise tlslite.errors.TLSAlert: If a TLS alert is signalled. |
- @raise tlslite.errors.TLSAuthenticationError: If the checker |
- doesn't like the other party's authentication credentials. |
- """ |
- handshaker = self._handshakeClientAsync(sharedKeyParams=(username, |
- sharedKey), settings=settings, checker=checker) |
- if async: |
- return handshaker |
- for result in handshaker: |
- pass |
- |
- def _handshakeClientAsync(self, srpParams=(), certParams=(), |
- unknownParams=(), sharedKeyParams=(), |
+ def _handshakeClientAsync(self, srpParams=(), certParams=(), anonParams=(), |
session=None, settings=None, checker=None, |
- recursive=False): |
+ nextProtos=None, serverName="", reqTack=True): |
handshaker = self._handshakeClientAsyncHelper(srpParams=srpParams, |
- certParams=certParams, unknownParams=unknownParams, |
- sharedKeyParams=sharedKeyParams, session=session, |
- settings=settings, recursive=recursive) |
+ certParams=certParams, |
+ anonParams=anonParams, |
+ session=session, |
+ settings=settings, |
+ serverName=serverName, |
+ nextProtos=nextProtos, |
+ reqTack=reqTack) |
for result in self._handshakeWrapperAsync(handshaker, checker): |
yield result |
- def _handshakeClientAsyncHelper(self, srpParams, certParams, unknownParams, |
- sharedKeyParams, session, settings, recursive): |
- if not recursive: |
- self._handshakeStart(client=True) |
+ def _handshakeClientAsyncHelper(self, srpParams, certParams, anonParams, |
+ session, settings, serverName, nextProtos, reqTack): |
+ |
+ self._handshakeStart(client=True) |
#Unpack parameters |
- srpUsername = None # srpParams |
- password = None # srpParams |
- clientCertChain = None # certParams |
- privateKey = None # certParams |
- srpCallback = None # unknownParams |
- certCallback = None # unknownParams |
- #session # sharedKeyParams (or session) |
- #settings # settings |
+ srpUsername = None # srpParams[0] |
+ password = None # srpParams[1] |
+ clientCertChain = None # certParams[0] |
+ privateKey = None # certParams[1] |
+ # Allow only one of (srpParams, certParams, anonParams) |
if srpParams: |
+ assert(not certParams) |
+ assert(not anonParams) |
srpUsername, password = srpParams |
- elif certParams: |
+ if certParams: |
+ assert(not srpParams) |
+ assert(not anonParams) |
clientCertChain, privateKey = certParams |
- elif unknownParams: |
- srpCallback, certCallback = unknownParams |
- elif sharedKeyParams: |
- session = Session()._createSharedKey(*sharedKeyParams) |
- |
- if not settings: |
- settings = HandshakeSettings() |
- settings = settings._filter() |
+ if anonParams: |
+ assert(not srpParams) |
+ assert(not certParams) |
#Validate parameters |
if srpUsername and not password: |
raise ValueError("Caller passed a username but no password") |
if password and not srpUsername: |
raise ValueError("Caller passed a password but no username") |
- |
if clientCertChain and not privateKey: |
raise ValueError("Caller passed a certChain but no privateKey") |
if privateKey and not clientCertChain: |
raise ValueError("Caller passed a privateKey but no certChain") |
+ if reqTack: |
+ if not tackpyLoaded: |
+ reqTack = False |
+ if not settings or not settings.useExperimentalTackExtension: |
+ reqTack = False |
+ if nextProtos is not None: |
+ if len(nextProtos) == 0: |
+ raise ValueError("Caller passed no nextProtos") |
+ |
+ # Validates the settings and filters out any unsupported ciphers |
+ # or crypto libraries that were requested |
+ if not settings: |
+ settings = HandshakeSettings() |
+ settings = settings._filter() |
if clientCertChain: |
- foundType = False |
- try: |
- import cryptoIDlib.CertChain |
- if isinstance(clientCertChain, cryptoIDlib.CertChain.CertChain): |
- if "cryptoID" not in settings.certificateTypes: |
- raise ValueError("Client certificate doesn't "\ |
- "match Handshake Settings") |
- settings.certificateTypes = ["cryptoID"] |
- foundType = True |
- except ImportError: |
- pass |
- if not foundType and isinstance(clientCertChain, |
- X509CertChain): |
- if "x509" not in settings.certificateTypes: |
- raise ValueError("Client certificate doesn't match "\ |
- "Handshake Settings") |
- settings.certificateTypes = ["x509"] |
- foundType = True |
- if not foundType: |
+ if not isinstance(clientCertChain, X509CertChain): |
raise ValueError("Unrecognized certificate type") |
- |
- |
+ if "x509" not in settings.certificateTypes: |
+ raise ValueError("Client certificate doesn't match "\ |
+ "Handshake Settings") |
+ |
if session: |
+ # session.valid() ensures session is resumable and has |
+ # non-empty sessionID |
if not session.valid(): |
session = None #ignore non-resumable sessions... |
- elif session.resumable and \ |
- (session.srpUsername != srpUsername): |
- raise ValueError("Session username doesn't match") |
+ elif session.resumable: |
+ if session.srpUsername != srpUsername: |
+ raise ValueError("Session username doesn't match") |
+ if session.serverName != serverName: |
+ raise ValueError("Session servername doesn't match") |
#Add Faults to parameters |
if srpUsername and self.fault == Fault.badUsername: |
srpUsername += "GARBAGE" |
if password and self.fault == Fault.badPassword: |
password += "GARBAGE" |
- if sharedKeyParams: |
- identifier = sharedKeyParams[0] |
- sharedKey = sharedKeyParams[1] |
- if self.fault == Fault.badIdentifier: |
- identifier += "GARBAGE" |
- session = Session()._createSharedKey(identifier, sharedKey) |
- elif self.fault == Fault.badSharedKey: |
- sharedKey += "GARBAGE" |
- session = Session()._createSharedKey(identifier, sharedKey) |
- |
- |
- #Initialize locals |
- serverCertChain = None |
- cipherSuite = 0 |
- certificateType = CertificateType.x509 |
- premasterSecret = None |
- #Get client nonce |
- clientRandom = getRandomBytes(32) |
+ #Tentatively set the version to the client's minimum version. |
+ #We'll use this for the ClientHello, and if an error occurs |
+ #parsing the Server Hello, we'll use this version for the response |
+ self.version = settings.maxVersion |
+ |
+ # OK Start sending messages! |
+ # ***************************** |
+ |
+ # Send the ClientHello. |
+ for result in self._clientSendClientHello(settings, session, |
+ srpUsername, srpParams, certParams, |
+ anonParams, serverName, nextProtos, |
+ reqTack): |
+ if result in (0,1): yield result |
+ else: break |
+ clientHello = result |
+ |
+ #Get the ServerHello. |
+ for result in self._clientGetServerHello(settings, clientHello): |
+ if result in (0,1): yield result |
+ else: break |
+ serverHello = result |
+ cipherSuite = serverHello.cipher_suite |
+ |
+ # Choose a matching Next Protocol from server list against ours |
+ # (string or None) |
+ nextProto = self._clientSelectNextProto(nextProtos, serverHello) |
+ |
+ #If the server elected to resume the session, it is handled here. |
+ for result in self._clientResume(session, serverHello, |
+ clientHello.random, |
+ settings.cipherImplementations, |
+ nextProto): |
+ if result in (0,1): yield result |
+ else: break |
+ if result == "resumed_and_finished": |
+ self._handshakeDone(resumed=True) |
+ return |
+ |
+ #If the server selected an SRP ciphersuite, the client finishes |
+ #reading the post-ServerHello messages, then derives a |
+ #premasterSecret and sends a corresponding ClientKeyExchange. |
+ if cipherSuite in CipherSuite.srpAllSuites: |
+ for result in self._clientSRPKeyExchange(\ |
+ settings, cipherSuite, serverHello.certificate_type, |
+ srpUsername, password, |
+ clientHello.random, serverHello.random, |
+ serverHello.tackExt): |
+ if result in (0,1): yield result |
+ else: break |
+ (premasterSecret, serverCertChain, tackExt) = result |
+ |
+ #If the server selected an anonymous ciphersuite, the client |
+ #finishes reading the post-ServerHello messages. |
+ elif cipherSuite in CipherSuite.anonSuites: |
+ for result in self._clientAnonKeyExchange(settings, cipherSuite, |
+ clientHello.random, serverHello.random): |
+ if result in (0,1): yield result |
+ else: break |
+ (premasterSecret, serverCertChain, tackExt) = result |
+ |
+ #If the server selected a certificate-based RSA ciphersuite, |
+ #the client finishes reading the post-ServerHello messages. If |
+ #a CertificateRequest message was sent, the client responds with |
+ #a Certificate message containing its certificate chain (if any), |
+ #and also produces a CertificateVerify message that signs the |
+ #ClientKeyExchange. |
+ else: |
+ for result in self._clientRSAKeyExchange(settings, cipherSuite, |
+ clientCertChain, privateKey, |
+ serverHello.certificate_type, |
+ clientHello.random, serverHello.random, |
+ serverHello.tackExt): |
+ if result in (0,1): yield result |
+ else: break |
+ (premasterSecret, serverCertChain, clientCertChain, |
+ tackExt) = result |
+ |
+ #After having previously sent a ClientKeyExchange, the client now |
+ #initiates an exchange of Finished messages. |
+ for result in self._clientFinished(premasterSecret, |
+ clientHello.random, |
+ serverHello.random, |
+ cipherSuite, settings.cipherImplementations, |
+ nextProto): |
+ if result in (0,1): yield result |
+ else: break |
+ masterSecret = result |
+ |
+ # Create the session object which is used for resumptions |
+ self.session = Session() |
+ self.session.create(masterSecret, serverHello.session_id, cipherSuite, |
+ srpUsername, clientCertChain, serverCertChain, |
+ tackExt, serverHello.tackExt!=None, serverName) |
+ self._handshakeDone(resumed=False) |
+ |
+ def _clientSendClientHello(self, settings, session, srpUsername, |
+ srpParams, certParams, anonParams, |
+ serverName, nextProtos, reqTack): |
#Initialize acceptable ciphersuites |
- cipherSuites = [] |
+ cipherSuites = [CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV] |
if srpParams: |
- cipherSuites += CipherSuite.getSrpRsaSuites(settings.cipherNames) |
- cipherSuites += CipherSuite.getSrpSuites(settings.cipherNames) |
+ cipherSuites += CipherSuite.getSrpAllSuites(settings) |
elif certParams: |
- cipherSuites += CipherSuite.getRsaSuites(settings.cipherNames) |
- elif unknownParams: |
- if srpCallback: |
- cipherSuites += \ |
- CipherSuite.getSrpRsaSuites(settings.cipherNames) |
- cipherSuites += \ |
- CipherSuite.getSrpSuites(settings.cipherNames) |
- cipherSuites += CipherSuite.getRsaSuites(settings.cipherNames) |
- elif sharedKeyParams: |
- cipherSuites += CipherSuite.getRsaSuites(settings.cipherNames) |
+ cipherSuites += CipherSuite.getCertSuites(settings) |
+ elif anonParams: |
+ cipherSuites += CipherSuite.getAnonSuites(settings) |
else: |
- cipherSuites += CipherSuite.getRsaSuites(settings.cipherNames) |
+ assert(False) |
#Initialize acceptable certificate types |
certificateTypes = settings._getCertificateTypes() |
- |
- #Tentatively set the version to the client's minimum version. |
- #We'll use this for the ClientHello, and if an error occurs |
- #parsing the Server Hello, we'll use this version for the response |
- self.version = settings.maxVersion |
- |
+ |
#Either send ClientHello (with a resumable session)... |
- if session: |
- #If it's a resumable (i.e. not a shared-key session), then its |
+ if session and session.sessionID: |
+ #If it's resumable, then its |
#ciphersuite must be one of the acceptable ciphersuites |
- if (not sharedKeyParams) and \ |
- session.cipherSuite not in cipherSuites: |
+ if session.cipherSuite not in cipherSuites: |
raise ValueError("Session's cipher suite not consistent "\ |
"with parameters") |
else: |
clientHello = ClientHello() |
- clientHello.create(settings.maxVersion, clientRandom, |
+ clientHello.create(settings.maxVersion, getRandomBytes(32), |
session.sessionID, cipherSuites, |
- certificateTypes, session.srpUsername) |
+ certificateTypes, |
+ session.srpUsername, |
+ reqTack, nextProtos is not None, |
+ session.serverName) |
#Or send ClientHello (without) |
else: |
clientHello = ClientHello() |
- clientHello.create(settings.maxVersion, clientRandom, |
- createByteArraySequence([]), cipherSuites, |
- certificateTypes, srpUsername) |
+ clientHello.create(settings.maxVersion, getRandomBytes(32), |
+ bytearray(0), cipherSuites, |
+ certificateTypes, |
+ srpUsername, |
+ reqTack, nextProtos is not None, |
+ serverName) |
for result in self._sendMsg(clientHello): |
yield result |
+ yield clientHello |
- #Get ServerHello (or missing_srp_username) |
- for result in self._getMsg((ContentType.handshake, |
- ContentType.alert), |
- HandshakeType.server_hello): |
- if result in (0,1): |
- yield result |
- else: |
- break |
- msg = result |
- |
- if isinstance(msg, ServerHello): |
- serverHello = msg |
- elif isinstance(msg, Alert): |
- alert = msg |
- |
- #If it's not a missing_srp_username, re-raise |
- if alert.description != AlertDescription.missing_srp_username: |
- self._shutdown(False) |
- raise TLSRemoteAlert(alert) |
- |
- #If we're not in SRP callback mode, we won't have offered SRP |
- #without a username, so we shouldn't get this alert |
- if not srpCallback: |
- for result in self._sendError(\ |
- AlertDescription.unexpected_message): |
- yield result |
- srpParams = srpCallback() |
- #If the callback returns None, cancel the handshake |
- if srpParams == None: |
- for result in self._sendError(AlertDescription.user_canceled): |
- yield result |
- #Recursively perform handshake |
- for result in self._handshakeClientAsyncHelper(srpParams, |
- None, None, None, None, settings, True): |
- yield result |
- return |
+ def _clientGetServerHello(self, settings, clientHello): |
+ for result in self._getMsg(ContentType.handshake, |
+ HandshakeType.server_hello): |
+ if result in (0,1): yield result |
+ else: break |
+ serverHello = result |
#Get the server version. Do this before anything else, so any |
#error alerts will use the server's version |
@@ -570,12 +563,12 @@ class TLSConnection(TLSRecordLayer): |
AlertDescription.protocol_version, |
"Too new version: %s" % str(serverHello.server_version)): |
yield result |
- if serverHello.cipher_suite not in cipherSuites: |
+ if serverHello.cipher_suite not in clientHello.cipher_suites: |
for result in self._sendError(\ |
AlertDescription.illegal_parameter, |
"Server responded with incorrect ciphersuite"): |
yield result |
- if serverHello.certificate_type not in certificateTypes: |
+ if serverHello.certificate_type not in clientHello.certificate_types: |
for result in self._sendError(\ |
AlertDescription.illegal_parameter, |
"Server responded with incorrect certificate type"): |
@@ -585,365 +578,401 @@ class TLSConnection(TLSRecordLayer): |
AlertDescription.illegal_parameter, |
"Server responded with incorrect compression method"): |
yield result |
+ if serverHello.tackExt: |
+ if not clientHello.tack: |
+ for result in self._sendError(\ |
+ AlertDescription.illegal_parameter, |
+ "Server responded with unrequested Tack Extension"): |
+ yield result |
+ if serverHello.next_protos and not clientHello.supports_npn: |
+ for result in self._sendError(\ |
+ AlertDescription.illegal_parameter, |
+ "Server responded with unrequested NPN Extension"): |
+ yield result |
+ if not serverHello.tackExt.verifySignatures(): |
+ for result in self._sendError(\ |
+ AlertDescription.decrypt_error, |
+ "TackExtension contains an invalid signature"): |
+ yield result |
+ yield serverHello |
- #Get the server nonce |
- serverRandom = serverHello.random |
- |
+ def _clientSelectNextProto(self, nextProtos, serverHello): |
+ # nextProtos is None or non-empty list of strings |
+ # serverHello.next_protos is None or possibly-empty list of strings |
+ # |
+ # !!! We assume the client may have specified nextProtos as a list of |
+ # strings so we convert them to bytearrays (it's awkward to require |
+ # the user to specify a list of bytearrays or "bytes", and in |
+ # Python 2.6 bytes() is just an alias for str() anyways... |
+ if nextProtos is not None and serverHello.next_protos is not None: |
+ for p in nextProtos: |
+ if bytearray(p) in serverHello.next_protos: |
+ return bytearray(p) |
+ else: |
+ # If the client doesn't support any of server's protocols, |
+ # or the server doesn't advertise any (next_protos == []) |
+ # the client SHOULD select the first protocol it supports. |
+ return bytearray(nextProtos[0]) |
+ return None |
+ |
+ def _clientResume(self, session, serverHello, clientRandom, |
+ cipherImplementations, nextProto): |
#If the server agrees to resume |
if session and session.sessionID and \ |
- serverHello.session_id == session.sessionID: |
+ serverHello.session_id == session.sessionID: |
- #If a shared-key, we're flexible about suites; otherwise the |
- #server-chosen suite has to match the session's suite |
- if sharedKeyParams: |
- session.cipherSuite = serverHello.cipher_suite |
- elif serverHello.cipher_suite != session.cipherSuite: |
+ if serverHello.cipher_suite != session.cipherSuite: |
for result in self._sendError(\ |
AlertDescription.illegal_parameter,\ |
"Server's ciphersuite doesn't match session"): |
yield result |
+ #Calculate pending connection states |
+ self._calcPendingStates(session.cipherSuite, |
+ session.masterSecret, |
+ clientRandom, serverHello.random, |
+ cipherImplementations) |
+ |
+ #Exchange ChangeCipherSpec and Finished messages |
+ for result in self._getFinished(session.masterSecret): |
+ yield result |
+ for result in self._sendFinished(session.masterSecret, nextProto): |
+ yield result |
+ |
#Set the session for this connection |
self.session = session |
+ yield "resumed_and_finished" |
+ |
+ def _clientSRPKeyExchange(self, settings, cipherSuite, certificateType, |
+ srpUsername, password, |
+ clientRandom, serverRandom, tackExt): |
+ |
+ #If the server chose an SRP+RSA suite... |
+ if cipherSuite in CipherSuite.srpCertSuites: |
+ #Get Certificate, ServerKeyExchange, ServerHelloDone |
+ for result in self._getMsg(ContentType.handshake, |
+ HandshakeType.certificate, certificateType): |
+ if result in (0,1): yield result |
+ else: break |
+ serverCertificate = result |
+ else: |
+ serverCertificate = None |
- #Calculate pending connection states |
- self._calcPendingStates(clientRandom, serverRandom, |
- settings.cipherImplementations) |
+ for result in self._getMsg(ContentType.handshake, |
+ HandshakeType.server_key_exchange, cipherSuite): |
+ if result in (0,1): yield result |
+ else: break |
+ serverKeyExchange = result |
- #Exchange ChangeCipherSpec and Finished messages |
- for result in self._getChangeCipherSpec(): |
+ for result in self._getMsg(ContentType.handshake, |
+ HandshakeType.server_hello_done): |
+ if result in (0,1): yield result |
+ else: break |
+ serverHelloDone = result |
+ |
+ #Calculate SRP premaster secret |
+ #Get and check the server's group parameters and B value |
+ N = serverKeyExchange.srp_N |
+ g = serverKeyExchange.srp_g |
+ s = serverKeyExchange.srp_s |
+ B = serverKeyExchange.srp_B |
+ |
+ if (g,N) not in goodGroupParameters: |
+ for result in self._sendError(\ |
+ AlertDescription.insufficient_security, |
+ "Unknown group parameters"): |
+ yield result |
+ if numBits(N) < settings.minKeySize: |
+ for result in self._sendError(\ |
+ AlertDescription.insufficient_security, |
+ "N value is too small: %d" % numBits(N)): |
yield result |
- for result in self._getFinished(): |
+ if numBits(N) > settings.maxKeySize: |
+ for result in self._sendError(\ |
+ AlertDescription.insufficient_security, |
+ "N value is too large: %d" % numBits(N)): |
yield result |
- for result in self._sendFinished(): |
+ if B % N == 0: |
+ for result in self._sendError(\ |
+ AlertDescription.illegal_parameter, |
+ "Suspicious B value"): |
yield result |
- #Mark the connection as open |
- self._handshakeDone(resumed=True) |
+ #Check the server's signature, if server chose an |
+ #SRP+RSA suite |
+ serverCertChain = None |
+ if cipherSuite in CipherSuite.srpCertSuites: |
+ #Hash ServerKeyExchange/ServerSRPParams |
+ hashBytes = serverKeyExchange.hash(clientRandom, serverRandom) |
- #If server DOES NOT agree to resume |
- else: |
+ #Extract signature bytes from ServerKeyExchange |
+ sigBytes = serverKeyExchange.signature |
+ if len(sigBytes) == 0: |
+ for result in self._sendError(\ |
+ AlertDescription.illegal_parameter, |
+ "Server sent an SRP ServerKeyExchange "\ |
+ "message without a signature"): |
+ yield result |
- if sharedKeyParams: |
+ # Get server's public key from the Certificate message |
+ # Also validate the chain against the ServerHello's TACKext (if any) |
+ # If none, and a TACK cert is present, return its TACKext |
+ for result in self._clientGetKeyFromChain(serverCertificate, |
+ settings, tackExt): |
+ if result in (0,1): yield result |
+ else: break |
+ publicKey, serverCertChain, tackExt = result |
+ |
+ #Verify signature |
+ if not publicKey.verify(sigBytes, hashBytes): |
for result in self._sendError(\ |
- AlertDescription.user_canceled, |
- "Was expecting a shared-key resumption"): |
+ AlertDescription.decrypt_error, |
+ "Signature failed to verify"): |
yield result |
- #We've already validated these |
- cipherSuite = serverHello.cipher_suite |
- certificateType = serverHello.certificate_type |
+ #Calculate client's ephemeral DH values (a, A) |
+ a = bytesToNumber(getRandomBytes(32)) |
+ A = powMod(g, a, N) |
- #If the server chose an SRP suite... |
- if cipherSuite in CipherSuite.srpSuites: |
- #Get ServerKeyExchange, ServerHelloDone |
- for result in self._getMsg(ContentType.handshake, |
- HandshakeType.server_key_exchange, cipherSuite): |
- if result in (0,1): |
- yield result |
- else: |
- break |
- serverKeyExchange = result |
+ #Calculate client's static DH values (x, v) |
+ x = makeX(s, bytearray(srpUsername, "utf-8"), |
+ bytearray(password, "utf-8")) |
+ v = powMod(g, x, N) |
- for result in self._getMsg(ContentType.handshake, |
- HandshakeType.server_hello_done): |
- if result in (0,1): |
- yield result |
- else: |
- break |
- serverHelloDone = result |
+ #Calculate u |
+ u = makeU(N, A, B) |
- #If the server chose an SRP+RSA suite... |
- elif cipherSuite in CipherSuite.srpRsaSuites: |
- #Get Certificate, ServerKeyExchange, ServerHelloDone |
- for result in self._getMsg(ContentType.handshake, |
- HandshakeType.certificate, certificateType): |
- if result in (0,1): |
- yield result |
- else: |
- break |
- serverCertificate = result |
+ #Calculate premaster secret |
+ k = makeK(N, g) |
+ S = powMod((B - (k*v)) % N, a+(u*x), N) |
- for result in self._getMsg(ContentType.handshake, |
- HandshakeType.server_key_exchange, cipherSuite): |
- if result in (0,1): |
- yield result |
- else: |
- break |
- serverKeyExchange = result |
+ if self.fault == Fault.badA: |
+ A = N |
+ S = 0 |
+ |
+ premasterSecret = numberToByteArray(S) |
- for result in self._getMsg(ContentType.handshake, |
- HandshakeType.server_hello_done): |
- if result in (0,1): |
- yield result |
- else: |
- break |
- serverHelloDone = result |
+ #Send ClientKeyExchange |
+ for result in self._sendMsg(\ |
+ ClientKeyExchange(cipherSuite).createSRP(A)): |
+ yield result |
+ yield (premasterSecret, serverCertChain, tackExt) |
+ |
- #If the server chose an RSA suite... |
- elif cipherSuite in CipherSuite.rsaSuites: |
- #Get Certificate[, CertificateRequest], ServerHelloDone |
- for result in self._getMsg(ContentType.handshake, |
- HandshakeType.certificate, certificateType): |
- if result in (0,1): |
- yield result |
- else: |
- break |
- serverCertificate = result |
- |
- for result in self._getMsg(ContentType.handshake, |
- (HandshakeType.server_hello_done, |
- HandshakeType.certificate_request)): |
- if result in (0,1): |
- yield result |
- else: |
- break |
- msg = result |
- |
- certificateRequest = None |
- if isinstance(msg, CertificateRequest): |
- certificateRequest = msg |
- for result in self._getMsg(ContentType.handshake, |
- HandshakeType.server_hello_done): |
- if result in (0,1): |
- yield result |
- else: |
- break |
- serverHelloDone = result |
- elif isinstance(msg, ServerHelloDone): |
- serverHelloDone = msg |
- else: |
- raise AssertionError() |
+ def _clientRSAKeyExchange(self, settings, cipherSuite, |
+ clientCertChain, privateKey, |
+ certificateType, |
+ clientRandom, serverRandom, |
+ tackExt): |
+ #Get Certificate[, CertificateRequest], ServerHelloDone |
+ for result in self._getMsg(ContentType.handshake, |
+ HandshakeType.certificate, certificateType): |
+ if result in (0,1): yield result |
+ else: break |
+ serverCertificate = result |
- #Calculate SRP premaster secret, if server chose an SRP or |
- #SRP+RSA suite |
- if cipherSuite in CipherSuite.srpSuites + \ |
- CipherSuite.srpRsaSuites: |
- #Get and check the server's group parameters and B value |
- N = serverKeyExchange.srp_N |
- g = serverKeyExchange.srp_g |
- s = serverKeyExchange.srp_s |
- B = serverKeyExchange.srp_B |
+ # Get CertificateRequest or ServerHelloDone |
+ for result in self._getMsg(ContentType.handshake, |
+ (HandshakeType.server_hello_done, |
+ HandshakeType.certificate_request)): |
+ if result in (0,1): yield result |
+ else: break |
+ msg = result |
+ certificateRequest = None |
+ if isinstance(msg, CertificateRequest): |
+ certificateRequest = msg |
+ # We got CertificateRequest, so this must be ServerHelloDone |
+ for result in self._getMsg(ContentType.handshake, |
+ HandshakeType.server_hello_done): |
+ if result in (0,1): yield result |
+ else: break |
+ serverHelloDone = result |
+ elif isinstance(msg, ServerHelloDone): |
+ serverHelloDone = msg |
+ |
+ # Get server's public key from the Certificate message |
+ # Also validate the chain against the ServerHello's TACKext (if any) |
+ # If none, and a TACK cert is present, return its TACKext |
+ for result in self._clientGetKeyFromChain(serverCertificate, |
+ settings, tackExt): |
+ if result in (0,1): yield result |
+ else: break |
+ publicKey, serverCertChain, tackExt = result |
+ |
+ #Calculate premaster secret |
+ premasterSecret = getRandomBytes(48) |
+ premasterSecret[0] = settings.maxVersion[0] |
+ premasterSecret[1] = settings.maxVersion[1] |
+ |
+ if self.fault == Fault.badPremasterPadding: |
+ premasterSecret[0] = 5 |
+ if self.fault == Fault.shortPremasterSecret: |
+ premasterSecret = premasterSecret[:-1] |
+ |
+ #Encrypt premaster secret to server's public key |
+ encryptedPreMasterSecret = publicKey.encrypt(premasterSecret) |
+ |
+ #If client authentication was requested, send Certificate |
+ #message, either with certificates or empty |
+ if certificateRequest: |
+ clientCertificate = Certificate(certificateType) |
- if (g,N) not in goodGroupParameters: |
- for result in self._sendError(\ |
- AlertDescription.untrusted_srp_parameters, |
- "Unknown group parameters"): |
- yield result |
- if numBits(N) < settings.minKeySize: |
- for result in self._sendError(\ |
- AlertDescription.untrusted_srp_parameters, |
- "N value is too small: %d" % numBits(N)): |
- yield result |
- if numBits(N) > settings.maxKeySize: |
- for result in self._sendError(\ |
- AlertDescription.untrusted_srp_parameters, |
- "N value is too large: %d" % numBits(N)): |
- yield result |
- if B % N == 0: |
+ if clientCertChain: |
+ #Check to make sure we have the same type of |
+ #certificates the server requested |
+ wrongType = False |
+ if certificateType == CertificateType.x509: |
+ if not isinstance(clientCertChain, X509CertChain): |
+ wrongType = True |
+ if wrongType: |
for result in self._sendError(\ |
- AlertDescription.illegal_parameter, |
- "Suspicious B value"): |
- yield result |
- |
- #Check the server's signature, if server chose an |
- #SRP+RSA suite |
- if cipherSuite in CipherSuite.srpRsaSuites: |
- #Hash ServerKeyExchange/ServerSRPParams |
- hashBytes = serverKeyExchange.hash(clientRandom, |
- serverRandom) |
- |
- #Extract signature bytes from ServerKeyExchange |
- sigBytes = serverKeyExchange.signature |
- if len(sigBytes) == 0: |
- for result in self._sendError(\ |
- AlertDescription.illegal_parameter, |
- "Server sent an SRP ServerKeyExchange "\ |
- "message without a signature"): |
- yield result |
- |
- #Get server's public key from the Certificate message |
- for result in self._getKeyFromChain(serverCertificate, |
- settings): |
- if result in (0,1): |
- yield result |
- else: |
- break |
- publicKey, serverCertChain = result |
- |
- #Verify signature |
- if not publicKey.verify(sigBytes, hashBytes): |
- for result in self._sendError(\ |
- AlertDescription.decrypt_error, |
- "Signature failed to verify"): |
- yield result |
- |
- |
- #Calculate client's ephemeral DH values (a, A) |
- a = bytesToNumber(getRandomBytes(32)) |
- A = powMod(g, a, N) |
- |
- #Calculate client's static DH values (x, v) |
- x = makeX(bytesToString(s), srpUsername, password) |
- v = powMod(g, x, N) |
- |
- #Calculate u |
- u = makeU(N, A, B) |
- |
- #Calculate premaster secret |
- k = makeK(N, g) |
- S = powMod((B - (k*v)) % N, a+(u*x), N) |
- |
- if self.fault == Fault.badA: |
- A = N |
- S = 0 |
- premasterSecret = numberToBytes(S) |
- |
- #Send ClientKeyExchange |
- for result in self._sendMsg(\ |
- ClientKeyExchange(cipherSuite).createSRP(A)): |
- yield result |
- |
- |
- #Calculate RSA premaster secret, if server chose an RSA suite |
- elif cipherSuite in CipherSuite.rsaSuites: |
- |
- #Handle the presence of a CertificateRequest |
- if certificateRequest: |
- if unknownParams and certCallback: |
- certParamsNew = certCallback() |
- if certParamsNew: |
- clientCertChain, privateKey = certParamsNew |
- |
- #Get server's public key from the Certificate message |
- for result in self._getKeyFromChain(serverCertificate, |
- settings): |
- if result in (0,1): |
+ AlertDescription.handshake_failure, |
+ "Client certificate is of wrong type"): |
yield result |
- else: |
- break |
- publicKey, serverCertChain = result |
- |
- |
- #Calculate premaster secret |
- premasterSecret = getRandomBytes(48) |
- premasterSecret[0] = settings.maxVersion[0] |
- premasterSecret[1] = settings.maxVersion[1] |
- |
- if self.fault == Fault.badPremasterPadding: |
- premasterSecret[0] = 5 |
- if self.fault == Fault.shortPremasterSecret: |
- premasterSecret = premasterSecret[:-1] |
- |
- #Encrypt premaster secret to server's public key |
- encryptedPreMasterSecret = publicKey.encrypt(premasterSecret) |
- |
- #If client authentication was requested, send Certificate |
- #message, either with certificates or empty |
- if certificateRequest: |
- clientCertificate = Certificate(certificateType) |
- |
- if clientCertChain: |
- #Check to make sure we have the same type of |
- #certificates the server requested |
- wrongType = False |
- if certificateType == CertificateType.x509: |
- if not isinstance(clientCertChain, X509CertChain): |
- wrongType = True |
- elif certificateType == CertificateType.cryptoID: |
- if not isinstance(clientCertChain, |
- cryptoIDlib.CertChain.CertChain): |
- wrongType = True |
- if wrongType: |
- for result in self._sendError(\ |
- AlertDescription.handshake_failure, |
- "Client certificate is of wrong type"): |
- yield result |
- |
- clientCertificate.create(clientCertChain) |
- for result in self._sendMsg(clientCertificate): |
- yield result |
- else: |
- #The server didn't request client auth, so we |
- #zeroize these so the clientCertChain won't be |
- #stored in the session. |
- privateKey = None |
- clientCertChain = None |
- |
- #Send ClientKeyExchange |
- clientKeyExchange = ClientKeyExchange(cipherSuite, |
- self.version) |
- clientKeyExchange.createRSA(encryptedPreMasterSecret) |
- for result in self._sendMsg(clientKeyExchange): |
- yield result |
+ clientCertificate.create(clientCertChain) |
+ for result in self._sendMsg(clientCertificate): |
+ yield result |
+ else: |
+ #The server didn't request client auth, so we |
+ #zeroize these so the clientCertChain won't be |
+ #stored in the session. |
+ privateKey = None |
+ clientCertChain = None |
+ |
+ #Send ClientKeyExchange |
+ clientKeyExchange = ClientKeyExchange(cipherSuite, |
+ self.version) |
+ clientKeyExchange.createRSA(encryptedPreMasterSecret) |
+ for result in self._sendMsg(clientKeyExchange): |
+ yield result |
- #If client authentication was requested and we have a |
- #private key, send CertificateVerify |
- if certificateRequest and privateKey: |
- if self.version == (3,0): |
- #Create a temporary session object, just for the |
- #purpose of creating the CertificateVerify |
- session = Session() |
- session._calcMasterSecret(self.version, |
- premasterSecret, |
- clientRandom, |
- serverRandom) |
- verifyBytes = self._calcSSLHandshakeHash(\ |
- session.masterSecret, "") |
- elif self.version in ((3,1), (3,2)): |
- verifyBytes = stringToBytes(\ |
- self._handshake_md5.digest() + \ |
- self._handshake_sha.digest()) |
- if self.fault == Fault.badVerifyMessage: |
- verifyBytes[0] = ((verifyBytes[0]+1) % 256) |
- signedBytes = privateKey.sign(verifyBytes) |
- certificateVerify = CertificateVerify() |
- certificateVerify.create(signedBytes) |
- for result in self._sendMsg(certificateVerify): |
- yield result |
+ #If client authentication was requested and we have a |
+ #private key, send CertificateVerify |
+ if certificateRequest and privateKey: |
+ if self.version == (3,0): |
+ masterSecret = calcMasterSecret(self.version, |
+ premasterSecret, |
+ clientRandom, |
+ serverRandom) |
+ verifyBytes = self._calcSSLHandshakeHash(masterSecret, b"") |
+ elif self.version in ((3,1), (3,2)): |
+ verifyBytes = self._handshake_md5.digest() + \ |
+ self._handshake_sha.digest() |
+ if self.fault == Fault.badVerifyMessage: |
+ verifyBytes[0] = ((verifyBytes[0]+1) % 256) |
+ signedBytes = privateKey.sign(verifyBytes) |
+ certificateVerify = CertificateVerify() |
+ certificateVerify.create(signedBytes) |
+ for result in self._sendMsg(certificateVerify): |
+ yield result |
+ yield (premasterSecret, serverCertChain, clientCertChain, tackExt) |
+ def _clientAnonKeyExchange(self, settings, cipherSuite, clientRandom, |
+ serverRandom): |
+ for result in self._getMsg(ContentType.handshake, |
+ HandshakeType.server_key_exchange, cipherSuite): |
+ if result in (0,1): yield result |
+ else: break |
+ serverKeyExchange = result |
- #Create the session object |
- self.session = Session() |
- self.session._calcMasterSecret(self.version, premasterSecret, |
- clientRandom, serverRandom) |
- self.session.sessionID = serverHello.session_id |
- self.session.cipherSuite = cipherSuite |
- self.session.srpUsername = srpUsername |
- self.session.clientCertChain = clientCertChain |
- self.session.serverCertChain = serverCertChain |
+ for result in self._getMsg(ContentType.handshake, |
+ HandshakeType.server_hello_done): |
+ if result in (0,1): yield result |
+ else: break |
+ serverHelloDone = result |
+ |
+ #calculate Yc |
+ dh_p = serverKeyExchange.dh_p |
+ dh_g = serverKeyExchange.dh_g |
+ dh_Xc = bytesToNumber(getRandomBytes(32)) |
+ dh_Ys = serverKeyExchange.dh_Ys |
+ dh_Yc = powMod(dh_g, dh_Xc, dh_p) |
+ |
+ #Send ClientKeyExchange |
+ for result in self._sendMsg(\ |
+ ClientKeyExchange(cipherSuite, self.version).createDH(dh_Yc)): |
+ yield result |
+ |
+ #Calculate premaster secret |
+ S = powMod(dh_Ys, dh_Xc, dh_p) |
+ premasterSecret = numberToByteArray(S) |
+ |
+ yield (premasterSecret, None, None) |
+ |
+ def _clientFinished(self, premasterSecret, clientRandom, serverRandom, |
+ cipherSuite, cipherImplementations, nextProto): |
+ |
+ masterSecret = calcMasterSecret(self.version, premasterSecret, |
+ clientRandom, serverRandom) |
+ self._calcPendingStates(cipherSuite, masterSecret, |
+ clientRandom, serverRandom, |
+ cipherImplementations) |
- #Calculate pending connection states |
- self._calcPendingStates(clientRandom, serverRandom, |
- settings.cipherImplementations) |
+ #Exchange ChangeCipherSpec and Finished messages |
+ for result in self._sendFinished(masterSecret, nextProto): |
+ yield result |
+ for result in self._getFinished(masterSecret, nextProto=nextProto): |
+ yield result |
+ yield masterSecret |
- #Exchange ChangeCipherSpec and Finished messages |
- for result in self._sendFinished(): |
+ def _clientGetKeyFromChain(self, certificate, settings, tackExt=None): |
+ #Get and check cert chain from the Certificate message |
+ certChain = certificate.certChain |
+ if not certChain or certChain.getNumCerts() == 0: |
+ for result in self._sendError(AlertDescription.illegal_parameter, |
+ "Other party sent a Certificate message without "\ |
+ "certificates"): |
yield result |
- for result in self._getChangeCipherSpec(): |
+ |
+ #Get and check public key from the cert chain |
+ publicKey = certChain.getEndEntityPublicKey() |
+ if len(publicKey) < settings.minKeySize: |
+ for result in self._sendError(AlertDescription.handshake_failure, |
+ "Other party's public key too small: %d" % len(publicKey)): |
yield result |
- for result in self._getFinished(): |
+ if len(publicKey) > settings.maxKeySize: |
+ for result in self._sendError(AlertDescription.handshake_failure, |
+ "Other party's public key too large: %d" % len(publicKey)): |
yield result |
+ |
+ # If there's no TLS Extension, look for a TACK cert |
+ if tackpyLoaded: |
+ if not tackExt: |
+ tackExt = certChain.getTackExt() |
+ |
+ # If there's a TACK (whether via TLS or TACK Cert), check that it |
+ # matches the cert chain |
+ if tackExt and tackExt.tacks: |
+ for tack in tackExt.tacks: |
+ if not certChain.checkTack(tack): |
+ for result in self._sendError( |
+ AlertDescription.illegal_parameter, |
+ "Other party's TACK doesn't match their public key"): |
+ yield result |
+ |
+ yield publicKey, certChain, tackExt |
- #Mark the connection as open |
- self.session._setResumable(True) |
- self._handshakeDone(resumed=False) |
+ #********************************************************* |
+ # Server Handshake Functions |
+ #********************************************************* |
- def handshakeServer(self, sharedKeyDB=None, verifierDB=None, |
+ def handshakeServer(self, verifierDB=None, |
certChain=None, privateKey=None, reqCert=False, |
sessionCache=None, settings=None, checker=None, |
- reqCAs=None, tlsIntolerant=0, |
- signedCertTimestamps=None, fallbackSCSV=False, |
- ocspResponse=None): |
+ reqCAs = None, |
+ tacks=None, activationFlags=0, |
+ nextProtos=None, anon=False, |
+ tlsIntolerant=None, signedCertTimestamps=None, |
+ fallbackSCSV=False, ocspResponse=None): |
"""Perform a handshake in the role of server. |
This function performs an SSL or TLS handshake. Depending on |
the arguments and the behavior of the client, this function can |
- perform a shared-key, SRP, or certificate-based handshake. It |
+ perform an SRP, or certificate-based handshake. It |
can also perform a combined SRP and server-certificate |
handshake. |
@@ -961,67 +990,62 @@ class TLSConnection(TLSRecordLayer): |
If an exception is raised, the connection will have been |
automatically closed (if it was ever open). |
- @type sharedKeyDB: L{tlslite.SharedKeyDB.SharedKeyDB} |
- @param sharedKeyDB: A database of shared symmetric keys |
- associated with usernames. If the client performs a |
- shared-key handshake, the session's sharedKeyUsername |
- attribute will be set. |
- |
- @type verifierDB: L{tlslite.VerifierDB.VerifierDB} |
+ @type verifierDB: L{tlslite.verifierdb.VerifierDB} |
@param verifierDB: A database of SRP password verifiers |
associated with usernames. If the client performs an SRP |
handshake, the session's srpUsername attribute will be set. |
- @type certChain: L{tlslite.X509CertChain.X509CertChain} or |
- L{cryptoIDlib.CertChain.CertChain} |
+ @type certChain: L{tlslite.x509certchain.X509CertChain} |
@param certChain: The certificate chain to be used if the |
client requests server certificate authentication. |
- @type privateKey: L{tlslite.utils.RSAKey.RSAKey} |
+ @type privateKey: L{tlslite.utils.rsakey.RSAKey} |
@param privateKey: The private key to be used if the client |
requests server certificate authentication. |
@type reqCert: bool |
@param reqCert: Whether to request client certificate |
authentication. This only applies if the client chooses server |
- certificate authentication; if the client chooses SRP or |
- shared-key authentication, this will be ignored. If the client |
+ certificate authentication; if the client chooses SRP |
+ authentication, this will be ignored. If the client |
performs a client certificate authentication, the sessions's |
clientCertChain attribute will be set. |
- @type sessionCache: L{tlslite.SessionCache.SessionCache} |
+ @type sessionCache: L{tlslite.sessioncache.SessionCache} |
@param sessionCache: An in-memory cache of resumable sessions. |
The client can resume sessions from this cache. Alternatively, |
if the client performs a full handshake, a new session will be |
added to the cache. |
- @type settings: L{tlslite.HandshakeSettings.HandshakeSettings} |
+ @type settings: L{tlslite.handshakesettings.HandshakeSettings} |
@param settings: Various settings which can be used to control |
the ciphersuites and SSL/TLS version chosen by the server. |
- @type checker: L{tlslite.Checker.Checker} |
+ @type checker: L{tlslite.checker.Checker} |
@param checker: A Checker instance. This instance will be |
invoked to examine the other party's authentication |
credentials, if the handshake completes succesfully. |
- |
- @type reqCAs: list of L{array.array} of unsigned bytes |
+ |
+ @type reqCAs: list of L{bytearray} of unsigned bytes |
@param reqCAs: A collection of DER-encoded DistinguishedNames that |
will be sent along with a certificate request. This does not affect |
- verification. |
+ verification. |
+ |
+ @type nextProtos: list of strings. |
+ @param nextProtos: A list of upper layer protocols to expose to the |
+ clients through the Next-Protocol Negotiation Extension, |
+ if they support it. |
+ |
+ @type tlsIntolerant: (int, int) or None |
+ @param tlsIntolerant: If tlsIntolerant is not None, the server will |
+ simulate TLS version intolerance by returning a fatal handshake_failure |
+ alert to all TLS versions tlsIntolerant or higher. |
@type signedCertTimestamps: str |
@param signedCertTimestamps: A SignedCertificateTimestampList (as a |
binary 8-bit string) that will be sent as a TLS extension whenever |
the client announces support for the extension. |
- @type tlsIntolerant: int |
- @param tlsIntolerant: if non-zero, the server will simulate TLS |
- version intolerance by returning a fatal, handshake_failure alert. |
- The versions to which it's intolerant vary depending on the value: |
- 1: reject all TLS versions. |
- 2: reject TLS 1.1 or higher. |
- 3: reject TLS 1.2 or higher. |
- |
@type fallbackSCSV: bool |
@param fallbackSCSV: if true, the server will implement |
TLS_FALLBACK_SCSV and thus reject connections using less than the |
@@ -1044,19 +1068,27 @@ class TLSConnection(TLSRecordLayer): |
@raise tlslite.errors.TLSAuthenticationError: If the checker |
doesn't like the other party's authentication credentials. |
""" |
- for result in self.handshakeServerAsync(sharedKeyDB, verifierDB, |
+ for result in self.handshakeServerAsync(verifierDB, |
certChain, privateKey, reqCert, sessionCache, settings, |
- checker, reqCAs, tlsIntolerant, signedCertTimestamps, |
- fallbackSCSV, ocspResponse): |
+ checker, reqCAs, |
+ tacks=tacks, activationFlags=activationFlags, |
+ nextProtos=nextProtos, anon=anon, tlsIntolerant=tlsIntolerant, |
+ signedCertTimestamps=signedCertTimestamps, |
+ fallbackSCSV=fallbackSCSV, ocspResponse=ocspResponse): |
pass |
- def handshakeServerAsync(self, sharedKeyDB=None, verifierDB=None, |
+ def handshakeServerAsync(self, verifierDB=None, |
certChain=None, privateKey=None, reqCert=False, |
sessionCache=None, settings=None, checker=None, |
- reqCAs=None, tlsIntolerant=0, |
+ reqCAs=None, |
+ tacks=None, activationFlags=0, |
+ nextProtos=None, anon=False, |
+ tlsIntolerant=None, |
signedCertTimestamps=None, |
- fallbackSCSV=False, ocspResponse=None): |
+ fallbackSCSV=False, |
+ ocspResponse=None |
+ ): |
"""Start a server handshake operation on the TLS connection. |
This function returns a generator which behaves similarly to |
@@ -1069,35 +1101,47 @@ class TLSConnection(TLSRecordLayer): |
@return: A generator; see above for details. |
""" |
handshaker = self._handshakeServerAsyncHelper(\ |
- sharedKeyDB=sharedKeyDB, |
verifierDB=verifierDB, certChain=certChain, |
privateKey=privateKey, reqCert=reqCert, |
- sessionCache=sessionCache, settings=settings, |
- reqCAs=reqCAs, |
+ sessionCache=sessionCache, settings=settings, |
+ reqCAs=reqCAs, |
+ tacks=tacks, activationFlags=activationFlags, |
+ nextProtos=nextProtos, anon=anon, |
tlsIntolerant=tlsIntolerant, |
signedCertTimestamps=signedCertTimestamps, |
- fallbackSCSV=fallbackSCSV, ocspResponse=ocspResponse) |
- |
+ fallbackSCSV=fallbackSCSV, |
+ ocspResponse=ocspResponse) |
for result in self._handshakeWrapperAsync(handshaker, checker): |
yield result |
- def _handshakeServerAsyncHelper(self, sharedKeyDB, verifierDB, |
- certChain, privateKey, reqCert, |
- sessionCache, settings, reqCAs, |
- tlsIntolerant, signedCertTimestamps, |
- fallbackSCSV, ocspResponse): |
+ def _handshakeServerAsyncHelper(self, verifierDB, |
+ certChain, privateKey, reqCert, sessionCache, |
+ settings, reqCAs, |
+ tacks, activationFlags, |
+ nextProtos, anon, |
+ tlsIntolerant, signedCertTimestamps, fallbackSCSV, |
+ ocspResponse): |
self._handshakeStart(client=False) |
- if (not sharedKeyDB) and (not verifierDB) and (not certChain): |
+ if (not verifierDB) and (not certChain) and not anon: |
raise ValueError("Caller passed no authentication credentials") |
if certChain and not privateKey: |
raise ValueError("Caller passed a certChain but no privateKey") |
if privateKey and not certChain: |
raise ValueError("Caller passed a privateKey but no certChain") |
if reqCAs and not reqCert: |
- raise ValueError("Caller passed reqCAs but not reqCert") |
+ raise ValueError("Caller passed reqCAs but not reqCert") |
+ if certChain and not isinstance(certChain, X509CertChain): |
+ raise ValueError("Unrecognized certificate type") |
+ if activationFlags and not tacks: |
+ raise ValueError("Nonzero activationFlags requires tacks") |
+ if tacks: |
+ if not tackpyLoaded: |
+ raise ValueError("tackpy is not loaded") |
+ if not settings or not settings.useExperimentalTackExtension: |
+ raise ValueError("useExperimentalTackExtension not enabled") |
if signedCertTimestamps and not certChain: |
raise ValueError("Caller passed signedCertTimestamps but no " |
"certChain") |
@@ -1105,36 +1149,129 @@ class TLSConnection(TLSRecordLayer): |
if not settings: |
settings = HandshakeSettings() |
settings = settings._filter() |
+ |
+ # OK Start exchanging messages |
+ # ****************************** |
+ |
+ # Handle ClientHello and resumption |
+ for result in self._serverGetClientHello(settings, certChain,\ |
+ verifierDB, sessionCache, |
+ anon, tlsIntolerant, fallbackSCSV): |
+ if result in (0,1): yield result |
+ elif result == None: |
+ self._handshakeDone(resumed=True) |
+ return # Handshake was resumed, we're done |
+ else: break |
+ (clientHello, cipherSuite) = result |
+ |
+ #If not a resumption... |
+ |
+ # Create the ServerHello message |
+ if sessionCache: |
+ sessionID = getRandomBytes(32) |
+ else: |
+ sessionID = bytearray(0) |
+ |
+ if not clientHello.supports_npn: |
+ nextProtos = None |
+ |
+ # If not doing a certificate-based suite, discard the TACK |
+ if not cipherSuite in CipherSuite.certAllSuites: |
+ tacks = None |
+ |
+ # Prepare a TACK Extension if requested |
+ if clientHello.tack: |
+ tackExt = TackExtension.create(tacks, activationFlags) |
+ else: |
+ tackExt = None |
+ serverHello = ServerHello() |
+ serverHello.create(self.version, getRandomBytes(32), sessionID, \ |
+ cipherSuite, CertificateType.x509, tackExt, |
+ nextProtos) |
+ serverHello.channel_id = clientHello.channel_id |
+ if clientHello.support_signed_cert_timestamps: |
+ serverHello.signed_cert_timestamps = signedCertTimestamps |
+ if clientHello.status_request: |
+ serverHello.status_request = ocspResponse |
+ |
+ # Perform the SRP key exchange |
+ clientCertChain = None |
+ if cipherSuite in CipherSuite.srpAllSuites: |
+ for result in self._serverSRPKeyExchange(clientHello, serverHello, |
+ verifierDB, cipherSuite, |
+ privateKey, certChain): |
+ if result in (0,1): yield result |
+ else: break |
+ premasterSecret = result |
+ |
+ # Perform the RSA key exchange |
+ elif cipherSuite in CipherSuite.certSuites: |
+ for result in self._serverCertKeyExchange(clientHello, serverHello, |
+ certChain, privateKey, |
+ reqCert, reqCAs, cipherSuite, |
+ settings, ocspResponse): |
+ if result in (0,1): yield result |
+ else: break |
+ (premasterSecret, clientCertChain) = result |
+ |
+ # Perform anonymous Diffie Hellman key exchange |
+ elif cipherSuite in CipherSuite.anonSuites: |
+ for result in self._serverAnonKeyExchange(clientHello, serverHello, |
+ cipherSuite, settings): |
+ if result in (0,1): yield result |
+ else: break |
+ premasterSecret = result |
+ |
+ else: |
+ assert(False) |
+ |
+ # Exchange Finished messages |
+ for result in self._serverFinished(premasterSecret, |
+ clientHello.random, serverHello.random, |
+ cipherSuite, settings.cipherImplementations, |
+ nextProtos, clientHello.channel_id): |
+ if result in (0,1): yield result |
+ else: break |
+ masterSecret = result |
+ #Create the session object |
+ self.session = Session() |
+ if cipherSuite in CipherSuite.certAllSuites: |
+ serverCertChain = certChain |
+ else: |
+ serverCertChain = None |
+ srpUsername = None |
+ serverName = None |
+ if clientHello.srp_username: |
+ srpUsername = clientHello.srp_username.decode("utf-8") |
+ if clientHello.server_name: |
+ serverName = clientHello.server_name.decode("utf-8") |
+ self.session.create(masterSecret, serverHello.session_id, cipherSuite, |
+ srpUsername, clientCertChain, serverCertChain, |
+ tackExt, serverHello.tackExt!=None, serverName) |
+ |
+ #Add the session object to the session cache |
+ if sessionCache and sessionID: |
+ sessionCache[sessionID] = self.session |
+ |
+ self._handshakeDone(resumed=False) |
+ |
+ |
+ def _serverGetClientHello(self, settings, certChain, verifierDB, |
+ sessionCache, anon, tlsIntolerant, fallbackSCSV): |
#Initialize acceptable cipher suites |
cipherSuites = [] |
if verifierDB: |
if certChain: |
cipherSuites += \ |
- CipherSuite.getSrpRsaSuites(settings.cipherNames) |
- cipherSuites += CipherSuite.getSrpSuites(settings.cipherNames) |
- if sharedKeyDB or certChain: |
- cipherSuites += CipherSuite.getRsaSuites(settings.cipherNames) |
- |
- #Initialize acceptable certificate type |
- certificateType = None |
- if certChain: |
- try: |
- import cryptoIDlib.CertChain |
- if isinstance(certChain, cryptoIDlib.CertChain.CertChain): |
- certificateType = CertificateType.cryptoID |
- except ImportError: |
- pass |
- if isinstance(certChain, X509CertChain): |
- certificateType = CertificateType.x509 |
- if certificateType == None: |
- raise ValueError("Unrecognized certificate type") |
- |
- #Initialize locals |
- clientCertChain = None |
- serverCertChain = None #We may set certChain to this later |
- postFinishedError = None |
- doingChannelID = False |
+ CipherSuite.getSrpCertSuites(settings) |
+ cipherSuites += CipherSuite.getSrpSuites(settings) |
+ elif certChain: |
+ cipherSuites += CipherSuite.getCertSuites(settings) |
+ elif anon: |
+ cipherSuites += CipherSuite.getAnonSuites(settings) |
+ else: |
+ assert(False) |
#Tentatively set version to most-desirable version, so if an error |
#occurs parsing the ClientHello, this is what we'll use for the |
@@ -1144,10 +1281,8 @@ class TLSConnection(TLSRecordLayer): |
#Get ClientHello |
for result in self._getMsg(ContentType.handshake, |
HandshakeType.client_hello): |
- if result in (0,1): |
- yield result |
- else: |
- break |
+ if result in (0,1): yield result |
+ else: break |
clientHello = result |
#If client's version is too low, reject it |
@@ -1158,79 +1293,36 @@ class TLSConnection(TLSRecordLayer): |
"Too old version: %s" % str(clientHello.client_version)): |
yield result |
- #If tlsIntolerant is nonzero, reject certain TLS versions. |
- #1: reject all TLS versions. |
- #2: reject TLS 1.1 or higher. |
- #3: reject TLS 1.2 or higher. |
- if (tlsIntolerant == 1 and clientHello.client_version > (3, 0) or |
- tlsIntolerant == 2 and clientHello.client_version > (3, 1) or |
- tlsIntolerant == 3 and clientHello.client_version > (3, 2)): |
+ #If simulating TLS intolerance, reject certain TLS versions. |
+ elif (tlsIntolerant is not None and |
+ clientHello.client_version >= tlsIntolerant): |
for result in self._sendError(\ |
AlertDescription.handshake_failure): |
yield result |
#If client's version is too high, propose my highest version |
- if clientHello.client_version > settings.maxVersion: |
+ elif clientHello.client_version > settings.maxVersion: |
self.version = settings.maxVersion |
- else: |
- #Set the version to the client's version |
- self.version = clientHello.client_version |
- if (fallbackSCSV and |
- clientHello.client_version < settings.maxVersion): |
- for cipherSuite in clientHello.cipher_suites: |
- if cipherSuite == 0x5600: |
- for result in self._sendError(\ |
- AlertDescription.inappropriate_fallback): |
- yield result |
- #Get the client nonce; create server nonce |
- clientRandom = clientHello.random |
- serverRandom = getRandomBytes(32) |
+ #Detect if the client performed an inappropriate fallback. |
+ elif fallbackSCSV and clientHello.client_version < settings.maxVersion: |
+ if CipherSuite.TLS_FALLBACK_SCSV in clientHello.cipher_suites: |
+ for result in self._sendError(\ |
+ AlertDescription.inappropriate_fallback): |
+ yield result |
- #Calculate the first cipher suite intersection. |
- #This is the 'privileged' ciphersuite. We'll use it if we're |
- #doing a shared-key resumption or a new negotiation. In fact, |
- #the only time we won't use it is if we're resuming a non-sharedkey |
- #session, in which case we use the ciphersuite from the session. |
- # |
- #Given the current ciphersuite ordering, this means we prefer SRP |
- #over non-SRP. |
- for cipherSuite in cipherSuites: |
- if cipherSuite in clientHello.cipher_suites: |
- break |
else: |
- for result in self._sendError(\ |
- AlertDescription.handshake_failure): |
- yield result |
+ #Set the version to the client's version |
+ self.version = clientHello.client_version |
- #If resumption was requested... |
- if clientHello.session_id and (sharedKeyDB or sessionCache): |
+ #If resumption was requested and we have a session cache... |
+ if clientHello.session_id and sessionCache: |
session = None |
- #Check in the sharedKeys container |
- if sharedKeyDB and len(clientHello.session_id)==16: |
- try: |
- #Trim off zero padding, if any |
- for x in range(16): |
- if clientHello.session_id[x]==0: |
- break |
- self.allegedSharedKeyUsername = bytesToString(\ |
- clientHello.session_id[:x]) |
- session = sharedKeyDB[self.allegedSharedKeyUsername] |
- if not session.sharedKey: |
- raise AssertionError() |
- #use privileged ciphersuite |
- session.cipherSuite = cipherSuite |
- except KeyError: |
- pass |
- |
- #Then check in the session cache |
+ #Check in the session cache |
if sessionCache and not session: |
try: |
- session = sessionCache[bytesToString(\ |
- clientHello.session_id)] |
- if session.sharedKey: |
- raise AssertionError() |
+ session = sessionCache[clientHello.session_id] |
if not session.resumable: |
raise AssertionError() |
#Check for consistency with ClientHello |
@@ -1243,390 +1335,460 @@ class TLSConnection(TLSRecordLayer): |
AlertDescription.handshake_failure): |
yield result |
if clientHello.srp_username: |
- if clientHello.srp_username != session.srpUsername: |
+ if not session.srpUsername or \ |
+ clientHello.srp_username != bytearray(session.srpUsername, "utf-8"): |
for result in self._sendError(\ |
AlertDescription.handshake_failure): |
yield result |
+ if clientHello.server_name: |
+ if not session.serverName or \ |
+ clientHello.server_name != bytearray(session.serverName, "utf-8"): |
+ for result in self._sendError(\ |
+ AlertDescription.handshake_failure): |
+ yield result |
except KeyError: |
pass |
#If a session is found.. |
if session: |
- #Set the session |
- self.session = session |
- |
#Send ServerHello |
serverHello = ServerHello() |
- serverHello.create(self.version, serverRandom, |
+ serverHello.create(self.version, getRandomBytes(32), |
session.sessionID, session.cipherSuite, |
- certificateType) |
- serverHello.channel_id = clientHello.channel_id |
- doingChannelID = clientHello.channel_id |
+ CertificateType.x509, None, None) |
for result in self._sendMsg(serverHello): |
yield result |
- #From here on, the client's messages must have the right version |
+ #From here on, the client's messages must have right version |
self._versionCheck = True |
#Calculate pending connection states |
- self._calcPendingStates(clientRandom, serverRandom, |
- settings.cipherImplementations) |
+ self._calcPendingStates(session.cipherSuite, |
+ session.masterSecret, |
+ clientHello.random, |
+ serverHello.random, |
+ settings.cipherImplementations) |
#Exchange ChangeCipherSpec and Finished messages |
- for result in self._sendFinished(): |
+ for result in self._sendFinished(session.masterSecret): |
yield result |
- for result in self._getChangeCipherSpec(): |
+ for result in self._getFinished(session.masterSecret): |
yield result |
- if doingChannelID: |
- for result in self._getEncryptedExtensions(): |
- yield result |
- for result in self._getFinished(): |
- yield result |
- |
- #Mark the connection as open |
- self._handshakeDone(resumed=True) |
- return |
+ #Set the session |
+ self.session = session |
+ |
+ yield None # Handshake done! |
- #If not a resumption... |
- |
- #TRICKY: we might have chosen an RSA suite that was only deemed |
- #acceptable because of the shared-key resumption. If the shared- |
- #key resumption failed, because the identifier wasn't recognized, |
- #we might fall through to here, where we have an RSA suite |
- #chosen, but no certificate. |
- if cipherSuite in CipherSuite.rsaSuites and not certChain: |
+ #Calculate the first cipher suite intersection. |
+ #This is the 'privileged' ciphersuite. We'll use it if we're |
+ #doing a new negotiation. In fact, |
+ #the only time we won't use it is if we're resuming a |
+ #session, in which case we use the ciphersuite from the session. |
+ # |
+ #Use the client's preferences for now. |
+ for cipherSuite in clientHello.cipher_suites: |
+ if cipherSuite in cipherSuites: |
+ break |
+ else: |
for result in self._sendError(\ |
- AlertDescription.handshake_failure): |
+ AlertDescription.handshake_failure, |
+ "No mutual ciphersuite"): |
yield result |
- |
+ if cipherSuite in CipherSuite.srpAllSuites and \ |
+ not clientHello.srp_username: |
+ for result in self._sendError(\ |
+ AlertDescription.unknown_psk_identity, |
+ "Client sent a hello, but without the SRP username"): |
+ yield result |
+ |
#If an RSA suite is chosen, check for certificate type intersection |
- #(We do this check down here because if the mismatch occurs but the |
- # client is using a shared-key session, it's okay) |
- if cipherSuite in CipherSuite.rsaSuites + \ |
- CipherSuite.srpRsaSuites: |
- if certificateType not in clientHello.certificate_types: |
- for result in self._sendError(\ |
- AlertDescription.handshake_failure, |
- "the client doesn't support my certificate type"): |
- yield result |
- |
- #Move certChain -> serverCertChain, now that we're using it |
- serverCertChain = certChain |
- |
- |
- #Create sessionID |
- if sessionCache: |
- sessionID = getRandomBytes(32) |
- else: |
- sessionID = createByteArraySequence([]) |
+ if cipherSuite in CipherSuite.certAllSuites and CertificateType.x509 \ |
+ not in clientHello.certificate_types: |
+ for result in self._sendError(\ |
+ AlertDescription.handshake_failure, |
+ "the client doesn't support my certificate type"): |
+ yield result |
- #If we've selected an SRP suite, exchange keys and calculate |
- #premaster secret: |
- if cipherSuite in CipherSuite.srpSuites + CipherSuite.srpRsaSuites: |
+ # If resumption was not requested, or |
+ # we have no session cache, or |
+ # the client's session_id was not found in cache: |
+ yield (clientHello, cipherSuite) |
- #If there's no SRP username... |
- if not clientHello.srp_username: |
+ def _serverSRPKeyExchange(self, clientHello, serverHello, verifierDB, |
+ cipherSuite, privateKey, serverCertChain): |
- #Ask the client to re-send ClientHello with one |
- for result in self._sendMsg(Alert().create(\ |
- AlertDescription.missing_srp_username, |
- AlertLevel.warning)): |
- yield result |
+ srpUsername = clientHello.srp_username.decode("utf-8") |
+ self.allegedSrpUsername = srpUsername |
+ #Get parameters from username |
+ try: |
+ entry = verifierDB[srpUsername] |
+ except KeyError: |
+ for result in self._sendError(\ |
+ AlertDescription.unknown_psk_identity): |
+ yield result |
+ (N, g, s, v) = entry |
+ |
+ #Calculate server's ephemeral DH values (b, B) |
+ b = bytesToNumber(getRandomBytes(32)) |
+ k = makeK(N, g) |
+ B = (powMod(g, b, N) + (k*v)) % N |
+ |
+ #Create ServerKeyExchange, signing it if necessary |
+ serverKeyExchange = ServerKeyExchange(cipherSuite) |
+ serverKeyExchange.createSRP(N, g, s, B) |
+ if cipherSuite in CipherSuite.srpCertSuites: |
+ hashBytes = serverKeyExchange.hash(clientHello.random, |
+ serverHello.random) |
+ serverKeyExchange.signature = privateKey.sign(hashBytes) |
+ |
+ #Send ServerHello[, Certificate], ServerKeyExchange, |
+ #ServerHelloDone |
+ msgs = [] |
+ msgs.append(serverHello) |
+ if cipherSuite in CipherSuite.srpCertSuites: |
+ certificateMsg = Certificate(CertificateType.x509) |
+ certificateMsg.create(serverCertChain) |
+ msgs.append(certificateMsg) |
+ msgs.append(serverKeyExchange) |
+ msgs.append(ServerHelloDone()) |
+ for result in self._sendMsgs(msgs): |
+ yield result |
- #Get ClientHello |
- for result in self._getMsg(ContentType.handshake, |
- HandshakeType.client_hello): |
- if result in (0,1): |
- yield result |
- else: |
- break |
- clientHello = result |
- |
- #Check ClientHello |
- #If client's version is too low, reject it (COPIED CODE; BAD!) |
- if clientHello.client_version < settings.minVersion: |
- self.version = settings.minVersion |
- for result in self._sendError(\ |
- AlertDescription.protocol_version, |
- "Too old version: %s" % str(clientHello.client_version)): |
- yield result |
+ #From here on, the client's messages must have the right version |
+ self._versionCheck = True |
- #If client's version is too high, propose my highest version |
- elif clientHello.client_version > settings.maxVersion: |
- self.version = settings.maxVersion |
+ #Get and check ClientKeyExchange |
+ for result in self._getMsg(ContentType.handshake, |
+ HandshakeType.client_key_exchange, |
+ cipherSuite): |
+ if result in (0,1): yield result |
+ else: break |
+ clientKeyExchange = result |
+ A = clientKeyExchange.srp_A |
+ if A % N == 0: |
+ for result in self._sendError(AlertDescription.illegal_parameter, |
+ "Suspicious A value"): |
+ yield result |
+ assert(False) # Just to ensure we don't fall through somehow |
- else: |
- #Set the version to the client's version |
- self.version = clientHello.client_version |
- |
- #Recalculate the privileged cipher suite, making sure to |
- #pick an SRP suite |
- cipherSuites = [c for c in cipherSuites if c in \ |
- CipherSuite.srpSuites + \ |
- CipherSuite.srpRsaSuites] |
- for cipherSuite in cipherSuites: |
- if cipherSuite in clientHello.cipher_suites: |
- break |
- else: |
- for result in self._sendError(\ |
- AlertDescription.handshake_failure): |
- yield result |
+ #Calculate u |
+ u = makeU(N, A, B) |
- #Get the client nonce; create server nonce |
- clientRandom = clientHello.random |
- serverRandom = getRandomBytes(32) |
+ #Calculate premaster secret |
+ S = powMod((A * powMod(v,u,N)) % N, b, N) |
+ premasterSecret = numberToByteArray(S) |
+ |
+ yield premasterSecret |
- #The username better be there, this time |
- if not clientHello.srp_username: |
- for result in self._sendError(\ |
- AlertDescription.illegal_parameter, |
- "Client resent a hello, but without the SRP"\ |
- " username"): |
- yield result |
+ def _serverCertKeyExchange(self, clientHello, serverHello, |
+ serverCertChain, privateKey, |
+ reqCert, reqCAs, cipherSuite, |
+ settings, ocspResponse): |
+ #Send ServerHello, Certificate[, CertificateRequest], |
+ #ServerHelloDone |
+ msgs = [] |
- #Get username |
- self.allegedSrpUsername = clientHello.srp_username |
+ # If we verify a client cert chain, return it |
+ clientCertChain = None |
- #Get parameters from username |
- try: |
- entry = verifierDB[self.allegedSrpUsername] |
- except KeyError: |
- for result in self._sendError(\ |
- AlertDescription.unknown_srp_username): |
- yield result |
- (N, g, s, v) = entry |
- |
- #Calculate server's ephemeral DH values (b, B) |
- b = bytesToNumber(getRandomBytes(32)) |
- k = makeK(N, g) |
- B = (powMod(g, b, N) + (k*v)) % N |
- |
- #Create ServerKeyExchange, signing it if necessary |
- serverKeyExchange = ServerKeyExchange(cipherSuite) |
- serverKeyExchange.createSRP(N, g, stringToBytes(s), B) |
- if cipherSuite in CipherSuite.srpRsaSuites: |
- hashBytes = serverKeyExchange.hash(clientRandom, |
- serverRandom) |
- serverKeyExchange.signature = privateKey.sign(hashBytes) |
- |
- #Send ServerHello[, Certificate], ServerKeyExchange, |
- #ServerHelloDone |
- msgs = [] |
- serverHello = ServerHello() |
- serverHello.create(self.version, serverRandom, sessionID, |
- cipherSuite, certificateType) |
- msgs.append(serverHello) |
- if cipherSuite in CipherSuite.srpRsaSuites: |
- certificateMsg = Certificate(certificateType) |
- certificateMsg.create(serverCertChain) |
- msgs.append(certificateMsg) |
- msgs.append(serverKeyExchange) |
- msgs.append(ServerHelloDone()) |
- for result in self._sendMsgs(msgs): |
- yield result |
+ msgs.append(serverHello) |
+ msgs.append(Certificate(CertificateType.x509).create(serverCertChain)) |
+ if serverHello.status_request: |
+ msgs.append(CertificateStatus().create(ocspResponse)) |
+ if reqCert and reqCAs: |
+ msgs.append(CertificateRequest().create(\ |
+ [ClientCertificateType.rsa_sign], reqCAs)) |
+ elif reqCert: |
+ msgs.append(CertificateRequest()) |
+ msgs.append(ServerHelloDone()) |
+ for result in self._sendMsgs(msgs): |
+ yield result |
- #From here on, the client's messages must have the right version |
- self._versionCheck = True |
+ #From here on, the client's messages must have the right version |
+ self._versionCheck = True |
- #Get and check ClientKeyExchange |
- for result in self._getMsg(ContentType.handshake, |
- HandshakeType.client_key_exchange, |
- cipherSuite): |
- if result in (0,1): |
- yield result |
- else: |
- break |
- clientKeyExchange = result |
- A = clientKeyExchange.srp_A |
- if A % N == 0: |
- postFinishedError = (AlertDescription.illegal_parameter, |
- "Suspicious A value") |
- #Calculate u |
- u = makeU(N, A, B) |
- |
- #Calculate premaster secret |
- S = powMod((A * powMod(v,u,N)) % N, b, N) |
- premasterSecret = numberToBytes(S) |
- |
- |
- #If we've selected an RSA suite, exchange keys and calculate |
- #premaster secret: |
- elif cipherSuite in CipherSuite.rsaSuites: |
- |
- #Send ServerHello, Certificate[, CertificateRequest], |
- #ServerHelloDone |
- msgs = [] |
- serverHello = ServerHello().create( |
- self.version, serverRandom, |
- sessionID, cipherSuite, certificateType) |
- serverHello.channel_id = clientHello.channel_id |
- if clientHello.support_signed_cert_timestamps: |
- serverHello.signed_cert_timestamps = signedCertTimestamps |
- serverHello.status_request = (clientHello.status_request and |
- ocspResponse) |
- doingChannelID = clientHello.channel_id |
- msgs.append(serverHello) |
- msgs.append(Certificate(certificateType).create(serverCertChain)) |
- if serverHello.status_request: |
- msgs.append(CertificateStatus().create(ocspResponse)) |
- if reqCert and reqCAs: |
- msgs.append(CertificateRequest().create([], reqCAs)) |
- elif reqCert: |
- msgs.append(CertificateRequest()) |
- msgs.append(ServerHelloDone()) |
- for result in self._sendMsgs(msgs): |
- yield result |
+ #Get [Certificate,] (if was requested) |
+ if reqCert: |
+ if self.version == (3,0): |
+ for result in self._getMsg((ContentType.handshake, |
+ ContentType.alert), |
+ HandshakeType.certificate, |
+ CertificateType.x509): |
+ if result in (0,1): yield result |
+ else: break |
+ msg = result |
- #From here on, the client's messages must have the right version |
- self._versionCheck = True |
- |
- #Get [Certificate,] (if was requested) |
- if reqCert: |
- if self.version == (3,0): |
- for result in self._getMsg((ContentType.handshake, |
- ContentType.alert), |
- HandshakeType.certificate, |
- certificateType): |
- if result in (0,1): |
- yield result |
- else: |
- break |
- msg = result |
- |
- if isinstance(msg, Alert): |
- #If it's not a no_certificate alert, re-raise |
- alert = msg |
- if alert.description != \ |
- AlertDescription.no_certificate: |
- self._shutdown(False) |
- raise TLSRemoteAlert(alert) |
- elif isinstance(msg, Certificate): |
- clientCertificate = msg |
- if clientCertificate.certChain and \ |
- clientCertificate.certChain.getNumCerts()!=0: |
- clientCertChain = clientCertificate.certChain |
- else: |
- raise AssertionError() |
- elif self.version in ((3,1), (3,2)): |
- for result in self._getMsg(ContentType.handshake, |
- HandshakeType.certificate, |
- certificateType): |
- if result in (0,1): |
- yield result |
- else: |
- break |
- clientCertificate = result |
+ if isinstance(msg, Alert): |
+ #If it's not a no_certificate alert, re-raise |
+ alert = msg |
+ if alert.description != \ |
+ AlertDescription.no_certificate: |
+ self._shutdown(False) |
+ raise TLSRemoteAlert(alert) |
+ elif isinstance(msg, Certificate): |
+ clientCertificate = msg |
if clientCertificate.certChain and \ |
clientCertificate.certChain.getNumCerts()!=0: |
clientCertChain = clientCertificate.certChain |
else: |
raise AssertionError() |
+ elif self.version in ((3,1), (3,2)): |
+ for result in self._getMsg(ContentType.handshake, |
+ HandshakeType.certificate, |
+ CertificateType.x509): |
+ if result in (0,1): yield result |
+ else: break |
+ clientCertificate = result |
+ if clientCertificate.certChain and \ |
+ clientCertificate.certChain.getNumCerts()!=0: |
+ clientCertChain = clientCertificate.certChain |
+ else: |
+ raise AssertionError() |
+ |
+ #Get ClientKeyExchange |
+ for result in self._getMsg(ContentType.handshake, |
+ HandshakeType.client_key_exchange, |
+ cipherSuite): |
+ if result in (0,1): yield result |
+ else: break |
+ clientKeyExchange = result |
+ |
+ #Decrypt ClientKeyExchange |
+ premasterSecret = privateKey.decrypt(\ |
+ clientKeyExchange.encryptedPreMasterSecret) |
+ |
+ # On decryption failure randomize premaster secret to avoid |
+ # Bleichenbacher's "million message" attack |
+ randomPreMasterSecret = getRandomBytes(48) |
+ versionCheck = (premasterSecret[0], premasterSecret[1]) |
+ if not premasterSecret: |
+ premasterSecret = randomPreMasterSecret |
+ elif len(premasterSecret)!=48: |
+ premasterSecret = randomPreMasterSecret |
+ elif versionCheck != clientHello.client_version: |
+ if versionCheck != self.version: #Tolerate buggy IE clients |
+ premasterSecret = randomPreMasterSecret |
- #Get ClientKeyExchange |
+ #Get and check CertificateVerify, if relevant |
+ if clientCertChain: |
+ if self.version == (3,0): |
+ masterSecret = calcMasterSecret(self.version, premasterSecret, |
+ clientHello.random, serverHello.random) |
+ verifyBytes = self._calcSSLHandshakeHash(masterSecret, b"") |
+ elif self.version in ((3,1), (3,2)): |
+ verifyBytes = self._handshake_md5.digest() + \ |
+ self._handshake_sha.digest() |
for result in self._getMsg(ContentType.handshake, |
- HandshakeType.client_key_exchange, |
- cipherSuite): |
- if result in (0,1): |
+ HandshakeType.certificate_verify): |
+ if result in (0,1): yield result |
+ else: break |
+ certificateVerify = result |
+ publicKey = clientCertChain.getEndEntityPublicKey() |
+ if len(publicKey) < settings.minKeySize: |
+ for result in self._sendError(\ |
+ AlertDescription.handshake_failure, |
+ "Client's public key too small: %d" % len(publicKey)): |
yield result |
- else: |
- break |
- clientKeyExchange = result |
- #Decrypt ClientKeyExchange |
- premasterSecret = privateKey.decrypt(\ |
- clientKeyExchange.encryptedPreMasterSecret) |
+ if len(publicKey) > settings.maxKeySize: |
+ for result in self._sendError(\ |
+ AlertDescription.handshake_failure, |
+ "Client's public key too large: %d" % len(publicKey)): |
+ yield result |
- randomPreMasterSecret = getRandomBytes(48) |
- versionCheck = (premasterSecret[0], premasterSecret[1]) |
- if not premasterSecret: |
- premasterSecret = randomPreMasterSecret |
- elif len(premasterSecret)!=48: |
- premasterSecret = randomPreMasterSecret |
- elif versionCheck != clientHello.client_version: |
- if versionCheck != self.version: #Tolerate buggy IE clients |
- premasterSecret = randomPreMasterSecret |
+ if not publicKey.verify(certificateVerify.signature, verifyBytes): |
+ for result in self._sendError(\ |
+ AlertDescription.decrypt_error, |
+ "Signature failed to verify"): |
+ yield result |
+ yield (premasterSecret, clientCertChain) |
+ |
+ |
+ def _serverAnonKeyExchange(self, clientHello, serverHello, cipherSuite, |
+ settings): |
+ # Calculate DH p, g, Xs, Ys |
+ dh_p = getRandomSafePrime(32, False) |
+ dh_g = getRandomNumber(2, dh_p) |
+ dh_Xs = bytesToNumber(getRandomBytes(32)) |
+ dh_Ys = powMod(dh_g, dh_Xs, dh_p) |
+ |
+ #Create ServerKeyExchange |
+ serverKeyExchange = ServerKeyExchange(cipherSuite) |
+ serverKeyExchange.createDH(dh_p, dh_g, dh_Ys) |
+ |
+ #Send ServerHello[, Certificate], ServerKeyExchange, |
+ #ServerHelloDone |
+ msgs = [] |
+ msgs.append(serverHello) |
+ msgs.append(serverKeyExchange) |
+ msgs.append(ServerHelloDone()) |
+ for result in self._sendMsgs(msgs): |
+ yield result |
+ |
+ #From here on, the client's messages must have the right version |
+ self._versionCheck = True |
+ |
+ #Get and check ClientKeyExchange |
+ for result in self._getMsg(ContentType.handshake, |
+ HandshakeType.client_key_exchange, |
+ cipherSuite): |
+ if result in (0,1): |
+ yield result |
+ else: |
+ break |
+ clientKeyExchange = result |
+ dh_Yc = clientKeyExchange.dh_Yc |
+ |
+ if dh_Yc % dh_p == 0: |
+ for result in self._sendError(AlertDescription.illegal_parameter, |
+ "Suspicious dh_Yc value"): |
+ yield result |
+ assert(False) # Just to ensure we don't fall through somehow |
- #Get and check CertificateVerify, if relevant |
- if clientCertChain: |
- if self.version == (3,0): |
- #Create a temporary session object, just for the purpose |
- #of checking the CertificateVerify |
- session = Session() |
- session._calcMasterSecret(self.version, premasterSecret, |
- clientRandom, serverRandom) |
- verifyBytes = self._calcSSLHandshakeHash(\ |
- session.masterSecret, "") |
- elif self.version in ((3,1), (3,2)): |
- verifyBytes = stringToBytes(self._handshake_md5.digest() +\ |
- self._handshake_sha.digest()) |
- for result in self._getMsg(ContentType.handshake, |
- HandshakeType.certificate_verify): |
- if result in (0,1): |
- yield result |
- else: |
- break |
- certificateVerify = result |
- publicKey = clientCertChain.getEndEntityPublicKey() |
- if len(publicKey) < settings.minKeySize: |
- postFinishedError = (AlertDescription.handshake_failure, |
- "Client's public key too small: %d" % len(publicKey)) |
- if len(publicKey) > settings.maxKeySize: |
- postFinishedError = (AlertDescription.handshake_failure, |
- "Client's public key too large: %d" % len(publicKey)) |
- |
- if not publicKey.verify(certificateVerify.signature, |
- verifyBytes): |
- postFinishedError = (AlertDescription.decrypt_error, |
- "Signature failed to verify") |
+ #Calculate premaster secre |
+ S = powMod(dh_Yc,dh_Xs,dh_p) |
+ premasterSecret = numberToByteArray(S) |
+ |
+ yield premasterSecret |
- #Create the session object |
- self.session = Session() |
- self.session._calcMasterSecret(self.version, premasterSecret, |
+ def _serverFinished(self, premasterSecret, clientRandom, serverRandom, |
+ cipherSuite, cipherImplementations, nextProtos, |
+ doingChannelID): |
+ masterSecret = calcMasterSecret(self.version, premasterSecret, |
clientRandom, serverRandom) |
- self.session.sessionID = sessionID |
- self.session.cipherSuite = cipherSuite |
- self.session.srpUsername = self.allegedSrpUsername |
- self.session.clientCertChain = clientCertChain |
- self.session.serverCertChain = serverCertChain |
- |
+ |
#Calculate pending connection states |
- self._calcPendingStates(clientRandom, serverRandom, |
- settings.cipherImplementations) |
+ self._calcPendingStates(cipherSuite, masterSecret, |
+ clientRandom, serverRandom, |
+ cipherImplementations) |
#Exchange ChangeCipherSpec and Finished messages |
- for result in self._getChangeCipherSpec(): |
+ for result in self._getFinished(masterSecret, |
+ expect_next_protocol=nextProtos is not None, |
+ expect_channel_id=doingChannelID): |
yield result |
- if doingChannelID: |
- for result in self._getEncryptedExtensions(): |
- yield result |
- for result in self._getFinished(): |
+ |
+ for result in self._sendFinished(masterSecret): |
yield result |
+ |
+ yield masterSecret |
- #If we were holding a post-finished error until receiving the client |
- #finished message, send it now. We delay the call until this point |
- #because calling sendError() throws an exception, and our caller might |
- #shut down the socket upon receiving the exception. If he did, and the |
- #client was still sending its ChangeCipherSpec or Finished messages, it |
- #would cause a socket error on the client side. This is a lot of |
- #consideration to show to misbehaving clients, but this would also |
- #cause problems with fault-testing. |
- if postFinishedError: |
- for result in self._sendError(*postFinishedError): |
+ |
+ #********************************************************* |
+ # Shared Handshake Functions |
+ #********************************************************* |
+ |
+ |
+ def _sendFinished(self, masterSecret, nextProto=None): |
+ #Send ChangeCipherSpec |
+ for result in self._sendMsg(ChangeCipherSpec()): |
+ yield result |
+ |
+ #Switch to pending write state |
+ self._changeWriteState() |
+ |
+ if nextProto is not None: |
+ nextProtoMsg = NextProtocol().create(nextProto) |
+ for result in self._sendMsg(nextProtoMsg): |
yield result |
- for result in self._sendFinished(): |
+ #Calculate verification data |
+ verifyData = self._calcFinished(masterSecret, True) |
+ if self.fault == Fault.badFinished: |
+ verifyData[0] = (verifyData[0]+1)%256 |
+ |
+ #Send Finished message under new state |
+ finished = Finished(self.version).create(verifyData) |
+ for result in self._sendMsg(finished): |
yield result |
- #Add the session object to the session cache |
- if sessionCache and sessionID: |
- sessionCache[bytesToString(sessionID)] = self.session |
+ def _getFinished(self, masterSecret, expect_next_protocol=False, nextProto=None, |
+ expect_channel_id=False): |
+ #Get and check ChangeCipherSpec |
+ for result in self._getMsg(ContentType.change_cipher_spec): |
+ if result in (0,1): |
+ yield result |
+ changeCipherSpec = result |
- #Mark the connection as open |
- self.session._setResumable(True) |
- self._handshakeDone(resumed=False) |
+ if changeCipherSpec.type != 1: |
+ for result in self._sendError(AlertDescription.illegal_parameter, |
+ "ChangeCipherSpec type incorrect"): |
+ yield result |
+ |
+ #Switch to pending read state |
+ self._changeReadState() |
+ |
+ #Server Finish - Are we waiting for a next protocol echo? |
+ if expect_next_protocol: |
+ for result in self._getMsg(ContentType.handshake, HandshakeType.next_protocol): |
+ if result in (0,1): |
+ yield result |
+ if result is None: |
+ for result in self._sendError(AlertDescription.unexpected_message, |
+ "Didn't get NextProtocol message"): |
+ yield result |
+ |
+ self.next_proto = result.next_proto |
+ else: |
+ self.next_proto = None |
+ |
+ #Client Finish - Only set the next_protocol selected in the connection |
+ if nextProto: |
+ self.next_proto = nextProto |
+ |
+ #Server Finish - Are we waiting for a EncryptedExtensions? |
+ if expect_channel_id: |
+ for result in self._getMsg(ContentType.handshake, HandshakeType.encrypted_extensions): |
+ if result in (0,1): |
+ yield result |
+ if result is None: |
+ for result in self._sendError(AlertDescription.unexpected_message, |
+ "Didn't get EncryptedExtensions message"): |
+ yield result |
+ encrypted_extensions = result |
+ self.channel_id = result.channel_id_key |
+ else: |
+ self.channel_id = None |
+ |
+ #Calculate verification data |
+ verifyData = self._calcFinished(masterSecret, False) |
+ |
+ #Get and check Finished message under new state |
+ for result in self._getMsg(ContentType.handshake, |
+ HandshakeType.finished): |
+ if result in (0,1): |
+ yield result |
+ finished = result |
+ if finished.verify_data != verifyData: |
+ for result in self._sendError(AlertDescription.decrypt_error, |
+ "Finished message is incorrect"): |
+ yield result |
+ |
+ def _calcFinished(self, masterSecret, send=True): |
+ if self.version == (3,0): |
+ if (self._client and send) or (not self._client and not send): |
+ senderStr = b"\x43\x4C\x4E\x54" |
+ else: |
+ senderStr = b"\x53\x52\x56\x52" |
+ |
+ verifyData = self._calcSSLHandshakeHash(masterSecret, senderStr) |
+ return verifyData |
+ |
+ elif self.version in ((3,1), (3,2)): |
+ if (self._client and send) or (not self._client and not send): |
+ label = b"client finished" |
+ else: |
+ label = b"server finished" |
+ |
+ handshakeHashes = self._handshake_md5.digest() + \ |
+ self._handshake_sha.digest() |
+ verifyData = PRF(masterSecret, label, handshakeHashes, 12) |
+ return verifyData |
+ else: |
+ raise AssertionError() |
def _handshakeWrapperAsync(self, handshaker, checker): |
@@ -1643,27 +1805,11 @@ class TLSConnection(TLSRecordLayer): |
for result in self._sendMsg(alert): |
yield result |
raise |
- except: |
- self._shutdown(False) |
+ except GeneratorExit: |
+ raise |
+ except TLSAlert as alert: |
+ if not self.fault: |
raise |
- else: |
- try: |
- for result in handshaker: |
- yield result |
- if checker: |
- try: |
- checker(self) |
- except TLSAuthenticationError: |
- alert = Alert().create(AlertDescription.close_notify, |
- AlertLevel.fatal) |
- for result in self._sendMsg(alert): |
- yield result |
- raise |
- except socket.error, e: |
- raise TLSFaultError("socket error!") |
- except TLSAbruptCloseError, e: |
- raise TLSFaultError("abrupt close error!") |
- except TLSAlert, alert: |
if alert.description not in Fault.faultAlerts[self.fault]: |
raise TLSFaultError(str(alert)) |
else: |
@@ -1671,28 +1817,3 @@ class TLSConnection(TLSRecordLayer): |
except: |
self._shutdown(False) |
raise |
- else: |
- raise TLSFaultError("No error!") |
- |
- |
- def _getKeyFromChain(self, certificate, settings): |
- #Get and check cert chain from the Certificate message |
- certChain = certificate.certChain |
- if not certChain or certChain.getNumCerts() == 0: |
- for result in self._sendError(AlertDescription.illegal_parameter, |
- "Other party sent a Certificate message without "\ |
- "certificates"): |
- yield result |
- |
- #Get and check public key from the cert chain |
- publicKey = certChain.getEndEntityPublicKey() |
- if len(publicKey) < settings.minKeySize: |
- for result in self._sendError(AlertDescription.handshake_failure, |
- "Other party's public key too small: %d" % len(publicKey)): |
- yield result |
- if len(publicKey) > settings.maxKeySize: |
- for result in self._sendError(AlertDescription.handshake_failure, |
- "Other party's public key too large: %d" % len(publicKey)): |
- yield result |
- |
- yield publicKey, certChain |