| Index: third_party/tlslite/tlslite/TLSRecordLayer.py
|
| diff --git a/third_party/tlslite/tlslite/TLSRecordLayer.py b/third_party/tlslite/tlslite/TLSRecordLayer.py
|
| deleted file mode 100644
|
| index 933b95a9de6346998a0644e6f19ed2362b095d2e..0000000000000000000000000000000000000000
|
| --- a/third_party/tlslite/tlslite/TLSRecordLayer.py
|
| +++ /dev/null
|
| @@ -1,1150 +0,0 @@
|
| -"""Helper class for TLSConnection."""
|
| -from __future__ import generators
|
| -
|
| -from utils.compat import *
|
| -from utils.cryptomath import *
|
| -from utils.cipherfactory import createAES, createRC4, createTripleDES
|
| -from utils.codec import *
|
| -from errors import *
|
| -from messages import *
|
| -from mathtls import *
|
| -from constants import *
|
| -from utils.cryptomath import getRandomBytes
|
| -from utils import hmac
|
| -from FileObject import FileObject
|
| -
|
| -# The sha module is deprecated in Python 2.6
|
| -try:
|
| - import sha
|
| -except ImportError:
|
| - from hashlib import sha1 as sha
|
| -
|
| -# The md5 module is deprecated in Python 2.6
|
| -try:
|
| - import md5
|
| -except ImportError:
|
| - from hashlib import md5
|
| -
|
| -import socket
|
| -import errno
|
| -import traceback
|
| -
|
| -class _ConnectionState:
|
| - def __init__(self):
|
| - self.macContext = None
|
| - self.encContext = None
|
| - self.seqnum = 0
|
| -
|
| - def getSeqNumStr(self):
|
| - w = Writer(8)
|
| - w.add(self.seqnum, 8)
|
| - seqnumStr = bytesToString(w.bytes)
|
| - self.seqnum += 1
|
| - return seqnumStr
|
| -
|
| -
|
| -class TLSRecordLayer:
|
| - """
|
| - This class handles data transmission for a TLS connection.
|
| -
|
| - Its only subclass is L{tlslite.TLSConnection.TLSConnection}. We've
|
| - separated the code in this class from TLSConnection to make things
|
| - more readable.
|
| -
|
| -
|
| - @type sock: socket.socket
|
| - @ivar sock: The underlying socket object.
|
| -
|
| - @type session: L{tlslite.Session.Session}
|
| - @ivar session: The session corresponding to this connection.
|
| -
|
| - Due to TLS session resumption, multiple connections can correspond
|
| - to the same underlying session.
|
| -
|
| - @type version: tuple
|
| - @ivar version: The TLS version being used for this connection.
|
| -
|
| - (3,0) means SSL 3.0, and (3,1) means TLS 1.0.
|
| -
|
| - @type closed: bool
|
| - @ivar closed: If this connection is closed.
|
| -
|
| - @type resumed: bool
|
| - @ivar resumed: If this connection is based on a resumed session.
|
| -
|
| - @type allegedSharedKeyUsername: str or None
|
| - @ivar allegedSharedKeyUsername: This is set to the shared-key
|
| - username asserted by the client, whether the handshake succeeded or
|
| - not. If the handshake fails, this can be inspected to
|
| - determine if a guessing attack is in progress against a particular
|
| - user account.
|
| -
|
| - @type allegedSrpUsername: str or None
|
| - @ivar allegedSrpUsername: This is set to the SRP username
|
| - asserted by the client, whether the handshake succeeded or not.
|
| - If the handshake fails, this can be inspected to determine
|
| - if a guessing attack is in progress against a particular user
|
| - account.
|
| -
|
| - @type closeSocket: bool
|
| - @ivar closeSocket: If the socket should be closed when the
|
| - connection is closed (writable).
|
| -
|
| - If you set this to True, TLS Lite will assume the responsibility of
|
| - closing the socket when the TLS Connection is shutdown (either
|
| - through an error or through the user calling close()). The default
|
| - is False.
|
| -
|
| - @type ignoreAbruptClose: bool
|
| - @ivar ignoreAbruptClose: If an abrupt close of the socket should
|
| - raise an error (writable).
|
| -
|
| - If you set this to True, TLS Lite will not raise a
|
| - L{tlslite.errors.TLSAbruptCloseError} exception if the underlying
|
| - socket is unexpectedly closed. Such an unexpected closure could be
|
| - caused by an attacker. However, it also occurs with some incorrect
|
| - TLS implementations.
|
| -
|
| - You should set this to True only if you're not worried about an
|
| - attacker truncating the connection, and only if necessary to avoid
|
| - spurious errors. The default is False.
|
| -
|
| - @sort: __init__, read, readAsync, write, writeAsync, close, closeAsync,
|
| - getCipherImplementation, getCipherName
|
| - """
|
| -
|
| - def __init__(self, sock):
|
| - self.sock = sock
|
| -
|
| - #My session object (Session instance; read-only)
|
| - self.session = None
|
| -
|
| - #Am I a client or server?
|
| - self._client = None
|
| -
|
| - #Buffers for processing messages
|
| - self._handshakeBuffer = []
|
| - self._readBuffer = ""
|
| -
|
| - #Handshake digests
|
| - self._handshake_md5 = md5.md5()
|
| - self._handshake_sha = sha.sha()
|
| -
|
| - #TLS Protocol Version
|
| - self.version = (0,0) #read-only
|
| - self._versionCheck = False #Once we choose a version, this is True
|
| -
|
| - #Current and Pending connection states
|
| - self._writeState = _ConnectionState()
|
| - self._readState = _ConnectionState()
|
| - self._pendingWriteState = _ConnectionState()
|
| - self._pendingReadState = _ConnectionState()
|
| -
|
| - #Is the connection open?
|
| - self.closed = True #read-only
|
| - self._refCount = 0 #Used to trigger closure
|
| -
|
| - #Is this a resumed (or shared-key) session?
|
| - self.resumed = False #read-only
|
| -
|
| - #What username did the client claim in his handshake?
|
| - self.allegedSharedKeyUsername = None
|
| - self.allegedSrpUsername = None
|
| -
|
| - #On a call to close(), do we close the socket? (writeable)
|
| - self.closeSocket = False
|
| -
|
| - #If the socket is abruptly closed, do we ignore it
|
| - #and pretend the connection was shut down properly? (writeable)
|
| - self.ignoreAbruptClose = False
|
| -
|
| - #Fault we will induce, for testing purposes
|
| - self.fault = None
|
| -
|
| - #*********************************************************
|
| - # Public Functions START
|
| - #*********************************************************
|
| -
|
| - def read(self, max=None, min=1):
|
| - """Read some data from the TLS connection.
|
| -
|
| - This function will block until at least 'min' bytes are
|
| - available (or the connection is closed).
|
| -
|
| - If an exception is raised, the connection will have been
|
| - automatically closed.
|
| -
|
| - @type max: int
|
| - @param max: The maximum number of bytes to return.
|
| -
|
| - @type min: int
|
| - @param min: The minimum number of bytes to return
|
| -
|
| - @rtype: str
|
| - @return: A string of no more than 'max' bytes, and no fewer
|
| - than 'min' (unless the connection has been closed, in which
|
| - case fewer than 'min' bytes may 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.
|
| - """
|
| - for result in self.readAsync(max, min):
|
| - pass
|
| - return result
|
| -
|
| - def readAsync(self, max=None, min=1):
|
| - """Start a read operation on the TLS connection.
|
| -
|
| - This function returns a generator which behaves similarly to
|
| - read(). 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 a string if the read operation has
|
| - completed.
|
| -
|
| - @rtype: iterable
|
| - @return: A generator; see above for details.
|
| - """
|
| - try:
|
| - while len(self._readBuffer)<min and not self.closed:
|
| - try:
|
| - for result in self._getMsg(ContentType.application_data):
|
| - if result in (0,1):
|
| - yield result
|
| - applicationData = result
|
| - self._readBuffer += bytesToString(applicationData.write())
|
| - except TLSRemoteAlert, alert:
|
| - if alert.description != AlertDescription.close_notify:
|
| - raise
|
| - except TLSAbruptCloseError:
|
| - if not self.ignoreAbruptClose:
|
| - raise
|
| - else:
|
| - self._shutdown(True)
|
| -
|
| - if max == None:
|
| - max = len(self._readBuffer)
|
| -
|
| - returnStr = self._readBuffer[:max]
|
| - self._readBuffer = self._readBuffer[max:]
|
| - yield returnStr
|
| - except:
|
| - self._shutdown(False)
|
| - raise
|
| -
|
| - def write(self, s):
|
| - """Write some data to the TLS connection.
|
| -
|
| - This function will block until all the data has been sent.
|
| -
|
| - If an exception is raised, the connection will have been
|
| - automatically closed.
|
| -
|
| - @type s: str
|
| - @param s: The data to transmit to the other party.
|
| -
|
| - @raise socket.error: If a socket error occurs.
|
| - """
|
| - for result in self.writeAsync(s):
|
| - pass
|
| -
|
| - def writeAsync(self, s):
|
| - """Start a write operation on the TLS connection.
|
| -
|
| - This function returns a generator which behaves similarly to
|
| - write(). Successive invocations of the generator will return
|
| - 1 if it is waiting to write to the socket, or will raise
|
| - StopIteration if the write operation has completed.
|
| -
|
| - @rtype: iterable
|
| - @return: A generator; see above for details.
|
| - """
|
| - try:
|
| - if self.closed:
|
| - raise ValueError()
|
| -
|
| - index = 0
|
| - blockSize = 16384
|
| - skipEmptyFrag = False
|
| - while 1:
|
| - startIndex = index * blockSize
|
| - endIndex = startIndex + blockSize
|
| - if startIndex >= len(s):
|
| - break
|
| - if endIndex > len(s):
|
| - endIndex = len(s)
|
| - block = stringToBytes(s[startIndex : endIndex])
|
| - applicationData = ApplicationData().create(block)
|
| - for result in self._sendMsg(applicationData, skipEmptyFrag):
|
| - yield result
|
| - skipEmptyFrag = True #only send an empy fragment on 1st message
|
| - index += 1
|
| - except:
|
| - self._shutdown(False)
|
| - raise
|
| -
|
| - def close(self):
|
| - """Close the TLS connection.
|
| -
|
| - This function will block until it has exchanged close_notify
|
| - alerts with the other party. After doing so, it will shut down the
|
| - TLS connection. Further attempts to read through this connection
|
| - will return "". Further attempts to write through this connection
|
| - will raise ValueError.
|
| -
|
| - If makefile() has been called on this connection, the connection
|
| - will be not be closed until the connection object and all file
|
| - objects have been closed.
|
| -
|
| - Even if an exception is raised, the connection will have been
|
| - closed.
|
| -
|
| - @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.
|
| - """
|
| - if not self.closed:
|
| - for result in self._decrefAsync():
|
| - pass
|
| -
|
| - def closeAsync(self):
|
| - """Start a close operation on the TLS connection.
|
| -
|
| - This function returns a generator which behaves similarly to
|
| - close(). 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
|
| - close operation has completed.
|
| -
|
| - @rtype: iterable
|
| - @return: A generator; see above for details.
|
| - """
|
| - if not self.closed:
|
| - for result in self._decrefAsync():
|
| - yield result
|
| -
|
| - def _decrefAsync(self):
|
| - self._refCount -= 1
|
| - if self._refCount == 0 and not self.closed:
|
| - try:
|
| - for result in self._sendMsg(Alert().create(\
|
| - AlertDescription.close_notify, AlertLevel.warning)):
|
| - yield result
|
| - alert = None
|
| - # Forcing a shutdown as WinHTTP does not seem to be
|
| - # responsive to the close notify.
|
| - prevCloseSocket = self.closeSocket
|
| - self.closeSocket = True
|
| - self._shutdown(True)
|
| - self.closeSocket = prevCloseSocket
|
| - while not alert:
|
| - for result in self._getMsg((ContentType.alert, \
|
| - ContentType.application_data)):
|
| - if result in (0,1):
|
| - yield result
|
| - if result.contentType == ContentType.alert:
|
| - alert = result
|
| - if alert.description == AlertDescription.close_notify:
|
| - self._shutdown(True)
|
| - else:
|
| - raise TLSRemoteAlert(alert)
|
| - except (socket.error, TLSAbruptCloseError):
|
| - #If the other side closes the socket, that's okay
|
| - self._shutdown(True)
|
| - except:
|
| - self._shutdown(False)
|
| - raise
|
| -
|
| - def getCipherName(self):
|
| - """Get the name of the cipher used with this connection.
|
| -
|
| - @rtype: str
|
| - @return: The name of the cipher used with this connection.
|
| - Either 'aes128', 'aes256', 'rc4', or '3des'.
|
| - """
|
| - if not self._writeState.encContext:
|
| - return None
|
| - return self._writeState.encContext.name
|
| -
|
| - def getCipherImplementation(self):
|
| - """Get the name of the cipher implementation used with
|
| - this connection.
|
| -
|
| - @rtype: str
|
| - @return: The name of the cipher implementation used with
|
| - this connection. Either 'python', 'cryptlib', 'openssl',
|
| - or 'pycrypto'.
|
| - """
|
| - if not self._writeState.encContext:
|
| - return None
|
| - return self._writeState.encContext.implementation
|
| -
|
| -
|
| -
|
| - #Emulate a socket, somewhat -
|
| - def send(self, s):
|
| - """Send data to the TLS connection (socket emulation).
|
| -
|
| - @raise socket.error: If a socket error occurs.
|
| - """
|
| - self.write(s)
|
| - return len(s)
|
| -
|
| - def sendall(self, s):
|
| - """Send data to the TLS connection (socket emulation).
|
| -
|
| - @raise socket.error: If a socket error occurs.
|
| - """
|
| - self.write(s)
|
| -
|
| - def recv(self, bufsize):
|
| - """Get some data from the TLS connection (socket emulation).
|
| -
|
| - @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.
|
| - """
|
| - return self.read(bufsize)
|
| -
|
| - def makefile(self, mode='r', bufsize=-1):
|
| - """Create a file object for the TLS connection (socket emulation).
|
| -
|
| - @rtype: L{tlslite.FileObject.FileObject}
|
| - """
|
| - self._refCount += 1
|
| - return FileObject(self, mode, bufsize)
|
| -
|
| - def getsockname(self):
|
| - """Return the socket's own address (socket emulation)."""
|
| - return self.sock.getsockname()
|
| -
|
| - def getpeername(self):
|
| - """Return the remote address to which the socket is connected
|
| - (socket emulation)."""
|
| - return self.sock.getpeername()
|
| -
|
| - def settimeout(self, value):
|
| - """Set a timeout on blocking socket operations (socket emulation)."""
|
| - return self.sock.settimeout(value)
|
| -
|
| - def gettimeout(self):
|
| - """Return the timeout associated with socket operations (socket
|
| - emulation)."""
|
| - return self.sock.gettimeout()
|
| -
|
| - def setsockopt(self, level, optname, value):
|
| - """Set the value of the given socket option (socket emulation)."""
|
| - return self.sock.setsockopt(level, optname, value)
|
| -
|
| -
|
| - #*********************************************************
|
| - # Public Functions END
|
| - #*********************************************************
|
| -
|
| - def _shutdown(self, resumable):
|
| - self._writeState = _ConnectionState()
|
| - self._readState = _ConnectionState()
|
| - #Don't do this: self._readBuffer = ""
|
| - self.version = (0,0)
|
| - self._versionCheck = False
|
| - self.closed = True
|
| - if self.closeSocket:
|
| - self.sock.close()
|
| -
|
| - #Even if resumable is False, we'll never toggle this on
|
| - if not resumable and self.session:
|
| - self.session.resumable = False
|
| -
|
| -
|
| - def _sendError(self, alertDescription, errorStr=None):
|
| - alert = Alert().create(alertDescription, AlertLevel.fatal)
|
| - for result in self._sendMsg(alert):
|
| - yield result
|
| - self._shutdown(False)
|
| - raise TLSLocalAlert(alert, errorStr)
|
| -
|
| - def _sendMsgs(self, msgs):
|
| - skipEmptyFrag = False
|
| - for msg in msgs:
|
| - for result in self._sendMsg(msg, skipEmptyFrag):
|
| - yield result
|
| - skipEmptyFrag = True
|
| -
|
| - def _sendMsg(self, msg, skipEmptyFrag=False):
|
| - bytes = msg.write()
|
| - contentType = msg.contentType
|
| -
|
| - #Whenever we're connected and asked to send a message,
|
| - #we first send an empty Application Data message. This prevents
|
| - #an attacker from launching a chosen-plaintext attack based on
|
| - #knowing the next IV.
|
| - if not self.closed and not skipEmptyFrag and self.version == (3,1):
|
| - if self._writeState.encContext:
|
| - if self._writeState.encContext.isBlockCipher:
|
| - for result in self._sendMsg(ApplicationData(),
|
| - skipEmptyFrag=True):
|
| - yield result
|
| -
|
| - #Update handshake hashes
|
| - if contentType == ContentType.handshake:
|
| - bytesStr = bytesToString(bytes)
|
| - self._handshake_md5.update(bytesStr)
|
| - self._handshake_sha.update(bytesStr)
|
| -
|
| - #Calculate MAC
|
| - if self._writeState.macContext:
|
| - seqnumStr = self._writeState.getSeqNumStr()
|
| - bytesStr = bytesToString(bytes)
|
| - mac = self._writeState.macContext.copy()
|
| - mac.update(seqnumStr)
|
| - mac.update(chr(contentType))
|
| - if self.version == (3,0):
|
| - mac.update( chr( int(len(bytes)/256) ) )
|
| - mac.update( chr( int(len(bytes)%256) ) )
|
| - elif self.version in ((3,1), (3,2)):
|
| - mac.update(chr(self.version[0]))
|
| - mac.update(chr(self.version[1]))
|
| - mac.update( chr( int(len(bytes)/256) ) )
|
| - mac.update( chr( int(len(bytes)%256) ) )
|
| - else:
|
| - raise AssertionError()
|
| - mac.update(bytesStr)
|
| - macString = mac.digest()
|
| - macBytes = stringToBytes(macString)
|
| - if self.fault == Fault.badMAC:
|
| - macBytes[0] = (macBytes[0]+1) % 256
|
| -
|
| - #Encrypt for Block or Stream Cipher
|
| - if self._writeState.encContext:
|
| - #Add padding and encrypt (for Block Cipher):
|
| - if self._writeState.encContext.isBlockCipher:
|
| -
|
| - #Add TLS 1.1 fixed block
|
| - if self.version == (3,2):
|
| - bytes = self.fixedIVBlock + bytes
|
| -
|
| - #Add padding: bytes = bytes + (macBytes + paddingBytes)
|
| - currentLength = len(bytes) + len(macBytes) + 1
|
| - blockLength = self._writeState.encContext.block_size
|
| - paddingLength = blockLength-(currentLength % blockLength)
|
| -
|
| - paddingBytes = createByteArraySequence([paddingLength] * \
|
| - (paddingLength+1))
|
| - if self.fault == Fault.badPadding:
|
| - paddingBytes[0] = (paddingBytes[0]+1) % 256
|
| - endBytes = concatArrays(macBytes, paddingBytes)
|
| - bytes = concatArrays(bytes, endBytes)
|
| - #Encrypt
|
| - plaintext = stringToBytes(bytes)
|
| - ciphertext = self._writeState.encContext.encrypt(plaintext)
|
| - bytes = stringToBytes(ciphertext)
|
| -
|
| - #Encrypt (for Stream Cipher)
|
| - else:
|
| - bytes = concatArrays(bytes, macBytes)
|
| - plaintext = bytesToString(bytes)
|
| - ciphertext = self._writeState.encContext.encrypt(plaintext)
|
| - bytes = stringToBytes(ciphertext)
|
| -
|
| - #Add record header and send
|
| - r = RecordHeader3().create(self.version, contentType, len(bytes))
|
| - s = bytesToString(concatArrays(r.write(), bytes))
|
| - while 1:
|
| - try:
|
| - bytesSent = self.sock.send(s) #Might raise socket.error
|
| - except socket.error, why:
|
| - if why[0] == errno.EWOULDBLOCK:
|
| - yield 1
|
| - continue
|
| - else:
|
| - raise
|
| - if bytesSent == len(s):
|
| - return
|
| - s = s[bytesSent:]
|
| - yield 1
|
| -
|
| -
|
| - def _getMsg(self, expectedType, secondaryType=None, constructorType=None):
|
| - try:
|
| - if not isinstance(expectedType, tuple):
|
| - expectedType = (expectedType,)
|
| -
|
| - #Spin in a loop, until we've got a non-empty record of a type we
|
| - #expect. The loop will be repeated if:
|
| - # - we receive a renegotiation attempt; we send no_renegotiation,
|
| - # then try again
|
| - # - we receive an empty application-data fragment; we try again
|
| - while 1:
|
| - for result in self._getNextRecord():
|
| - if result in (0,1):
|
| - yield result
|
| - recordHeader, p = result
|
| -
|
| - #If this is an empty application-data fragment, try again
|
| - if recordHeader.type == ContentType.application_data:
|
| - if p.index == len(p.bytes):
|
| - continue
|
| -
|
| - #If we received an unexpected record type...
|
| - if recordHeader.type not in expectedType:
|
| -
|
| - #If we received an alert...
|
| - if recordHeader.type == ContentType.alert:
|
| - alert = Alert().parse(p)
|
| -
|
| - #We either received a fatal error, a warning, or a
|
| - #close_notify. In any case, we're going to close the
|
| - #connection. In the latter two cases we respond with
|
| - #a close_notify, but ignore any socket errors, since
|
| - #the other side might have already closed the socket.
|
| - if alert.level == AlertLevel.warning or \
|
| - alert.description == AlertDescription.close_notify:
|
| -
|
| - #If the sendMsg() call fails because the socket has
|
| - #already been closed, we will be forgiving and not
|
| - #report the error nor invalidate the "resumability"
|
| - #of the session.
|
| - try:
|
| - alertMsg = Alert()
|
| - alertMsg.create(AlertDescription.close_notify,
|
| - AlertLevel.warning)
|
| - for result in self._sendMsg(alertMsg):
|
| - yield result
|
| - except socket.error:
|
| - pass
|
| -
|
| - if alert.description == \
|
| - AlertDescription.close_notify:
|
| - self._shutdown(True)
|
| - elif alert.level == AlertLevel.warning:
|
| - self._shutdown(False)
|
| -
|
| - else: #Fatal alert:
|
| - self._shutdown(False)
|
| -
|
| - #Raise the alert as an exception
|
| - raise TLSRemoteAlert(alert)
|
| -
|
| - #If we received a renegotiation attempt...
|
| - if recordHeader.type == ContentType.handshake:
|
| - subType = p.get(1)
|
| - reneg = False
|
| - if self._client:
|
| - if subType == HandshakeType.hello_request:
|
| - reneg = True
|
| - else:
|
| - if subType == HandshakeType.client_hello:
|
| - reneg = True
|
| - #Send no_renegotiation, then try again
|
| - if reneg:
|
| - alertMsg = Alert()
|
| - alertMsg.create(AlertDescription.no_renegotiation,
|
| - AlertLevel.warning)
|
| - for result in self._sendMsg(alertMsg):
|
| - yield result
|
| - continue
|
| -
|
| - #Otherwise: this is an unexpected record, but neither an
|
| - #alert nor renegotiation
|
| - for result in self._sendError(\
|
| - AlertDescription.unexpected_message,
|
| - "received type=%d" % recordHeader.type):
|
| - yield result
|
| -
|
| - break
|
| -
|
| - #Parse based on content_type
|
| - if recordHeader.type == ContentType.change_cipher_spec:
|
| - yield ChangeCipherSpec().parse(p)
|
| - elif recordHeader.type == ContentType.alert:
|
| - yield Alert().parse(p)
|
| - elif recordHeader.type == ContentType.application_data:
|
| - yield ApplicationData().parse(p)
|
| - elif recordHeader.type == ContentType.handshake:
|
| - #Convert secondaryType to tuple, if it isn't already
|
| - if not isinstance(secondaryType, tuple):
|
| - secondaryType = (secondaryType,)
|
| -
|
| - #If it's a handshake message, check handshake header
|
| - if recordHeader.ssl2:
|
| - subType = p.get(1)
|
| - if subType != HandshakeType.client_hello:
|
| - for result in self._sendError(\
|
| - AlertDescription.unexpected_message,
|
| - "Can only handle SSLv2 ClientHello messages"):
|
| - yield result
|
| - if HandshakeType.client_hello not in secondaryType:
|
| - for result in self._sendError(\
|
| - AlertDescription.unexpected_message):
|
| - yield result
|
| - subType = HandshakeType.client_hello
|
| - else:
|
| - subType = p.get(1)
|
| - if subType not in secondaryType:
|
| - for result in self._sendError(\
|
| - AlertDescription.unexpected_message,
|
| - "Expecting %s, got %s" % (str(secondaryType), subType)):
|
| - yield result
|
| -
|
| - #Update handshake hashes
|
| - sToHash = bytesToString(p.bytes)
|
| - self._handshake_md5.update(sToHash)
|
| - self._handshake_sha.update(sToHash)
|
| -
|
| - #Parse based on handshake type
|
| - if subType == HandshakeType.client_hello:
|
| - yield ClientHello(recordHeader.ssl2).parse(p)
|
| - elif subType == HandshakeType.server_hello:
|
| - yield ServerHello().parse(p)
|
| - elif subType == HandshakeType.certificate:
|
| - yield Certificate(constructorType).parse(p)
|
| - elif subType == HandshakeType.certificate_request:
|
| - yield CertificateRequest().parse(p)
|
| - elif subType == HandshakeType.certificate_verify:
|
| - yield CertificateVerify().parse(p)
|
| - elif subType == HandshakeType.server_key_exchange:
|
| - yield ServerKeyExchange(constructorType).parse(p)
|
| - elif subType == HandshakeType.server_hello_done:
|
| - yield ServerHelloDone().parse(p)
|
| - elif subType == HandshakeType.client_key_exchange:
|
| - yield ClientKeyExchange(constructorType, \
|
| - self.version).parse(p)
|
| - elif subType == HandshakeType.finished:
|
| - yield Finished(self.version).parse(p)
|
| - elif subType == HandshakeType.encrypted_extensions:
|
| - yield EncryptedExtensions().parse(p)
|
| - else:
|
| - raise AssertionError()
|
| -
|
| - #If an exception was raised by a Parser or Message instance:
|
| - except SyntaxError, e:
|
| - for result in self._sendError(AlertDescription.decode_error,
|
| - formatExceptionTrace(e)):
|
| - yield result
|
| -
|
| -
|
| - #Returns next record or next handshake message
|
| - def _getNextRecord(self):
|
| -
|
| - #If there's a handshake message waiting, return it
|
| - if self._handshakeBuffer:
|
| - recordHeader, bytes = self._handshakeBuffer[0]
|
| - self._handshakeBuffer = self._handshakeBuffer[1:]
|
| - yield (recordHeader, Parser(bytes))
|
| - return
|
| -
|
| - #Otherwise...
|
| - #Read the next record header
|
| - bytes = createByteArraySequence([])
|
| - recordHeaderLength = 1
|
| - ssl2 = False
|
| - while 1:
|
| - try:
|
| - s = self.sock.recv(recordHeaderLength-len(bytes))
|
| - except socket.error, why:
|
| - if why[0] == errno.EWOULDBLOCK:
|
| - yield 0
|
| - continue
|
| - else:
|
| - raise
|
| -
|
| - #If the connection was abruptly closed, raise an error
|
| - if len(s)==0:
|
| - raise TLSAbruptCloseError()
|
| -
|
| - bytes += stringToBytes(s)
|
| - if len(bytes)==1:
|
| - if bytes[0] in ContentType.all:
|
| - ssl2 = False
|
| - recordHeaderLength = 5
|
| - elif bytes[0] == 128:
|
| - ssl2 = True
|
| - recordHeaderLength = 2
|
| - else:
|
| - raise SyntaxError()
|
| - if len(bytes) == recordHeaderLength:
|
| - break
|
| -
|
| - #Parse the record header
|
| - if ssl2:
|
| - r = RecordHeader2().parse(Parser(bytes))
|
| - else:
|
| - r = RecordHeader3().parse(Parser(bytes))
|
| -
|
| - #Check the record header fields
|
| - if r.length > 18432:
|
| - for result in self._sendError(AlertDescription.record_overflow):
|
| - yield result
|
| -
|
| - #Read the record contents
|
| - bytes = createByteArraySequence([])
|
| - while 1:
|
| - try:
|
| - s = self.sock.recv(r.length - len(bytes))
|
| - except socket.error, why:
|
| - if why[0] == errno.EWOULDBLOCK:
|
| - yield 0
|
| - continue
|
| - else:
|
| - raise
|
| -
|
| - #If the connection is closed, raise a socket error
|
| - if len(s)==0:
|
| - raise TLSAbruptCloseError()
|
| -
|
| - bytes += stringToBytes(s)
|
| - if len(bytes) == r.length:
|
| - break
|
| -
|
| - #Check the record header fields (2)
|
| - #We do this after reading the contents from the socket, so that
|
| - #if there's an error, we at least don't leave extra bytes in the
|
| - #socket..
|
| - #
|
| - # THIS CHECK HAS NO SECURITY RELEVANCE (?), BUT COULD HURT INTEROP.
|
| - # SO WE LEAVE IT OUT FOR NOW.
|
| - #
|
| - #if self._versionCheck and r.version != self.version:
|
| - # for result in self._sendError(AlertDescription.protocol_version,
|
| - # "Version in header field: %s, should be %s" % (str(r.version),
|
| - # str(self.version))):
|
| - # yield result
|
| -
|
| - #Decrypt the record
|
| - for result in self._decryptRecord(r.type, bytes):
|
| - if result in (0,1):
|
| - yield result
|
| - else:
|
| - break
|
| - bytes = result
|
| - p = Parser(bytes)
|
| -
|
| - #If it doesn't contain handshake messages, we can just return it
|
| - if r.type != ContentType.handshake:
|
| - yield (r, p)
|
| - #If it's an SSLv2 ClientHello, we can return it as well
|
| - elif r.ssl2:
|
| - yield (r, p)
|
| - else:
|
| - #Otherwise, we loop through and add the handshake messages to the
|
| - #handshake buffer
|
| - while 1:
|
| - if p.index == len(bytes): #If we're at the end
|
| - if not self._handshakeBuffer:
|
| - for result in self._sendError(\
|
| - AlertDescription.decode_error, \
|
| - "Received empty handshake record"):
|
| - yield result
|
| - break
|
| - #There needs to be at least 4 bytes to get a header
|
| - if p.index+4 > len(bytes):
|
| - for result in self._sendError(\
|
| - AlertDescription.decode_error,
|
| - "A record has a partial handshake message (1)"):
|
| - yield result
|
| - p.get(1) # skip handshake type
|
| - msgLength = p.get(3)
|
| - if p.index+msgLength > len(bytes):
|
| - for result in self._sendError(\
|
| - AlertDescription.decode_error,
|
| - "A record has a partial handshake message (2)"):
|
| - yield result
|
| -
|
| - handshakePair = (r, bytes[p.index-4 : p.index+msgLength])
|
| - self._handshakeBuffer.append(handshakePair)
|
| - p.index += msgLength
|
| -
|
| - #We've moved at least one handshake message into the
|
| - #handshakeBuffer, return the first one
|
| - recordHeader, bytes = self._handshakeBuffer[0]
|
| - self._handshakeBuffer = self._handshakeBuffer[1:]
|
| - yield (recordHeader, Parser(bytes))
|
| -
|
| -
|
| - def _decryptRecord(self, recordType, bytes):
|
| - if self._readState.encContext:
|
| -
|
| - #Decrypt if it's a block cipher
|
| - if self._readState.encContext.isBlockCipher:
|
| - blockLength = self._readState.encContext.block_size
|
| - if len(bytes) % blockLength != 0:
|
| - for result in self._sendError(\
|
| - AlertDescription.decryption_failed,
|
| - "Encrypted data not a multiple of blocksize"):
|
| - yield result
|
| - ciphertext = bytesToString(bytes)
|
| - plaintext = self._readState.encContext.decrypt(ciphertext)
|
| - if self.version == (3,2): #For TLS 1.1, remove explicit IV
|
| - plaintext = plaintext[self._readState.encContext.block_size : ]
|
| - bytes = stringToBytes(plaintext)
|
| -
|
| - #Check padding
|
| - paddingGood = True
|
| - paddingLength = bytes[-1]
|
| - if (paddingLength+1) > len(bytes):
|
| - paddingGood=False
|
| - totalPaddingLength = 0
|
| - else:
|
| - if self.version == (3,0):
|
| - totalPaddingLength = paddingLength+1
|
| - elif self.version in ((3,1), (3,2)):
|
| - totalPaddingLength = paddingLength+1
|
| - paddingBytes = bytes[-totalPaddingLength:-1]
|
| - for byte in paddingBytes:
|
| - if byte != paddingLength:
|
| - paddingGood = False
|
| - totalPaddingLength = 0
|
| - else:
|
| - raise AssertionError()
|
| -
|
| - #Decrypt if it's a stream cipher
|
| - else:
|
| - paddingGood = True
|
| - ciphertext = bytesToString(bytes)
|
| - plaintext = self._readState.encContext.decrypt(ciphertext)
|
| - bytes = stringToBytes(plaintext)
|
| - totalPaddingLength = 0
|
| -
|
| - #Check MAC
|
| - macGood = True
|
| - macLength = self._readState.macContext.digest_size
|
| - endLength = macLength + totalPaddingLength
|
| - if endLength > len(bytes):
|
| - macGood = False
|
| - else:
|
| - #Read MAC
|
| - startIndex = len(bytes) - endLength
|
| - endIndex = startIndex + macLength
|
| - checkBytes = bytes[startIndex : endIndex]
|
| -
|
| - #Calculate MAC
|
| - seqnumStr = self._readState.getSeqNumStr()
|
| - bytes = bytes[:-endLength]
|
| - bytesStr = bytesToString(bytes)
|
| - mac = self._readState.macContext.copy()
|
| - mac.update(seqnumStr)
|
| - mac.update(chr(recordType))
|
| - if self.version == (3,0):
|
| - mac.update( chr( int(len(bytes)/256) ) )
|
| - mac.update( chr( int(len(bytes)%256) ) )
|
| - elif self.version in ((3,1), (3,2)):
|
| - mac.update(chr(self.version[0]))
|
| - mac.update(chr(self.version[1]))
|
| - mac.update( chr( int(len(bytes)/256) ) )
|
| - mac.update( chr( int(len(bytes)%256) ) )
|
| - else:
|
| - raise AssertionError()
|
| - mac.update(bytesStr)
|
| - macString = mac.digest()
|
| - macBytes = stringToBytes(macString)
|
| -
|
| - #Compare MACs
|
| - if macBytes != checkBytes:
|
| - macGood = False
|
| -
|
| - if not (paddingGood and macGood):
|
| - for result in self._sendError(AlertDescription.bad_record_mac,
|
| - "MAC failure (or padding failure)"):
|
| - yield result
|
| -
|
| - yield bytes
|
| -
|
| - def _handshakeStart(self, client):
|
| - self._client = client
|
| - self._handshake_md5 = md5.md5()
|
| - self._handshake_sha = sha.sha()
|
| - self._handshakeBuffer = []
|
| - self.allegedSharedKeyUsername = None
|
| - self.allegedSrpUsername = None
|
| - self._refCount = 1
|
| -
|
| - def _handshakeDone(self, resumed):
|
| - self.resumed = resumed
|
| - self.closed = False
|
| -
|
| - def _calcPendingStates(self, clientRandom, serverRandom, implementations):
|
| - if self.session.cipherSuite in CipherSuite.aes128Suites:
|
| - macLength = 20
|
| - keyLength = 16
|
| - ivLength = 16
|
| - createCipherFunc = createAES
|
| - elif self.session.cipherSuite in CipherSuite.aes256Suites:
|
| - macLength = 20
|
| - keyLength = 32
|
| - ivLength = 16
|
| - createCipherFunc = createAES
|
| - elif self.session.cipherSuite in CipherSuite.rc4Suites:
|
| - macLength = 20
|
| - keyLength = 16
|
| - ivLength = 0
|
| - createCipherFunc = createRC4
|
| - elif self.session.cipherSuite in CipherSuite.tripleDESSuites:
|
| - macLength = 20
|
| - keyLength = 24
|
| - ivLength = 8
|
| - createCipherFunc = createTripleDES
|
| - else:
|
| - raise AssertionError()
|
| -
|
| - if self.version == (3,0):
|
| - createMACFunc = MAC_SSL
|
| - elif self.version in ((3,1), (3,2)):
|
| - createMACFunc = hmac.HMAC
|
| -
|
| - outputLength = (macLength*2) + (keyLength*2) + (ivLength*2)
|
| -
|
| - #Calculate Keying Material from Master Secret
|
| - if self.version == (3,0):
|
| - keyBlock = PRF_SSL(self.session.masterSecret,
|
| - concatArrays(serverRandom, clientRandom),
|
| - outputLength)
|
| - elif self.version in ((3,1), (3,2)):
|
| - keyBlock = PRF(self.session.masterSecret,
|
| - "key expansion",
|
| - concatArrays(serverRandom,clientRandom),
|
| - outputLength)
|
| - else:
|
| - raise AssertionError()
|
| -
|
| - #Slice up Keying Material
|
| - clientPendingState = _ConnectionState()
|
| - serverPendingState = _ConnectionState()
|
| - p = Parser(keyBlock)
|
| - clientMACBlock = bytesToString(p.getFixBytes(macLength))
|
| - serverMACBlock = bytesToString(p.getFixBytes(macLength))
|
| - clientKeyBlock = bytesToString(p.getFixBytes(keyLength))
|
| - serverKeyBlock = bytesToString(p.getFixBytes(keyLength))
|
| - clientIVBlock = bytesToString(p.getFixBytes(ivLength))
|
| - serverIVBlock = bytesToString(p.getFixBytes(ivLength))
|
| - clientPendingState.macContext = createMACFunc(clientMACBlock,
|
| - digestmod=sha)
|
| - serverPendingState.macContext = createMACFunc(serverMACBlock,
|
| - digestmod=sha)
|
| - clientPendingState.encContext = createCipherFunc(clientKeyBlock,
|
| - clientIVBlock,
|
| - implementations)
|
| - serverPendingState.encContext = createCipherFunc(serverKeyBlock,
|
| - serverIVBlock,
|
| - implementations)
|
| -
|
| - #Assign new connection states to pending states
|
| - if self._client:
|
| - self._pendingWriteState = clientPendingState
|
| - self._pendingReadState = serverPendingState
|
| - else:
|
| - self._pendingWriteState = serverPendingState
|
| - self._pendingReadState = clientPendingState
|
| -
|
| - if self.version == (3,2) and ivLength:
|
| - #Choose fixedIVBlock for TLS 1.1 (this is encrypted with the CBC
|
| - #residue to create the IV for each sent block)
|
| - self.fixedIVBlock = getRandomBytes(ivLength)
|
| -
|
| - def _changeWriteState(self):
|
| - self._writeState = self._pendingWriteState
|
| - self._pendingWriteState = _ConnectionState()
|
| -
|
| - def _changeReadState(self):
|
| - self._readState = self._pendingReadState
|
| - self._pendingReadState = _ConnectionState()
|
| -
|
| - def _sendFinished(self):
|
| - #Send ChangeCipherSpec
|
| - for result in self._sendMsg(ChangeCipherSpec()):
|
| - yield result
|
| -
|
| - #Switch to pending write state
|
| - self._changeWriteState()
|
| -
|
| - #Calculate verification data
|
| - verifyData = self._calcFinished(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
|
| -
|
| - def _getChangeCipherSpec(self):
|
| - #Get and check ChangeCipherSpec
|
| - for result in self._getMsg(ContentType.change_cipher_spec):
|
| - if result in (0,1):
|
| - yield result
|
| - changeCipherSpec = result
|
| -
|
| - if changeCipherSpec.type != 1:
|
| - for result in self._sendError(AlertDescription.illegal_parameter,
|
| - "ChangeCipherSpec type incorrect"):
|
| - yield result
|
| -
|
| - #Switch to pending read state
|
| - self._changeReadState()
|
| -
|
| - def _getEncryptedExtensions(self):
|
| - for result in self._getMsg(ContentType.handshake,
|
| - HandshakeType.encrypted_extensions):
|
| - if result in (0,1):
|
| - yield result
|
| - encrypted_extensions = result
|
| - self.channel_id = encrypted_extensions.channel_id_key
|
| -
|
| - def _getFinished(self):
|
| - #Calculate verification data
|
| - verifyData = self._calcFinished(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, send=True):
|
| - if self.version == (3,0):
|
| - if (self._client and send) or (not self._client and not send):
|
| - senderStr = "\x43\x4C\x4E\x54"
|
| - else:
|
| - senderStr = "\x53\x52\x56\x52"
|
| -
|
| - verifyData = self._calcSSLHandshakeHash(self.session.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 = "client finished"
|
| - else:
|
| - label = "server finished"
|
| -
|
| - handshakeHashes = stringToBytes(self._handshake_md5.digest() + \
|
| - self._handshake_sha.digest())
|
| - verifyData = PRF(self.session.masterSecret, label, handshakeHashes,
|
| - 12)
|
| - return verifyData
|
| - else:
|
| - raise AssertionError()
|
| -
|
| - #Used for Finished messages and CertificateVerify messages in SSL v3
|
| - def _calcSSLHandshakeHash(self, masterSecret, label):
|
| - masterSecretStr = bytesToString(masterSecret)
|
| -
|
| - imac_md5 = self._handshake_md5.copy()
|
| - imac_sha = self._handshake_sha.copy()
|
| -
|
| - imac_md5.update(label + masterSecretStr + '\x36'*48)
|
| - imac_sha.update(label + masterSecretStr + '\x36'*40)
|
| -
|
| - md5Str = md5.md5(masterSecretStr + ('\x5c'*48) + \
|
| - imac_md5.digest()).digest()
|
| - shaStr = sha.sha(masterSecretStr + ('\x5c'*40) + \
|
| - imac_sha.digest()).digest()
|
| -
|
| - return stringToBytes(md5Str + shaStr)
|
|
|