Skip to content

back to Reference (Gold) summary

Reference (Gold): tlslite-ng

Pytest Summary for test unit_tests

status count
passed 1653
skipped 32
xfailed 2
total 1687
collected 1687

Failed pytests:

test_tlslite_utils_python_ecdsakey.py::TestECDSAKey::test_sign_and_verify_with_sha384

test_tlslite_utils_python_ecdsakey.py::TestECDSAKey::test_sign_and_verify_with_sha384
self = 
testcase = 
rawexcinfo = (, BadDigestError('this curve (NIST256p) is too short for the length of your digest (384)'), )
reason = ''

    def addExpectedFailure(
        self,
        testcase: unittest.TestCase,
        rawexcinfo: _SysExcInfoType,
        reason: str = "",
    ) -> None:
        try:
>           xfail(str(reason))
E           _pytest.outcomes.XFailed

.venv/lib/python3.12/site-packages/_pytest/unittest.py:295: XFailed

test_tlslite_utils_python_ecdsakey.py::TestECDSAKey::test_sign_and_verify_with_sha512

test_tlslite_utils_python_ecdsakey.py::TestECDSAKey::test_sign_and_verify_with_sha512
self = 
testcase = 
rawexcinfo = (, BadDigestError('this curve (NIST256p) is too short for the length of your digest (512)'), )
reason = ''

    def addExpectedFailure(
        self,
        testcase: unittest.TestCase,
        rawexcinfo: _SysExcInfoType,
        reason: str = "",
    ) -> None:
        try:
>           xfail(str(reason))
E           _pytest.outcomes.XFailed

.venv/lib/python3.12/site-packages/_pytest/unittest.py:295: XFailed

Patch diff

diff --git a/tlslite/api.py b/tlslite/api.py
index de04c6a..589bdb5 100644
--- a/tlslite/api.py
+++ b/tlslite/api.py
@@ -1,4 +1,13 @@
-__version__ = '0.8.0-beta1'
+# Authors:
+#   Trevor Perrin
+#   Esteban Sanchez (FosanzDev) - python 3.12 port
+#
+# See the LICENSE file for legal information regarding use of this file.
+
+__version__ = "0.8.0-beta1"
+# the whole module is about importing most commonly used methods, for use
+# by other applications
+# pylint: disable=unused-import
 from .constants import AlertLevel, AlertDescription, Fault
 from .errors import *
 from .checker import Checker
@@ -9,22 +18,39 @@ from .tlsconnection import TLSConnection
 from .verifierdb import VerifierDB
 from .x509 import X509
 from .x509certchain import X509CertChain
+
 from .integration.httptlsconnection import HTTPTLSConnection
 from .integration.tlssocketservermixin import TLSSocketServerMixIn
+
 try:
-    from .integration.tlsasynciodispatchermixin import TLSAsyncioDispatcherMixIn
+    from .integration.tlsasynciodispatchermixin \
+        import TLSAsyncioDispatcherMixIn
+
 except ImportError:
+    # NOTE: asyncio is not available in base python 2, so this try-except
+    # block is necessary to avoid breaking the import of the
+    # rest of the module.
     pass
+
 try:
     from .integration.tlsasyncdispatchermixin import TLSAsyncDispatcherMixIn
 except ImportError:
+    # NOTE: Left this try-except block as is, due to the possibility to use
+    # both asyncore and asyncio in the same project no matter the python
+    # version (if the asyncore module is available).
     pass
+
 from .integration.pop3_tls import POP3_TLS
 from .integration.imap4_tls import IMAP4_TLS
 from .integration.smtp_tls import SMTP_TLS
 from .integration.xmlrpctransport import XMLRPCTransport
-from .integration.xmlrpcserver import TLSXMLRPCRequestHandler, TLSXMLRPCServer, MultiPathTLSXMLRPCServer
-from .utils.cryptomath import m2cryptoLoaded, gmpyLoaded, pycryptoLoaded, prngName, GMPY2_LOADED
-from .utils.keyfactory import generateRSAKey, parsePEMKey, parseAsPublicKey, parsePrivateKey
+from .integration.xmlrpcserver import TLSXMLRPCRequestHandler, \
+                                      TLSXMLRPCServer, \
+                                      MultiPathTLSXMLRPCServer
+
+from .utils.cryptomath import m2cryptoLoaded, gmpyLoaded, \
+                             pycryptoLoaded, prngName, GMPY2_LOADED
+from .utils.keyfactory import generateRSAKey, parsePEMKey, \
+                             parseAsPublicKey, parsePrivateKey
 from .utils.tackwrapper import tackpyLoaded
 from .dh import parse as parseDH
diff --git a/tlslite/basedb.py b/tlslite/basedb.py
index 2941ed2..427f9d3 100644
--- a/tlslite/basedb.py
+++ b/tlslite/basedb.py
@@ -1,15 +1,21 @@
+# Authors: 
+#   Trevor Perrin
+#   Martin von Loewis - python 3 port
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """Base class for SharedKeyDB and VerifierDB."""
+
 try:
     import anydbm
 except ImportError:
+    # Python 3
     import dbm as anydbm
 import threading
 import time
 import logging

-
 class BaseDB(object):
-
     def __init__(self, filename, type):
         self.type = type
         self.filename = filename
@@ -25,7 +31,20 @@ class BaseDB(object):

         :raises anydbm.error: If there's a problem creating the database.
         """
-        pass
+        logger = logging.getLogger(__name__)
+
+        if self.filename:
+            logger.debug('server %s - create - will open db', time.time())
+            self.db = anydbm.open(self.filename, "n") #raises anydbm.error
+            logger.debug('server %s - create - setting type', time.time())
+            self.db["--Reserved--type"] = self.type
+            logger.debug('server %s - create - syncing', time.time())
+            self.db.sync()
+            logger.debug('server %s - create - fun exit', time.time())
+        else:
+            logger.debug('server %s - create - using dict() as DB',
+                         time.time())
+            self.db = {}

     def open(self):
         """
@@ -34,22 +53,33 @@ class BaseDB(object):
         :raises anydbm.error: If there's a problem opening the database.
         :raises ValueError: If the database is not of the right type.
         """
-        pass
+        if not self.filename:
+            raise ValueError("Can only open on-disk databases")
+        self.db = anydbm.open(self.filename, "w") #raises anydbm.error
+        try:
+            if self.db["--Reserved--type"] != self.type:
+                raise ValueError("Not a %s database" % self.type)
+        except KeyError:
+            raise ValueError("Not a recognized database")

     def __getitem__(self, username):
         if self.db == None:
-            raise AssertionError('DB not open')
+            raise AssertionError("DB not open")
+
         self.lock.acquire()
         try:
             valueStr = self.db[username]
         finally:
             self.lock.release()
+
         return self._getItem(username, valueStr)

     def __setitem__(self, username, value):
         if self.db == None:
-            raise AssertionError('DB not open')
+            raise AssertionError("DB not open")
+
         valueStr = self._setItem(username, value)
+
         self.lock.acquire()
         try:
             self.db[username] = valueStr
@@ -60,10 +90,11 @@ class BaseDB(object):

     def __delitem__(self, username):
         if self.db == None:
-            raise AssertionError('DB not open')
+            raise AssertionError("DB not open")
+
         self.lock.acquire()
         try:
-            del self.db[username]
+            del(self.db[username])
             if self.filename:
                 self.db.sync()
         finally:
@@ -80,13 +111,18 @@ class BaseDB(object):
             otherwise.
         """
         if self.db == None:
-            raise AssertionError('DB not open')
+            raise AssertionError("DB not open")
+
         self.lock.acquire()
         try:
             return username in self.db
         finally:
             self.lock.release()

+    def check(self, username, param):
+        value = self.__getitem__(username)
+        return self._checkItem(value, username, param)
+
     def keys(self):
         """
         Return a list of usernames in the database.
@@ -94,4 +130,13 @@ class BaseDB(object):
         :rtype: list
         :returns: The usernames in the database.
         """
-        pass
+        if self.db == None:
+            raise AssertionError("DB not open")
+
+        self.lock.acquire()
+        try:
+            usernames = self.db.keys()
+        finally:
+            self.lock.release()
+        usernames = [u for u in usernames if not u.startswith("--Reserved--")]
+        return usernames
diff --git a/tlslite/bufferedsocket.py b/tlslite/bufferedsocket.py
index 8fd2025..e0534f5 100644
--- a/tlslite/bufferedsocket.py
+++ b/tlslite/bufferedsocket.py
@@ -1,4 +1,9 @@
+# Copyright (c) 2016, Hubert Kario
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """Wrapper around the socket.socket interface that provides buffering"""
+
 from collections import deque


@@ -24,23 +29,38 @@ class BufferedSocket(object):

     def send(self, data):
         """Send data to the socket"""
-        pass
+        if self.buffer_writes:
+            self._write_queue.append(data)
+            return len(data)
+        return self.socket.send(data)

     def sendall(self, data):
         """Send data to the socket"""
-        pass
+        if self.buffer_writes:
+            self._write_queue.append(data)
+            return None
+        return self.socket.sendall(data)

     def flush(self):
         """Send all buffered data"""
-        pass
+        buf = bytearray()
+        for i in self._write_queue:
+            buf += i
+        self._write_queue.clear()
+        if buf:
+            self.socket.sendall(buf)

     def recv(self, bufsize):
         """Receive data from socket (socket emulation)"""
-        pass
+        if not self._read_buffer:
+            self._read_buffer += self.socket.recv(max(4096, bufsize))
+        ret = self._read_buffer[:bufsize]
+        del self._read_buffer[:bufsize]
+        return ret

     def getsockname(self):
         """Return the socket's own address (socket emulation)."""
-        pass
+        return self.socket.getsockname()

     def getpeername(self):
         """
@@ -48,11 +68,11 @@ class BufferedSocket(object):

         (socket emulation)
         """
-        pass
+        return self.socket.getpeername()

     def settimeout(self, value):
         """Set a timeout on blocking socket operations (socket emulation)."""
-        pass
+        return self.socket.settimeout(value)

     def gettimeout(self):
         """
@@ -60,16 +80,18 @@ class BufferedSocket(object):

         (socket emulation)
         """
-        pass
+        return self.socket.gettimeout()

     def setsockopt(self, level, optname, value):
         """Set the value of the given socket option (socket emulation)."""
-        pass
+        return self.socket.setsockopt(level, optname, value)

     def shutdown(self, how):
         """Shutdown the underlying socket."""
-        pass
+        self.flush()
+        return self.socket.shutdown(how)

     def close(self):
         """Close the underlying socket."""
-        pass
+        self.flush()
+        return self.socket.close()
diff --git a/tlslite/checker.py b/tlslite/checker.py
index e581507..255c988 100644
--- a/tlslite/checker.py
+++ b/tlslite/checker.py
@@ -1,4 +1,8 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 """Class for post-handshake certificate checking."""
+
 from .x509 import X509
 from .x509certchain import X509CertChain
 from .errors import *
@@ -18,7 +22,9 @@ class Checker(object):
     Currently, the Checker can check an X.509 chain.
     """

-    def __init__(self, x509Fingerprint=None, checkResumedSession=False):
+    def __init__(self,
+                 x509Fingerprint=None,
+                 checkResumedSession=False):
         """
         Create a new Checker instance.

@@ -34,6 +40,7 @@ class Checker(object):
             session was checked once, we don't need to bother
             re-checking it.
         """
+
         self.x509Fingerprint = x509Fingerprint
         self.checkResumedSession = checkResumedSession

@@ -51,18 +58,20 @@ class Checker(object):
         """
         if not self.checkResumedSession and connection.resumed:
             return
+
         if self.x509Fingerprint:
             if connection._client:
                 chain = connection.session.serverCertChain
             else:
                 chain = connection.session.clientCertChain
+
             if self.x509Fingerprint:
                 if isinstance(chain, X509CertChain):
                     if self.x509Fingerprint:
                         if chain.getFingerprint() != self.x509Fingerprint:
-                            raise TLSFingerprintError(
-                                'X.509 fingerprint mismatch: %s, %s' % (
-                                chain.getFingerprint(), self.x509Fingerprint))
+                            raise TLSFingerprintError(\
+                                "X.509 fingerprint mismatch: %s, %s" % \
+                                (chain.getFingerprint(), self.x509Fingerprint))
                 elif chain:
                     raise TLSAuthenticationTypeError()
                 else:
diff --git a/tlslite/constants.py b/tlslite/constants.py
index 6ad3e02..dd958c5 100644
--- a/tlslite/constants.py
+++ b/tlslite/constants.py
@@ -1,10 +1,36 @@
+# Authors: 
+#   Trevor Perrin
+#   Google - defining ClientCertificateType
+#   Google (adapted by Sam Rushing) - NPN support
+#   Dimitris Moraitis - Anon ciphersuites
+#   Dave Baggett (Arcode Corporation) - canonicalCipherName
+#   Yngve Pettersen (ported by Paul Sokolovsky) - TLS 1.2
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 from .utils.compat import a2b_hex
+
 """Constants used in various places."""
-TLS_1_3_DRAFT = 3, 4
-TLS_1_3_HRR = a2b_hex(
-    'CF21AD74E59A6111BE1D8C021E65B891C2A211167ABB8C5E079E09E2C8A8339C')
-TLS_1_1_DOWNGRADE_SENTINEL = a2b_hex('444F574E47524400')
-TLS_1_2_DOWNGRADE_SENTINEL = a2b_hex('444F574E47524401')
+
+
+# protocol version number used for negotiating TLS 1.3 between implementations
+# of the draft specification
+# DEPRECATED!
+TLS_1_3_DRAFT = (3, 4)
+
+
+# ServerHello.random value meaning that the message is a HelloRetryRequest
+TLS_1_3_HRR = a2b_hex("CF21AD74E59A6111BE1D8C021E65B891"
+                      "C2A211167ABB8C5E079E09E2C8A8339C")
+
+# last bytes of ServerHello.random to be used when negotiating TLS 1.1 or
+# earlier while supporting TLS 1.2 or greater
+TLS_1_1_DOWNGRADE_SENTINEL = a2b_hex("444F574E47524400")
+
+# last bytes of ServerHello.random to be used when negotiating TLS 1.2
+# while supporting TLS 1.3 or greater
+TLS_1_2_DOWNGRADE_SENTINEL = a2b_hex("444F574E47524401")
+
 RSA_PSS_OID = bytes(a2b_hex('06092a864886f70d01010a'))


@@ -14,7 +40,11 @@ class TLSEnum(object):
     @classmethod
     def _recursiveVars(cls, klass):
         """Call vars recursively on base classes"""
-        pass
+        fields = dict()
+        for basecls in klass.__bases__:
+            fields.update(cls._recursiveVars(basecls))
+        fields.update(dict(vars(klass)))
+        return fields

     @classmethod
     def toRepr(cls, value, blacklist=None):
@@ -23,12 +53,23 @@ class TLSEnum(object):

         name if found, None otherwise
         """
-        pass
+        fields = cls._recursiveVars(cls)
+        if blacklist is None:
+            blacklist = []
+        return next((key for key, val in fields.items() \
+                    if key not in ('__weakref__', '__dict__', '__doc__',
+                                   '__module__') and \
+                       key not in blacklist and \
+                        val == value), None)

     @classmethod
     def toStr(cls, value, blacklist=None):
         """Convert numeric type to human-readable string if possible"""
-        pass
+        ret = cls.toRepr(value, blacklist)
+        if ret is not None:
+            return ret
+        else:
+            return '{0}'.format(value)


 class CertificateType(TLSEnum):
@@ -41,13 +82,14 @@ class ClientCertificateType(TLSEnum):
     dss_sign = 2
     rsa_fixed_dh = 3
     dss_fixed_dh = 4
-    ecdsa_sign = 64
-    rsa_fixed_ecdh = 65
-    ecdsa_fixed_ecdh = 66
+    ecdsa_sign = 64  # RFC 8422
+    rsa_fixed_ecdh = 65  # RFC 8422
+    ecdsa_fixed_ecdh = 66  # RFC 8422


 class SSL2HandshakeType(TLSEnum):
     """SSL2 Handshake Protocol message types."""
+
     error = 0
     client_hello = 1
     client_master_key = 2
@@ -61,21 +103,23 @@ class SSL2HandshakeType(TLSEnum):

 class SSL2ErrorDescription(TLSEnum):
     """SSL2 Handshake protocol error message descriptions"""
-    no_cipher = 1
-    no_certificate = 2
-    bad_certificate = 4
-    unsupported_certificate_type = 6
+
+    no_cipher = 0x0001
+    no_certificate = 0x0002
+    bad_certificate = 0x0004
+    unsupported_certificate_type = 0x0006


 class HandshakeType(TLSEnum):
     """Message types in TLS Handshake protocol"""
+
     hello_request = 0
     client_hello = 1
     server_hello = 2
     new_session_ticket = 4
-    end_of_early_data = 5
-    hello_retry_request = 6
-    encrypted_extensions = 8
+    end_of_early_data = 5  # TLS 1.3
+    hello_retry_request = 6  # TLS 1.3
+    encrypted_extensions = 8  # TLS 1.3
     certificate = 11
     server_key_exchange = 12
     certificate_request = 13
@@ -84,59 +128,65 @@ class HandshakeType(TLSEnum):
     client_key_exchange = 16
     finished = 20
     certificate_status = 22
-    key_update = 24
+    key_update = 24  # TLS 1.3
     next_protocol = 67
-    message_hash = 254
+    message_hash = 254  # TLS 1.3


 class ContentType(TLSEnum):
     """TLS record layer content types of payloads"""
+
     change_cipher_spec = 20
     alert = 21
     handshake = 22
     application_data = 23
-    heartbeat = 24
-    all = 20, 21, 22, 23, 24
+    heartbeat = 24  # RFC 6520
+    all = (20, 21, 22, 23, 24)

     @classmethod
     def toRepr(cls, value, blacklist=None):
         """Convert numeric type to name representation"""
-        pass
+        if blacklist is None:
+            blacklist = []
+        blacklist.append('all')
+        return super(ContentType, cls).toRepr(value, blacklist)


 class ExtensionType(TLSEnum):
     """TLS Extension Type registry values"""
-    server_name = 0
-    max_fragment_length = 1
-    status_request = 5
-    cert_type = 9
-    supported_groups = 10
-    ec_point_formats = 11
-    srp = 12
-    signature_algorithms = 13
-    heartbeat = 15
-    alpn = 16
-    client_hello_padding = 21
-    encrypt_then_mac = 22
-    extended_master_secret = 23
-    record_size_limit = 28
-    session_ticket = 35
-    extended_random = 40
-    pre_shared_key = 41
-    early_data = 42
-    supported_versions = 43
-    cookie = 44
-    psk_key_exchange_modes = 45
-    post_handshake_auth = 49
-    signature_algorithms_cert = 50
-    key_share = 51
+
+    server_name = 0  # RFC 6066 / 4366
+    max_fragment_length = 1  # RFC 6066 / 4366
+    status_request = 5  # RFC 6066 / 4366
+    cert_type = 9  # RFC 6091
+    supported_groups = 10  # RFC 4492, RFC-ietf-tls-negotiated-ff-dhe-10
+    ec_point_formats = 11  # RFC 4492
+    srp = 12  # RFC 5054
+    signature_algorithms = 13  # RFC 5246
+    heartbeat = 15  # RFC 6520
+    alpn = 16  # RFC 7301
+    client_hello_padding = 21  # RFC 7685
+    encrypt_then_mac = 22  # RFC 7366
+    extended_master_secret = 23  # RFC 7627
+    record_size_limit = 28  # RFC 8449
+    session_ticket = 35 # RFC 5077
+    extended_random = 40  # draft-rescorla-tls-extended-random-02
+    pre_shared_key = 41  # TLS 1.3
+    early_data = 42  # TLS 1.3
+    supported_versions = 43  # TLS 1.3
+    cookie = 44  # TLS 1.3
+    psk_key_exchange_modes = 45  # TLS 1.3
+    post_handshake_auth = 49  # TLS 1.3
+    signature_algorithms_cert = 50  # TLS 1.3
+    key_share = 51  # TLS 1.3
     supports_npn = 13172
-    tack = 62208
-    renegotiation_info = 65281
+    tack = 0xF300
+    renegotiation_info = 0xff01  # RFC 5746


 class HashAlgorithm(TLSEnum):
     """Hash algorithm IDs used in TLSv1.2"""
+
     none = 0
     md5 = 1
     sha1 = 2
@@ -144,17 +194,18 @@ class HashAlgorithm(TLSEnum):
     sha256 = 4
     sha384 = 5
     sha512 = 6
-    intrinsic = 8
+    intrinsic = 8  # RFC 8422


 class SignatureAlgorithm(TLSEnum):
     """Signing algorithms used in TLSv1.2"""
+
     anonymous = 0
     rsa = 1
     dsa = 2
     ecdsa = 3
-    ed25519 = 7
-    ed448 = 8
+    ed25519 = 7  # RFC 8422
+    ed448 = 8  # RFC 8422


 class SignatureScheme(TLSEnum):
@@ -164,37 +215,46 @@ class SignatureScheme(TLSEnum):
     This is the replacement for the HashAlgorithm and SignatureAlgorithm
     lists. Introduced with TLSv1.3.
     """
-    rsa_pkcs1_sha1 = 2, 1
-    rsa_pkcs1_sha224 = 3, 1
-    rsa_pkcs1_sha256 = 4, 1
-    rsa_pkcs1_sha384 = 5, 1
-    rsa_pkcs1_sha512 = 6, 1
-    ecdsa_sha1 = 2, 3
-    ecdsa_sha224 = 3, 3
-    ecdsa_secp256r1_sha256 = 4, 3
-    ecdsa_secp384r1_sha384 = 5, 3
-    ecdsa_secp521r1_sha512 = 6, 3
-    rsa_pss_rsae_sha256 = 8, 4
-    rsa_pss_rsae_sha384 = 8, 5
-    rsa_pss_rsae_sha512 = 8, 6
-    ed25519 = 8, 7
-    ed448 = 8, 8
-    rsa_pss_pss_sha256 = 8, 9
-    rsa_pss_pss_sha384 = 8, 10
-    rsa_pss_pss_sha512 = 8, 11
-    rsa_pss_sha256 = 8, 4
-    rsa_pss_sha384 = 8, 5
-    rsa_pss_sha512 = 8, 6
-    dsa_sha1 = 2, 2
-    dsa_sha224 = 3, 2
-    dsa_sha256 = 4, 2
-    dsa_sha384 = 5, 2
-    dsa_sha512 = 6, 2
+
+    rsa_pkcs1_sha1 = (2, 1)
+    rsa_pkcs1_sha224 = (3, 1)
+    rsa_pkcs1_sha256 = (4, 1)
+    rsa_pkcs1_sha384 = (5, 1)
+    rsa_pkcs1_sha512 = (6, 1)
+    ecdsa_sha1 = (2, 3)
+    ecdsa_sha224 = (3, 3)
+    ecdsa_secp256r1_sha256 = (4, 3)
+    ecdsa_secp384r1_sha384 = (5, 3)
+    ecdsa_secp521r1_sha512 = (6, 3)
+    rsa_pss_rsae_sha256 = (8, 4)
+    rsa_pss_rsae_sha384 = (8, 5)
+    rsa_pss_rsae_sha512 = (8, 6)
+    ed25519 = (8, 7)  # RFC 8422
+    ed448 = (8, 8)  # RFC 8422
+    rsa_pss_pss_sha256 = (8, 9)
+    rsa_pss_pss_sha384 = (8, 10)
+    rsa_pss_pss_sha512 = (8, 11)
+
+    # backwards compatibility (for TLS1.2)
+    rsa_pss_sha256 = (8, 4)
+    rsa_pss_sha384 = (8, 5)
+    rsa_pss_sha512 = (8, 6)
+
+    dsa_sha1 = (2, 2)
+    dsa_sha224 = (3, 2)
+    dsa_sha256 = (4, 2)
+    dsa_sha384 = (5, 2)
+    dsa_sha512 = (6, 2)

     @classmethod
     def toRepr(cls, value, blacklist=None):
         """Convert numeric type to name representation"""
-        pass
+        if blacklist is None:
+            blacklist = []
+        blacklist += ['getKeyType', 'getPadding', 'getHash',
+                      'rsa_pss_sha256', 'rsa_pss_sha384', 'rsa_pss_sha512']
+
+        return super(SignatureScheme, cls).toRepr(value, blacklist)

     @staticmethod
     def getKeyType(scheme):
@@ -203,17 +263,51 @@ class SignatureScheme(TLSEnum):

         E.g. for "rsa_pkcs1_sha1" it returns "rsa"
         """
-        pass
+        if scheme in ("ed25519", "ed448"):
+            return "eddsa"
+        try:
+            getattr(SignatureScheme, scheme)
+        except AttributeError:
+            raise ValueError("\"{0}\" scheme is unknown".format(scheme))
+        vals = scheme.split('_', 4)
+        return vals[0]

     @staticmethod
     def getPadding(scheme):
         """Return the name of padding scheme used in signature scheme."""
-        pass
+        try:
+            getattr(SignatureScheme, scheme)
+        except AttributeError:
+            raise ValueError("\"{0}\" scheme is unknown".format(scheme))
+        vals = scheme.split('_', 4)
+        assert len(vals) in (3, 4)
+        if len(vals) == 3:
+            kType, padding, _ = vals
+        else:
+            kType, padding, _, _ = vals
+        assert kType == 'rsa'
+        return padding

     @staticmethod
     def getHash(scheme):
         """Return the name of hash used in signature scheme."""
-        pass
+        # there is no explicit hash in the EDDSA, see RFC 8422
+        if scheme in ("ed25519", "ed448"):
+            return "intrinsic"
+        try:
+            getattr(SignatureScheme, scheme)
+        except AttributeError:
+            raise ValueError("\"{0}\" scheme is unknown".format(scheme))
+        vals = scheme.split('_', 4)
+        assert len(vals) in (2, 3, 4)
+        if len(vals) == 2:
+            kType, hName = vals
+        elif len(vals) == 3:
+            kType, _, hName = vals
+        else:
+            kType, _, _, hName = vals
+        assert kType in ('rsa', 'ecdsa', 'dsa')
+        return hName


 class AlgorithmOID(TLSEnum):
@@ -224,49 +318,63 @@ class AlgorithmOID(TLSEnum):
     the value is the algorithm id.
     """
     oid = {}
-    oid[bytes(a2b_hex('06072a8648ce3d0401'))] = SignatureScheme.ecdsa_sha1
-    oid[bytes(a2b_hex('06082a8648ce3d040301'))] = SignatureScheme.ecdsa_sha224
-    oid[bytes(a2b_hex('06082a8648ce3d040302'))
-        ] = SignatureScheme.ecdsa_secp256r1_sha256
-    oid[bytes(a2b_hex('06082a8648ce3d040303'))
-        ] = SignatureScheme.ecdsa_secp384r1_sha384
-    oid[bytes(a2b_hex('06082a8648ce3d040304'))
-        ] = SignatureScheme.ecdsa_secp521r1_sha512
-    oid[bytes(a2b_hex('06092a864886f70d010104'))
-        ] = HashAlgorithm.md5, SignatureAlgorithm.rsa
-    oid[bytes(a2b_hex('06092a864886f70d010105'))
-        ] = SignatureScheme.rsa_pkcs1_sha1
-    oid[bytes(a2b_hex('06092a864886f70d01010e'))
-        ] = SignatureScheme.rsa_pkcs1_sha224
-    oid[bytes(a2b_hex('06092a864886f70d01010b'))
-        ] = SignatureScheme.rsa_pkcs1_sha256
-    oid[bytes(a2b_hex('06092a864886f70d01010c'))
-        ] = SignatureScheme.rsa_pkcs1_sha384
-    oid[bytes(a2b_hex('06092a864886f70d01010d'))
-        ] = SignatureScheme.rsa_pkcs1_sha512
-    oid[bytes(a2b_hex('300b0609608648016503040201'))
-        ] = SignatureScheme.rsa_pss_rsae_sha256
-    oid[bytes(a2b_hex('300b0609608648016503040202'))
-        ] = SignatureScheme.rsa_pss_rsae_sha384
-    oid[bytes(a2b_hex('300b0609608648016503040203'))
-        ] = SignatureScheme.rsa_pss_rsae_sha512
-    oid[bytes(a2b_hex('300d06096086480165030402010500'))
-        ] = SignatureScheme.rsa_pss_rsae_sha256
-    oid[bytes(a2b_hex('300d06096086480165030402020500'))
-        ] = SignatureScheme.rsa_pss_rsae_sha384
-    oid[bytes(a2b_hex('300d06096086480165030402030500'))
-        ] = SignatureScheme.rsa_pss_rsae_sha512
-    oid[bytes(a2b_hex('06072A8648CE380403'))] = SignatureScheme.dsa_sha1
-    oid[bytes(a2b_hex('0609608648016503040301'))] = SignatureScheme.dsa_sha224
-    oid[bytes(a2b_hex('0609608648016503040302'))] = SignatureScheme.dsa_sha256
-    oid[bytes(a2b_hex('0609608648016503040303'))] = SignatureScheme.dsa_sha384
-    oid[bytes(a2b_hex('0609608648016503040304'))] = SignatureScheme.dsa_sha512
-    oid[bytes(a2b_hex('06032b6570'))] = SignatureScheme.ed25519
-    oid[bytes(a2b_hex('06032b6571'))] = SignatureScheme.ed448
+
+    oid[bytes(a2b_hex('06072a8648ce3d0401'))] = \
+            SignatureScheme.ecdsa_sha1
+    oid[bytes(a2b_hex('06082a8648ce3d040301'))] = \
+            SignatureScheme.ecdsa_sha224
+    oid[bytes(a2b_hex('06082a8648ce3d040302'))] = \
+            SignatureScheme.ecdsa_secp256r1_sha256
+    oid[bytes(a2b_hex('06082a8648ce3d040303'))] = \
+            SignatureScheme.ecdsa_secp384r1_sha384
+    oid[bytes(a2b_hex('06082a8648ce3d040304'))] = \
+            SignatureScheme.ecdsa_secp521r1_sha512
+    oid[bytes(a2b_hex('06092a864886f70d010104'))] = \
+            (HashAlgorithm.md5, SignatureAlgorithm.rsa)
+    oid[bytes(a2b_hex('06092a864886f70d010105'))] = \
+            SignatureScheme.rsa_pkcs1_sha1
+    oid[bytes(a2b_hex('06092a864886f70d01010e'))] = \
+            SignatureScheme.rsa_pkcs1_sha224
+    oid[bytes(a2b_hex('06092a864886f70d01010b'))] = \
+            SignatureScheme.rsa_pkcs1_sha256
+    oid[bytes(a2b_hex('06092a864886f70d01010c'))] = \
+            SignatureScheme.rsa_pkcs1_sha384
+    oid[bytes(a2b_hex('06092a864886f70d01010d'))] = \
+            SignatureScheme.rsa_pkcs1_sha512
+    oid[bytes(a2b_hex('300b0609608648016503040201'))] = \
+            SignatureScheme.rsa_pss_rsae_sha256
+    oid[bytes(a2b_hex('300b0609608648016503040202'))] = \
+            SignatureScheme.rsa_pss_rsae_sha384
+    oid[bytes(a2b_hex('300b0609608648016503040203'))] = \
+            SignatureScheme.rsa_pss_rsae_sha512
+    # for RSA-PSS an AlgorithmIdentifier with and without NULL parameters
+    # is valid. See RFC 4055 Section 2.1
+    oid[bytes(a2b_hex('300d06096086480165030402010500'))] = \
+            SignatureScheme.rsa_pss_rsae_sha256
+    oid[bytes(a2b_hex('300d06096086480165030402020500'))] = \
+            SignatureScheme.rsa_pss_rsae_sha384
+    oid[bytes(a2b_hex('300d06096086480165030402030500'))] = \
+            SignatureScheme.rsa_pss_rsae_sha512
+    oid[bytes(a2b_hex('06072A8648CE380403'))] = \
+            SignatureScheme.dsa_sha1
+    oid[bytes(a2b_hex('0609608648016503040301'))] = \
+            SignatureScheme.dsa_sha224
+    oid[bytes(a2b_hex('0609608648016503040302'))] = \
+            SignatureScheme.dsa_sha256
+    oid[bytes(a2b_hex('0609608648016503040303'))] = \
+            SignatureScheme.dsa_sha384
+    oid[bytes(a2b_hex('0609608648016503040304'))] = \
+            SignatureScheme.dsa_sha512
+    oid[bytes(a2b_hex('06032b6570'))] = \
+            SignatureScheme.ed25519
+    oid[bytes(a2b_hex('06032b6571'))] = \
+            SignatureScheme.ed448


 class GroupName(TLSEnum):
     """Name of groups supported for (EC)DH key exchange"""
+
+    # RFC4492
     sect163k1 = 1
     sect163r1 = 2
     sect163r2 = 3
@@ -293,46 +401,67 @@ class GroupName(TLSEnum):
     secp384r1 = 24
     secp521r1 = 25
     allEC = list(range(1, 26))
+
+    # RFC7027
     brainpoolP256r1 = 26
     brainpoolP384r1 = 27
     brainpoolP512r1 = 28
     allEC.extend(list(range(26, 29)))
+
+    # draft-ietf-tls-rfc4492bis
     x25519 = 29
     x448 = 30
     allEC.extend(list(range(29, 31)))
+
+    # RFC7919
     ffdhe2048 = 256
     ffdhe3072 = 257
     ffdhe4096 = 258
     ffdhe6144 = 259
     ffdhe8192 = 260
     allFF = list(range(256, 261))
+
     all = allEC + allFF

     @classmethod
     def toRepr(cls, value, blacklist=None):
         """Convert numeric type to name representation"""
-        pass
+        if blacklist is None:
+            blacklist = []
+        blacklist += ['all', 'allEC', 'allFF']
+        return super(GroupName, cls).toRepr(value, blacklist)


-TLS_1_3_FORBIDDEN_GROUPS = frozenset().union(range(1, 23), range(26, 29), (
-    65281, 65282))
+# groups forbidden by RFC 8446 section B.3.1.4
+TLS_1_3_FORBIDDEN_GROUPS = frozenset().union(
+    range(1, 0x17),
+    range(0x1A, 0x1D),
+    (0xff01, 0xff02))


 class ECPointFormat(TLSEnum):
     """Names and ID's of supported EC point formats."""
+
     uncompressed = 0
     ansiX962_compressed_prime = 1
     ansiX962_compressed_char2 = 2
-    all = [uncompressed, ansiX962_compressed_prime, ansiX962_compressed_char2]
+
+    all = [uncompressed,
+           ansiX962_compressed_prime,
+           ansiX962_compressed_char2]

     @classmethod
     def toRepr(cls, value, blacklist=None):
         """Convert numeric type to name representation."""
-        pass
+        if blacklist is None:
+            blacklist = []
+        blacklist.append('all')
+        return super(ECPointFormat, cls).toRepr(value, blacklist)


 class ECCurveType(TLSEnum):
     """Types of ECC curves supported in TLS from RFC4492"""
+
     explicit_prime = 1
     explicit_char2 = 2
     named_curve = 3
@@ -340,34 +469,40 @@ class ECCurveType(TLSEnum):

 class NameType(TLSEnum):
     """Type of entries in Server Name Indication extension."""
+
     host_name = 0


 class CertificateStatusType(TLSEnum):
     """Type of responses in the status_request and CertificateStatus msgs."""
+
     ocsp = 1


 class HeartbeatMode(TLSEnum):
     """Types of heartbeat modes from RFC 6520"""
+
     PEER_ALLOWED_TO_SEND = 1
     PEER_NOT_ALLOWED_TO_SEND = 2


 class HeartbeatMessageType(TLSEnum):
     """Types of heartbeat messages from RFC 6520"""
+
     heartbeat_request = 1
     heartbeat_response = 2


 class KeyUpdateMessageType(TLSEnum):
     """Types of keyupdate messages from RFC 8446"""
+
     update_not_requested = 0
     update_requested = 1


 class AlertLevel(TLSEnum):
     """Enumeration of TLS Alert protocol levels"""
+
     warning = 1
     fatal = 2

@@ -402,6 +537,7 @@ class AlertDescription(TLSEnum):

     :cvar user_canceled: The handshake is being cancelled for some reason.
     """
+
     close_notify = 0
     unexpected_message = 10
     bad_record_mac = 20
@@ -409,7 +545,7 @@ class AlertDescription(TLSEnum):
     record_overflow = 22
     decompression_failure = 30
     handshake_failure = 40
-    no_certificate = 41
+    no_certificate = 41 #SSLv3
     bad_certificate = 42
     unsupported_certificate = 43
     certificate_revoked = 44
@@ -428,23 +564,25 @@ class AlertDescription(TLSEnum):
     user_canceled = 90
     no_renegotiation = 100
     missing_extension = 109
-    unsupported_extension = 110
-    certificate_unobtainable = 111
-    unrecognized_name = 112
-    bad_certificate_status_response = 113
-    bad_certificate_hash_value = 114
+    unsupported_extension = 110  # RFC 5246
+    certificate_unobtainable = 111  # RFC 6066
+    unrecognized_name = 112  # RFC 6066
+    bad_certificate_status_response = 113  # RFC 6066
+    bad_certificate_hash_value = 114  # RFC 6066
     unknown_psk_identity = 115
-    certificate_required = 116
-    no_application_protocol = 120
+    certificate_required = 116  # RFC 8446
+    no_application_protocol = 120  # RFC 7301


 class PskKeyExchangeMode(TLSEnum):
     """Values used in the PSK Key Exchange Modes extension."""
+
     psk_ke = 0
     psk_dhe_ke = 1


 class CipherSuite:
+
     """
     Numeric values of ciphersuites and ciphersuite types

@@ -475,277 +613,356 @@ class CipherSuite:
         Diffie-Hellman key exchange
     :cvar ietfNames: dictionary with string names of the ciphersuites
     """
+
     ietfNames = {}
-    SSL_CK_RC4_128_WITH_MD5 = 65664
-    ietfNames[65664] = 'SSL_CK_RC4_128_WITH_MD5'
-    SSL_CK_RC4_128_EXPORT40_WITH_MD5 = 131200
-    ietfNames[131200] = 'SSL_CK_RC4_128_EXPORT40_WITH_MD5'
-    SSL_CK_RC2_128_CBC_WITH_MD5 = 196736
-    ietfNames[196736] = 'SSL_CK_RC2_128_CBC_WITH_MD5'
-    SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5 = 262272
-    ietfNames[262272] = 'SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5'
-    SSL_CK_IDEA_128_CBC_WITH_MD5 = 327808
-    ietfNames[327808] = 'SSL_CK_IDEA_128_CBC_WITH_MD5'
-    SSL_CK_DES_64_CBC_WITH_MD5 = 393280
-    ietfNames[393280] = 'SSL_CK_DES_64_CBC_WITH_MD5'
-    SSL_CK_DES_192_EDE3_CBC_WITH_MD5 = 458944
-    ietfNames[458944] = 'SSL_CK_DES_192_EDE3_CBC_WITH_MD5'
+
+# the ciphesuite names come from IETF, we want to keep them
+#pylint: disable = invalid-name
+
+    # SSLv2 from draft-hickman-netscape-ssl-00.txt
+    SSL_CK_RC4_128_WITH_MD5 = 0x010080
+    ietfNames[0x010080] = 'SSL_CK_RC4_128_WITH_MD5'
+    SSL_CK_RC4_128_EXPORT40_WITH_MD5 = 0x020080
+    ietfNames[0x020080] = 'SSL_CK_RC4_128_EXPORT40_WITH_MD5'
+    SSL_CK_RC2_128_CBC_WITH_MD5 = 0x030080
+    ietfNames[0x030080] = 'SSL_CK_RC2_128_CBC_WITH_MD5'
+    SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5 = 0x040080
+    ietfNames[0x040080] = 'SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5'
+    SSL_CK_IDEA_128_CBC_WITH_MD5 = 0x050080
+    ietfNames[0x050080] = 'SSL_CK_IDEA_128_CBC_WITH_MD5'
+    SSL_CK_DES_64_CBC_WITH_MD5 = 0x060040
+    ietfNames[0x060040] = 'SSL_CK_DES_64_CBC_WITH_MD5'
+    SSL_CK_DES_192_EDE3_CBC_WITH_MD5 = 0x0700C0
+    ietfNames[0x0700C0] = 'SSL_CK_DES_192_EDE3_CBC_WITH_MD5'
+
+    #: SSL2 ciphersuites which use RC4 symmetric cipher
     ssl2rc4 = []
     ssl2rc4.append(SSL_CK_RC4_128_WITH_MD5)
     ssl2rc4.append(SSL_CK_RC4_128_EXPORT40_WITH_MD5)
+
+    #: SSL2 ciphersuites which use RC2 symmetric cipher
     ssl2rc2 = []
     ssl2rc2.append(SSL_CK_RC2_128_CBC_WITH_MD5)
     ssl2rc2.append(SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5)
+
+    #: SSL2 ciphersuites which use IDEA symmetric cipher
     ssl2idea = [SSL_CK_IDEA_128_CBC_WITH_MD5]
+
+    #: SSL2 ciphersuites which use (single) DES symmetric cipher
     ssl2des = [SSL_CK_DES_64_CBC_WITH_MD5]
+
+    #: SSL2 ciphersuites which use 3DES symmetric cipher
     ssl2_3des = [SSL_CK_DES_192_EDE3_CBC_WITH_MD5]
+
+    #: SSL2 ciphersuites which encrypt only part (40 bits) of the key
     ssl2export = []
     ssl2export.append(SSL_CK_RC4_128_EXPORT40_WITH_MD5)
     ssl2export.append(SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5)
+
+    #: SSL2 ciphersuties which use 128 bit key
     ssl2_128Key = []
     ssl2_128Key.append(SSL_CK_RC4_128_WITH_MD5)
     ssl2_128Key.append(SSL_CK_RC4_128_EXPORT40_WITH_MD5)
     ssl2_128Key.append(SSL_CK_RC2_128_CBC_WITH_MD5)
     ssl2_128Key.append(SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5)
     ssl2_128Key.append(SSL_CK_IDEA_128_CBC_WITH_MD5)
+
+    #: SSL2 ciphersuites which use 64 bit key
     ssl2_64Key = [SSL_CK_DES_64_CBC_WITH_MD5]
+
+    #: SSL2 ciphersuites which use 192 bit key
     ssl2_192Key = [SSL_CK_DES_192_EDE3_CBC_WITH_MD5]
-    TLS_RSA_WITH_NULL_MD5 = 1
-    ietfNames[1] = 'TLS_RSA_WITH_NULL_MD5'
-    TLS_RSA_WITH_NULL_SHA = 2
-    ietfNames[2] = 'TLS_RSA_WITH_NULL_SHA'
-    TLS_RSA_WITH_RC4_128_MD5 = 4
-    ietfNames[4] = 'TLS_RSA_WITH_RC4_128_MD5'
-    TLS_RSA_WITH_RC4_128_SHA = 5
-    ietfNames[5] = 'TLS_RSA_WITH_RC4_128_SHA'
-    TLS_RSA_WITH_3DES_EDE_CBC_SHA = 10
-    ietfNames[10] = 'TLS_RSA_WITH_3DES_EDE_CBC_SHA'
-    TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 13
-    ietfNames[13] = 'TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA'
-    TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 19
-    ietfNames[19] = 'TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA'
-    TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 22
-    ietfNames[22] = 'TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA'
-    TLS_DH_ANON_WITH_RC4_128_MD5 = 24
-    ietfNames[24] = 'TLS_DH_ANON_WITH_RC4_128_MD5'
-    TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA = 27
-    ietfNames[27] = 'TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA'
-    TLS_RSA_WITH_AES_128_CBC_SHA = 47
-    ietfNames[47] = 'TLS_RSA_WITH_AES_128_CBC_SHA'
-    TLS_DH_DSS_WITH_AES_128_CBC_SHA = 48
-    ietfNames[48] = 'TLS_DH_DSS_WITH_AES_128_CBC_SHA'
-    TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 50
-    ietfNames[50] = 'TLS_DHE_DSS_WITH_AES_128_CBC_SHA'
-    TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 51
-    ietfNames[51] = 'TLS_DHE_RSA_WITH_AES_128_CBC_SHA'
-    TLS_DH_ANON_WITH_AES_128_CBC_SHA = 52
-    ietfNames[52] = 'TLS_DH_ANON_WITH_AES_128_CBC_SHA'
-    TLS_RSA_WITH_AES_256_CBC_SHA = 53
-    ietfNames[53] = 'TLS_RSA_WITH_AES_256_CBC_SHA'
-    TLS_DH_DSS_WITH_AES_256_CBC_SHA = 54
-    ietfNames[54] = 'TLS_DH_DSS_WITH_AES_256_CBC_SHA'
-    TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 56
-    ietfNames[56] = 'TLS_DHE_DSS_WITH_AES_256_CBC_SHA'
-    TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 57
-    ietfNames[57] = 'TLS_DHE_RSA_WITH_AES_256_CBC_SHA'
-    TLS_DH_ANON_WITH_AES_256_CBC_SHA = 58
-    ietfNames[58] = 'TLS_DH_ANON_WITH_AES_256_CBC_SHA'
-    TLS_RSA_WITH_NULL_SHA256 = 59
-    ietfNames[59] = 'TLS_RSA_WITH_NULL_SHA256'
-    TLS_RSA_WITH_AES_128_CBC_SHA256 = 60
-    ietfNames[60] = 'TLS_RSA_WITH_AES_128_CBC_SHA256'
-    TLS_RSA_WITH_AES_256_CBC_SHA256 = 61
-    ietfNames[61] = 'TLS_RSA_WITH_AES_256_CBC_SHA256'
-    TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 62
-    ietfNames[62] = 'TLS_DH_DSS_WITH_AES_128_CBC_SHA256'
-    TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 64
-    ietfNames[64] = 'TLS_DHE_DSS_WITH_AES_128_CBC_SHA256'
-    TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 103
-    ietfNames[103] = 'TLS_DHE_RSA_WITH_AES_128_CBC_SHA256'
-    TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 104
-    ietfNames[104] = 'TLS_DH_DSS_WITH_AES_256_CBC_SHA256'
-    TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 106
-    ietfNames[106] = 'TLS_DHE_DSS_WITH_AES_256_CBC_SHA256'
-    TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 107
-    ietfNames[107] = 'TLS_DHE_RSA_WITH_AES_256_CBC_SHA256'
-    TLS_DH_ANON_WITH_AES_128_CBC_SHA256 = 108
-    ietfNames[108] = 'TLS_DH_ANON_WITH_AES_128_CBC_SHA256'
-    TLS_DH_ANON_WITH_AES_256_CBC_SHA256 = 109
-    ietfNames[109] = 'TLS_DH_ANON_WITH_AES_256_CBC_SHA256'
-    TLS_RSA_WITH_AES_128_GCM_SHA256 = 156
-    ietfNames[156] = 'TLS_RSA_WITH_AES_128_GCM_SHA256'
-    TLS_RSA_WITH_AES_256_GCM_SHA384 = 157
-    ietfNames[157] = 'TLS_RSA_WITH_AES_256_GCM_SHA384'
-    TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 158
-    ietfNames[158] = 'TLS_DHE_RSA_WITH_AES_128_GCM_SHA256'
-    TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 159
-    ietfNames[159] = 'TLS_DHE_RSA_WITH_AES_256_GCM_SHA384'
-    TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = 162
-    ietfNames[162] = 'TLS_DHE_DSS_WITH_AES_128_GCM_SHA256'
-    TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 163
-    ietfNames[163] = 'TLS_DHE_DSS_WITH_AES_256_GCM_SHA384'
-    TLS_DH_DSS_WITH_AES_128_GCM_SHA256 = 164
-    ietfNames[164] = 'TLS_DH_DSS_WITH_AES_128_GCM_SHA256'
-    TLS_DH_DSS_WITH_AES_256_GCM_SHA384 = 165
-    ietfNames[165] = 'TLS_DH_DSS_WITH_AES_256_GCM_SHA384'
-    TLS_DH_ANON_WITH_AES_128_GCM_SHA256 = 166
-    ietfNames[166] = 'TLS_DH_ANON_WITH_AES_128_GCM_SHA256'
-    TLS_DH_ANON_WITH_AES_256_GCM_SHA384 = 167
-    ietfNames[167] = 'TLS_DH_ANON_WITH_AES_256_GCM_SHA384'
-    TLS_RSA_WITH_AES_128_CCM = 49308
-    ietfNames[49308] = 'TLS_RSA_WITH_AES_128_CCM'
-    TLS_RSA_WITH_AES_256_CCM = 49309
-    ietfNames[49309] = 'TLS_RSA_WITH_AES_256_CCM'
-    TLS_DHE_RSA_WITH_AES_128_CCM = 49310
-    ietfNames[49310] = 'TLS_DHE_RSA_WITH_AES_128_CCM'
-    TLS_DHE_RSA_WITH_AES_256_CCM = 49311
-    ietfNames[49311] = 'TLS_DHE_RSA_WITH_AES_256_CCM'
-    TLS_RSA_WITH_AES_128_CCM_8 = 49312
-    ietfNames[49312] = 'TLS_RSA_WITH_AES_128_CCM_8'
-    TLS_RSA_WITH_AES_256_CCM_8 = 49313
-    ietfNames[49313] = 'TLS_RSA_WITH_AES_256_CCM_8'
-    TLS_DHE_RSA_WITH_AES_128_CCM_8 = 49314
-    ietfNames[49314] = 'TLS_DHE_RSA_WITH_AES_128_CCM_8'
-    TLS_DHE_RSA_WITH_AES_256_CCM_8 = 49315
-    ietfNames[49315] = 'TLS_DHE_RSA_WITH_AES_256_CCM_8'
-    TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 255
-    ietfNames[255] = 'TLS_EMPTY_RENEGOTIATION_INFO_SCSV'
-    TLS_AES_128_GCM_SHA256 = 4865
-    ietfNames[4865] = 'TLS_AES_128_GCM_SHA256'
-    TLS_AES_256_GCM_SHA384 = 4866
-    ietfNames[4866] = 'TLS_AES_256_GCM_SHA384'
-    TLS_CHACHA20_POLY1305_SHA256 = 4867
-    ietfNames[4867] = 'TLS_CHACHA20_POLY1305_SHA256'
-    TLS_AES_128_CCM_SHA256 = 4868
-    ietfNames[4868] = 'TLS_AES_128_CCM_SHA256'
-    TLS_AES_128_CCM_8_SHA256 = 4869
-    ietfNames[4869] = 'TLS_AES_128_CCM_8_SHA256'
-    TLS_FALLBACK_SCSV = 22016
-    ietfNames[22016] = 'TLS_FALLBACK_SCSV'
-    TLS_ECDH_ECDSA_WITH_NULL_SHA = 49153
-    ietfNames[49153] = 'TLS_ECDH_ECDSA_WITH_NULL_SHA'
-    TLS_ECDH_ECDSA_WITH_RC4_128_SHA = 49154
-    ietfNames[49154] = 'TLS_ECDH_ECDSA_WITH_RC4_128_SHA'
-    TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = 49155
-    ietfNames[49155] = 'TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA'
-    TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = 49156
-    ietfNames[49156] = 'TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA'
-    TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = 49157
-    ietfNames[49157] = 'TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA'
-    TLS_ECDHE_ECDSA_WITH_NULL_SHA = 49158
-    ietfNames[49158] = 'TLS_ECDHE_ECDSA_WITH_NULL_SHA'
-    TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = 49159
-    ietfNames[49159] = 'TLS_ECDHE_ECDSA_WITH_RC4_128_SHA'
-    TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = 49160
-    ietfNames[49160] = 'TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA'
-    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 49161
-    ietfNames[49161] = 'TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA'
-    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 49162
-    ietfNames[49162] = 'TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA'
-    TLS_ECDH_RSA_WITH_NULL_SHA = 49163
-    ietfNames[49163] = 'TLS_ECDH_RSA_WITH_NULL_SHA'
-    TLS_ECDH_RSA_WITH_RC4_128_SHA = 49164
-    ietfNames[49164] = 'TLS_ECDH_RSA_WITH_RC4_128_SHA'
-    TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = 49165
-    ietfNames[49165] = 'TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA'
-    TLS_ECDH_RSA_WITH_AES_128_CBC_SHA = 49166
-    ietfNames[49166] = 'TLS_ECDH_RSA_WITH_AES_128_CBC_SHA'
-    TLS_ECDH_RSA_WITH_AES_256_CBC_SHA = 49167
-    ietfNames[49167] = 'TLS_ECDH_RSA_WITH_AES_256_CBC_SHA'
-    TLS_ECDHE_RSA_WITH_NULL_SHA = 49168
-    ietfNames[49168] = 'TLS_ECDHE_RSA_WITH_NULL_SHA'
-    TLS_ECDHE_RSA_WITH_RC4_128_SHA = 49169
-    ietfNames[49169] = 'TLS_ECDHE_RSA_WITH_RC4_128_SHA'
-    TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = 49170
-    ietfNames[49170] = 'TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA'
-    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 49171
-    ietfNames[49171] = 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA'
-    TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 49172
-    ietfNames[49172] = 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA'
-    TLS_ECDH_ANON_WITH_NULL_SHA = 49173
-    ietfNames[49173] = 'TLS_ECDH_ANON_WITH_NULL_SHA'
-    TLS_ECDH_ANON_WITH_RC4_128_SHA = 49174
-    ietfNames[49174] = 'TLS_ECDH_ANON_WITH_RC4_128_SHA'
-    TLS_ECDH_ANON_WITH_3DES_EDE_CBC_SHA = 49175
-    ietfNames[49175] = 'TLS_ECDH_ANON_WITH_3DES_EDE_CBC_SHA'
-    TLS_ECDH_ANON_WITH_AES_128_CBC_SHA = 49176
-    ietfNames[49176] = 'TLS_ECDH_ANON_WITH_AES_128_CBC_SHA'
-    TLS_ECDH_ANON_WITH_AES_256_CBC_SHA = 49177
-    ietfNames[49177] = 'TLS_ECDH_ANON_WITH_AES_256_CBC_SHA'
-    TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = 49178
-    ietfNames[49178] = 'TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA'
-    TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = 49179
-    ietfNames[49179] = 'TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA'
-    TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA = 49180
-    ietfNames[49180] = 'TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA'
-    TLS_SRP_SHA_WITH_AES_128_CBC_SHA = 49181
-    ietfNames[49181] = 'TLS_SRP_SHA_WITH_AES_128_CBC_SHA'
-    TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = 49182
-    ietfNames[49182] = 'TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA'
-    TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA = 49183
-    ietfNames[49183] = 'TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA'
-    TLS_SRP_SHA_WITH_AES_256_CBC_SHA = 49184
-    ietfNames[49184] = 'TLS_SRP_SHA_WITH_AES_256_CBC_SHA'
-    TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = 49185
-    ietfNames[49185] = 'TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA'
-    TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA = 49186
-    ietfNames[49186] = 'TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA'
-    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 49187
-    ietfNames[49187] = 'TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256'
-    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 49188
-    ietfNames[49188] = 'TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384'
-    TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 = 49189
-    ietfNames[49189] = 'TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256'
-    TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 = 49190
-    ietfNames[49190] = 'TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384'
-    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 49191
-    ietfNames[49191] = 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256'
-    TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 49192
-    ietfNames[49192] = 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384'
-    TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 = 49193
-    ietfNames[49193] = 'TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256'
-    TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 = 49194
-    ietfNames[49194] = 'TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384'
-    TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 49195
-    ietfNames[49195] = 'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256'
-    TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 49196
-    ietfNames[49196] = 'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384'
-    TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 = 49197
-    ietfNames[49197] = 'TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256'
-    TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 = 49198
-    ietfNames[49198] = 'TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384'
-    TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 49199
-    ietfNames[49199] = 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256'
-    TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 49200
-    ietfNames[49200] = 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384'
-    TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 = 49201
-    ietfNames[49201] = 'TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256'
-    TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = 49202
-    ietfNames[49202] = 'TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384'
-    TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_draft_00 = 52385
-    ietfNames[52385] = 'TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_draft_00'
-    TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_draft_00 = 52386
-    ietfNames[52386] = 'TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_draft_00'
-    TLS_DHE_RSA_WITH_CHACHA20_POLY1305_draft_00 = 52387
-    ietfNames[52387] = 'TLS_DHE_RSA_WITH_CHACHA20_POLY1305_draft_00'
-    TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 52392
-    ietfNames[52392] = 'TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256'
-    TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 52393
-    ietfNames[52393] = 'TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256'
-    TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 52394
-    ietfNames[52394] = 'TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256'
-    TLS_ECDHE_ECDSA_WITH_AES_128_CCM = 49324
-    ietfNames[49324] = 'TLS_ECDHE_ECDSA_WITH_AES_128_CCM'
-    TLS_ECDHE_ECDSA_WITH_AES_256_CCM = 49325
-    ietfNames[49325] = 'TLS_ECDHE_ECDSA_WITH_AES_256_CCM'
-    TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 = 49326
-    ietfNames[49326] = 'TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8'
-    TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 = 49327
-    ietfNames[49327] = 'TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8'
+
+    #
+    # SSLv3 and TLS cipher suite definitions
+    #
+
+    # RFC 5246 - TLS v1.2 Protocol
+    TLS_RSA_WITH_NULL_MD5 = 0x0001
+    ietfNames[0x0001] = 'TLS_RSA_WITH_NULL_MD5'
+    TLS_RSA_WITH_NULL_SHA = 0x0002
+    ietfNames[0x0002] = 'TLS_RSA_WITH_NULL_SHA'
+    TLS_RSA_WITH_RC4_128_MD5 = 0x0004
+    ietfNames[0x0004] = 'TLS_RSA_WITH_RC4_128_MD5'
+    TLS_RSA_WITH_RC4_128_SHA = 0x0005
+    ietfNames[0x0005] = 'TLS_RSA_WITH_RC4_128_SHA'
+    TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A
+    ietfNames[0x000A] = 'TLS_RSA_WITH_3DES_EDE_CBC_SHA'
+    TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x000D
+    ietfNames[0x000D] = 'TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA'
+    TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x0013
+    ietfNames[0x0013] = 'TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA'
+    TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x0016
+    ietfNames[0x0016] = 'TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA'
+    TLS_DH_ANON_WITH_RC4_128_MD5 = 0x0018
+    ietfNames[0x0018] = 'TLS_DH_ANON_WITH_RC4_128_MD5'
+    TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA = 0x001B
+    ietfNames[0x001B] = 'TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA'
+    TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F
+    ietfNames[0x002F] = 'TLS_RSA_WITH_AES_128_CBC_SHA'
+    TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x0030
+    ietfNames[0x0030] = 'TLS_DH_DSS_WITH_AES_128_CBC_SHA'
+    TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x0032
+    ietfNames[0x0032] = 'TLS_DHE_DSS_WITH_AES_128_CBC_SHA'
+    TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033
+    ietfNames[0x0033] = 'TLS_DHE_RSA_WITH_AES_128_CBC_SHA'
+    TLS_DH_ANON_WITH_AES_128_CBC_SHA = 0x0034
+    ietfNames[0x0034] = 'TLS_DH_ANON_WITH_AES_128_CBC_SHA'
+    TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035
+    ietfNames[0x0035] = 'TLS_RSA_WITH_AES_256_CBC_SHA'
+    TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x0036
+    ietfNames[0x0036] = 'TLS_DH_DSS_WITH_AES_256_CBC_SHA'
+    TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x0038
+    ietfNames[0x0038] = 'TLS_DHE_DSS_WITH_AES_256_CBC_SHA'
+    TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039
+    ietfNames[0x0039] = 'TLS_DHE_RSA_WITH_AES_256_CBC_SHA'
+    TLS_DH_ANON_WITH_AES_256_CBC_SHA = 0x003A
+    ietfNames[0x003A] = 'TLS_DH_ANON_WITH_AES_256_CBC_SHA'
+    TLS_RSA_WITH_NULL_SHA256 = 0x003B
+    ietfNames[0x003B] = 'TLS_RSA_WITH_NULL_SHA256'
+    TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x003C
+    ietfNames[0x003C] = 'TLS_RSA_WITH_AES_128_CBC_SHA256'
+    TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x003D
+    ietfNames[0x003D] = 'TLS_RSA_WITH_AES_256_CBC_SHA256'
+    TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 0x003E
+    ietfNames[0x003E] = 'TLS_DH_DSS_WITH_AES_128_CBC_SHA256'
+    TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x0040
+    ietfNames[0x0040] = 'TLS_DHE_DSS_WITH_AES_128_CBC_SHA256'
+    TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067
+    ietfNames[0x0067] = 'TLS_DHE_RSA_WITH_AES_128_CBC_SHA256'
+    TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 0x0068
+    ietfNames[0x0068] = 'TLS_DH_DSS_WITH_AES_256_CBC_SHA256'
+    TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x006A
+    ietfNames[0x006A] = 'TLS_DHE_DSS_WITH_AES_256_CBC_SHA256'
+    TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B
+    ietfNames[0x006B] = 'TLS_DHE_RSA_WITH_AES_256_CBC_SHA256'
+    TLS_DH_ANON_WITH_AES_128_CBC_SHA256 = 0x006C
+    ietfNames[0x006C] = 'TLS_DH_ANON_WITH_AES_128_CBC_SHA256'
+    TLS_DH_ANON_WITH_AES_256_CBC_SHA256 = 0x006D
+    ietfNames[0x006D] = 'TLS_DH_ANON_WITH_AES_256_CBC_SHA256'
+
+    # RFC 5288 - AES-GCM ciphers for TLSv1.2
+    TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C
+    ietfNames[0x009C] = 'TLS_RSA_WITH_AES_128_GCM_SHA256'
+    TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D
+    ietfNames[0x009D] = 'TLS_RSA_WITH_AES_256_GCM_SHA384'
+    TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E
+    ietfNames[0x009E] = 'TLS_DHE_RSA_WITH_AES_128_GCM_SHA256'
+    TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F
+    ietfNames[0x009F] = 'TLS_DHE_RSA_WITH_AES_256_GCM_SHA384'
+    TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = 0x00A2
+    ietfNames[0x00A2] = 'TLS_DHE_DSS_WITH_AES_128_GCM_SHA256'
+    TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x00A3
+    ietfNames[0x00A3] = 'TLS_DHE_DSS_WITH_AES_256_GCM_SHA384'
+    TLS_DH_DSS_WITH_AES_128_GCM_SHA256 = 0x00A4
+    ietfNames[0x00A4] = 'TLS_DH_DSS_WITH_AES_128_GCM_SHA256'
+    TLS_DH_DSS_WITH_AES_256_GCM_SHA384 = 0x00A5
+    ietfNames[0x00A5] = 'TLS_DH_DSS_WITH_AES_256_GCM_SHA384'
+    TLS_DH_ANON_WITH_AES_128_GCM_SHA256 = 0x00A6
+    ietfNames[0x00A6] = 'TLS_DH_ANON_WITH_AES_128_GCM_SHA256'
+    TLS_DH_ANON_WITH_AES_256_GCM_SHA384 = 0x00A7
+    ietfNames[0x00A7] = 'TLS_DH_ANON_WITH_AES_256_GCM_SHA384'
+
+    # RFC 6655 - AES-CCM ciphers for TLSv1.2
+    TLS_RSA_WITH_AES_128_CCM = 0xC09C
+    ietfNames[0xC09C] = 'TLS_RSA_WITH_AES_128_CCM'
+    TLS_RSA_WITH_AES_256_CCM = 0xC09D
+    ietfNames[0xC09D] = 'TLS_RSA_WITH_AES_256_CCM'
+    TLS_DHE_RSA_WITH_AES_128_CCM = 0xC09E
+    ietfNames[0xC09E] = 'TLS_DHE_RSA_WITH_AES_128_CCM'
+    TLS_DHE_RSA_WITH_AES_256_CCM = 0xC09F
+    ietfNames[0xC09F] = 'TLS_DHE_RSA_WITH_AES_256_CCM'
+    TLS_RSA_WITH_AES_128_CCM_8 = 0xC0A0
+    ietfNames[0xC0A0] = 'TLS_RSA_WITH_AES_128_CCM_8'
+    TLS_RSA_WITH_AES_256_CCM_8 = 0xC0A1
+    ietfNames[0xC0A1] = 'TLS_RSA_WITH_AES_256_CCM_8'
+    TLS_DHE_RSA_WITH_AES_128_CCM_8 = 0xC0A2
+    ietfNames[0xC0A2] = 'TLS_DHE_RSA_WITH_AES_128_CCM_8'
+    TLS_DHE_RSA_WITH_AES_256_CCM_8 = 0xC0A3
+    ietfNames[0xC0A3] = 'TLS_DHE_RSA_WITH_AES_256_CCM_8'
+
+
+    # Weird pseudo-ciphersuite from RFC 5746
+    # Signals that "secure renegotiation" is supported
+    # We actually don't do any renegotiation, but this
+    # prevents renegotiation attacks
+    TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF
+    ietfNames[0x00FF] = 'TLS_EMPTY_RENEGOTIATION_INFO_SCSV'
+
+    # TLS 1.3 ciphersuites
+    TLS_AES_128_GCM_SHA256 = 0x1301
+    ietfNames[0x1301] = 'TLS_AES_128_GCM_SHA256'
+    TLS_AES_256_GCM_SHA384 = 0x1302
+    ietfNames[0x1302] = 'TLS_AES_256_GCM_SHA384'
+    TLS_CHACHA20_POLY1305_SHA256 = 0x1303
+    ietfNames[0x1303] = 'TLS_CHACHA20_POLY1305_SHA256'
+    TLS_AES_128_CCM_SHA256 = 0x1304
+    ietfNames[0x1304] = 'TLS_AES_128_CCM_SHA256'
+    TLS_AES_128_CCM_8_SHA256 = 0x1305
+    ietfNames[0x1305] = 'TLS_AES_128_CCM_8_SHA256'
+
+    # RFC 7507 - Fallback Signaling Cipher Suite Value for Preventing Protocol
+    # Downgrade Attacks
+    TLS_FALLBACK_SCSV = 0x5600
+    ietfNames[0x5600] = 'TLS_FALLBACK_SCSV'
+
+    # RFC 4492 - ECC Cipher Suites for TLS
+    # unsupported - no support for ECDSA certificates
+    TLS_ECDH_ECDSA_WITH_NULL_SHA = 0xC001
+    ietfNames[0xC001] = 'TLS_ECDH_ECDSA_WITH_NULL_SHA'
+    TLS_ECDH_ECDSA_WITH_RC4_128_SHA = 0xC002
+    ietfNames[0xC002] = 'TLS_ECDH_ECDSA_WITH_RC4_128_SHA'
+    TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC003
+    ietfNames[0xC003] = 'TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA'
+    TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = 0xC004
+    ietfNames[0xC004] = 'TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA'
+    TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = 0xC005
+    ietfNames[0xC005] = 'TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA'
+    TLS_ECDHE_ECDSA_WITH_NULL_SHA = 0xC006
+    ietfNames[0xC006] = 'TLS_ECDHE_ECDSA_WITH_NULL_SHA'
+    TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = 0xC007
+    ietfNames[0xC007] = 'TLS_ECDHE_ECDSA_WITH_RC4_128_SHA'
+    TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC008
+    ietfNames[0xC008] = 'TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA'
+    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009
+    ietfNames[0xC009] = 'TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA'
+    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A
+    ietfNames[0xC00A] = 'TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA'
+    TLS_ECDH_RSA_WITH_NULL_SHA = 0xC00B
+    ietfNames[0xC00B] = 'TLS_ECDH_RSA_WITH_NULL_SHA'
+    TLS_ECDH_RSA_WITH_RC4_128_SHA = 0xC00C
+    ietfNames[0xC00C] = 'TLS_ECDH_RSA_WITH_RC4_128_SHA'
+    TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = 0xC00D
+    ietfNames[0xC00D] = 'TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA'
+    TLS_ECDH_RSA_WITH_AES_128_CBC_SHA = 0xC00E
+    ietfNames[0xC00E] = 'TLS_ECDH_RSA_WITH_AES_128_CBC_SHA'
+    TLS_ECDH_RSA_WITH_AES_256_CBC_SHA = 0xC00F
+    ietfNames[0xC00F] = 'TLS_ECDH_RSA_WITH_AES_256_CBC_SHA'
+
+    # RFC 4492 - ECC Cipher Suites for TLS
+    TLS_ECDHE_RSA_WITH_NULL_SHA = 0xC010
+    ietfNames[0xC010] = 'TLS_ECDHE_RSA_WITH_NULL_SHA'
+    TLS_ECDHE_RSA_WITH_RC4_128_SHA = 0xC011
+    ietfNames[0xC011] = 'TLS_ECDHE_RSA_WITH_RC4_128_SHA'
+    TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = 0xC012
+    ietfNames[0xC012] = 'TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA'
+    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013
+    ietfNames[0xC013] = 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA'
+    TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014
+    ietfNames[0xC014] = 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA'
+    TLS_ECDH_ANON_WITH_NULL_SHA = 0xC015
+    ietfNames[0xC015] = 'TLS_ECDH_ANON_WITH_NULL_SHA'
+    TLS_ECDH_ANON_WITH_RC4_128_SHA = 0xC016
+    ietfNames[0xC016] = 'TLS_ECDH_ANON_WITH_RC4_128_SHA'
+    TLS_ECDH_ANON_WITH_3DES_EDE_CBC_SHA = 0xC017
+    ietfNames[0xC017] = 'TLS_ECDH_ANON_WITH_3DES_EDE_CBC_SHA'
+    TLS_ECDH_ANON_WITH_AES_128_CBC_SHA = 0xC018
+    ietfNames[0xC018] = 'TLS_ECDH_ANON_WITH_AES_128_CBC_SHA'
+    TLS_ECDH_ANON_WITH_AES_256_CBC_SHA = 0xC019
+    ietfNames[0xC019] = 'TLS_ECDH_ANON_WITH_AES_256_CBC_SHA'
+
+    # RFC 5054 - Secure Remote Password (SRP) Protocol for TLS Authentication
+    TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA  = 0xC01A
+    ietfNames[0xC01A] = 'TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA'
+    TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = 0xC01B
+    ietfNames[0xC01B] = 'TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA'
+    TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA = 0xC01C
+    ietfNames[0xC01C] = 'TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA'
+    TLS_SRP_SHA_WITH_AES_128_CBC_SHA = 0xC01D
+    ietfNames[0xC01D] = 'TLS_SRP_SHA_WITH_AES_128_CBC_SHA'
+    TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = 0xC01E
+    ietfNames[0xC01E] = 'TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA'
+    TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA = 0xC01F
+    ietfNames[0xC01F] = 'TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA'
+    TLS_SRP_SHA_WITH_AES_256_CBC_SHA = 0xC020
+    ietfNames[0xC020] = 'TLS_SRP_SHA_WITH_AES_256_CBC_SHA'
+    TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = 0xC021
+    ietfNames[0xC021] = 'TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA'
+    TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA = 0xC022
+    ietfNames[0xC022] = 'TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA'
+
+    # RFC 5289 - ECC Ciphers with SHA-256/SHA-384 HMAC and AES-GCM
+    # unsupported! - no support for ECDSA certificates
+    TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023
+    ietfNames[0xC023] = 'TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256'
+    TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024
+    ietfNames[0xC024] = 'TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384'
+    TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC025
+    ietfNames[0xC025] = 'TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256'
+    TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC026
+    ietfNames[0xC026] = 'TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384'
+
+    # RFC 5289 - ECC Ciphers with SHA-256/SHA-384 HMAC and AES-GCM
+    TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027
+    ietfNames[0xC027] = 'TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256'
+    TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028
+    ietfNames[0xC028] = 'TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384'
+
+    # RFC 5289 - ECC Ciphers with SHA-256/SHA-384 HMAC and AES-GCM
+    # unsupported
+    TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 = 0xC029
+    ietfNames[0xC029] = 'TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256'
+    TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 = 0xC02A
+    ietfNames[0xC02A] = 'TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384'
+    TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B
+    ietfNames[0xC02B] = 'TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256'
+    TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C
+    ietfNames[0xC02C] = 'TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384'
+    TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02D
+    ietfNames[0xC02D] = 'TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256'
+    TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02E
+    ietfNames[0xC02E] = 'TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384'
+
+    # RFC 5289 - ECC Ciphers with SHA-256/SHA-384 HMAC and AES-GCM
+    TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F
+    ietfNames[0xC02F] = 'TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256'
+    TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030
+    ietfNames[0xC030] = 'TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384'
+
+    # RFC 5289 - ECC Ciphers with SHA-256/SHA-384 HMAC and AES-GCM
+    # unsupported
+    TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 = 0xC031
+    ietfNames[0xC031] = 'TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256'
+    TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = 0xC032
+    ietfNames[0xC032] = 'TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384'
+
+    # draft-ietf-tls-chacha20-poly1305-00
+    # ChaCha20/Poly1305 based Cipher Suites for TLS1.2
+    TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_draft_00 = 0xCCA1
+    ietfNames[0xCCA1] = 'TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_draft_00'
+    TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_draft_00 = 0xCCA2
+    ietfNames[0xCCA2] = 'TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_draft_00'
+    TLS_DHE_RSA_WITH_CHACHA20_POLY1305_draft_00 = 0xCCA3
+    ietfNames[0xCCA3] = 'TLS_DHE_RSA_WITH_CHACHA20_POLY1305_draft_00'
+
+    # RFC 7905 - ChaCha20-Poly1305 Cipher Suites for TLS
+    TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA8
+    ietfNames[0xCCA8] = 'TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256'
+    TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA9
+    ietfNames[0xCCA9] = 'TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256'
+    TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAA
+    ietfNames[0xCCAA] = 'TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256'
+
+    # RFC 7251 - AES-CCM ECC Ciphers for TLS
+    TLS_ECDHE_ECDSA_WITH_AES_128_CCM = 0xC0AC
+    ietfNames[0xC0AC] = 'TLS_ECDHE_ECDSA_WITH_AES_128_CCM'
+    TLS_ECDHE_ECDSA_WITH_AES_256_CCM = 0xC0AD
+    ietfNames[0xC0AD] = 'TLS_ECDHE_ECDSA_WITH_AES_256_CCM'
+    TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 = 0xC0AE
+    ietfNames[0xC0AE] = 'TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8'
+    TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 = 0xC0AF
+    ietfNames[0xC0AF] = 'TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8'
+
+#pylint: enable = invalid-name
+    #
+    # Define cipher suite families below
+    #
+
+    #: 3DES CBC ciphers
     tripleDESSuites = []
     tripleDESSuites.append(TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA)
-    tripleDESSuites.append(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA)
-    tripleDESSuites.append(TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA)
+    tripleDESSuites.append(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA)  # unsupported
+    tripleDESSuites.append(TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA)  # unsupported
     tripleDESSuites.append(TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA)
     tripleDESSuites.append(TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA)
     tripleDESSuites.append(TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA)
@@ -753,9 +970,12 @@ class CipherSuite:
     tripleDESSuites.append(TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA)
     tripleDESSuites.append(TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA)
     tripleDESSuites.append(TLS_ECDH_ANON_WITH_3DES_EDE_CBC_SHA)
-    tripleDESSuites.append(TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA)
-    tripleDESSuites.append(TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA)
-    tripleDESSuites.append(TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA)
+    tripleDESSuites.append(TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA)    # unsupported
+    tripleDESSuites.append(TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA)   # unsupported
+    tripleDESSuites.append(TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA)   # unsupp
+
+
+    #: AES-128 CBC ciphers
     aes128Suites = []
     aes128Suites.append(TLS_SRP_SHA_WITH_AES_128_CBC_SHA)
     aes128Suites.append(TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA)
@@ -767,18 +987,20 @@ class CipherSuite:
     aes128Suites.append(TLS_DH_ANON_WITH_AES_128_CBC_SHA256)
     aes128Suites.append(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256)
     aes128Suites.append(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA)
-    aes128Suites.append(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256)
-    aes128Suites.append(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA)
-    aes128Suites.append(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256)
-    aes128Suites.append(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA)
+    aes128Suites.append(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256)  # unsupported
+    aes128Suites.append(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA)  # unsupported
+    aes128Suites.append(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256)  # unsupported
+    aes128Suites.append(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA)  # unsupported
     aes128Suites.append(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA)
     aes128Suites.append(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256)
     aes128Suites.append(TLS_ECDH_ANON_WITH_AES_128_CBC_SHA)
-    aes128Suites.append(TLS_DH_DSS_WITH_AES_128_CBC_SHA)
-    aes128Suites.append(TLS_DHE_DSS_WITH_AES_128_CBC_SHA)
-    aes128Suites.append(TLS_DH_DSS_WITH_AES_128_CBC_SHA256)
-    aes128Suites.append(TLS_DHE_DSS_WITH_AES_128_CBC_SHA256)
-    aes128Suites.append(TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA)
+    aes128Suites.append(TLS_DH_DSS_WITH_AES_128_CBC_SHA)    # unsupported
+    aes128Suites.append(TLS_DHE_DSS_WITH_AES_128_CBC_SHA)   # unsupported
+    aes128Suites.append(TLS_DH_DSS_WITH_AES_128_CBC_SHA256) # unsupported
+    aes128Suites.append(TLS_DHE_DSS_WITH_AES_128_CBC_SHA256)    # unsupported
+    aes128Suites.append(TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA)   # unsupported
+
+    #: AES-256 CBC ciphers
     aes256Suites = []
     aes256Suites.append(TLS_SRP_SHA_WITH_AES_256_CBC_SHA)
     aes256Suites.append(TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA)
@@ -790,86 +1012,108 @@ class CipherSuite:
     aes256Suites.append(TLS_DH_ANON_WITH_AES_256_CBC_SHA256)
     aes256Suites.append(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384)
     aes256Suites.append(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA)
-    aes256Suites.append(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384)
-    aes256Suites.append(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA)
-    aes256Suites.append(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384)
-    aes256Suites.append(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA)
+    aes256Suites.append(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384)  # unsupported
+    aes256Suites.append(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA)  # unsupported
+    aes256Suites.append(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384)  # unsupported
+    aes256Suites.append(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA)  # unsupported
     aes256Suites.append(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA)
     aes256Suites.append(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384)
     aes256Suites.append(TLS_ECDH_ANON_WITH_AES_256_CBC_SHA)
-    aes256Suites.append(TLS_DH_DSS_WITH_AES_256_CBC_SHA)
-    aes256Suites.append(TLS_DHE_DSS_WITH_AES_256_CBC_SHA)
-    aes256Suites.append(TLS_DH_DSS_WITH_AES_256_CBC_SHA256)
-    aes256Suites.append(TLS_DHE_DSS_WITH_AES_256_CBC_SHA256)
-    aes256Suites.append(TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA)
+    aes256Suites.append(TLS_DH_DSS_WITH_AES_256_CBC_SHA)    # unsupported
+    aes256Suites.append(TLS_DHE_DSS_WITH_AES_256_CBC_SHA)   # unsupported
+    aes256Suites.append(TLS_DH_DSS_WITH_AES_256_CBC_SHA256) # unsupported
+    aes256Suites.append(TLS_DHE_DSS_WITH_AES_256_CBC_SHA256)    # unsupported
+    aes256Suites.append(TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA)   # unsupported
+
+    #: AES-128 GCM ciphers
     aes128GcmSuites = []
     aes128GcmSuites.append(TLS_RSA_WITH_AES_128_GCM_SHA256)
     aes128GcmSuites.append(TLS_DHE_RSA_WITH_AES_128_GCM_SHA256)
     aes128GcmSuites.append(TLS_DH_ANON_WITH_AES_128_GCM_SHA256)
     aes128GcmSuites.append(TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256)
-    aes128GcmSuites.append(TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256)
-    aes128GcmSuites.append(TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256)
+    aes128GcmSuites.append(TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256)  # unsupp
+    aes128GcmSuites.append(TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256)  # unsupp
     aes128GcmSuites.append(TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256)
     aes128GcmSuites.append(TLS_AES_128_GCM_SHA256)
-    aes128GcmSuites.append(TLS_DHE_DSS_WITH_AES_128_GCM_SHA256)
-    aes128GcmSuites.append(TLS_DH_DSS_WITH_AES_128_GCM_SHA256)
+    aes128GcmSuites.append(TLS_DHE_DSS_WITH_AES_128_GCM_SHA256) # unsupported
+    aes128GcmSuites.append(TLS_DH_DSS_WITH_AES_128_GCM_SHA256)  # unsupported
+
+    #: AES-256-GCM ciphers (implicit SHA384, see sha384PrfSuites)
     aes256GcmSuites = []
     aes256GcmSuites.append(TLS_RSA_WITH_AES_256_GCM_SHA384)
     aes256GcmSuites.append(TLS_DHE_RSA_WITH_AES_256_GCM_SHA384)
     aes256GcmSuites.append(TLS_DH_ANON_WITH_AES_256_GCM_SHA384)
     aes256GcmSuites.append(TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384)
-    aes256GcmSuites.append(TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384)
-    aes256GcmSuites.append(TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384)
+    aes256GcmSuites.append(TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384)  # unsupp
+    aes256GcmSuites.append(TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384)  # unsupported
     aes256GcmSuites.append(TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384)
     aes256GcmSuites.append(TLS_AES_256_GCM_SHA384)
-    aes256GcmSuites.append(TLS_DHE_DSS_WITH_AES_256_GCM_SHA384)
-    aes256GcmSuites.append(TLS_DH_DSS_WITH_AES_256_GCM_SHA384)
+    aes256GcmSuites.append(TLS_DHE_DSS_WITH_AES_256_GCM_SHA384) # unsupported
+    aes256GcmSuites.append(TLS_DH_DSS_WITH_AES_256_GCM_SHA384)  # unsupported
+
+    #: AES-128 CCM_8 ciphers
     aes128Ccm_8Suites = []
     aes128Ccm_8Suites.append(TLS_RSA_WITH_AES_128_CCM_8)
     aes128Ccm_8Suites.append(TLS_DHE_RSA_WITH_AES_128_CCM_8)
     aes128Ccm_8Suites.append(TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8)
     aes128Ccm_8Suites.append(TLS_AES_128_CCM_8_SHA256)
+
+    #: AES-128 CCM ciphers
     aes128CcmSuites = []
     aes128CcmSuites.append(TLS_RSA_WITH_AES_128_CCM)
     aes128CcmSuites.append(TLS_DHE_RSA_WITH_AES_128_CCM)
     aes128CcmSuites.append(TLS_ECDHE_ECDSA_WITH_AES_128_CCM)
     aes128CcmSuites.append(TLS_AES_128_CCM_SHA256)
+
+    #: AES-256 CCM_8 ciphers
     aes256Ccm_8Suites = []
     aes256Ccm_8Suites.append(TLS_RSA_WITH_AES_256_CCM_8)
     aes256Ccm_8Suites.append(TLS_DHE_RSA_WITH_AES_256_CCM_8)
     aes256Ccm_8Suites.append(TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8)
+
+    # AES-256 CCM ciphers
     aes256CcmSuites = []
     aes256CcmSuites.append(TLS_RSA_WITH_AES_256_CCM)
     aes256CcmSuites.append(TLS_DHE_RSA_WITH_AES_256_CCM)
     aes256CcmSuites.append(TLS_ECDHE_ECDSA_WITH_AES_256_CCM)
+
+    #: CHACHA20 cipher, 00'th IETF draft (implicit POLY1305 authenticator)
     chacha20draft00Suites = []
     chacha20draft00Suites.append(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_draft_00)
     chacha20draft00Suites.append(
         TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_draft_00)
     chacha20draft00Suites.append(TLS_DHE_RSA_WITH_CHACHA20_POLY1305_draft_00)
+
+    #: CHACHA20 cipher (implicit POLY1305 authenticator, SHA256 PRF)
     chacha20Suites = []
     chacha20Suites.append(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256)
     chacha20Suites.append(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256)
     chacha20Suites.append(TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256)
     chacha20Suites.append(TLS_CHACHA20_POLY1305_SHA256)
+
+    #: RC4 128 stream cipher
     rc4Suites = []
     rc4Suites.append(TLS_ECDHE_RSA_WITH_RC4_128_SHA)
     rc4Suites.append(TLS_ECDHE_ECDSA_WITH_RC4_128_SHA)
-    rc4Suites.append(TLS_ECDH_ECDSA_WITH_RC4_128_SHA)
-    rc4Suites.append(TLS_ECDH_RSA_WITH_RC4_128_SHA)
+    rc4Suites.append(TLS_ECDH_ECDSA_WITH_RC4_128_SHA)  # unsupported
+    rc4Suites.append(TLS_ECDH_RSA_WITH_RC4_128_SHA)  # unsupported
     rc4Suites.append(TLS_DH_ANON_WITH_RC4_128_MD5)
     rc4Suites.append(TLS_RSA_WITH_RC4_128_SHA)
     rc4Suites.append(TLS_RSA_WITH_RC4_128_MD5)
     rc4Suites.append(TLS_ECDH_ANON_WITH_RC4_128_SHA)
+
+    #: no encryption
     nullSuites = []
     nullSuites.append(TLS_RSA_WITH_NULL_MD5)
     nullSuites.append(TLS_RSA_WITH_NULL_SHA)
     nullSuites.append(TLS_RSA_WITH_NULL_SHA256)
     nullSuites.append(TLS_ECDHE_ECDSA_WITH_NULL_SHA)
-    nullSuites.append(TLS_ECDH_ECDSA_WITH_NULL_SHA)
-    nullSuites.append(TLS_ECDH_RSA_WITH_NULL_SHA)
+    nullSuites.append(TLS_ECDH_ECDSA_WITH_NULL_SHA)  # unsupported
+    nullSuites.append(TLS_ECDH_RSA_WITH_NULL_SHA)  # unsupported
     nullSuites.append(TLS_ECDHE_RSA_WITH_NULL_SHA)
     nullSuites.append(TLS_ECDH_ANON_WITH_NULL_SHA)
+
+    #: SHA-1 HMAC, protocol default PRF
     shaSuites = []
     shaSuites.append(TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA)
     shaSuites.append(TLS_SRP_SHA_WITH_AES_128_CBC_SHA)
@@ -877,9 +1121,9 @@ class CipherSuite:
     shaSuites.append(TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA)
     shaSuites.append(TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA)
     shaSuites.append(TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA)
-    shaSuites.append(TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA)
-    shaSuites.append(TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA)
-    shaSuites.append(TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA)
+    shaSuites.append(TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA) # unsupported
+    shaSuites.append(TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA)  # unsupported
+    shaSuites.append(TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA)  # unsupported
     shaSuites.append(TLS_RSA_WITH_3DES_EDE_CBC_SHA)
     shaSuites.append(TLS_RSA_WITH_AES_128_CBC_SHA)
     shaSuites.append(TLS_RSA_WITH_AES_256_CBC_SHA)
@@ -887,31 +1131,31 @@ class CipherSuite:
     shaSuites.append(TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA)
     shaSuites.append(TLS_DHE_RSA_WITH_AES_128_CBC_SHA)
     shaSuites.append(TLS_DHE_RSA_WITH_AES_256_CBC_SHA)
-    shaSuites.append(TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA)
-    shaSuites.append(TLS_DHE_DSS_WITH_AES_128_CBC_SHA)
-    shaSuites.append(TLS_DHE_DSS_WITH_AES_256_CBC_SHA)
+    shaSuites.append(TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA) # unsupported
+    shaSuites.append(TLS_DHE_DSS_WITH_AES_128_CBC_SHA)  # unsupported
+    shaSuites.append(TLS_DHE_DSS_WITH_AES_256_CBC_SHA)  # unsupported
     shaSuites.append(TLS_DH_ANON_WITH_AES_128_CBC_SHA)
     shaSuites.append(TLS_DH_ANON_WITH_AES_256_CBC_SHA)
     shaSuites.append(TLS_DH_ANON_WITH_3DES_EDE_CBC_SHA)
-    shaSuites.append(TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA)
-    shaSuites.append(TLS_DH_DSS_WITH_AES_128_CBC_SHA)
-    shaSuites.append(TLS_DH_DSS_WITH_AES_256_CBC_SHA)
+    shaSuites.append(TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA)  # unsupported
+    shaSuites.append(TLS_DH_DSS_WITH_AES_128_CBC_SHA)   # unsupported
+    shaSuites.append(TLS_DH_DSS_WITH_AES_256_CBC_SHA)   # unsupported
     shaSuites.append(TLS_RSA_WITH_NULL_SHA)
     shaSuites.append(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA)
     shaSuites.append(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA)
     shaSuites.append(TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA)
     shaSuites.append(TLS_ECDHE_ECDSA_WITH_RC4_128_SHA)
     shaSuites.append(TLS_ECDHE_ECDSA_WITH_NULL_SHA)
-    shaSuites.append(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA)
-    shaSuites.append(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA)
-    shaSuites.append(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA)
-    shaSuites.append(TLS_ECDH_ECDSA_WITH_RC4_128_SHA)
-    shaSuites.append(TLS_ECDH_ECDSA_WITH_NULL_SHA)
-    shaSuites.append(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA)
-    shaSuites.append(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA)
-    shaSuites.append(TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA)
-    shaSuites.append(TLS_ECDH_RSA_WITH_RC4_128_SHA)
-    shaSuites.append(TLS_ECDH_RSA_WITH_NULL_SHA)
+    shaSuites.append(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA)  # unsupported
+    shaSuites.append(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA)  # unsupported
+    shaSuites.append(TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA)  # unsupported
+    shaSuites.append(TLS_ECDH_ECDSA_WITH_RC4_128_SHA)  # unsupported
+    shaSuites.append(TLS_ECDH_ECDSA_WITH_NULL_SHA)  # unsupported
+    shaSuites.append(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA)  # unsupported
+    shaSuites.append(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA)  # unsupported
+    shaSuites.append(TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA)  # unsupported
+    shaSuites.append(TLS_ECDH_RSA_WITH_RC4_128_SHA)  # unsupported
+    shaSuites.append(TLS_ECDH_RSA_WITH_NULL_SHA)  # unsupported
     shaSuites.append(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA)
     shaSuites.append(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA)
     shaSuites.append(TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA)
@@ -922,6 +1166,8 @@ class CipherSuite:
     shaSuites.append(TLS_ECDH_ANON_WITH_3DES_EDE_CBC_SHA)
     shaSuites.append(TLS_ECDH_ANON_WITH_RC4_128_SHA)
     shaSuites.append(TLS_ECDH_ANON_WITH_NULL_SHA)
+
+    #: SHA-256 HMAC, SHA-256 PRF
     sha256Suites = []
     sha256Suites.append(TLS_RSA_WITH_AES_128_CBC_SHA256)
     sha256Suites.append(TLS_RSA_WITH_AES_256_CBC_SHA256)
@@ -931,19 +1177,25 @@ class CipherSuite:
     sha256Suites.append(TLS_DH_ANON_WITH_AES_128_CBC_SHA256)
     sha256Suites.append(TLS_DH_ANON_WITH_AES_256_CBC_SHA256)
     sha256Suites.append(TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256)
-    sha256Suites.append(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256)
-    sha256Suites.append(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256)
+    sha256Suites.append(TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256)  # unsupported
+    sha256Suites.append(TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256)  # unsupported
     sha256Suites.append(TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256)
+
+    #: SHA-384 HMAC, SHA-384 PRF
     sha384Suites = []
     sha384Suites.append(TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384)
-    sha384Suites.append(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384)
-    sha384Suites.append(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384)
+    sha384Suites.append(TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384)  # unsupported
+    sha384Suites.append(TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384)  # unsupported
     sha384Suites.append(TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384)
-    sha384Suites.append(TLS_DHE_DSS_WITH_AES_256_GCM_SHA384)
-    sha384Suites.append(TLS_DH_DSS_WITH_AES_256_GCM_SHA384)
+    sha384Suites.append(TLS_DHE_DSS_WITH_AES_256_GCM_SHA384)    # unsupported
+    sha384Suites.append(TLS_DH_DSS_WITH_AES_256_GCM_SHA384) # unsupported
+
+    #: stream cipher construction
     streamSuites = []
     streamSuites.extend(rc4Suites)
     streamSuites.extend(nullSuites)
+
+    #: AEAD integrity, any PRF
     aeadSuites = []
     aeadSuites.extend(aes128GcmSuites)
     aeadSuites.extend(aes256GcmSuites)
@@ -953,25 +1205,41 @@ class CipherSuite:
     aeadSuites.extend(aes256Ccm_8Suites)
     aeadSuites.extend(chacha20Suites)
     aeadSuites.extend(chacha20draft00Suites)
+
+    #: any with SHA384 PRF
     sha384PrfSuites = []
     sha384PrfSuites.extend(sha384Suites)
     sha384PrfSuites.extend(aes256GcmSuites)
+
+    #: MD-5 HMAC, protocol default PRF
     md5Suites = []
     md5Suites.append(TLS_DH_ANON_WITH_RC4_128_MD5)
     md5Suites.append(TLS_RSA_WITH_RC4_128_MD5)
     md5Suites.append(TLS_RSA_WITH_NULL_MD5)
+
+    #: SSL3, TLS1.0, TLS1.1 and TLS1.2 compatible ciphers
     ssl3Suites = []
     ssl3Suites.extend(shaSuites)
     ssl3Suites.extend(md5Suites)
+
+    #: TLS1.2 specific ciphersuites
     tls12Suites = []
     tls12Suites.extend(sha256Suites)
     tls12Suites.extend(sha384Suites)
     tls12Suites.extend(aeadSuites)
+
+    #: any that will end up using SHA256 PRF in TLS 1.2 or later
     sha256PrfSuites = []
     sha256PrfSuites.extend(tls12Suites)
     for i in sha384PrfSuites:
         sha256PrfSuites.remove(i)
+
+    #: TLS1.3 specific ciphersuites
     tls13Suites = []
+
+    # TLS 1.3 suites are not a superset of TLS 1.2 suites, but they
+    # use the same mechanism (AEAD), so we need to remove TLS 1.3 items
+    # from the TLS 1.2 list
     tls13Suites.append(TLS_AES_256_GCM_SHA384)
     tls12Suites.remove(TLS_AES_256_GCM_SHA384)
     tls13Suites.append(TLS_AES_128_GCM_SHA256)
@@ -986,24 +1254,134 @@ class CipherSuite:
     @staticmethod
     def filterForVersion(suites, minVersion, maxVersion):
         """Return a copy of suites without ciphers incompatible with version"""
-        pass
+        includeSuites = set([])
+        if (3, 0) <= minVersion <= (3, 3):
+            includeSuites.update(CipherSuite.ssl3Suites)
+        if maxVersion >= (3, 3) and minVersion <= (3, 3):
+            includeSuites.update(CipherSuite.tls12Suites)
+        if maxVersion > (3, 3):
+            includeSuites.update(CipherSuite.tls13Suites)
+        return [s for s in suites if s in includeSuites]

     @staticmethod
     def filter_for_certificate(suites, cert_chain):
         """Return a copy of suites without ciphers incompatible with the cert.
         """
-        pass
+        includeSuites = set([])
+        includeSuites.update(CipherSuite.tls13Suites)
+        if cert_chain:
+            if cert_chain.x509List[0].certAlg in ("rsa", "rsa-pss"):
+                includeSuites.update(CipherSuite.certAllSuites)
+            if cert_chain.x509List[0].certAlg == "rsa-pss":
+                # suites in which RSA encryption is used can't be used with
+                # rsa-pss
+                includeSuites.symmetric_difference_update(
+                    CipherSuite.certSuites)
+            if cert_chain.x509List[0].certAlg in ("ecdsa", "Ed25519", "Ed448"):
+                includeSuites.update(CipherSuite.ecdheEcdsaSuites)
+            if cert_chain.x509List[0].certAlg == "dsa":
+                includeSuites.update(CipherSuite.dheDsaSuites)
+        else:
+            includeSuites.update(CipherSuite.srpSuites)
+            includeSuites.update(CipherSuite.anonSuites)
+            includeSuites.update(CipherSuite.ecdhAnonSuites)
+        return [s for s in suites if s in includeSuites]

     @staticmethod
     def filter_for_prfs(suites, prfs):
         """Return a copy of suites without ciphers incompatible with the
         specified prfs (sha256 or sha384)"""
-        pass
+        includeSuites = set()
+        prfs = set(prfs)
+        if None in prfs:
+            prfs.update(["sha256"])
+            prfs.remove(None)
+        assert len(prfs) <= 2, prfs
+
+        if "sha256" in prfs:
+            includeSuites.update(CipherSuite.sha256PrfSuites)
+        if "sha384" in prfs:
+            includeSuites.update(CipherSuite.sha384PrfSuites)
+        return [s for s in suites if s in includeSuites]
+
+    @staticmethod
+    def _filterSuites(suites, settings, version=None):
+        if version is None:
+            version = settings.maxVersion
+        macNames = settings.macNames
+        cipherNames = settings.cipherNames
+        keyExchangeNames = settings.keyExchangeNames
+        macSuites = []
+        if "sha" in macNames:
+            macSuites += CipherSuite.shaSuites
+        if "sha256" in macNames and version >= (3, 3):
+            macSuites += CipherSuite.sha256Suites
+        if "sha384" in macNames and version >= (3, 3):
+            macSuites += CipherSuite.sha384Suites
+        if "md5" in macNames:
+            macSuites += CipherSuite.md5Suites
+        if "aead" in macNames and version >= (3, 3):
+            macSuites += CipherSuite.aeadSuites
+
+        cipherSuites = []
+        if "chacha20-poly1305" in cipherNames and version >= (3, 3):
+            cipherSuites += CipherSuite.chacha20Suites
+        if "chacha20-poly1305_draft00" in cipherNames and version >= (3, 3):
+            cipherSuites += CipherSuite.chacha20draft00Suites
+        if "aes128gcm" in cipherNames and version >= (3, 3):
+            cipherSuites += CipherSuite.aes128GcmSuites
+        if "aes256gcm" in cipherNames and version >= (3, 3):
+            cipherSuites += CipherSuite.aes256GcmSuites
+        if "aes128ccm" in cipherNames and version >= (3, 3):
+            cipherSuites += CipherSuite.aes128CcmSuites
+        if "aes128ccm_8" in cipherNames and version >= (3, 3):
+            cipherSuites += CipherSuite.aes128Ccm_8Suites
+        if "aes256ccm" in cipherNames and version >= (3, 3):
+            cipherSuites += CipherSuite.aes256CcmSuites
+        if "aes256ccm_8" in cipherNames and version >= (3, 3):
+            cipherSuites += CipherSuite.aes256Ccm_8Suites
+        if "aes128" in cipherNames:
+            cipherSuites += CipherSuite.aes128Suites
+        if "aes256" in cipherNames:
+            cipherSuites += CipherSuite.aes256Suites
+        if "3des" in cipherNames:
+            cipherSuites += CipherSuite.tripleDESSuites
+        if "rc4" in cipherNames:
+            cipherSuites += CipherSuite.rc4Suites
+        if "null" in cipherNames:
+            cipherSuites += CipherSuite.nullSuites
+
+        keyExchangeSuites = []
+        if version >= (3, 4):
+            keyExchangeSuites += CipherSuite.tls13Suites
+        if "rsa" in keyExchangeNames:
+            keyExchangeSuites += CipherSuite.certSuites
+        if "dhe_rsa" in keyExchangeNames:
+            keyExchangeSuites += CipherSuite.dheCertSuites
+        if "dhe_dsa" in keyExchangeNames:
+            keyExchangeSuites += CipherSuite.dheDsaSuites
+        if "ecdhe_rsa" in keyExchangeNames:
+            keyExchangeSuites += CipherSuite.ecdheCertSuites
+        if "ecdhe_ecdsa" in keyExchangeNames:
+            keyExchangeSuites += CipherSuite.ecdheEcdsaSuites
+        if "srp_sha" in keyExchangeNames:
+            keyExchangeSuites += CipherSuite.srpSuites
+        if "srp_sha_rsa" in keyExchangeNames:
+            keyExchangeSuites += CipherSuite.srpCertSuites
+        if "dh_anon" in keyExchangeNames:
+            keyExchangeSuites += CipherSuite.anonSuites
+        if "ecdh_anon" in keyExchangeNames:
+            keyExchangeSuites += CipherSuite.ecdhAnonSuites
+
+        return [s for s in suites if s in macSuites and
+                s in cipherSuites and s in keyExchangeSuites]

     @classmethod
     def getTLS13Suites(cls, settings, version=None):
         """Return cipher suites that are TLS 1.3 specific."""
-        pass
+        return cls._filterSuites(CipherSuite.tls13Suites, settings, version)
+
+    #: SRP key exchange, no certificate base authentication
     srpSuites = []
     srpSuites.append(TLS_SRP_SHA_WITH_AES_256_CBC_SHA)
     srpSuites.append(TLS_SRP_SHA_WITH_AES_128_CBC_SHA)
@@ -1012,7 +1390,9 @@ class CipherSuite:
     @classmethod
     def getSrpSuites(cls, settings, version=None):
         """Return SRP cipher suites matching settings"""
-        pass
+        return cls._filterSuites(CipherSuite.srpSuites, settings, version)
+
+    #: SRP key exchange, RSA authentication
     srpCertSuites = []
     srpCertSuites.append(TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA)
     srpCertSuites.append(TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA)
@@ -1021,22 +1401,28 @@ class CipherSuite:
     @classmethod
     def getSrpCertSuites(cls, settings, version=None):
         """Return SRP cipher suites that use server certificates"""
-        pass
+        return cls._filterSuites(CipherSuite.srpCertSuites, settings, version)
+
+    #: SRP key exchange, DSA authentication
     srpDsaSuites = []
-    srpDsaSuites.append(TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA)
-    srpDsaSuites.append(TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA)
-    srpDsaSuites.append(TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA)
+    srpDsaSuites.append(TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA) # unsupported
+    srpDsaSuites.append(TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA)  # unsupported
+    srpDsaSuites.append(TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA)  # unsupported

     @classmethod
     def getSrpDsaSuites(cls, settings, version=None):
         """Return SRP DSA cipher suites that use server certificates"""
-        pass
+        return cls._filterSuites(CipherSuite.srpCertSuites, settings, version)
+
+    #: All that use SRP key exchange
     srpAllSuites = srpSuites + srpCertSuites

     @classmethod
     def getSrpAllSuites(cls, settings, version=None):
         """Return all SRP cipher suites matching settings"""
-        pass
+        return cls._filterSuites(CipherSuite.srpAllSuites, settings, version)
+
+    #: RSA key exchange, RSA authentication
     certSuites = []
     certSuites.append(TLS_RSA_WITH_AES_256_GCM_SHA384)
     certSuites.append(TLS_RSA_WITH_AES_128_GCM_SHA256)
@@ -1058,7 +1444,9 @@ class CipherSuite:
     @classmethod
     def getCertSuites(cls, settings, version=None):
         """Return ciphers with RSA authentication matching settings"""
-        pass
+        return cls._filterSuites(CipherSuite.certSuites, settings, version)
+
+    #: FFDHE key exchange, RSA authentication
     dheCertSuites = []
     dheCertSuites.append(TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256)
     dheCertSuites.append(TLS_DHE_RSA_WITH_CHACHA20_POLY1305_draft_00)
@@ -1077,7 +1465,9 @@ class CipherSuite:
     @classmethod
     def getDheCertSuites(cls, settings, version=None):
         """Provide authenticated DHE ciphersuites matching settings"""
-        pass
+        return cls._filterSuites(CipherSuite.dheCertSuites, settings, version)
+
+    #: ECDHE key exchange, RSA authentication
     ecdheCertSuites = []
     ecdheCertSuites.append(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256)
     ecdheCertSuites.append(TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_draft_00)
@@ -1094,9 +1484,12 @@ class CipherSuite:
     @classmethod
     def getEcdheCertSuites(cls, settings, version=None):
         """Provide authenticated ECDHE ciphersuites matching settings"""
-        pass
-    certAllSuites = (srpCertSuites + certSuites + dheCertSuites +
-        ecdheCertSuites)
+        return cls._filterSuites(CipherSuite.ecdheCertSuites, settings, version)
+
+    #: RSA authentication
+    certAllSuites = srpCertSuites + certSuites + dheCertSuites + ecdheCertSuites
+
+    #: ECDHE key exchange, ECDSA authentication
     ecdheEcdsaSuites = []
     ecdheEcdsaSuites.append(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256)
     ecdheEcdsaSuites.append(TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_draft_00)
@@ -1117,7 +1510,10 @@ class CipherSuite:
     @classmethod
     def getEcdsaSuites(cls, settings, version=None):
         """Provide ECDSA authenticated ciphersuites matching settings"""
-        pass
+        return cls._filterSuites(CipherSuite.ecdheEcdsaSuites,
+                                 settings, version)
+
+    #: DHE key exchange, DSA authentication
     dheDsaSuites = []
     dheDsaSuites.append(TLS_DHE_DSS_WITH_AES_256_GCM_SHA384)
     dheDsaSuites.append(TLS_DHE_DSS_WITH_AES_128_GCM_SHA256)
@@ -1130,7 +1526,10 @@ class CipherSuite:
     @classmethod
     def getDheDsaSuites(cls, settings, version=None):
         """Provide DSA authenticated ciphersuites matching settings"""
-        pass
+        return cls._filterSuites(CipherSuite.dheDsaSuites,
+                                 settings, version)
+
+    #: anon FFDHE key exchange
     anonSuites = []
     anonSuites.append(TLS_DH_ANON_WITH_AES_256_GCM_SHA384)
     anonSuites.append(TLS_DH_ANON_WITH_AES_128_GCM_SHA256)
@@ -1144,8 +1543,11 @@ class CipherSuite:
     @classmethod
     def getAnonSuites(cls, settings, version=None):
         """Provide anonymous DH ciphersuites matching settings"""
-        pass
+        return cls._filterSuites(CipherSuite.anonSuites, settings, version)
+
     dhAllSuites = dheCertSuites + anonSuites + dheDsaSuites
+
+    #: anon ECDHE key exchange
     ecdhAnonSuites = []
     ecdhAnonSuites.append(TLS_ECDH_ANON_WITH_AES_256_CBC_SHA)
     ecdhAnonSuites.append(TLS_ECDH_ANON_WITH_AES_128_CBC_SHA)
@@ -1156,46 +1558,103 @@ class CipherSuite:
     @classmethod
     def getEcdhAnonSuites(cls, settings, version=None):
         """Provide anonymous ECDH ciphersuites matching settings"""
-        pass
+        return cls._filterSuites(CipherSuite.ecdhAnonSuites, settings, version)
+
+    #: all ciphersuites which use ephemeral ECDH key exchange
     ecdhAllSuites = ecdheEcdsaSuites + ecdheCertSuites + ecdhAnonSuites

     @staticmethod
     def canonicalCipherName(ciphersuite):
         """Return the canonical name of the cipher whose number is provided."""
-        pass
+        if ciphersuite in CipherSuite.aes128GcmSuites:
+            return "aes128gcm"
+        elif ciphersuite in CipherSuite.aes256GcmSuites:
+            return "aes256gcm"
+        elif ciphersuite in CipherSuite.aes128Ccm_8Suites:
+            return "aes128ccm_8"
+        elif ciphersuite in CipherSuite.aes128CcmSuites:
+            return "aes128ccm"
+        elif ciphersuite in CipherSuite.aes256CcmSuites:
+            return "aes256ccm"
+        elif ciphersuite in CipherSuite.aes256Ccm_8Suites:
+            return "aes256ccm_8"
+        elif ciphersuite in CipherSuite.aes128Suites:
+            return "aes128"
+        elif ciphersuite in CipherSuite.aes256Suites:
+            return "aes256"
+        elif ciphersuite in CipherSuite.rc4Suites:
+            return "rc4"
+        elif ciphersuite in CipherSuite.tripleDESSuites:
+            return "3des"
+        elif ciphersuite in CipherSuite.nullSuites:
+            return "null"
+        elif ciphersuite in CipherSuite.chacha20draft00Suites:
+            return "chacha20-poly1305_draft00"
+        elif ciphersuite in CipherSuite.chacha20Suites:
+            return "chacha20-poly1305"
+        else:
+            return None

     @staticmethod
     def canonicalMacName(ciphersuite):
         """Return the canonical name of the MAC whose number is provided."""
-        pass
-
-
+        if ciphersuite in CipherSuite.sha384Suites:
+            return "sha384"
+        elif ciphersuite in CipherSuite.sha256Suites:
+            return "sha256"
+        elif ciphersuite in CipherSuite.shaSuites:
+            return "sha"
+        elif ciphersuite in CipherSuite.md5Suites:
+            return "md5"
+        else:
+            return None
+
+
+# The following faults are induced as part of testing.  The faultAlerts
+# dictionary describes the allowed alerts that may be triggered by these
+# faults.
 class Fault:
     badUsername = 101
     badPassword = 102
     badA = 103
-    clientSrpFaults = list(range(101, 104))
+    clientSrpFaults = list(range(101,104))
+
     badVerifyMessage = 601
-    clientCertFaults = list(range(601, 602))
+    clientCertFaults = list(range(601,602))
+
     badPremasterPadding = 501
     shortPremasterSecret = 502
-    clientNoAuthFaults = list(range(501, 503))
+    clientNoAuthFaults = list(range(501,503))
+
     badB = 201
-    serverFaults = list(range(201, 202))
+    serverFaults = list(range(201,202))
+
     badFinished = 300
     badMAC = 301
     badPadding = 302
-    genericFaults = list(range(300, 303))
-    faultAlerts = {badUsername: (AlertDescription.unknown_psk_identity,
-        AlertDescription.bad_record_mac), badPassword: (AlertDescription.
-        bad_record_mac,), badA: (AlertDescription.illegal_parameter,),
-        badPremasterPadding: (AlertDescription.bad_record_mac,),
-        shortPremasterSecret: (AlertDescription.bad_record_mac,),
-        badVerifyMessage: (AlertDescription.decrypt_error,), badFinished: (
-        AlertDescription.decrypt_error,), badMAC: (AlertDescription.
-        bad_record_mac,), badPadding: (AlertDescription.bad_record_mac,)}
-    faultNames = {badUsername: 'bad username', badPassword: 'bad password',
-        badA: 'bad A', badPremasterPadding: 'bad premaster padding',
-        shortPremasterSecret: 'short premaster secret', badVerifyMessage:
-        'bad verify message', badFinished: 'bad finished message', badMAC:
-        'bad MAC', badPadding: 'bad padding'}
+    genericFaults = list(range(300,303))
+
+    faultAlerts = {\
+        badUsername: (AlertDescription.unknown_psk_identity, \
+                      AlertDescription.bad_record_mac),\
+        badPassword: (AlertDescription.bad_record_mac,),\
+        badA: (AlertDescription.illegal_parameter,),\
+        badPremasterPadding: (AlertDescription.bad_record_mac,),\
+        shortPremasterSecret: (AlertDescription.bad_record_mac,),\
+        badVerifyMessage: (AlertDescription.decrypt_error,),\
+        badFinished: (AlertDescription.decrypt_error,),\
+        badMAC: (AlertDescription.bad_record_mac,),\
+        badPadding: (AlertDescription.bad_record_mac,)
+        }
+
+    faultNames = {\
+        badUsername: "bad username",\
+        badPassword: "bad password",\
+        badA: "bad A",\
+        badPremasterPadding: "bad premaster padding",\
+        shortPremasterSecret: "short premaster secret",\
+        badVerifyMessage: "bad verify message",\
+        badFinished: "bad finished message",\
+        badMAC: "bad MAC",\
+        badPadding: "bad padding"
+        }
diff --git a/tlslite/defragmenter.py b/tlslite/defragmenter.py
index 3a1038b..84da895 100644
--- a/tlslite/defragmenter.py
+++ b/tlslite/defragmenter.py
@@ -1,12 +1,20 @@
+# Copyright (c) 2015, Hubert Kario
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """Helper package for handling fragmentation of messages."""
+
 from __future__ import generators
+
 from .utils.codec import Parser
 from .utils.deprecations import deprecated_attrs, deprecated_params


-@deprecated_attrs({'add_static_size': 'addStaticSize', 'add_dynamic_size':
-    'addDynamicSize', 'add_data': 'addData', 'get_message': 'getMessage',
-    'clear_buffers': 'clearBuffers'})
+@deprecated_attrs({"add_static_size": "addStaticSize",
+                   "add_dynamic_size": "addDynamicSize",
+                   "add_data": "addData",
+                   "get_message": "getMessage",
+                   "clear_buffers": "clearBuffers"})
 class Defragmenter(object):
     """
     Class for demultiplexing TLS messages.
@@ -30,30 +38,95 @@ class Defragmenter(object):
         self.buffers = {}
         self.decoders = {}

-    @deprecated_params({'msg_type': 'msgType'})
+    @deprecated_params({"msg_type": "msgType"})
     def add_static_size(self, msg_type, size):
         """Add a message type which all messages are of same length"""
-        pass
+        if msg_type in self.priorities:
+            raise ValueError("Message type already defined")
+        if size < 1:
+            raise ValueError("Message size must be positive integer")
+
+        self.priorities += [msg_type]

-    @deprecated_params({'msg_type': 'msgType', 'size_offset': 'sizeOffset',
-        'size_of_size': 'sizeOfSize'})
+        self.buffers[msg_type] = bytearray(0)
+        def size_handler(data):
+            """
+            Size of message in parameter
+
+            If complete message is present in parameter returns its size,
+            None otherwise.
+            """
+            if len(data) < size:
+                return None
+            else:
+                return size
+        self.decoders[msg_type] = size_handler
+
+    @deprecated_params({"msg_type": "msgType",
+                        "size_offset": "sizeOffset",
+                        "size_of_size": "sizeOfSize"})
     def add_dynamic_size(self, msg_type, size_offset, size_of_size):
         """Add a message type which has a dynamic size set in a header"""
-        pass
+        if msg_type in self.priorities:
+            raise ValueError("Message type already defined")
+        if size_of_size < 1:
+            raise ValueError("Size of size must be positive integer")
+        if size_offset < 0:
+            raise ValueError("Offset can't be negative")
+
+        self.priorities += [msg_type]
+        self.buffers[msg_type] = bytearray(0)

-    @deprecated_params({'msg_type': 'msgType'})
+        def size_handler(data):
+            """
+            Size of message in parameter
+
+            If complete message is present in parameter returns its size,
+            None otherwise.
+            """
+            if len(data) < size_offset+size_of_size:
+                return None
+            else:
+                parser = Parser(data)
+                # skip the header
+                parser.skip_bytes(size_offset)
+
+                payload_length = parser.get(size_of_size)
+                if parser.getRemainingLength() < payload_length:
+                    # not enough bytes in buffer
+                    return None
+                return size_offset + size_of_size + payload_length
+
+        self.decoders[msg_type] = size_handler
+
+    @deprecated_params({"msg_type": "msgType"})
     def add_data(self, msg_type, data):
         """Adds data to buffers"""
-        pass
+        if msg_type not in self.priorities:
+            raise ValueError("Message type not defined")
+
+        self.buffers[msg_type] += data

     def get_message(self):
         """Extract the highest priority complete message from buffer"""
-        pass
+        for msg_type in self.priorities:
+            buf = self.buffers[msg_type]
+            length = self.decoders[msg_type](buf)
+            if length is None:
+                continue
+
+            # extract message
+            data = buf[:length]
+            # remove it from buffer
+            del buf[:length]
+            return (msg_type, data)
+        return None

     def clear_buffers(self):
         """Remove all data from buffers"""
-        pass
+        for key in self.buffers.keys():
+            self.buffers[key] = bytearray(0)

     def is_empty(self):
         """Return True if all buffers are empty."""
-        pass
+        return all(not i for i in self.buffers.values())
diff --git a/tlslite/dh.py b/tlslite/dh.py
index a8cab41..922d1ee 100644
--- a/tlslite/dh.py
+++ b/tlslite/dh.py
@@ -1,4 +1,8 @@
+# Author:
+#    Hubert Kario
+
 """Handling of Diffie-Hellman parameter files."""
+
 from .utils.asn1parser import ASN1Parser
 from .utils.pem import dePem
 from .utils.cryptomath import bytesToNumber
@@ -11,7 +15,12 @@ def parseBinary(data):
     :param bytes data: DH parameters
     :rtype: tuple of int
     """
-    pass
+    parser = ASN1Parser(data)
+
+    prime = parser.getChild(0)
+    gen = parser.getChild(1)
+
+    return (bytesToNumber(gen.value), bytesToNumber(prime.value))


 def parse(data):
@@ -24,4 +33,10 @@ def parse(data):
     :rtype: tuple of int
     :returns: generator and prime
     """
-    pass
+    try:
+        return parseBinary(data)
+    except (SyntaxError, TypeError):
+        pass
+
+    binData = dePem(data, "DH PARAMETERS")
+    return parseBinary(binData)
diff --git a/tlslite/errors.py b/tlslite/errors.py
index 31ddafa..b91200f 100644
--- a/tlslite/errors.py
+++ b/tlslite/errors.py
@@ -1,7 +1,13 @@
+# Authors: 
+#   Trevor Perrin
+#   Dave Baggett (Arcode Corporation) - Added TLSUnsupportedError.
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """Exception classes."""
 import socket
-from .constants import AlertDescription, AlertLevel

+from .constants import AlertDescription, AlertLevel

 class BaseTLSException(Exception):
     """
@@ -11,11 +17,13 @@ class BaseTLSException(Exception):
     caught by tlslite
     consumers
     """
+
     pass


 class EncryptionError(BaseTLSException):
     """Base class for exceptions thrown while encrypting."""
+
     pass


@@ -29,6 +37,7 @@ class TLSError(BaseTLSException):

 class TLSClosedConnectionError(TLSError, socket.error):
     """An attempt was made to use the connection after it was closed."""
+
     pass


@@ -41,11 +50,13 @@ class TLSAbruptCloseError(TLSError):
     to truncate the connection.  It could also signify a misbehaving
     TLS implementation, or a random network failure.
     """
+
     pass


 class TLSAlert(TLSError):
     """A TLS alert has been signalled."""
+
     pass


@@ -72,7 +83,7 @@ class TLSLocalAlert(TLSAlert):
     def __str__(self):
         alertStr = AlertDescription.toStr(self.description)
         if self.message:
-            return alertStr + ': ' + self.message
+            return alertStr + ": " + self.message
         else:
             return alertStr

@@ -111,30 +122,35 @@ class TLSAuthenticationError(TLSError):
     the Checker objects to how the other party authenticated, a
     subclass of this exception will be raised.
     """
+
     pass


 class TLSNoAuthenticationError(TLSAuthenticationError):
     """The Checker was expecting the other party to authenticate with a
     certificate chain, but this did not occur."""
+
     pass


 class TLSAuthenticationTypeError(TLSAuthenticationError):
     """The Checker was expecting the other party to authenticate with a
     different type of certificate chain."""
+
     pass


 class TLSFingerprintError(TLSAuthenticationError):
     """The Checker was expecting the other party to authenticate with a
     certificate chain that matches a different fingerprint."""
+
     pass


 class TLSAuthorizationError(TLSAuthenticationError):
     """The Checker was expecting the other party to authenticate with a
     certificate chain that has a different authorization."""
+
     pass


@@ -143,6 +159,7 @@ class TLSValidationError(TLSAuthenticationError):
     chain is invalid."""

     def __init__(self, msg, info=None):
+        # Include a dict containing info about this validation failure
         TLSAuthenticationError.__init__(self, msg)
         self.info = info

@@ -155,12 +172,14 @@ class TLSFaultError(TLSError):
     set to induce some sort of
     faulty behavior, and the other party doesn't respond appropriately.
     """
+
     pass


 class TLSUnsupportedError(TLSError):
     """The implementation doesn't support the requested (or required)
     capabilities."""
+
     pass


@@ -169,21 +188,25 @@ class TLSInternalError(TLSError):

     Caused by incorrect use of API.
     """
+
     pass


 class TLSProtocolException(BaseTLSException):
     """Exceptions used internally for handling errors in received messages"""
+
     pass


 class TLSIllegalParameterException(TLSProtocolException):
     """Parameters specified in message were incorrect or invalid"""
+
     pass


 class TLSDecodeError(TLSProtocolException):
     """The received message encoding does not match specification."""
+
     pass


@@ -192,59 +215,71 @@ class TLSUnexpectedMessage(TLSProtocolException):
     The received message was unexpected or parsing of Inner Plaintext
     failed
     """
+
     pass


 class TLSRecordOverflow(TLSProtocolException):
     """The received record size was too big"""
+
     pass


 class TLSDecryptionFailed(TLSProtocolException):
     """Decryption of data was unsuccessful"""
+
     pass


 class TLSBadRecordMAC(TLSProtocolException):
     """Bad MAC (or padding in case of mac-then-encrypt)"""
+
     pass


 class TLSInsufficientSecurity(TLSProtocolException):
     """Parameters selected by user are too weak"""
+
     pass


 class TLSUnknownPSKIdentity(TLSProtocolException):
     """The PSK or SRP identity is unknown"""
+
     pass


 class TLSHandshakeFailure(TLSProtocolException):
     """Could not find acceptable set of handshake parameters"""
+
     pass


 class MaskTooLongError(EncryptionError):
     """The maskLen passed into function is too high"""
+
     pass


 class MessageTooLongError(EncryptionError):
     """The message passed into function is too long"""
+
     pass


 class EncodingError(EncryptionError):
     """An error appeared while encoding"""
+
     pass


 class InvalidSignature(EncryptionError):
     """Verification function found invalid signature"""
+
     pass


 class UnknownRSAType(EncryptionError):
     """Unknown RSA algorithm type passed"""
+
     pass
diff --git a/tlslite/extensions.py b/tlslite/extensions.py
index 0f877f7..7d72ce1 100644
--- a/tlslite/extensions.py
+++ b/tlslite/extensions.py
@@ -1,10 +1,18 @@
+# Copyright (c) 2014, 2015 Hubert Kario
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """ Helper package for handling TLS extensions encountered in ClientHello
 and ServerHello messages.
 """
+
 from __future__ import generators
 from collections import namedtuple
 from .utils.codec import Writer, Parser, DecodeError
-from .constants import NameType, ExtensionType, CertificateStatusType, SignatureAlgorithm, HashAlgorithm, SignatureScheme, PskKeyExchangeMode, CertificateType, GroupName, ECPointFormat, HeartbeatMode
+from .constants import NameType, ExtensionType, CertificateStatusType, \
+        SignatureAlgorithm, HashAlgorithm, SignatureScheme, \
+        PskKeyExchangeMode, CertificateType, GroupName, ECPointFormat, \
+        HeartbeatMode
 from .errors import TLSInternalError


@@ -81,13 +89,15 @@ class TLSExtension(object):
         and the value is the class. Includes only those extensions that require
         special handlers for the Hello Retry Request message.
     """
+    # actual definition at the end of file, after definitions of all classes
     _universalExtensions = {}
     _serverExtensions = {}
+    # _encryptedExtensions = {}
     _certificateExtensions = {}
     _hrrExtensions = {}

-    def __init__(self, server=False, extType=None, encExt=False, cert=False,
-        hrr=False):
+    def __init__(self, server=False, extType=None, encExt=False,
+                 cert=False, hrr=False):
         """
         Creates a generic TLS extension.

@@ -126,15 +136,16 @@ class TLSExtension(object):

         :rtype: bytearray
         """
-        pass
+        return self._extData

     def _oldCreate(self, extType, data):
         """Legacy handling of create method"""
-        pass
+        self.extType = extType
+        self._extData = data

     def _newCreate(self, data):
         """New format for create method"""
-        pass
+        self._extData = data

     def create(self, *args, **kwargs):
         """
@@ -156,7 +167,16 @@ class TLSExtension(object):
         :param bytearray data: raw data representing extension on the wire
         :rtype: TLSExtension
         """
-        pass
+        # old style
+        if len(args) + len(kwargs) == 2:
+            self._oldCreate(*args, **kwargs)
+        # new style
+        elif len(args) + len(kwargs) == 1:
+            self._newCreate(*args, **kwargs)
+        else:
+            raise TypeError("Invalid number of arguments")
+
+        return self

     def write(self):
         """Returns encoded extension, as encoded on the wire
@@ -170,12 +190,22 @@ class TLSExtension(object):

         :raises AssertionError: when the object was not initialized
         """
-        pass
+        assert self.extType is not None
+
+        w = Writer()
+        w.addTwo(self.extType)
+        data = self.extData
+        w.addTwo(len(data))
+        w.bytes += data
+        return w.bytes

     @staticmethod
     def _parseExt(parser, extType, extLength, extList):
         """Parse a extension using a predefined constructor"""
-        pass
+        ext = extList[extType]()
+        extParser = Parser(parser.getFixBytes(extLength))
+        ext = ext.parse(extParser)
+        return ext

     def parse(self, p):
         """Parses extension from on the wire format
@@ -193,7 +223,31 @@ class TLSExtension(object):

         :rtype: TLSExtension
         """
-        pass
+        extType = p.get(2)
+        try:
+            extLength = p.get(2)
+
+            for handler_t, handlers in (
+                    (self.cert, self._certificateExtensions),
+                    # (self.encExtType, self._encryptedExtensions),
+                    (self.serverType, self._serverExtensions),
+                    (self.hrr, self._hrrExtensions),
+                    (True, self._universalExtensions)):
+                if handler_t and extType in handlers:
+                    return self._parseExt(p, extType, extLength, handlers)
+
+            # if there is no custom handler for the extension, just save the
+            # extension data as there are extensions which
+            # don't require specific handlers and indicate option by mere presence
+            # also, we need to be able to handle unrecognised extensions in
+            # ClientHello and CertificateRequest
+            self.extType = extType
+            self._extData = p.getFixBytes(extLength)
+            assert len(self._extData) == extLength
+        except DecodeError as exc:
+            raise DecodeError("Malformed extension (type: {0}): {1}".format(
+                ExtensionType.toStr(extType), str(exc)))
+        return self

     def __eq__(self, that):
         """Test if two TLS extensions are effectively the same
@@ -204,8 +258,8 @@ class TLSExtension(object):
         Will return False for every object that's not an extension.
         """
         if hasattr(that, 'extType') and hasattr(that, 'extData'):
-            return (self.extType == that.extType and self.extData == that.
-                extData)
+            return self.extType == that.extType and \
+                    self.extData == that.extData
         return False

     def __repr__(self):
@@ -216,10 +270,11 @@ class TLSExtension(object):

         :rtype: str
         """
-        return (
-            'TLSExtension(extType={0!r}, extData={1!r}, serverType={2!r}, encExtType={3!r})'
-            .format(self.extType, self.extData, self.serverType, self.
-            encExtType))
+        return "TLSExtension(extType={0!r}, extData={1!r},"\
+               " serverType={2!r}, encExtType={3!r})".format(self.extType,
+                                                             self.extData,
+                                                             self.serverType,
+                                                             self.encExtType)


 class CustomNameExtension(TLSExtension):
@@ -231,7 +286,6 @@ class CustomNameExtension(TLSExtension):

     Must be subclassed.
     """
-
     def __init__(self, field_name, extType):
         """
         Create instance of the class.
@@ -250,7 +304,7 @@ class CustomNameExtension(TLSExtension):

         :rtype: bytearray
         """
-        pass
+        raise NotImplementedError("Abstract class")

     def create(self, values):
         """
@@ -258,7 +312,8 @@ class CustomNameExtension(TLSExtension):

         :param list values: list of values to save
         """
-        pass
+        self._internal_value = values
+        return self

     def parse(self, parser):
         """
@@ -267,17 +322,19 @@ class CustomNameExtension(TLSExtension):
         :param tlslite.utils.codec.Parser parser: data
         :rtype: Extension
         """
-        pass
+        raise NotImplementedError("Abstract class")

     def __getattr__(self, name):
         """Return the special field name value."""
         if name == '_field_name':
-            raise AttributeError("type object '{0}' has no attribute '{1}'"
+            raise AttributeError(
+                "type object '{0}' has no attribute '{1}'"
                 .format(self.__class__.__name__, name))
         if name == self._field_name:
             return self._internal_value
-        raise AttributeError("type object '{0}' has no attribute '{1}'".
-            format(self.__class__.__name__, name))
+        raise AttributeError(
+            "type object '{0}' has no attribute '{1}'"
+            .format(self.__class__.__name__, name))

     def __setattr__(self, name, value):
         """Set the special field value."""
@@ -315,7 +372,12 @@ class VarBytesExtension(CustomNameExtension):

         :rtype: bytearray
         """
-        pass
+        if self._internal_value is None:
+            return bytearray(0)
+
+        writer = Writer()
+        writer.add_var_bytes(self._internal_value, self._length_length)
+        return writer.bytes

     def parse(self, parser):
         """Deserialise extension from on-the-wire data.
@@ -323,15 +385,25 @@ class VarBytesExtension(CustomNameExtension):
         :param tlslite.utils.codec.Parser parser: data
         :rtype: TLSExtension
         """
-        pass
+        if not parser.getRemainingLength():
+            self._internal_value = None
+            return self
+
+        self._internal_value = parser.getVarBytes(self._length_length)
+
+        if parser.getRemainingLength():
+            raise DecodeError("Extra data after extension payload")
+
+        return self

     def __repr__(self):
         """Return human readable representation of the extension."""
         if self._internal_value is not None:
-            return '{0}(len({1})={2})'.format(self.__class__.__name__, self
-                ._field_name, len(self._internal_value))
-        return '{0}({1}=None)'.format(self.__class__.__name__, self._field_name
-            )
+            return "{0}(len({1})={2})".format(self.__class__.__name__,
+                                              self._field_name,
+                                              len(self._internal_value))
+        return "{0}({1}=None)".format(self.__class__.__name__,
+                                      self._field_name)


 class ListExtension(CustomNameExtension):
@@ -357,12 +429,17 @@ class ListExtension(CustomNameExtension):

     def _list_to_repr(self):
         """Return human redable representation of the item list"""
-        pass
+        if not self._internal_value or not self._item_enum:
+            return "{0!r}".format(self._internal_value)
+
+        return "[{0}]".format(
+            ", ".join(self._item_enum.toStr(i) for i in self._internal_value))

     def __repr__(self):
         """Return human readable representation of the extension."""
-        return '{0}({1}={2})'.format(self.__class__.__name__, self.
-            _field_name, self._list_to_repr())
+        return "{0}({1}={2})".format(self.__class__.__name__,
+                                     self._field_name,
+                                     self._list_to_repr())


 class VarListExtension(ListExtension):
@@ -374,7 +451,7 @@ class VarListExtension(ListExtension):
     """

     def __init__(self, elemLength, lengthLength, fieldName, extType,
-        item_enum=None):
+                 item_enum=None):
         """Create handler for extension that has a list of items as payload.

         :param int elemLength: number of bytes needed to encode single element
@@ -384,7 +461,7 @@ class VarListExtension(ListExtension):
         :param class item_enum: TLSEnum class that defines entries in the list
         """
         super(VarListExtension, self).__init__(fieldName, extType=extType,
-            item_enum=item_enum)
+                                               item_enum=item_enum)
         self._elemLength = elemLength
         self._lengthLength = lengthLength

@@ -395,7 +472,14 @@ class VarListExtension(ListExtension):

         :rtype: bytearray
         """
-        pass
+        if self._internal_value is None:
+            return bytearray(0)
+
+        writer = Writer()
+        writer.addVarSeq(self._internal_value,
+                         self._elemLength,
+                         self._lengthLength)
+        return writer.bytes

     def parse(self, parser):
         """
@@ -404,7 +488,17 @@ class VarListExtension(ListExtension):
         :param tlslite.utils.codec.Parser parser: data
         :rtype: Extension
         """
-        pass
+        if parser.getRemainingLength() == 0:
+            self._internal_value = None
+            return self
+
+        self._internal_value = parser.getVarList(self._elemLength,
+                                                 self._lengthLength)
+
+        if parser.getRemainingLength():
+            raise DecodeError("Extra data after extension payload")
+
+        return self


 class VarSeqListExtension(ListExtension):
@@ -415,8 +509,8 @@ class VarSeqListExtension(ListExtension):
     of same-sized elements in same-sized tuples
     """

-    def __init__(self, elemLength, elemNum, lengthLength, fieldName,
-        extType, item_enum=None):
+    def __init__(self, elemLength, elemNum, lengthLength, fieldName, extType,
+                 item_enum=None):
         """
         Create a handler for extension that has a list of tuples as payload.

@@ -429,8 +523,8 @@ class VarSeqListExtension(ListExtension):
         :param int extType: numerical ID of the extension encoded
         :param class item_enum: TLSEnum class that defines entries in the list
         """
-        super(VarSeqListExtension, self).__init__(fieldName, extType=
-            extType, item_enum=item_enum)
+        super(VarSeqListExtension, self).__init__(fieldName, extType=extType,
+                                                  item_enum=item_enum)
         self._elemLength = elemLength
         self._elemNum = elemNum
         self._lengthLength = lengthLength
@@ -442,7 +536,14 @@ class VarSeqListExtension(ListExtension):

         :rtype: bytearray
         """
-        pass
+        if self._internal_value is None:
+            return bytearray(0)
+
+        writer = Writer()
+        writer.addVarTupleSeq(self._internal_value,
+                              self._elemLength,
+                              self._lengthLength)
+        return writer.bytes

     def parse(self, parser):
         """
@@ -451,7 +552,17 @@ class VarSeqListExtension(ListExtension):
         :param tlslite.utils.codec.Parser parser: data
         :rtype: Extension
         """
-        pass
+        if parser.getRemainingLength() == 0:
+            self._internal_value = None
+            return self
+
+        self._internal_value = parser.getVarTupleList(self._elemLength,
+                                                      self._elemNum,
+                                                      self._lengthLength)
+        if parser.getRemainingLength():
+            raise DecodeError("Extra data after extension payload")
+
+        return self


 class IntExtension(CustomNameExtension):
@@ -477,12 +588,15 @@ class IntExtension(CustomNameExtension):

     def _entry_to_repr(self):
         """Return human readable representation of the value."""
-        pass
+        if self._item_enum:
+            return self._item_enum.toStr(self._internal_value)
+        return str(self._internal_value)

     def __repr__(self):
         """Return human readable representation of the extension."""
-        return '{0}({1}={2})'.format(self.__class__.__name__, self.
-            _field_name, self._entry_to_repr())
+        return "{0}({1}={2})".format(self.__class__.__name__,
+                                     self._field_name,
+                                     self._entry_to_repr())

     @property
     def extData(self):
@@ -490,7 +604,12 @@ class IntExtension(CustomNameExtension):

         :rtype: bytearray
         """
-        pass
+        if self._internal_value is None:
+            return bytearray(0)
+
+        writer = Writer()
+        writer.add(self._internal_value, self._elem_length)
+        return writer.bytes

     def parse(self, parser):
         """
@@ -499,7 +618,16 @@ class IntExtension(CustomNameExtension):
         :param tlslite.utils.codec.Parser parser: data
         :rtype: Extension
         """
-        pass
+        if parser.getRemainingLength() == 0:
+            self._internal_value = None
+            return self
+
+        self._internal_value = parser.get(self._elem_length)
+
+        if parser.getRemainingLength():
+            raise DecodeError("Extra data after extension payload")
+
+        return self


 class SNIExtension(TLSExtension):
@@ -544,6 +672,7 @@ class SNIExtension(TLSExtension):
     :vartype ~.extData: bytearray
     :ivar ~.extData: raw representation of the extension
     """
+
     ServerName = namedtuple('ServerName', 'name_type name')

     def __init__(self):
@@ -561,7 +690,7 @@ class SNIExtension(TLSExtension):

         :rtype: str
         """
-        return 'SNIExtension(serverNames={0!r})'.format(self.serverNames)
+        return "SNIExtension(serverNames={0!r})".format(self.serverNames)

     def create(self, hostname=None, hostNames=None, serverNames=None):
         """
@@ -585,7 +714,25 @@ class SNIExtension(TLSExtension):

         :rtype: SNIExtension
         """
-        pass
+        if hostname is None and hostNames is None and serverNames is None:
+            self.serverNames = None
+            return self
+        else:
+            self.serverNames = []
+
+        if hostname:
+            self.serverNames += \
+                [SNIExtension.ServerName(NameType.host_name, hostname)]
+
+        if hostNames:
+            self.serverNames += \
+                [SNIExtension.ServerName(NameType.host_name, x) for x in
+                 hostNames]
+
+        if serverNames:
+            self.serverNames += serverNames
+
+        return self

     @property
     def hostNames(self):
@@ -593,7 +740,12 @@ class SNIExtension(TLSExtension):

         :rtype: tuple of bytearrays
         """
-        pass
+        # because we can't simulate assignments to array elements we return
+        # an immutable type
+        if self.serverNames is None:
+            return tuple()
+        return tuple([x.name for x in self.serverNames if
+                      x.name_type == NameType.host_name])

     @hostNames.setter
     def hostNames(self, hostNames):
@@ -606,7 +758,11 @@ class SNIExtension(TLSExtension):
         :param iterable hostNames: host names (bytearrays) to replace the
             old server names of type 0
         """
-        pass
+
+        self.serverNames = \
+            [SNIExtension.ServerName(NameType.host_name, x) for x in
+             hostNames] + \
+            [x for x in self.serverNames if x.name_type != NameType.host_name]

     @hostNames.deleter
     def hostNames(self):
@@ -614,7 +770,8 @@ class SNIExtension(TLSExtension):
         Remove all host names from extension, leaves other name types
         unmodified.
         """
-        pass
+        self.serverNames = [x for x in self.serverNames if
+                            x.name_type != NameType.host_name]

     @property
     def extData(self):
@@ -623,7 +780,20 @@ class SNIExtension(TLSExtension):

         :rtype: bytearray
         """
-        pass
+        if self.serverNames is None:
+            return bytearray(0)
+
+        w2 = Writer()
+        for server_name in self.serverNames:
+            w2.add(server_name.name_type, 1)
+            w2.add(len(server_name.name), 2)
+            w2.bytes += server_name.name
+
+        # note that when the array is empty we write it as array of length 0
+        w = Writer()
+        w.add(len(w2.bytes), 2)
+        w.bytes += w2.bytes
+        return w.bytes

     def write(self):
         """
@@ -634,7 +804,15 @@ class SNIExtension(TLSExtension):
             written
             on the wire, including the type, length and extension data
         """
-        pass
+
+        raw_data = self.extData
+
+        w = Writer()
+        w.add(self.extType, 2)
+        w.add(len(raw_data), 2)
+        w.bytes += raw_data
+
+        return w.bytes

     def parse(self, p):
         """
@@ -648,7 +826,22 @@ class SNIExtension(TLSExtension):
         :raises DecodeError: when the internal sizes don't match the attached
             data
         """
-        pass
+        if p.getRemainingLength() == 0:
+            return self
+
+        self.serverNames = []
+
+        p.startLengthCheck(2)
+        while not p.atLengthCheck():
+            sn_type = p.get(1)
+            sn_name = p.getVarBytes(2)
+            self.serverNames += [SNIExtension.ServerName(sn_type, sn_name)]
+        p.stopLengthCheck()
+
+        if p.getRemainingLength():
+            raise DecodeError("Extra data after extension payload")
+
+        return self


 class SupportedVersionsExtension(VarSeqListExtension):
@@ -670,8 +863,10 @@ class SupportedVersionsExtension(VarSeqListExtension):

     def __init__(self):
         """Create an instance of SupportedVersionsExtension."""
-        super(SupportedVersionsExtension, self).__init__(1, 2, 1,
-            'versions', extType=ExtensionType.supported_versions)
+        super(SupportedVersionsExtension, self).__init__(
+            1, 2, 1,
+            "versions",
+            extType=ExtensionType.supported_versions)


 class SrvSupportedVersionsExtension(TLSExtension):
@@ -691,8 +886,8 @@ class SrvSupportedVersionsExtension(TLSExtension):
     """

     def __init__(self):
-        super(SrvSupportedVersionsExtension, self).__init__(extType=
-            ExtensionType.supported_versions)
+        super(SrvSupportedVersionsExtension, self).__init__(
+            extType=ExtensionType.supported_versions)
         self.version = None

     def __repr__(self):
@@ -701,8 +896,8 @@ class SrvSupportedVersionsExtension(TLSExtension):

         :rtype: str
         """
-        return 'SrvSupportedVersionsExtension(version={0})'.format(self.version
-            )
+        return "SrvSupportedVersionsExtension(version={0})".format(
+            self.version)

     def create(self, version):
         """
@@ -711,7 +906,8 @@ class SrvSupportedVersionsExtension(TLSExtension):
         :param tuple version: Version selected by server.
         :rtype: SrvSupportedVersionsExtension
         """
-        pass
+        self.version = version
+        return self

     @property
     def extData(self):
@@ -720,7 +916,12 @@ class SrvSupportedVersionsExtension(TLSExtension):

         :rtype: bytearray
         """
-        pass
+        if self.version is None:
+            return bytearray()
+
+        writer = Writer()
+        writer.addFixSeq(self.version, 1)
+        return writer.bytes

     def parse(self, parser):
         """
@@ -732,7 +933,12 @@ class SrvSupportedVersionsExtension(TLSExtension):

         :rtype: SrvSupportedVersionsExtension
         """
-        pass
+        self.version = tuple(parser.getFixList(1, 2))
+
+        if parser.getRemainingLength():
+            raise DecodeError("Extra data after extension payload")
+
+        return self


 class ClientCertTypeExtension(VarListExtension):
@@ -757,8 +963,10 @@ class ClientCertTypeExtension(VarListExtension):

         See also: :py:meth:`create` and :py:meth:`parse`
         """
-        super(ClientCertTypeExtension, self).__init__(1, 1, 'certTypes',
-            ExtensionType.cert_type, CertificateType)
+        super(ClientCertTypeExtension, self).__init__(
+            1, 1, 'certTypes',
+            ExtensionType.cert_type,
+            CertificateType)


 class ServerCertTypeExtension(IntExtension):
@@ -782,8 +990,9 @@ class ServerCertTypeExtension(IntExtension):

         See also: :py:meth:`create` and :py:meth:`parse`
         """
-        super(ServerCertTypeExtension, self).__init__(1, 'cert_type',
-            ext_type=ExtensionType.cert_type, item_enum=CertificateType)
+        super(ServerCertTypeExtension, self).__init__(
+            1, 'cert_type', ext_type=ExtensionType.cert_type,
+            item_enum=CertificateType)
         self.serverType = True

     def parse(self, parser):
@@ -791,7 +1000,10 @@ class ServerCertTypeExtension(IntExtension):

         :param Parser p: parser with data
         """
-        pass
+        # generic code allows empty, this ext does not
+        if not parser.getRemainingLength():
+            raise DecodeError("Empty payload in extension")
+        return super(ServerCertTypeExtension, self).parse(parser)


 class SRPExtension(TLSExtension):
@@ -816,6 +1028,7 @@ class SRPExtension(TLSExtension):
         See also: :py:meth:`create` and :py:meth:`parse`
         """
         super(SRPExtension, self).__init__(extType=ExtensionType.srp)
+
         self.identity = None

     def __repr__(self):
@@ -824,7 +1037,7 @@ class SRPExtension(TLSExtension):

         :rtype: str
         """
-        return 'SRPExtension(identity={0!r})'.format(self.identity)
+        return "SRPExtension(identity={0!r})".format(self.identity)

     @property
     def extData(self):
@@ -833,7 +1046,15 @@ class SRPExtension(TLSExtension):

         :rtype: bytearray
         """
-        pass
+
+        if self.identity is None:
+            return bytearray(0)
+
+        w = Writer()
+        w.add(len(self.identity), 1)
+        w.addFixSeq(self.identity, 1)
+
+        return w.bytes

     def create(self, identity=None):
         """ Create and instance of SRPExtension with specified protocols
@@ -844,7 +1065,15 @@ class SRPExtension(TLSExtension):

         :raises ValueError: when the identity lenght is longer than 2^8-1
         """
-        pass
+
+        if identity is None:
+            return self
+
+        if len(identity) >= 2**8:
+            raise ValueError()
+
+        self.identity = identity
+        return self

     def parse(self, p):
         """
@@ -856,7 +1085,10 @@ class SRPExtension(TLSExtension):

         :rtype: SRPExtension
         """
-        pass
+
+        self.identity = p.getVarBytes(1)
+
+        return self


 class NPNExtension(TLSExtension):
@@ -880,6 +1112,7 @@ class NPNExtension(TLSExtension):
         See also: :py:meth:`create` and :py:meth:`parse`
         """
         super(NPNExtension, self).__init__(extType=ExtensionType.supports_npn)
+
         self.protocols = None

     def __repr__(self):
@@ -888,7 +1121,7 @@ class NPNExtension(TLSExtension):

         :rtype: str
         """
-        return 'NPNExtension(protocols={0!r})'.format(self.protocols)
+        return "NPNExtension(protocols={0!r})".format(self.protocols)

     @property
     def extData(self):
@@ -896,14 +1129,23 @@ class NPNExtension(TLSExtension):

         :rtype: bytearray
         """
-        pass
+        if self.protocols is None:
+            return bytearray(0)
+
+        w = Writer()
+        for prot in self.protocols:
+            w.add(len(prot), 1)
+            w.addFixSeq(prot, 1)
+
+        return w.bytes

     def create(self, protocols=None):
         """ Create an instance of NPNExtension with specified protocols

         :param list protocols: list of protocol names that are supported
         """
-        pass
+        self.protocols = protocols
+        return self

     def parse(self, p):
         """ Parse the extension from on the wire format
@@ -915,7 +1157,12 @@ class NPNExtension(TLSExtension):

         :rtype: NPNExtension
         """
-        pass
+        self.protocols = []
+
+        while p.getRemainingLength() > 0:
+            self.protocols += [p.getVarBytes(1)]
+
+        return self


 class TACKExtension(TLSExtension):
@@ -930,12 +1177,10 @@ class TACKExtension(TLSExtension):
     :ivar activation_flags: activation flags for the tacks
     """

-
     class TACK(object):
         """
         Implementation of the single TACK
         """
-
         def __init__(self):
             """
             Create a single TACK object
@@ -953,17 +1198,25 @@ class TACKExtension(TLSExtension):

             :rtype: str
             """
-            return (
-                'TACK(public_key={0!r}, min_generation={1!r}, generation={2!r}, expiration={3!r}, target_hash={4!r}, signature={5!r})'
-                .format(self.public_key, self.min_generation, self.
-                generation, self.expiration, self.target_hash, self.signature))
+            return "TACK(public_key={0!r}, min_generation={1!r}, "\
+                   "generation={2!r}, expiration={3!r}, target_hash={4!r}, "\
+                   "signature={5!r})"\
+                   .format(self.public_key, self.min_generation,
+                           self.generation, self.expiration, self.target_hash,
+                           self.signature)

         def create(self, public_key, min_generation, generation, expiration,
-            target_hash, signature):
+                   target_hash, signature):
             """
             Initialise the TACK with data
             """
-            pass
+            self.public_key = public_key
+            self.min_generation = min_generation
+            self.generation = generation
+            self.expiration = expiration
+            self.target_hash = target_hash
+            self.signature = signature
+            return self

         def write(self):
             """
@@ -971,7 +1224,20 @@ class TACKExtension(TLSExtension):

             :rtype: bytearray
             """
-            pass
+            w = Writer()
+            if len(self.public_key) != 64:
+                raise TLSInternalError("Public_key must be 64 bytes long")
+            w.bytes += self.public_key
+            w.add(self.min_generation, 1)
+            w.add(self.generation, 1)
+            w.add(self.expiration, 4)
+            if len(self.target_hash) != 32:
+                raise TLSInternalError("Target_hash must be 32 bytes long")
+            w.bytes += self.target_hash
+            if len(self.signature) != 64:
+                raise TLSInternalError("Signature must be 64 bytes long")
+            w.bytes += self.signature
+            return w.bytes

         def parse(self, p):
             """
@@ -983,7 +1249,14 @@ class TACKExtension(TLSExtension):
             :raises DecodeError: when the internal sizes don't match the
                 provided data
             """
-            pass
+
+            self.public_key = p.getFixBytes(64)
+            self.min_generation = p.get(1)
+            self.generation = p.get(1)
+            self.expiration = p.get(4)
+            self.target_hash = p.getFixBytes(32)
+            self.signature = p.getFixBytes(64)
+            return self

         def __eq__(self, other):
             """
@@ -991,15 +1264,18 @@ class TACKExtension(TLSExtension):

             Returns False for every object that's not a TACK
             """
-            if hasattr(other, 'public_key') and hasattr(other, 'min_generation'
-                ) and hasattr(other, 'generation') and hasattr(other,
-                'expiration') and hasattr(other, 'target_hash') and hasattr(
-                other, 'signature'):
-                if (self.public_key == other.public_key and self.
-                    min_generation == other.min_generation and self.
-                    generation == other.generation and self.expiration ==
-                    other.expiration and self.target_hash == other.
-                    target_hash and self.signature == other.signature):
+            if hasattr(other, 'public_key') and\
+                    hasattr(other, 'min_generation') and\
+                    hasattr(other, 'generation') and\
+                    hasattr(other, 'expiration') and\
+                    hasattr(other, 'target_hash') and\
+                    hasattr(other, 'signature'):
+                if self.public_key == other.public_key and\
+                   self.min_generation == other.min_generation and\
+                   self.generation == other.generation and\
+                   self.expiration == other.expiration and\
+                   self.target_hash == other.target_hash and\
+                   self.signature == other.signature:
                     return True
                 else:
                     return False
@@ -1013,6 +1289,7 @@ class TACKExtension(TLSExtension):
         See also: :py:meth:`create` and :py:meth`parse`
         """
         super(TACKExtension, self).__init__(extType=ExtensionType.tack)
+
         self.tacks = []
         self.activation_flags = 0

@@ -1022,8 +1299,8 @@ class TACKExtension(TLSExtension):

         :rtype: str
         """
-        return 'TACKExtension(activation_flags={0!r}, tacks={1!r})'.format(self
-            .activation_flags, self.tacks)
+        return "TACKExtension(activation_flags={0!r}, tacks={1!r})"\
+               .format(self.activation_flags, self.tacks)

     @property
     def extData(self):
@@ -1032,7 +1309,15 @@ class TACKExtension(TLSExtension):

         :rtype: bytearray
         """
-        pass
+        w2 = Writer()
+        for t in self.tacks:
+            w2.bytes += t.write()
+
+        w = Writer()
+        w.add(len(w2.bytes), 2)
+        w.bytes += w2.bytes
+        w.add(self.activation_flags, 1)
+        return w.bytes

     def create(self, tacks, activation_flags):
         """
@@ -1040,7 +1325,10 @@ class TACKExtension(TLSExtension):

         :rtype: TACKExtension
         """
-        pass
+
+        self.tacks = tacks
+        self.activation_flags = activation_flags
+        return self

     def parse(self, p):
         """
@@ -1050,7 +1338,16 @@ class TACKExtension(TLSExtension):

         :rtype: TACKExtension
         """
-        pass
+        self.tacks = []
+
+        p.startLengthCheck(2)
+        while not p.atLengthCheck():
+            tack = TACKExtension.TACK().parse(p)
+            self.tacks += [tack]
+        p.stopLengthCheck()
+        self.activation_flags = p.get(1)
+
+        return self


 class SupportedGroupsExtension(VarListExtension):
@@ -1065,8 +1362,10 @@ class SupportedGroupsExtension(VarListExtension):

     def __init__(self):
         """Create instance of class"""
-        super(SupportedGroupsExtension, self).__init__(2, 2, 'groups',
-            ExtensionType.supported_groups, GroupName)
+        super(SupportedGroupsExtension, self).__init__(
+            2, 2, 'groups',
+            ExtensionType.supported_groups,
+            GroupName)


 class ECPointFormatsExtension(VarListExtension):
@@ -1081,8 +1380,10 @@ class ECPointFormatsExtension(VarListExtension):

     def __init__(self):
         """Create instance of class"""
-        super(ECPointFormatsExtension, self).__init__(1, 1, 'formats',
-            ExtensionType.ec_point_formats, ECPointFormat)
+        super(ECPointFormatsExtension, self).__init__(
+            1, 1, 'formats',
+            ExtensionType.ec_point_formats,
+            ECPointFormat)


 class _SigListExt(VarSeqListExtension):
@@ -1097,7 +1398,18 @@ class _SigListExt(VarSeqListExtension):
         Override the one from ListExtension to be able to handle legacy
         signature algorithms.
         """
-        pass
+        if self.sigalgs is None:
+            return "None"
+
+        values = []
+        for alg in self.sigalgs:
+            name = SignatureScheme.toRepr(alg)
+            if name is None:
+                name = "({0}, {1})".format(HashAlgorithm.toStr(alg[0]),
+                                           SignatureAlgorithm.toStr(alg[1]))
+            values.append(name)
+
+        return "[{0}]".format(", ".join(values))


 class SignatureAlgorithmsExtension(_SigListExt):
@@ -1112,8 +1424,11 @@ class SignatureAlgorithmsExtension(_SigListExt):

     def __init__(self):
         """Create instance of class"""
-        super(SignatureAlgorithmsExtension, self).__init__(1, 2, 2,
-            'sigalgs', ExtensionType.signature_algorithms, SignatureScheme)
+        super(SignatureAlgorithmsExtension, self).__init__(
+            1, 2, 2,
+            'sigalgs',
+            ExtensionType.signature_algorithms,
+            SignatureScheme)


 class SignatureAlgorithmsCertExtension(_SigListExt):
@@ -1128,9 +1443,11 @@ class SignatureAlgorithmsCertExtension(_SigListExt):

     def __init__(self):
         """Create instance of class."""
-        super(SignatureAlgorithmsCertExtension, self).__init__(1, 2, 2,
-            'sigalgs', ExtensionType.signature_algorithms_cert, SignatureScheme
-            )
+        super(SignatureAlgorithmsCertExtension, self).__init__(
+            1, 2, 2,
+            'sigalgs',
+            ExtensionType.signature_algorithms_cert,
+            SignatureScheme)


 class PaddingExtension(TLSExtension):
@@ -1157,7 +1474,7 @@ class PaddingExtension(TLSExtension):

         :rtype: bytearray
         """
-        pass
+        return self.paddingData

     def create(self, size):
         """
@@ -1165,7 +1482,8 @@ class PaddingExtension(TLSExtension):

         :param int size: required padding size in bytes
         """
-        pass
+        self.paddingData = bytearray(size)
+        return self

     def parse(self, p):
         """
@@ -1178,7 +1496,8 @@ class PaddingExtension(TLSExtension):

         :rtype: TLSExtension
         """
-        pass
+        self.paddingData = p.getFixBytes(p.getRemainingLength())
+        return self


 class RenegotiationInfoExtension(VarBytesExtension):
@@ -1193,7 +1512,9 @@ class RenegotiationInfoExtension(VarBytesExtension):
         """Create instance"""
         extType = ExtensionType.renegotiation_info
         super(RenegotiationInfoExtension, self).__init__(
-            'renegotiated_connection', 1, extType)
+            'renegotiated_connection',
+            1,
+            extType)


 class ALPNExtension(TLSExtension):
@@ -1217,6 +1538,7 @@ class ALPNExtension(TLSExtension):
         See also: :py:meth:`create` and :py:meth:`parse`
         """
         super(ALPNExtension, self).__init__(extType=ExtensionType.alpn)
+
         self.protocol_names = None

     def __repr__(self):
@@ -1225,8 +1547,8 @@ class ALPNExtension(TLSExtension):

         :rtype: str
         """
-        return 'ALPNExtension(protocol_names={0!r})'.format(self.protocol_names
-            )
+        return "ALPNExtension(protocol_names={0!r})"\
+               .format(self.protocol_names)

     @property
     def extData(self):
@@ -1235,7 +1557,19 @@ class ALPNExtension(TLSExtension):

         :rtype: bytearray
         """
-        pass
+        if self.protocol_names is None:
+            return bytearray(0)
+
+        writer = Writer()
+        for prot in self.protocol_names:
+            writer.add(len(prot), 1)
+            writer.bytes += prot
+
+        writer2 = Writer()
+        writer2.add(len(writer.bytes), 2)
+        writer2.bytes += writer.bytes
+
+        return writer2.bytes

     def create(self, protocol_names=None):
         """
@@ -1243,7 +1577,8 @@ class ALPNExtension(TLSExtension):

         :param list protocols: list of protocol names that are to be sent
         """
-        pass
+        self.protocol_names = protocol_names
+        return self

     def parse(self, parser):
         """
@@ -1256,7 +1591,15 @@ class ALPNExtension(TLSExtension):

         :rtype: ALPNExtension
         """
-        pass
+        self.protocol_names = []
+        parser.startLengthCheck(2)
+        while not parser.atLengthCheck():
+            name_len = parser.get(1)
+            self.protocol_names.append(parser.getFixBytes(name_len))
+        parser.stopLengthCheck()
+        if parser.getRemainingLength() != 0:
+            raise DecodeError("Trailing data after protocol_name_list")
+        return self


 class StatusRequestExtension(TLSExtension):
@@ -1277,8 +1620,8 @@ class StatusRequestExtension(TLSExtension):

     def __init__(self):
         """Create instance of StatusRequestExtension."""
-        super(StatusRequestExtension, self).__init__(extType=ExtensionType.
-            status_request)
+        super(StatusRequestExtension, self).__init__(
+            extType=ExtensionType.status_request)
         self.status_type = None
         self.responder_id_list = []
         self.request_extensions = bytearray()
@@ -1289,10 +1632,11 @@ class StatusRequestExtension(TLSExtension):

         :rtype: str
         """
-        return (
-            'StatusRequestExtension(status_type={0}, responder_id_list={1!r}, request_extensions={2!r})'
-            .format(self.status_type, self.responder_id_list, self.
-            request_extensions))
+        return ("StatusRequestExtension(status_type={0}, "
+                "responder_id_list={1!r}, "
+                "request_extensions={2!r})").format(
+                    self.status_type, self.responder_id_list,
+                    self.request_extensions)

     @property
     def extData(self):
@@ -1301,10 +1645,25 @@ class StatusRequestExtension(TLSExtension):

         :rtype: bytearray
         """
-        pass
+        if self.status_type is None:
+            return bytearray()
+
+        writer = Writer()
+        writer.add(self.status_type, 1)
+        writer2 = Writer()
+        for i in self.responder_id_list:
+            writer2.add(len(i), 2)
+            writer2.bytes += i
+        writer.add(len(writer2.bytes), 2)
+        writer.bytes += writer2.bytes
+        writer.add(len(self.request_extensions), 2)
+        writer.bytes += self.request_extensions
+
+        return writer.bytes

     def create(self, status_type=CertificateStatusType.ocsp,
-        responder_id_list=tuple(), request_extensions=b''):
+               responder_id_list=tuple(),
+               request_extensions=b''):
         """
         Create an instance of StatusRequestExtension with specified options.

@@ -1317,7 +1676,10 @@ class StatusRequestExtension(TLSExtension):
         :param bytearray request_extensions: DER encoding of requested OCSP
             extensions
         """
-        pass
+        self.status_type = status_type
+        self.responder_id_list = list(responder_id_list)
+        self.request_extensions = bytearray(request_extensions)
+        return self

     def parse(self, parser):
         """
@@ -1327,7 +1689,23 @@ class StatusRequestExtension(TLSExtension):

         :rtype: StatusRequestExtension
         """
-        pass
+        # handling of server side message
+        if parser.getRemainingLength() == 0:
+            self.status_type = None
+            self.responder_id_list = []
+            self.request_extensions = bytearray()
+            return self
+
+        self.status_type = parser.get(1)
+        self.responder_id_list = []
+        parser.startLengthCheck(2)
+        while not parser.atLengthCheck():
+            self.responder_id_list.append(parser.getVarBytes(2))
+        parser.stopLengthCheck()
+        self.request_extensions = parser.getVarBytes(2)
+        if parser.getRemainingLength() != 0:
+            raise DecodeError("Trailing data after CertificateStatusRequest")
+        return self


 class CertificateStatusExtension(TLSExtension):
@@ -1335,23 +1713,36 @@ class CertificateStatusExtension(TLSExtension):

     def __init__(self):
         """Create instance of CertificateStatusExtension."""
-        super(CertificateStatusExtension, self).__init__(extType=
-            ExtensionType.status_request)
+        super(CertificateStatusExtension, self).__init__(
+            extType=ExtensionType.status_request)
         self.status_type = None
         self.response = None

     def create(self, status_type, response):
         """Set values of the extension."""
-        pass
+        self.status_type = status_type
+        self.response = response
+        return self

     def parse(self, parser):
         """Deserialise the data from on the wire representation."""
-        pass
+        self.status_type = parser.get(1)
+        if self.status_type == 1:
+            self.response = parser.getVarBytes(3)
+        else:
+            raise DecodeError("Unrecognised type")
+        if parser.getRemainingLength():
+            raise DecodeError("Trailing data")
+        return self

     @property
     def extData(self):
         """Serialise the object."""
-        pass
+        writer = Writer()
+        writer.add(self.status_type, 1)
+        writer.addVarSeq(self.response, 1, 3)
+
+        return writer.bytes


 class KeyShareEntry(object):
@@ -1373,7 +1764,10 @@ class KeyShareEntry(object):
             encoded during serialisation)
         :rtype: KeyShareEntry
         """
-        pass
+        self.group = group
+        self.key_exchange = key_exchange
+        self.private = private
+        return self

     def parse(self, parser):
         """
@@ -1383,7 +1777,9 @@ class KeyShareEntry(object):

         :rtype: KeyShareEntry
         """
-        pass
+        self.group = parser.get(2)
+        self.key_exchange = parser.getVarBytes(2)
+        return self

     def write(self, writer):
         """
@@ -1391,12 +1787,15 @@ class KeyShareEntry(object):

         :param Writer writer: buffer to write the data to
         """
-        pass
+        writer.addTwo(self.group)
+        writer.addTwo(len(self.key_exchange))
+        writer.bytes += self.key_exchange

     def __repr__(self):
         """Returns human readable representation of the extension."""
-        return 'KeyShareEntry(group={0},key_exchange={1})'.format(GroupName
-            .toStr(self.group), repr(self.key_exchange))
+        return "KeyShareEntry(group={0},key_exchange={1})".format(
+            GroupName.toStr(self.group),
+            repr(self.key_exchange))


 class HeartbeatExtension(IntExtension):
@@ -1406,14 +1805,17 @@ class HeartbeatExtension(IntExtension):
     :type mode: int
     :ivar mode: mode if peer is allowed or nor allowed to send responses
     """
-
     def __init__(self):
-        super(HeartbeatExtension, self).__init__(1, 'mode', ext_type=
-            ExtensionType.heartbeat, item_enum=HeartbeatMode)
+        super(HeartbeatExtension, self).__init__(
+            1, 'mode', ext_type=ExtensionType.heartbeat,
+            item_enum=HeartbeatMode)

     def parse(self, parser):
         """Deserialise the extension from on the wire data."""
-        pass
+        # the generic class allows for missing values, it's not allowed here
+        if not parser.getRemainingLength():
+            raise DecodeError("Empty extension payload")
+        return super(HeartbeatExtension, self).parse(parser)


 class ClientKeyShareExtension(TLSExtension):
@@ -1425,8 +1827,8 @@ class ClientKeyShareExtension(TLSExtension):

     def __init__(self):
         """Create instance of the object."""
-        super(ClientKeyShareExtension, self).__init__(extType=ExtensionType
-            .key_share)
+        super(ClientKeyShareExtension, self).__init__(extType=ExtensionType.
+                                                      key_share)
         self.client_shares = None

     @property
@@ -1436,11 +1838,20 @@ class ClientKeyShareExtension(TLSExtension):

         :rtype: bytearray
         """
-        pass
+        shares = Writer()
+        for share in self.client_shares:
+            share.write(shares)
+
+        w = Writer()
+        w.addTwo(len(shares.bytes))
+        w.bytes += shares.bytes
+
+        return w.bytes

     def create(self, client_shares):
         """Set the advertised client shares in the extension."""
-        pass
+        self.client_shares = client_shares
+        return self

     def parse(self, parser):
         """
@@ -1452,12 +1863,27 @@ class ClientKeyShareExtension(TLSExtension):

         :rtype: ClientKeyShareExtension
         """
-        pass
+        if not parser.getRemainingLength():
+            self.client_shares = None
+            return self
+
+        self.client_shares = []
+        parser.startLengthCheck(2)
+
+        while not parser.atLengthCheck():
+            self.client_shares.append(KeyShareEntry().parse(parser))
+
+        parser.stopLengthCheck()
+
+        if parser.getRemainingLength():
+            raise DecodeError("Trailing data in client Key Share extension")
+
+        return self

     def __repr__(self):
         """Output human readable representation of the object."""
-        return 'ClientKeyShareExtension(client_shares=[{0}])'.format(','.
-            join(repr(i) for i in self.client_shares))
+        return "ClientKeyShareExtension(client_shares=[{0}])".format(
+            ",".join(repr(i) for i in self.client_shares))


 class ServerKeyShareExtension(TLSExtension):
@@ -1469,18 +1895,25 @@ class ServerKeyShareExtension(TLSExtension):

     def __init__(self):
         """Create instance of the object."""
-        super(ServerKeyShareExtension, self).__init__(extType=ExtensionType
-            .key_share, server=True)
+        super(ServerKeyShareExtension, self).__init__(extType=ExtensionType.
+                                                      key_share,
+                                                      server=True)
         self.server_share = None

     def create(self, server_share):
         """Set the advertised server share in the extension."""
-        pass
+        self.server_share = server_share
+        return self

     @property
     def extData(self):
         """Serialise the payload of the extension"""
-        pass
+        if self.server_share is None:
+            return bytearray(0)
+
+        w = Writer()
+        self.server_share.write(w)
+        return w.bytes

     def parse(self, parser):
         """
@@ -1490,7 +1923,16 @@ class ServerKeyShareExtension(TLSExtension):

         :rtype: ServerKeyShareExtension
         """
-        pass
+        if not parser.getRemainingLength():
+            self.server_share = None
+            return self
+
+        self.server_share = KeyShareEntry().parse(parser)
+
+        if parser.getRemainingLength():
+            raise DecodeError("Trailing data in server Key Share extension")
+
+        return self


 class HRRKeyShareExtension(TLSExtension):
@@ -1500,21 +1942,27 @@ class HRRKeyShareExtension(TLSExtension):
     Extension for notifying the client of the server selected group for
     key exchange.
     """
-
     def __init__(self):
         """Create instance of the object."""
         super(HRRKeyShareExtension, self).__init__(extType=ExtensionType.
-            key_share, hrr=True)
+                                                   key_share,
+                                                   hrr=True)
         self.selected_group = None

     def create(self, selected_group):
         """Set the selected group in the extension."""
-        pass
+        self.selected_group = selected_group
+        return self

     @property
     def extData(self):
         """Serialise the payload of the extension."""
-        pass
+        if self.selected_group is None:
+            return bytearray(0)
+
+        w = Writer()
+        w.add(self.selected_group, 2)
+        return w.bytes

     def parse(self, parser):
         """Parse the extension from on the wire format.
@@ -1523,12 +1971,16 @@ class HRRKeyShareExtension(TLSExtension):

         :rtype: HRRKeyShareExtension
         """
-        pass
+        self.selected_group = parser.get(2)
+
+        if parser.getRemainingLength():
+            raise DecodeError("Trailing data in HRR Key Share extension")
+
+        return self


 class PskIdentity(object):
     """Handling of PskIdentity from PreSharedKey Extension."""
-
     def __init__(self):
         """Create instance of class."""
         super(PskIdentity, self).__init__()
@@ -1537,41 +1989,88 @@ class PskIdentity(object):

     def create(self, identity, obf_ticket_age):
         """Initialise instance."""
-        pass
+        self.identity = identity
+        self.obfuscated_ticket_age = obf_ticket_age
+        return self

     def write(self):
         """Serialise the object."""
-        pass
+        writer = Writer()
+        writer.addTwo(len(self.identity))
+        writer.bytes += self.identity
+        writer.addFour(self.obfuscated_ticket_age)
+        return writer.bytes

     def parse(self, parser):
         """Deserialize the object from bytearray."""
-        pass
+        self.identity = parser.getVarBytes(2)
+        self.obfuscated_ticket_age = parser.get(4)
+        return self


 class PreSharedKeyExtension(TLSExtension):
     """
     Class for handling Pre Shared Key negotiation.
     """
-
     def __init__(self):
         """Create instance of class."""
-        super(PreSharedKeyExtension, self).__init__(extType=ExtensionType.
-            pre_shared_key)
+        super(PreSharedKeyExtension, self).__init__(
+            extType=ExtensionType.pre_shared_key)
         self.identities = None
         self.binders = None

     def create(self, identities, binders):
         """Set list of offered PSKs."""
-        pass
+        self.identities = identities
+        self.binders = binders
+        return self

     @property
     def extData(self):
         """Serialise the payload of the extension."""
-        pass
+        if self.identities is None:
+            return bytearray()
+
+        writer = Writer()
+
+        iden_writer = Writer()
+        for i in self.identities:
+            iden_writer.bytes += i.write()
+        writer.add(len(iden_writer.bytes), 2)
+        writer.bytes += iden_writer.bytes
+
+        binder_writer = Writer()
+        for i in self.binders:
+            binder_writer.add(len(i), 1)
+            binder_writer.bytes += i
+        writer.add(len(binder_writer.bytes), 2)
+        writer.bytes += binder_writer.bytes
+        return writer.bytes

     def parse(self, parser):
         """Parse the extension from on the wire format."""
-        pass
+        if not parser.getRemainingLength():
+            self.identities = None
+            self.binders = None
+            return self
+
+        # PskIdentity identities<7..2^16-1>;
+        parser.startLengthCheck(2)
+        self.identities = []
+        while not parser.atLengthCheck():
+            self.identities.append(PskIdentity().parse(parser))
+        parser.stopLengthCheck()
+
+        # PskBinderEntry binders<33..2^16-1>;
+        parser.startLengthCheck(2)
+        self.binders = []
+        while not parser.atLengthCheck():
+            self.binders.append(parser.getVarBytes(1))
+        parser.stopLengthCheck()
+
+        if parser.getRemainingLength():
+            raise DecodeError("Trailing data after binders field")
+        return self


 class SrvPreSharedKeyExtension(IntExtension):
@@ -1579,8 +2078,8 @@ class SrvPreSharedKeyExtension(IntExtension):

     def __init__(self):
         """Create instance of class."""
-        super(SrvPreSharedKeyExtension, self).__init__(2, 'selected',
-            ext_type=ExtensionType.pre_shared_key)
+        super(SrvPreSharedKeyExtension, self).__init__(
+            2, 'selected', ext_type=ExtensionType.pre_shared_key)


 class PskKeyExchangeModesExtension(VarListExtension):
@@ -1588,8 +2087,10 @@ class PskKeyExchangeModesExtension(VarListExtension):

     def __init__(self):
         """Create instance of class."""
-        super(PskKeyExchangeModesExtension, self).__init__(1, 1, 'modes',
-            ExtensionType.psk_key_exchange_modes, PskKeyExchangeMode)
+        super(PskKeyExchangeModesExtension, self).__init__(
+            1, 1, 'modes',
+            ExtensionType.psk_key_exchange_modes,
+            PskKeyExchangeMode)


 class CookieExtension(VarBytesExtension):
@@ -1606,25 +2107,34 @@ class RecordSizeLimitExtension(IntExtension):

     def __init__(self):
         """Create instance."""
-        super(RecordSizeLimitExtension, self).__init__(2,
-            'record_size_limit', ExtensionType.record_size_limit)
+        super(RecordSizeLimitExtension, self).__init__(
+            2, 'record_size_limit', ExtensionType.record_size_limit)


 class SessionTicketExtension(TLSExtension):
     """
     Client and server session ticket extension from RFC 5077
     """
-
     def __init__(self):
         """Create instance of the object."""
         super(SessionTicketExtension, self).__init__(extType=ExtensionType.
-            session_ticket)
+                                                     session_ticket)
         self.ticket = None

+    def create(self, ticket):
+
+        self.ticket = ticket
+        return self
+
     @property
     def extData(self):
         """Serialise the payload of the extension."""
-        pass
+        if not self.ticket:
+            return bytearray(0)
+
+        w = Writer()
+        w.bytes += self.ticket
+        return w.bytes

     def parse(self, parser):
         """
@@ -1634,39 +2144,57 @@ class SessionTicketExtension(TLSExtension):

         :rtype: SessionTicketExtension
         """
-        pass
+        if not parser.getRemainingLength():
+            self.ticket = bytearray(0)
+            return self
+        self.ticket = parser.getFixBytes(parser.getRemainingLength())
+
+        return self

     def __repr__(self):
         """Return human readable representation of the extension."""
-        return '{0}({1}={2!r})'.format(self.__class__.__name__, 'ticket',
-            self.ticket)
-
-
-TLSExtension._universalExtensions = {ExtensionType.server_name:
-    SNIExtension, ExtensionType.status_request: StatusRequestExtension,
-    ExtensionType.cert_type: ClientCertTypeExtension, ExtensionType.
-    supported_groups: SupportedGroupsExtension, ExtensionType.
-    ec_point_formats: ECPointFormatsExtension, ExtensionType.srp:
-    SRPExtension, ExtensionType.signature_algorithms:
-    SignatureAlgorithmsExtension, ExtensionType.alpn: ALPNExtension,
-    ExtensionType.supports_npn: NPNExtension, ExtensionType.
-    client_hello_padding: PaddingExtension, ExtensionType.
-    renegotiation_info: RenegotiationInfoExtension, ExtensionType.heartbeat:
-    HeartbeatExtension, ExtensionType.supported_versions:
-    SupportedVersionsExtension, ExtensionType.key_share:
-    ClientKeyShareExtension, ExtensionType.signature_algorithms_cert:
-    SignatureAlgorithmsCertExtension, ExtensionType.pre_shared_key:
-    PreSharedKeyExtension, ExtensionType.psk_key_exchange_modes:
-    PskKeyExchangeModesExtension, ExtensionType.cookie: CookieExtension,
-    ExtensionType.record_size_limit: RecordSizeLimitExtension,
-    ExtensionType.session_ticket: SessionTicketExtension}
-TLSExtension._serverExtensions = {ExtensionType.cert_type:
-    ServerCertTypeExtension, ExtensionType.tack: TACKExtension,
-    ExtensionType.key_share: ServerKeyShareExtension, ExtensionType.
-    supported_versions: SrvSupportedVersionsExtension, ExtensionType.
-    pre_shared_key: SrvPreSharedKeyExtension}
-TLSExtension._certificateExtensions = {ExtensionType.status_request:
-    CertificateStatusExtension}
-TLSExtension._hrrExtensions = {ExtensionType.key_share:
-    HRRKeyShareExtension, ExtensionType.supported_versions:
-    SrvSupportedVersionsExtension}
+        return "{0}({1}={2!r})".format(self.__class__.__name__,
+                                       "ticket",
+                                       self.ticket)
+
+
+TLSExtension._universalExtensions = \
+    {
+        ExtensionType.server_name: SNIExtension,
+        ExtensionType.status_request: StatusRequestExtension,
+        ExtensionType.cert_type: ClientCertTypeExtension,
+        ExtensionType.supported_groups: SupportedGroupsExtension,
+        ExtensionType.ec_point_formats: ECPointFormatsExtension,
+        ExtensionType.srp: SRPExtension,
+        ExtensionType.signature_algorithms: SignatureAlgorithmsExtension,
+        ExtensionType.alpn: ALPNExtension,
+        ExtensionType.supports_npn: NPNExtension,
+        ExtensionType.client_hello_padding: PaddingExtension,
+        ExtensionType.renegotiation_info: RenegotiationInfoExtension,
+        ExtensionType.heartbeat: HeartbeatExtension,
+        ExtensionType.supported_versions: SupportedVersionsExtension,
+        ExtensionType.key_share: ClientKeyShareExtension,
+        ExtensionType.signature_algorithms_cert:
+            SignatureAlgorithmsCertExtension,
+        ExtensionType.pre_shared_key: PreSharedKeyExtension,
+        ExtensionType.psk_key_exchange_modes: PskKeyExchangeModesExtension,
+        ExtensionType.cookie: CookieExtension,
+        ExtensionType.record_size_limit: RecordSizeLimitExtension,
+        ExtensionType.session_ticket: SessionTicketExtension}
+
+TLSExtension._serverExtensions = \
+    {
+        ExtensionType.cert_type: ServerCertTypeExtension,
+        ExtensionType.tack: TACKExtension,
+        ExtensionType.key_share: ServerKeyShareExtension,
+        ExtensionType.supported_versions: SrvSupportedVersionsExtension,
+        ExtensionType.pre_shared_key: SrvPreSharedKeyExtension}
+
+TLSExtension._certificateExtensions = \
+    {
+        ExtensionType.status_request: CertificateStatusExtension}
+
+TLSExtension._hrrExtensions = \
+    {
+        ExtensionType.key_share: HRRKeyShareExtension,
+        ExtensionType.supported_versions: SrvSupportedVersionsExtension}
diff --git a/tlslite/handshakehashes.py b/tlslite/handshakehashes.py
index 6638766..8860edb 100644
--- a/tlslite/handshakehashes.py
+++ b/tlslite/handshakehashes.py
@@ -1,10 +1,14 @@
+# Copyright (c) 2015, Hubert Kario
+#
+# See the LICENSE file for legal information regarding use of this file.
 """Handling cryptographic hashes for handshake protocol"""
+
 from .utils.compat import compat26Str, compatHMAC
 from .utils.cryptomath import MD5, SHA1
 from .utils import tlshashlib as hashlib

-
 class HandshakeHashes(object):
+
     """
     Store and calculate necessary hashes for handshake protocol

@@ -28,7 +32,14 @@ class HandshakeHashes(object):

         :param bytearray data: serialized TLS handshake message
         """
-        pass
+        text = compat26Str(data)
+        self._handshakeMD5.update(text)
+        self._handshakeSHA.update(text)
+        self._handshakeSHA224.update(text)
+        self._handshakeSHA256.update(text)
+        self._handshakeSHA384.update(text)
+        self._handshakeSHA512.update(text)
+        self._handshake_buffer += text

     def digest(self, digest=None):
         """
@@ -38,7 +49,24 @@ class HandshakeHashes(object):

         :param str digest: name of digest to return
         """
-        pass
+        if digest is None:
+            return self._handshakeMD5.digest() + self._handshakeSHA.digest()
+        elif digest == 'md5':
+            return self._handshakeMD5.digest()
+        elif digest == 'sha1':
+            return self._handshakeSHA.digest()
+        elif digest == 'sha224':
+            return self._handshakeSHA224.digest()
+        elif digest == 'sha256':
+            return self._handshakeSHA256.digest()
+        elif digest == 'sha384':
+            return self._handshakeSHA384.digest()
+        elif digest == 'sha512':
+            return self._handshakeSHA512.digest()
+        elif digest == "intrinsic":
+            return self._handshake_buffer
+        else:
+            raise ValueError("Unknown digest name")

     def digestSSL(self, masterSecret, label):
         """
@@ -49,8 +77,24 @@ class HandshakeHashes(object):
         :param bytearray masterSecret: value of the master secret
         :param bytearray label: label to include in the calculation
         """
-        pass
+        #pylint: disable=maybe-no-member
+        imacMD5 = self._handshakeMD5.copy()
+        imacSHA = self._handshakeSHA.copy()
+        #pylint: enable=maybe-no-member
+
+        # the below difference in input for MD5 and SHA-1 is why we can't reuse
+        # digest() method
+        imacMD5.update(compatHMAC(label + masterSecret + bytearray([0x36]*48)))
+        imacSHA.update(compatHMAC(label + masterSecret + bytearray([0x36]*40)))
+
+        md5Bytes = MD5(masterSecret + bytearray([0x5c]*48) + \
+                         bytearray(imacMD5.digest()))
+        shaBytes = SHA1(masterSecret + bytearray([0x5c]*40) + \
+                         bytearray(imacSHA.digest()))
+
+        return md5Bytes + shaBytes

+    #pylint: disable=protected-access, maybe-no-member
     def copy(self):
         """
         Copy object
@@ -60,4 +104,12 @@ class HandshakeHashes(object):

         :rtype: HandshakeHashes
         """
-        pass
+        other = HandshakeHashes()
+        other._handshakeMD5 = self._handshakeMD5.copy()
+        other._handshakeSHA = self._handshakeSHA.copy()
+        other._handshakeSHA224 = self._handshakeSHA224.copy()
+        other._handshakeSHA256 = self._handshakeSHA256.copy()
+        other._handshakeSHA384 = self._handshakeSHA384.copy()
+        other._handshakeSHA512 = self._handshakeSHA512.copy()
+        other._handshake_buffer = bytearray(self._handshake_buffer)
+        return other
diff --git a/tlslite/handshakehelpers.py b/tlslite/handshakehelpers.py
index 67f7657..56b8d24 100644
--- a/tlslite/handshakehelpers.py
+++ b/tlslite/handshakehelpers.py
@@ -1,4 +1,10 @@
+# Authors:
+#   Karel Srot
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """Class with various handshake helpers."""
+
 from .extensions import PaddingExtension, PreSharedKeyExtension
 from .utils.cryptomath import derive_secret, secureHMAC, HKDF_expand_label
 from .utils.constanttime import ct_compare_digest
@@ -17,7 +23,22 @@ class HandshakeHelpers(object):

         :param ClientHello clientHello: ClientHello to be aligned
         """
-        pass
+        # Check clientHello size if padding extension should be added
+        # we want to add the extension even when using just SSLv3
+        # cut-off 4 bytes with the Hello header (ClientHello type + Length)
+        clientHelloLength = len(clientHello.write()) - 4
+        if 256 <= clientHelloLength <= 511:
+            if clientHello.extensions is None:
+                clientHello.extensions = []
+                # we need to recalculate the size after extension list addition
+                # results in extra 2 bytes, equals to
+                # clientHelloLength = len(clientHello.write()) - 4
+                clientHelloLength += 2
+            # we want to get 512 bytes in total, including the padding
+            # extension header (4B)
+            paddingExtensionInstance = PaddingExtension().create(
+                max(512 - clientHelloLength - 4, 0))
+            clientHello.extensions.append(paddingExtensionInstance)

     @staticmethod
     def _calc_binder(prf, psk, handshake_hash, external=True):
@@ -25,16 +46,35 @@ class HandshakeHelpers(object):
         Calculate the binder value for a given HandshakeHash (that includes
         a truncated client hello already)
         """
-        pass
+        assert prf in ('sha256', 'sha384')
+        key_len = 32 if prf == 'sha256' else 48
+
+        # HKDF-Extract(0, PSK)
+        early_secret = secureHMAC(bytearray(key_len), psk, prf)
+        if external:
+            binder_key = derive_secret(early_secret, b"ext binder", None, prf)
+        else:
+            binder_key = derive_secret(early_secret, b"res binder", None, prf)
+        finished_key = HKDF_expand_label(binder_key, b"finished", b"", key_len,
+                                         prf)
+        binder = secureHMAC(finished_key, handshake_hash.digest(prf), prf)
+        return binder

     @staticmethod
     def calc_res_binder_psk(iden, res_master_secret, tickets):
         """Calculate PSK associated with provided ticket identity."""
-        pass
+        ticket = [i for i in tickets if i.ticket == iden.identity][0]
+
+        ticket_hash = 'sha256' if len(res_master_secret) == 32 else 'sha384'
+
+        psk = HKDF_expand_label(res_master_secret, b"resumption",
+                                ticket.ticket_nonce, len(res_master_secret),
+                                ticket_hash)
+        return psk

     @staticmethod
-    def update_binders(client_hello, handshake_hashes, psk_configs, tickets
-        =None, res_master_secret=None):
+    def update_binders(client_hello, handshake_hashes, psk_configs,
+                       tickets=None, res_master_secret=None):
         """
         Sign the Client Hello using TLS 1.3 PSK binders.

@@ -48,11 +88,54 @@ class HandshakeHelpers(object):
         :param bytearray res_master_secret: secret associated with the
             tickets
         """
-        pass
+        ext = client_hello.extensions[-1]
+        if not isinstance(ext, PreSharedKeyExtension):
+            raise ValueError("Last extension in client_hello must be "
+                             "PreSharedKeyExtension")
+        if tickets and not res_master_secret:
+            raise ValueError("Tickets require setting res_master_secret")
+
+        hh = handshake_hashes.copy()
+
+        hh.update(client_hello.psk_truncate())
+
+        configs_iter = iter(psk_configs)
+        ticket_idens = []
+        if tickets:
+            ticket_idens = [i.ticket for i in tickets]
+
+        for i, iden in enumerate(ext.identities):
+            # identities that are tickets don't carry PSK directly
+            if iden.identity in ticket_idens:
+                binder_hash = 'sha256' if len(res_master_secret) == 32 \
+                    else 'sha384'
+                psk = HandshakeHelpers.calc_res_binder_psk(
+                    iden, res_master_secret, tickets)
+                external = False
+            else:
+                try:
+                    config = next(configs_iter)
+                    while config[0] != iden.identity:
+                        config = next(configs_iter)
+                except StopIteration:
+                    raise ValueError("psk_configs don't match the "
+                                     "PreSharedKeyExtension")
+
+                binder_hash = config[2] if len(config) > 2 else 'sha256'
+                psk = config[1]
+                external = True
+
+            binder = HandshakeHelpers._calc_binder(binder_hash,
+                                                   psk,
+                                                   hh,
+                                                   external)
+
+            # replace the fake value with calculated one
+            ext.binders[i] = binder

     @staticmethod
     def verify_binder(client_hello, handshake_hashes, position, secret, prf,
-        external=True):
+                      external=True):
         """Verify the PSK binder value in client hello.

         :param client_hello: ClientHello to verify
@@ -61,4 +144,21 @@ class HandshakeHelpers(object):
         :param secret: the secret PSK
         :param prf: name of the hash used as PRF
         """
-        pass
+        ext = client_hello.extensions[-1]
+        if not isinstance(ext, PreSharedKeyExtension):
+            raise TLSIllegalParameterException(
+                "Last extension in client_hello must be "
+                "PreSharedKeyExtension")
+
+        hh = handshake_hashes.copy()
+        hh.update(client_hello.psk_truncate())
+
+        binder = HandshakeHelpers._calc_binder(prf,
+                                               secret,
+                                               hh,
+                                               external)
+
+        if not ct_compare_digest(binder, ext.binders[position]):
+            raise TLSIllegalParameterException("Binder does not verify")
+
+        return True
diff --git a/tlslite/handshakesettings.py b/tlslite/handshakesettings.py
index 0dc638a..38e560a 100644
--- a/tlslite/handshakesettings.py
+++ b/tlslite/handshakesettings.py
@@ -1,43 +1,66 @@
+# Authors:
+#   Trevor Perrin
+#   Dave Baggett (Arcode Corporation) - cleanup handling of constants
+#   Yngve Pettersen (ported by Paul Sokolovsky) - TLS 1.2
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """Class for setting handshake parameters."""
+
 from .constants import CertificateType
 from .utils import cryptomath
 from .utils import cipherfactory
 from .utils.compat import ecdsaAllCurves, int_types
-CIPHER_NAMES = ['chacha20-poly1305', 'aes256gcm', 'aes128gcm', 'aes256ccm',
-    'aes128ccm', 'aes256', 'aes128', '3des']
-ALL_CIPHER_NAMES = CIPHER_NAMES + ['chacha20-poly1305_draft00',
-    'aes128ccm_8', 'aes256ccm_8', 'rc4', 'null']
-MAC_NAMES = ['sha', 'sha256', 'sha384', 'aead']
-ALL_MAC_NAMES = MAC_NAMES + ['md5']
-KEY_EXCHANGE_NAMES = ['ecdhe_ecdsa', 'rsa', 'dhe_rsa', 'ecdhe_rsa',
-    'srp_sha', 'srp_sha_rsa', 'ecdh_anon', 'dh_anon', 'dhe_dsa']
-CIPHER_IMPLEMENTATIONS = ['openssl', 'pycrypto', 'python']
-CERTIFICATE_TYPES = ['x509']
-RSA_SIGNATURE_HASHES = ['sha512', 'sha384', 'sha256', 'sha224', 'sha1']
-DSA_SIGNATURE_HASHES = ['sha512', 'sha384', 'sha256', 'sha224', 'sha1']
-ECDSA_SIGNATURE_HASHES = ['sha512', 'sha384', 'sha256', 'sha224', 'sha1']
-ALL_RSA_SIGNATURE_HASHES = RSA_SIGNATURE_HASHES + ['md5']
-SIGNATURE_SCHEMES = ['Ed25519', 'Ed448']
-RSA_SCHEMES = ['pss', 'pkcs1']
-CURVE_NAMES = ['x25519', 'x448', 'secp384r1', 'secp256r1', 'secp521r1']
-ALL_CURVE_NAMES = CURVE_NAMES + ['secp256k1', 'brainpoolP512r1',
-    'brainpoolP384r1', 'brainpoolP256r1']
+
+CIPHER_NAMES = ["chacha20-poly1305",
+                "aes256gcm", "aes128gcm",
+                "aes256ccm", "aes128ccm",
+                "aes256", "aes128",
+                "3des"]
+ALL_CIPHER_NAMES = CIPHER_NAMES + ["chacha20-poly1305_draft00",
+                                   "aes128ccm_8", "aes256ccm_8",
+                                   "rc4", "null"]
+# Don't allow "md5" by default
+MAC_NAMES = ["sha", "sha256", "sha384", "aead"]
+ALL_MAC_NAMES = MAC_NAMES + ["md5"]
+KEY_EXCHANGE_NAMES = ["ecdhe_ecdsa", "rsa", "dhe_rsa", "ecdhe_rsa", "srp_sha",
+                      "srp_sha_rsa", "ecdh_anon", "dh_anon", "dhe_dsa"]
+CIPHER_IMPLEMENTATIONS = ["openssl", "pycrypto", "python"]
+CERTIFICATE_TYPES = ["x509"]
+RSA_SIGNATURE_HASHES = ["sha512", "sha384", "sha256", "sha224", "sha1"]
+DSA_SIGNATURE_HASHES = ["sha512", "sha384", "sha256", "sha224", "sha1"]
+ECDSA_SIGNATURE_HASHES = ["sha512", "sha384", "sha256", "sha224", "sha1"]
+ALL_RSA_SIGNATURE_HASHES = RSA_SIGNATURE_HASHES + ["md5"]
+SIGNATURE_SCHEMES = ["Ed25519", "Ed448"]
+RSA_SCHEMES = ["pss", "pkcs1"]
+# while secp521r1 is the most secure, it's also much slower than the others
+# so place it as the last one
+CURVE_NAMES = ["x25519", "x448", "secp384r1", "secp256r1",
+               "secp521r1"]
+ALL_CURVE_NAMES = CURVE_NAMES + ["secp256k1", "brainpoolP512r1",
+                                 "brainpoolP384r1", "brainpoolP256r1"]
 if ecdsaAllCurves:
-    ALL_CURVE_NAMES += ['secp224r1', 'secp192r1']
-ALL_DH_GROUP_NAMES = ['ffdhe2048', 'ffdhe3072', 'ffdhe4096', 'ffdhe6144',
-    'ffdhe8192']
-CURVE_ALIASES = {'secp256r1': ('NIST256p', 'prime256v1', 'P-256'),
-    'secp384r1': ('NIST384p', 'P-384'), 'secp521r1': ('NIST521p', 'P-521'),
-    'secp256k1': ('SECP256k1',), 'secp192r1': ('NIST192p', 'P-192'),
-    'secp224r1': ('NIST224p', 'P-224'), 'brainpoolP256r1': (
-    'BRAINPOOLP256r1',), 'brainpoolP384r1': ('BRAINPOOLP384r1',),
-    'brainpoolP512r1': ('BRAINPOOLP512r1',)}
-TLS13_PERMITTED_GROUPS = ['secp256r1', 'secp384r1', 'secp521r1', 'x25519',
-    'x448', 'ffdhe2048', 'ffdhe3072', 'ffdhe4096', 'ffdhe6144', 'ffdhe8192']
-KNOWN_VERSIONS = (3, 0), (3, 1), (3, 2), (3, 3), (3, 4)
-TICKET_CIPHERS = ['chacha20-poly1305', 'aes256gcm', 'aes128gcm',
-    'aes128ccm', 'aes128ccm_8', 'aes256ccm', 'aes256ccm_8']
-PSK_MODES = ['psk_dhe_ke', 'psk_ke']
+    ALL_CURVE_NAMES += ["secp224r1", "secp192r1"]
+ALL_DH_GROUP_NAMES = ["ffdhe2048", "ffdhe3072", "ffdhe4096", "ffdhe6144",
+                      "ffdhe8192"]
+CURVE_ALIASES = {"secp256r1": ('NIST256p', 'prime256v1', 'P-256'),
+                 "secp384r1": ('NIST384p', 'P-384'),
+                 "secp521r1": ('NIST521p', 'P-521'),
+                 "secp256k1": ('SECP256k1',),
+                 "secp192r1": ('NIST192p', 'P-192'),
+                 "secp224r1": ('NIST224p', 'P-224'),
+                 "brainpoolP256r1": ('BRAINPOOLP256r1',),
+                 "brainpoolP384r1": ('BRAINPOOLP384r1',),
+                 "brainpoolP512r1": ('BRAINPOOLP512r1',)}
+# list of supported groups in TLS 1.3 as per RFC 8446, chapter 4.2.7. (excluding private use here)
+TLS13_PERMITTED_GROUPS = ["secp256r1", "secp384r1", "secp521r1",
+                          "x25519", "x448", "ffdhe2048",
+                          "ffdhe3072", "ffdhe4096", "ffdhe6144",
+                          "ffdhe8192"]
+KNOWN_VERSIONS = ((3, 0), (3, 1), (3, 2), (3, 3), (3, 4))
+TICKET_CIPHERS = ["chacha20-poly1305", "aes256gcm", "aes128gcm", "aes128ccm",
+                  "aes128ccm_8", "aes256ccm", "aes256ccm_8"]
+PSK_MODES = ["psk_dhe_ke", "psk_ke"]


 class Keypair(object):
@@ -57,14 +80,14 @@ class Keypair(object):
     :ivar certificates: the certificates to send to peer if the key is selected
         for use. The first one MUST include the public key of the ``key``
     """
-
     def __init__(self, key=None, certificates=tuple()):
         self.key = key
         self.certificates = certificates

     def validate(self):
         """Sanity check the keypair."""
-        pass
+        if not self.key or not self.certificates:
+            raise ValueError("Key or certificate missing in Keypair")


 class VirtualHost(object):
@@ -104,11 +127,14 @@ class VirtualHost(object):

     def matches_hostname(self, hostname):
         """Checks if the virtual host can serve hostname"""
-        pass
+        return hostname in self.hostnames

     def validate(self):
         """Sanity check the settings"""
-        pass
+        if not self.keys:
+            raise ValueError("Virtual host missing keys")
+        for i in self.keys:
+            i.validate()


 class HandshakeSettings(object):
@@ -331,18 +357,52 @@ class HandshakeSettings(object):

     def _init_key_settings(self):
         """Create default variables for key-related settings."""
-        pass
+        self.minKeySize = 1023
+        self.maxKeySize = 8193
+        self.rsaSigHashes = list(RSA_SIGNATURE_HASHES)
+        self.rsaSchemes = list(RSA_SCHEMES)
+        self.dsaSigHashes = list(DSA_SIGNATURE_HASHES)
+        self.virtual_hosts = []
+        # DH key settings
+        self.eccCurves = list(CURVE_NAMES)
+        self.dhParams = None
+        self.dhGroups = list(ALL_DH_GROUP_NAMES)
+        self.defaultCurve = "secp256r1"
+        self.keyShares = ["secp256r1", "x25519"]
+        self.padding_cb = None
+        self.use_heartbeat_extension = True
+        self.heartbeat_response_callback = None

     def _init_misc_extensions(self):
         """Default variables for assorted extensions."""
-        pass
+        self.certificateTypes = list(CERTIFICATE_TYPES)
+        self.useExperimentalTackExtension = False
+        self.sendFallbackSCSV = False
+        self.useEncryptThenMAC = True
+        self.ecdsaSigHashes = list(ECDSA_SIGNATURE_HASHES)
+        self.more_sig_schemes = list(SIGNATURE_SCHEMES)
+        self.usePaddingExtension = True
+        self.useExtendedMasterSecret = True
+        self.requireExtendedMasterSecret = False
+        # PSKs
+        self.pskConfigs = []
+        self.psk_modes = list(PSK_MODES)
+        # session tickets
+        self.ticketKeys = []
+        self.ticketCipher = "aes256gcm"
+        self.ticketLifetime = 24 * 60 * 60
+        self.max_early_data = 2 ** 14 + 16  # full record + tag
+        # send two tickets so that client can quickly ramp up number of
+        # resumed connections (as tickets are single-use in TLS 1.3
+        self.ticket_count = 2
+        self.record_size_limit = 2**14 + 1  # TLS 1.3 includes content type

     def __init__(self):
         """Initialise default values for settings."""
         self._init_key_settings()
         self._init_misc_extensions()
-        self.minVersion = 3, 1
-        self.maxVersion = 3, 4
+        self.minVersion = (3, 1)
+        self.maxVersion = (3, 4)
         self.versions = [(3, 4), (3, 3), (3, 2), (3, 1)]
         self.cipherNames = list(CIPHER_NAMES)
         self.macNames = list(MAC_NAMES)
@@ -352,87 +412,313 @@ class HandshakeSettings(object):
     @staticmethod
     def _sanityCheckKeySizes(other):
         """Check if key size limits are sane"""
-        pass
+        if other.minKeySize < 512:
+            raise ValueError("minKeySize too small")
+        if other.minKeySize > 16384:
+            raise ValueError("minKeySize too large")
+        if other.maxKeySize < 512:
+            raise ValueError("maxKeySize too small")
+        if other.maxKeySize > 16384:
+            raise ValueError("maxKeySize too large")
+        if other.maxKeySize < other.minKeySize:
+            raise ValueError("maxKeySize smaller than minKeySize")
+        # check also keys of virtual hosts
+        for i in other.virtual_hosts:
+            i.validate()

     @staticmethod
     def _not_matching(values, sieve):
         """Return list of items from values that are not in sieve."""
-        pass
+        return [val for val in values if val not in sieve]

     @staticmethod
     def _sanityCheckCipherSettings(other):
         """Check if specified cipher settings are known."""
-        pass
+        not_matching = HandshakeSettings._not_matching
+        unknownCiph = not_matching(other.cipherNames, ALL_CIPHER_NAMES)
+        if unknownCiph:
+            raise ValueError("Unknown cipher name: {0}".format(unknownCiph))
+
+        unknownMac = not_matching(other.macNames, ALL_MAC_NAMES)
+        if unknownMac:
+            raise ValueError("Unknown MAC name: {0}".format(unknownMac))
+
+        unknownKex = not_matching(other.keyExchangeNames, KEY_EXCHANGE_NAMES)
+        if unknownKex:
+            raise ValueError("Unknown key exchange name: {0}"
+                             .format(unknownKex))
+
+        unknownImpl = not_matching(other.cipherImplementations,
+                                   CIPHER_IMPLEMENTATIONS)
+        if unknownImpl:
+            raise ValueError("Unknown cipher implementation: {0}"
+                             .format(unknownImpl))

     @staticmethod
     def _sanityCheckECDHSettings(other):
         """Check ECDHE settings if they are sane."""
-        pass
+        not_matching = HandshakeSettings._not_matching
+
+        unknownCurve = not_matching(other.eccCurves, ALL_CURVE_NAMES)
+        if unknownCurve:
+            raise ValueError("Unknown ECC Curve name: {0}"
+                             .format(unknownCurve))
+
+        if other.defaultCurve not in ALL_CURVE_NAMES:
+            raise ValueError("Unknown default ECC Curve name: {0}"
+                             .format(other.defaultCurve))
+
+        nonAdvertisedGroup = [val for val in other.keyShares
+                              if val not in other.eccCurves and
+                              val not in other.dhGroups]
+        if nonAdvertisedGroup:
+            raise ValueError("Key shares for not enabled groups specified: {0}"
+                             .format(nonAdvertisedGroup))
+
+        unknownSigHash = not_matching(other.ecdsaSigHashes,
+                                      ECDSA_SIGNATURE_HASHES)
+        if unknownSigHash:
+            raise ValueError("Unknown ECDSA signature hash: '{0}'".\
+                             format(unknownSigHash))
+
+        unknownSigHash = not_matching(other.more_sig_schemes,
+                                      SIGNATURE_SCHEMES)
+        if unknownSigHash:
+            raise ValueError("Unkonwn more_sig_schemes specified: '{0}'"
+                             .format(unknownSigHash))
+
+        unknownDHGroup = not_matching(other.dhGroups, ALL_DH_GROUP_NAMES)
+        if unknownDHGroup:
+            raise ValueError("Unknown FFDHE group name: '{0}'"
+                             .format(unknownDHGroup))
+
+        # TLS 1.3 limits the allowed groups (RFC 8446,ch. 4.2.7.)
+        if other.maxVersion == (3, 4):
+            forbiddenGroup = HandshakeSettings._not_matching(other.eccCurves, TLS13_PERMITTED_GROUPS)
+            if forbiddenGroup:
+                raise ValueError("The following enabled groups are forbidden in TLS 1.3: {0}"
+                                 .format(forbiddenGroup))

     @staticmethod
     def _sanityCheckDHSettings(other):
         """Check if (EC)DHE settings are sane."""
-        pass
+        not_matching = HandshakeSettings._not_matching
+
+        HandshakeSettings._sanityCheckECDHSettings(other)
+
+        unknownKeyShare = [val for val in other.keyShares
+                           if val not in ALL_DH_GROUP_NAMES and
+                           val not in ALL_CURVE_NAMES]
+        if unknownKeyShare:
+            raise ValueError("Unknown key share: '{0}'"
+                             .format(unknownKeyShare))
+
+        if other.dhParams and (len(other.dhParams) != 2 or
+                               not isinstance(other.dhParams[0], int_types) or
+                               not isinstance(other.dhParams[1], int_types)):
+            raise ValueError("DH parameters need to be a tuple of integers")

     @staticmethod
     def _sanityCheckPrimitivesNames(other):
         """Check if specified cryptographic primitive names are known"""
-        pass
+        HandshakeSettings._sanityCheckCipherSettings(other)
+        HandshakeSettings._sanityCheckDHSettings(other)
+
+        not_matching = HandshakeSettings._not_matching
+
+        unknownType = not_matching(other.certificateTypes, CERTIFICATE_TYPES)
+        if unknownType:
+            raise ValueError("Unknown certificate type: {0}"
+                             .format(unknownType))
+
+        unknownSigHash = not_matching(other.rsaSigHashes,
+                                      ALL_RSA_SIGNATURE_HASHES)
+        if unknownSigHash:
+            raise ValueError("Unknown RSA signature hash: '{0}'"
+                             .format(unknownSigHash))
+
+        unknownRSAPad = not_matching(other.rsaSchemes, RSA_SCHEMES)
+        if unknownRSAPad:
+            raise ValueError("Unknown RSA padding mode: '{0}'"
+                             .format(unknownRSAPad))
+
+        unknownSigHash = not_matching(other.dsaSigHashes,
+                                      DSA_SIGNATURE_HASHES)
+        if unknownSigHash:
+            raise ValueError("Unknown DSA signature hash: '{0}'"
+                             .format(unknownSigHash))
+
+        if not other.rsaSigHashes and not other.ecdsaSigHashes and \
+                not other.dsaSigHashes and not other.more_sig_schemes and \
+                other.maxVersion >= (3, 3):
+            raise ValueError("TLS 1.2 requires signature algorithms to be set")

     @staticmethod
     def _sanityCheckProtocolVersions(other):
         """Check if set protocol version are sane"""
-        pass
+        if other.minVersion > other.maxVersion:
+            raise ValueError("Versions set incorrectly")
+        if other.minVersion not in KNOWN_VERSIONS:
+            raise ValueError("minVersion set incorrectly")
+        if other.maxVersion not in KNOWN_VERSIONS:
+            raise ValueError("maxVersion set incorrectly")
+
+        if other.maxVersion < (3, 4):
+            other.versions = [i for i in other.versions if i < (3, 4)]

     @staticmethod
     def _sanityCheckEMSExtension(other):
         """Check if settings for EMS are sane."""
-        pass
+        if other.useExtendedMasterSecret not in (True, False):
+            raise ValueError("useExtendedMasterSecret must be True or False")
+        if other.requireExtendedMasterSecret not in (True, False):
+            raise ValueError("requireExtendedMasterSecret must be True "
+                             "or False")
+        if other.requireExtendedMasterSecret and \
+                not other.useExtendedMasterSecret:
+            raise ValueError("requireExtendedMasterSecret requires "
+                             "useExtendedMasterSecret")

     @staticmethod
     def _sanityCheckExtensions(other):
         """Check if set extension settings are sane"""
-        pass
+        if other.useEncryptThenMAC not in (True, False):
+            raise ValueError("useEncryptThenMAC can only be True or False")
+
+        if other.usePaddingExtension not in (True, False):
+            raise ValueError("usePaddingExtension must be True or False")
+
+        if other.use_heartbeat_extension not in (True, False):
+            raise ValueError("use_heartbeat_extension must be True or False")
+
+        if other.heartbeat_response_callback and not other.use_heartbeat_extension:
+            raise ValueError("heartbeat_response_callback requires "
+                             "use_heartbeat_extension")
+
+        if other.record_size_limit is not None and \
+                not 64 <= other.record_size_limit <= 2**14 + 1:
+            raise ValueError("record_size_limit cannot exceed 2**14+1 bytes")
+
+        HandshakeSettings._sanityCheckEMSExtension(other)

     @staticmethod
     def _not_allowed_len(values, sieve):
         """Return True if length of any item in values is not in sieve."""
-        pass
+        sieve = set(sieve)
+        return any(len(i) not in sieve for i in values)

     @staticmethod
     def _sanityCheckPsks(other):
         """Check if the set PSKs are sane."""
-        pass
+        if HandshakeSettings._not_allowed_len(other.pskConfigs, [2, 3]):
+            raise ValueError("pskConfigs items must be a 2 or 3-element"
+                             "tuples")
+
+        badHashes = [i[2] for i in other.pskConfigs if
+                     len(i) == 3 and i[2] not in set(['sha256', 'sha384'])]
+        if badHashes:
+            raise ValueError("pskConfigs include invalid hash specifications: "
+                             "{0}".format(badHashes))
+
+        bad_psk_modes = [i for i in other.psk_modes if
+                         i not in PSK_MODES]
+        if bad_psk_modes:
+            raise ValueError("psk_modes includes invalid key exchange modes: "
+                             "{0}".format(bad_psk_modes))

     @staticmethod
     def _sanityCheckTicketSettings(other):
         """Check if the session ticket settings are sane."""
-        pass
+        if other.ticketCipher not in TICKET_CIPHERS:
+            raise ValueError("Invalid cipher for session ticket encryption: "
+                             "{0}".format(other.ticketCipher))
+
+        if HandshakeSettings._not_allowed_len(other.ticketKeys, [16, 32]):
+            raise ValueError("Session ticket encryption keys must be 16 or 32"
+                             "bytes long")
+
+        if not 0 < other.ticketLifetime <= 7 * 24 * 60 * 60:
+            raise ValueError("Ticket lifetime must be a positive integer "
+                             "smaller or equal 604800 (7 days)")
+
+        # while not ticket setting per-se, it is related to session tickets
+        if not 0 < other.max_early_data <= 2**64:
+            raise ValueError("max_early_data must be between 0 and 2GiB")
+
+        if not 0 <= other.ticket_count < 2**16:
+            raise ValueError("Incorrect amount for number of new session "
+                             "tickets to send")

     def _copy_cipher_settings(self, other):
         """Copy values related to cipher selection."""
-        pass
+        other.cipherNames = self.cipherNames
+        other.macNames = self.macNames
+        other.keyExchangeNames = self.keyExchangeNames
+        other.cipherImplementations = self.cipherImplementations
+        other.minVersion = self.minVersion
+        other.maxVersion = self.maxVersion
+        other.versions = self.versions

     def _copy_extension_settings(self, other):
         """Copy values of settings related to extensions."""
-        pass
+        other.useExtendedMasterSecret = self.useExtendedMasterSecret
+        other.requireExtendedMasterSecret = self.requireExtendedMasterSecret
+        other.useExperimentalTackExtension = self.useExperimentalTackExtension
+        other.sendFallbackSCSV = self.sendFallbackSCSV
+        other.useEncryptThenMAC = self.useEncryptThenMAC
+        other.usePaddingExtension = self.usePaddingExtension
+        # session tickets
+        other.padding_cb = self.padding_cb
+        other.ticketKeys = self.ticketKeys
+        other.ticketCipher = self.ticketCipher
+        other.ticketLifetime = self.ticketLifetime
+        other.max_early_data = self.max_early_data
+        other.ticket_count = self.ticket_count
+        other.record_size_limit = self.record_size_limit

     @staticmethod
     def _remove_all_matches(values, needle):
         """Remove all instances of needle from values."""
-        pass
+        values[:] = (i for i in values if i != needle)

     def _sanity_check_ciphers(self, other):
         """Remove unsupported ciphers in current configuration."""
-        pass
+        if not cipherfactory.tripleDESPresent:
+            other.cipherNames = other.cipherNames[:]
+            self._remove_all_matches(other.cipherNames, "3des")
+
+        if not other.cipherNames:
+            raise ValueError("No supported ciphers")

     def _sanity_check_implementations(self, other):
         """Remove all backends that are not loaded."""
-        pass
+        if not cryptomath.m2cryptoLoaded:
+            self._remove_all_matches(other.cipherImplementations, "openssl")
+        if not cryptomath.pycryptoLoaded:
+            self._remove_all_matches(other.cipherImplementations, "pycrypto")
+
+        if not other.cipherImplementations:
+            raise ValueError("No supported cipher implementations")

     def _copy_key_settings(self, other):
         """Copy key-related settings."""
-        pass
+        other.minKeySize = self.minKeySize
+        other.maxKeySize = self.maxKeySize
+        other.certificateTypes = self.certificateTypes
+        other.rsaSigHashes = self.rsaSigHashes
+        other.rsaSchemes = self.rsaSchemes
+        other.dsaSigHashes = self.dsaSigHashes
+        other.ecdsaSigHashes = self.ecdsaSigHashes
+        other.more_sig_schemes = self.more_sig_schemes
+        other.virtual_hosts = self.virtual_hosts
+        # DH key params
+        other.eccCurves = self.eccCurves
+        other.dhParams = self.dhParams
+        other.dhGroups = self.dhGroups
+        other.defaultCurve = self.defaultCurve
+        other.keyShares = self.keyShares
+        other.use_heartbeat_extension = self.use_heartbeat_extension
+        other.heartbeat_response_callback = self.heartbeat_response_callback

     def validate(self):
         """
@@ -443,8 +729,46 @@ class HandshakeSettings(object):
         :returns: a self-consistent copy of settings
         :raises ValueError: when settings are invalid, insecure or unsupported.
         """
-        pass
+        other = HandshakeSettings()
+
+        self._copy_cipher_settings(other)
+        self._copy_extension_settings(other)
+        self._copy_key_settings(other)
+
+        other.pskConfigs = self.pskConfigs
+        other.psk_modes = self.psk_modes
+
+        if not other.certificateTypes:
+            raise ValueError("No supported certificate types")
+
+        self._sanityCheckKeySizes(other)
+
+        self._sanityCheckPrimitivesNames(other)
+
+        self._sanityCheckProtocolVersions(other)
+
+        self._sanityCheckExtensions(other)
+
+        if other.maxVersion < (3, 3):
+            # No sha-2 and AEAD pre TLS 1.2
+            other.macNames = [e for e in self.macNames if
+                              e == "sha" or e == "md5"]
+
+        self._sanityCheckPsks(other)
+
+        self._sanityCheckTicketSettings(other)
+
+        self._sanity_check_implementations(other)
+        self._sanity_check_ciphers(other)
+
+        return other

     def getCertificateTypes(self):
         """Get list of certificate types as IDs"""
-        pass
+        ret = []
+        for ct in self.certificateTypes:
+            if ct == "x509":
+                ret.append(CertificateType.x509)
+            else:
+                raise AssertionError()
+        return ret
diff --git a/tlslite/integration/asyncstatemachine.py b/tlslite/integration/asyncstatemachine.py
index 2aa8a79..c6d8694 100644
--- a/tlslite/integration/asyncstatemachine.py
+++ b/tlslite/integration/asyncstatemachine.py
@@ -1,8 +1,10 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 """
 A state machine for using TLS Lite with asynchronous I/O.
 """

-
 class AsyncStateMachine:
     """
     This is an abstract class that's used to integrate TLS Lite with
@@ -27,6 +29,46 @@ class AsyncStateMachine:
         self.writer = None
         self._clear()

+    def _clear(self):
+        #These store the various asynchronous operations (i.e.
+        #generators).  Only one of them, at most, is ever active at a
+        #time.
+        self.handshaker = None
+        self.closer = None
+        self.reader = None
+        self.writer = None
+
+        #This stores the result from the last call to the
+        #currently active operation.  If 0 it indicates that the
+        #operation wants to read, if 1 it indicates that the
+        #operation wants to write.  If None, there is no active
+        #operation.
+        self.result = None
+
+    def _checkAssert(self, maxActive=1):
+        #This checks that only one operation, at most, is
+        #active, and that self.result is set appropriately.
+        activeOps = 0
+        if self.handshaker:
+            activeOps += 1
+        if self.closer:
+            activeOps += 1
+        if self.reader:
+            activeOps += 1
+        if self.writer:
+            activeOps += 1
+
+        if self.result == None:
+            if activeOps != 0:
+                raise AssertionError()
+        elif self.result in (0,1):
+            if activeOps != 1:
+                raise AssertionError()
+        else:
+            raise AssertionError()
+        if activeOps > maxActive:
+            raise AssertionError()
+
     def wantsReadEvent(self):
         """If the state machine wants to read.

@@ -37,7 +79,9 @@ class AsyncStateMachine:
         :rtype: bool or None
         :returns: If the state machine wants to read.
         """
-        pass
+        if self.result != None:
+            return self.result == 0
+        return None

     def wantsWriteEvent(self):
         """If the state machine wants to write.
@@ -49,7 +93,9 @@ class AsyncStateMachine:
         :rtype: bool or None
         :returns: If the state machine wants to write.
         """
-        pass
+        if self.result != None:
+            return self.result == 1
+        return None

     def outConnectEvent(self):
         """Called when a handshake operation completes.
@@ -79,11 +125,71 @@ class AsyncStateMachine:

     def inReadEvent(self):
         """Tell the state machine it can read from the socket."""
-        pass
+        try:
+            self._checkAssert()
+            if self.handshaker:
+                self._doHandshakeOp()
+            elif self.closer:
+                self._doCloseOp()
+            elif self.reader:
+                self._doReadOp()
+            elif self.writer:
+                self._doWriteOp()
+            else:
+                self.reader = self.tlsConnection.readAsync(16384)
+                self._doReadOp()
+        except:
+            self._clear()
+            raise

     def inWriteEvent(self):
         """Tell the state machine it can write to the socket."""
-        pass
+        try:
+            self._checkAssert()
+            if self.handshaker:
+                self._doHandshakeOp()
+            elif self.closer:
+                self._doCloseOp()
+            elif self.reader:
+                self._doReadOp()
+            elif self.writer:
+                self._doWriteOp()
+            else:
+                self.outWriteEvent()
+        except:
+            self._clear()
+            raise
+
+    def _doHandshakeOp(self):
+        try:
+            self.result = next(self.handshaker)
+        except StopIteration:
+            self.handshaker = None
+            self.result = None
+            self.outConnectEvent()
+
+    def _doCloseOp(self):
+        try:
+            self.result = next(self.closer)
+        except StopIteration:
+            self.closer = None
+            self.result = None
+            self.outCloseEvent()
+
+    def _doReadOp(self):
+        self.result = next(self.reader)
+        if not self.result in (0,1):
+            readBuffer = self.result
+            self.reader = None
+            self.result = None
+            self.outReadEvent(readBuffer)
+
+    def _doWriteOp(self):
+        try:
+            self.result = next(self.writer)
+        except StopIteration:
+            self.writer = None
+            self.result = None

     def setHandshakeOp(self, handshaker):
         """Start a handshake operation.
@@ -93,7 +199,13 @@ class AsyncStateMachine:
             :py:meth:`~.TLSConnection.handshakeServerAsync` , or
             handshakeClientxxx(..., async_=True).
         """
-        pass
+        try:
+            self._checkAssert(0)
+            self.handshaker = handshaker
+            self._doHandshakeOp()
+        except:
+            self._clear()
+            raise

     def setServerHandshakeOp(self, **args):
         """Start a handshake operation.
@@ -101,16 +213,30 @@ class AsyncStateMachine:
         The arguments passed to this function will be forwarded to
         :py:obj:`~tlslite.tlsconnection.TLSConnection.handshakeServerAsync`.
         """
-        pass
+        handshaker = self.tlsConnection.handshakeServerAsync(**args)
+        self.setHandshakeOp(handshaker)

     def setCloseOp(self):
         """Start a close operation.
         """
-        pass
+        try:
+            self._checkAssert(0)
+            self.closer = self.tlsConnection.closeAsync()
+            self._doCloseOp()
+        except:
+            self._clear()
+            raise

     def setWriteOp(self, writeBuffer):
         """Start a write operation.

         :param str writeBuffer: The string to transmit.
         """
-        pass
+        try:
+            self._checkAssert(0)
+            self.writer = self.tlsConnection.writeAsync(writeBuffer)
+            self._doWriteOp()
+        except:
+            self._clear()
+            raise
+
diff --git a/tlslite/integration/clienthelper.py b/tlslite/integration/clienthelper.py
index 3bda03e..9155ac2 100644
--- a/tlslite/integration/clienthelper.py
+++ b/tlslite/integration/clienthelper.py
@@ -1,17 +1,28 @@
+# Authors: 
+#   Trevor Perrin
+#   Dimitris Moraitis - Anon ciphersuites
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """
 A helper class for using TLS Lite with stdlib clients
 (httplib, xmlrpclib, imaplib, poplib).
 """
+
 from tlslite.checker import Checker
 from tlslite.utils.dns_utils import is_valid_hostname

-
 class ClientHelper(object):
     """This is a helper class used to integrate TLS Lite with various
     TLS clients (e.g. poplib, smtplib, httplib, etc.)"""

-    def __init__(self, username=None, password=None, certChain=None,
-        privateKey=None, checker=None, settings=None, anon=False, host=None):
+    def __init__(self,
+                 username=None, password=None,
+                 certChain=None, privateKey=None,
+                 checker=None,
+                 settings=None,
+                 anon=False,
+                 host=None):
         """
         For client authentication, use one of these argument
         combinations:
@@ -69,36 +80,84 @@ class ClientHelper(object):
             include the port (in which case the port will be stripped and
             ignored).
         """
+
         self.username = None
         self.password = None
         self.certChain = None
         self.privateKey = None
         self.checker = None
         self.anon = anon
-        if username and password and not (certChain or privateKey):
+
+        #SRP Authentication
+        if username and password and not \
+                (certChain or privateKey):
             self.username = username
             self.password = password
-        elif certChain and privateKey and not (username or password):
+
+        #Certificate Chain Authentication
+        elif certChain and privateKey and not \
+                (username or password):
             self.certChain = certChain
             self.privateKey = privateKey
-        elif not password and not username and not certChain and not privateKey:
+
+        #No Authentication
+        elif not password and not username and not \
+                certChain and not privateKey:
             pass
+
         else:
-            raise ValueError('Bad parameters')
+            raise ValueError("Bad parameters")
+
         self.checker = checker
         self.settings = settings
+
         self.tlsSession = None
+
         if host is not None and not self._isIP(host):
+            # name for SNI so port can't be sent
             colon = host.find(':')
             if colon > 0:
                 host = host[:colon]
             self.serverName = host
             if host and not is_valid_hostname(host):
-                raise ValueError('Invalid hostname: {0}'.format(host))
+                raise ValueError("Invalid hostname: {0}".format(host))
         else:
             self.serverName = None

     @staticmethod
     def _isIP(address):
         """Return True if the address is an IPv4 address"""
-        pass
+        if not address:
+            return False
+        vals = address.split('.')
+        if len(vals) != 4:
+            return False
+        for i in vals:
+            if not i.isdigit():
+                return False
+            j = int(i)
+            if not 0 <= j <= 255:
+                return False
+        return True
+
+    def _handshake(self, tlsConnection):
+        if self.username and self.password:
+            tlsConnection.handshakeClientSRP(username=self.username,
+                                             password=self.password,
+                                             checker=self.checker,
+                                             settings=self.settings,
+                                             session=self.tlsSession,
+                                             serverName=self.serverName)
+        elif self.anon:
+            tlsConnection.handshakeClientAnonymous(session=self.tlsSession,
+                                                   settings=self.settings,
+                                                   checker=self.checker,
+                                                   serverName=self.serverName)
+        else:
+            tlsConnection.handshakeClientCert(certChain=self.certChain,
+                                              privateKey=self.privateKey,
+                                              checker=self.checker,
+                                              settings=self.settings,
+                                              session=self.tlsSession,
+                                              serverName=self.serverName)
+        self.tlsSession = tlsConnection.session
diff --git a/tlslite/integration/httptlsconnection.py b/tlslite/integration/httptlsconnection.py
index 9d6129c..c922150 100644
--- a/tlslite/integration/httptlsconnection.py
+++ b/tlslite/integration/httptlsconnection.py
@@ -1,8 +1,18 @@
+# Authors: 
+#   Trevor Perrin
+#   Kees Bos - Added ignoreAbruptClose parameter
+#   Dimitris Moraitis - Anon ciphersuites
+#   Martin von Loewis - python 3 port
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """TLS Lite + httplib."""
+
 import socket
 try:
     import httplib
 except ImportError:
+    # Python 3
     from http import client as httplib
 from tlslite.tlsconnection import TLSConnection
 from tlslite.integration.clienthelper import ClientHelper
@@ -11,10 +21,15 @@ from tlslite.integration.clienthelper import ClientHelper
 class HTTPTLSConnection(httplib.HTTPConnection, ClientHelper):
     """This class extends L{httplib.HTTPConnection} to support TLS."""

-    def __init__(self, host, port=None, strict=None, timeout=socket.
-        _GLOBAL_DEFAULT_TIMEOUT, source_address=None, username=None,
-        password=None, certChain=None, privateKey=None, checker=None,
-        settings=None, ignoreAbruptClose=False, anon=False):
+    def __init__(self, host, port=None, strict=None,
+                timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
+                source_address=None,
+                username=None, password=None,
+                certChain=None, privateKey=None,
+                checker=None,
+                settings=None,
+                ignoreAbruptClose=False,
+                anon=False):
         """Create a new HTTPTLSConnection.

         For client authentication, use one of these argument
@@ -79,11 +94,27 @@ class HTTPTLSConnection(httplib.HTTPConnection, ClientHelper):
             unexpected hangup.
         """
         if source_address:
-            httplib.HTTPConnection.__init__(self, host=host, port=port,
-                timeout=timeout, source_address=source_address)
+            httplib.HTTPConnection.__init__(self,
+                    host=host,
+                    port=port,
+                    timeout=timeout,
+                    source_address=source_address)
         if not source_address:
-            httplib.HTTPConnection.__init__(self, host=host, port=port,
-                timeout=timeout)
+            httplib.HTTPConnection.__init__(self,
+                    host=host,
+                    port=port,
+                    timeout=timeout)
         self.ignoreAbruptClose = ignoreAbruptClose
-        ClientHelper.__init__(self, username, password, certChain,
-            privateKey, checker, settings, anon, host)
+        ClientHelper.__init__(self,
+                              username, password,
+                              certChain, privateKey,
+                              checker,
+                              settings,
+                              anon,
+                              host)
+
+    def connect(self):
+        httplib.HTTPConnection.connect(self)
+        self.sock = TLSConnection(self.sock)
+        self.sock.ignoreAbruptClose = self.ignoreAbruptClose
+        ClientHelper._handshake(self, self.sock)
diff --git a/tlslite/integration/imap4_tls.py b/tlslite/integration/imap4_tls.py
index 8583c78..11f5806 100644
--- a/tlslite/integration/imap4_tls.py
+++ b/tlslite/integration/imap4_tls.py
@@ -1,17 +1,24 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 """TLS Lite + imaplib."""
+
 import socket
 from imaplib import IMAP4
 from tlslite.tlsconnection import TLSConnection
 from tlslite.integration.clienthelper import ClientHelper
-IMAP4_TLS_PORT = 993

+# IMAP TLS PORT
+IMAP4_TLS_PORT = 993

 class IMAP4_TLS(IMAP4, ClientHelper):
     """This class extends :py:class:`imaplib.IMAP4` with TLS support."""

-    def __init__(self, host='', port=IMAP4_TLS_PORT, username=None,
-        password=None, certChain=None, privateKey=None, checker=None,
-        settings=None):
+    def __init__(self, host = '', port = IMAP4_TLS_PORT,
+                 username=None, password=None,
+                 certChain=None, privateKey=None,
+                 checker=None,
+                 settings=None):
         """Create a new IMAP4_TLS.

         For client authentication, use one of these argument
@@ -66,14 +73,30 @@ class IMAP4_TLS(IMAP4, ClientHelper):
             the ciphersuites, certificate types, and SSL/TLS versions
             offered by the client.
         """
-        ClientHelper.__init__(self, username, password, certChain,
-            privateKey, checker, settings)
+
+        ClientHelper.__init__(self,
+                 username, password,
+                 certChain, privateKey,
+                 checker,
+                 settings)
+
         IMAP4.__init__(self, host, port)

+    # the `timeout` is a new argument in python3.9, so checks with
+    # older python versions will complain about unmatched parameters
+    # pylint: disable=arguments-differ
     def open(self, host='', port=IMAP4_TLS_PORT, timeout=None):
         """Setup connection to remote server on "host:port".

         This connection will be used by the routines:
         read, readline, send, shutdown.
         """
-        pass
+        del timeout
+        self.host = host
+        self.port = port
+        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+        self.sock.connect((host, port))
+        self.sock = TLSConnection(self.sock)
+        ClientHelper._handshake(self, self.sock)
+        self.file = self.sock.makefile('rb')
+    # pylint: enable=arguments-differ
diff --git a/tlslite/integration/pop3_tls.py b/tlslite/integration/pop3_tls.py
index 2cce171..814e494 100644
--- a/tlslite/integration/pop3_tls.py
+++ b/tlslite/integration/pop3_tls.py
@@ -1,16 +1,22 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 """TLS Lite + poplib."""
+
 import socket
 from poplib import POP3, POP3_SSL_PORT
 from tlslite.tlsconnection import TLSConnection
 from tlslite.integration.clienthelper import ClientHelper

-
 class POP3_TLS(POP3, ClientHelper):
     """This class extends :py:class:`poplib.POP3` with TLS support."""

-    def __init__(self, host, port=POP3_SSL_PORT, timeout=socket.
-        _GLOBAL_DEFAULT_TIMEOUT, username=None, password=None, certChain=
-        None, privateKey=None, checker=None, settings=None):
+    def __init__(self, host, port = POP3_SSL_PORT,
+                 timeout=socket._GLOBAL_DEFAULT_TIMEOUT,
+                 username=None, password=None,
+                 certChain=None, privateKey=None,
+                 checker=None,
+                 settings=None):
         """Create a new POP3_TLS.

         For client authentication, use one of these argument
@@ -68,9 +74,12 @@ class POP3_TLS(POP3, ClientHelper):
         self.host = host
         self.port = port
         sock = socket.create_connection((host, port), timeout)
-        ClientHelper.__init__(self, username, password, certChain,
-            privateKey, checker, settings)
-        connection = TLSConnection(sock)
+        ClientHelper.__init__(self,
+                 username, password,
+                 certChain, privateKey,
+                 checker,
+                 settings)
+        connection = TLSConnection(sock) 
         ClientHelper._handshake(self, connection)
         self.sock = connection
         self.file = self.sock.makefile('rb')
diff --git a/tlslite/integration/smtp_tls.py b/tlslite/integration/smtp_tls.py
index 64501b6..1c94d0e 100644
--- a/tlslite/integration/smtp_tls.py
+++ b/tlslite/integration/smtp_tls.py
@@ -1,14 +1,20 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 """TLS Lite + smtplib."""
+
 from smtplib import SMTP
 from tlslite.tlsconnection import TLSConnection
 from tlslite.integration.clienthelper import ClientHelper

-
 class SMTP_TLS(SMTP):
     """This class extends :py:class:`smtplib.SMTP` with TLS support."""

-    def starttls(self, username=None, password=None, certChain=None,
-        privateKey=None, checker=None, settings=None):
+    def starttls(self,
+                 username=None, password=None,
+                 certChain=None, privateKey=None,
+                 checker=None,
+                 settings=None):
         """Puts the connection to the SMTP server into TLS mode.

         If the server supports TLS, this will encrypt the rest of the SMTP
@@ -60,4 +66,15 @@ class SMTP_TLS(SMTP):
             the ciphersuites, certificate types, and SSL/TLS versions
             offered by the client.
         """
-        pass
+        (resp, reply) = self.docmd("STARTTLS")
+        if resp == 220:
+            helper = ClientHelper(
+                     username, password, 
+                     certChain, privateKey,
+                     checker,
+                     settings)
+            conn = TLSConnection(self.sock)
+            helper._handshake(conn)
+            self.sock = conn
+            self.file = conn.makefile('rb')
+        return (resp, reply)
diff --git a/tlslite/integration/tlsasyncdispatchermixin.py b/tlslite/integration/tlsasyncdispatchermixin.py
index 49d0efe..5758608 100644
--- a/tlslite/integration/tlsasyncdispatchermixin.py
+++ b/tlslite/integration/tlsasyncdispatchermixin.py
@@ -1,4 +1,12 @@
+# Authors: 
+#   Trevor Perrin
+#   Martin von Loewis - python 3 port
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """TLS Lite + asyncore."""
+
+
 import asyncore
 from tlslite.tlsconnection import TLSConnection
 from .asyncstatemachine import AsyncStateMachine
@@ -72,13 +80,68 @@ class TLSAsyncDispatcherMixIn(AsyncStateMachine):
     and removes this instance from the asyncore loop.
     """

+
     def __init__(self, sock=None):
         AsyncStateMachine.__init__(self)
+
         if sock:
             self.tlsConnection = TLSConnection(sock)
+
+        #Calculate the sibling I'm being mixed in with.
+        #This is necessary since we override functions
+        #like readable(), handle_read(), etc., but we
+        #also want to call the sibling's versions.
         for cl in self.__class__.__bases__:
             if cl != TLSAsyncDispatcherMixIn and cl != AsyncStateMachine:
                 self.siblingClass = cl
                 break
         else:
             raise AssertionError()
+
+    def readable(self):
+        result = self.wantsReadEvent()
+        if result != None:
+            return result
+        return self.siblingClass.readable(self)
+
+    def writable(self):
+        result = self.wantsWriteEvent()
+        if result != None:
+            return result
+        return self.siblingClass.writable(self)
+
+    def handle_read(self):
+        self.inReadEvent()
+
+    def handle_write(self):
+        self.inWriteEvent()
+
+    def outConnectEvent(self):
+        self.siblingClass.handle_connect(self)
+
+    def outCloseEvent(self):
+        asyncore.dispatcher.close(self)
+
+    def outReadEvent(self, readBuffer):
+        self.readBuffer = readBuffer
+        self.siblingClass.handle_read(self)
+
+    def outWriteEvent(self):
+        self.siblingClass.handle_write(self)
+
+    def recv(self, bufferSize=16384):
+        if bufferSize < 16384 or self.readBuffer == None:
+            raise AssertionError()
+        returnValue = self.readBuffer
+        self.readBuffer = None
+        return returnValue
+
+    def send(self, writeBuffer):
+        self.setWriteOp(writeBuffer)
+        return len(writeBuffer)
+
+    def close(self):
+        if hasattr(self, "tlsConnection"):
+            self.setCloseOp()
+        else:
+            asyncore.dispatcher.close(self)
diff --git a/tlslite/integration/tlsasynciodispatchermixin.py b/tlslite/integration/tlsasynciodispatchermixin.py
index fc83a77..2df7a07 100644
--- a/tlslite/integration/tlsasynciodispatchermixin.py
+++ b/tlslite/integration/tlsasynciodispatchermixin.py
@@ -1,4 +1,10 @@
+# Authors:
+#   Esteban Sanchez (FosanzDev) - python 3.12 port
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """TLS Lite + asyncio."""
+
 import asyncio
 from tlslite.tlsconnection import TLSConnection
 from .asyncstatemachine import AsyncStateMachine
@@ -82,44 +88,82 @@ class TLSAsyncioDispatcherMixIn(asyncio.Protocol):

     def _get_sibling_class(self):
         """Get the sibling class that this class is mixed in with."""
-        pass
+        for cl in self.__class__.__bases__:
+            if cl not in (TLSAsyncioDispatcherMixIn, AsyncStateMachine):
+                return cl
+        raise AssertionError()
+
+    def connection_made(self, transport):
+        self.transport = transport
+        # Call the sibling class's connection_made method
+        if hasattr(self.sibling_class, 'connection_made'):
+            self.sibling_class.connection_made(transport)
+
+    def data_received(self, data):
+        self.read_buffer = data
+        if hasattr(self.sibling_class, 'data_received'):
+            self.sibling_class.data_received(self, data)
+
+    def connection_lost(self, exc):
+        self.sibling_class.connection_lost(self, exc)
+        if hasattr(self, "tls_connection"):
+            self.set_close_op()
+        else:
+            self.transport.close()

     def readable(self):
         """Check if the protocol is ready for reading."""
-        pass
+        result = self.wants_read_event()
+        return result if result is not None \
+            else self.sibling_class.readable(self)

     def writable(self):
         """Check if the protocol is ready for writing."""
-        pass
+        result = self.wants_write_event()
+        return result if result is not None \
+            else self.sibling_class.writable(self)

     def handle_read(self):
         """Handle a read event."""
-        pass
+        self.in_read_event()

     def handle_write(self):
         """Handle a write event."""
-        pass
+        self.in_write_event()

     def out_connect_event(self):
         """Handle an outgoing connect event."""
-        pass
+        self.sibling_class.handle_connect(self)

     def out_close_event(self):
         """Handle an outgoing close event."""
-        pass
+        self.transport.close()

     def out_read_event(self, read_buffer):
         """Handle an outgoing read event."""
-        pass
+        self.read_buffer = read_buffer
+        self.sibling_class.handle_read(self)

     def out_write_event(self):
         """Handle an outgoing write event."""
-        pass
+        self.sibling_class.handle_write(self)

     def recv(self, buffer_size=16384):
         """Receive data."""
-        pass
+        if buffer_size < 16384 or self.read_buffer is None:
+            raise AssertionError()
+        return_value = self.read_buffer
+        self.read_buffer = None
+        return return_value
+
+    def send(self, write_buffer):
+        self.set_write_op(write_buffer)
+        self.transport.write(write_buffer)
+        return len(write_buffer)

     def close(self):
         """Close the connection."""
-        pass
+        if hasattr(self, "tls_connection"):
+            self.set_close_op()
+        else:
+            self.transport.close()
diff --git a/tlslite/integration/tlssocketservermixin.py b/tlslite/integration/tlssocketservermixin.py
index 1bc8d49..a279ec1 100644
--- a/tlslite/integration/tlssocketservermixin.py
+++ b/tlslite/integration/tlssocketservermixin.py
@@ -1,6 +1,9 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 """TLS Lite + SocketServer."""
-from tlslite.tlsconnection import TLSConnection

+from tlslite.tlsconnection import TLSConnection

 class TLSSocketServerMixIn:
     """
@@ -45,3 +48,15 @@ class TLSSocketServerMixIn:
         httpd = MyHTTPServer(('localhost', 443), SimpleHTTPRequestHandler)
         httpd.serve_forever()
     """
+
+
+    def finish_request(self, sock, client_address):
+        tlsConnection = TLSConnection(sock)
+        if self.handshake(tlsConnection) == True:
+            self.RequestHandlerClass(tlsConnection, client_address, self)
+            tlsConnection.close()
+
+    #Implement this method to do some form of handshaking.  Return True
+    #if the handshake finishes properly and the request is authorized.
+    def handshake(self, tlsConnection):
+        raise NotImplementedError()
diff --git a/tlslite/integration/xmlrpcserver.py b/tlslite/integration/xmlrpcserver.py
index 1d62452..b9cd66f 100644
--- a/tlslite/integration/xmlrpcserver.py
+++ b/tlslite/integration/xmlrpcserver.py
@@ -1,7 +1,15 @@
+# Authors:
+#   Kees Bos
+#   Martin von Loewis - python 3 port
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """xmlrpcserver.py - simple XML RPC server supporting TLS."""
+
 try:
     from SimpleXMLRPCServer import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
 except ImportError:
+    # Python 3
     from xmlrpc.server import SimpleXMLRPCServer, SimpleXMLRPCRequestHandler
 from .tlssocketservermixin import TLSSocketServerMixIn

@@ -9,16 +17,28 @@ from .tlssocketservermixin import TLSSocketServerMixIn
 class TLSXMLRPCRequestHandler(SimpleXMLRPCRequestHandler):
     """XMLRPCRequestHandler using TLS."""

+    # Redefine the setup method (see SocketServer.StreamRequestHandler)
     def setup(self):
         """Setup the connection for TLS."""
-        pass
+        self.connection = self.request
+        if getattr(self, 'timeout', None) is not None:
+            # Python 2.7
+            self.connection.settimeout(self.timeout)
+        self.rfile = self.connection.makefile('rb', self.rbufsize)
+        self.wfile = self.connection.makefile('wb', self.wbufsize)

     def do_POST(self):
         """Handle the HTTPS POST request."""
-        pass
+        SimpleXMLRPCRequestHandler.do_POST(self)
+        try:
+            # shut down the connection
+            self.connection.shutdown()
+        except:
+            pass


-class TLSXMLRPCServer(TLSSocketServerMixIn, SimpleXMLRPCServer):
+class TLSXMLRPCServer(TLSSocketServerMixIn,
+                      SimpleXMLRPCServer):
     """Simple XML-RPC server using TLS."""

     def __init__(self, addr, *args, **kwargs):
diff --git a/tlslite/integration/xmlrpctransport.py b/tlslite/integration/xmlrpctransport.py
index 256ade1..6f60afa 100644
--- a/tlslite/integration/xmlrpctransport.py
+++ b/tlslite/integration/xmlrpctransport.py
@@ -1,8 +1,18 @@
+# Authors: 
+#   Trevor Perrin
+#   Kees Bos - Fixes for compatibility with different Python versions
+#   Martin von Loewis - python 3 port
+#
+# See the LICENSE file for legal information regarding use of this file.
+
+
 """TLS Lite + xmlrpclib."""
+
 try:
     import xmlrpclib
     import httplib
 except ImportError:
+    # Python 3
     from xmlrpc import client as xmlrpclib
     from http import client as httplib
 from tlslite.integration.httptlsconnection import HTTPTLSConnection
@@ -12,13 +22,18 @@ import tlslite.errors

 class XMLRPCTransport(xmlrpclib.Transport, ClientHelper):
     """Handles an HTTPS transaction to an XML-RPC server."""
+
+    # Pre python 2.7, the make_connection returns a HTTP class
     transport = xmlrpclib.Transport()
     conn_class_is_http = not hasattr(transport, '_connection')
-    del transport
-
-    def __init__(self, use_datetime=0, username=None, password=None,
-        certChain=None, privateKey=None, checker=None, settings=None,
-        ignoreAbruptClose=False):
+    del(transport)
+
+    def __init__(self, use_datetime=0,
+                 username=None, password=None,
+                 certChain=None, privateKey=None,
+                 checker=None,
+                 settings=None,
+                 ignoreAbruptClose=False):
         """
         Create a new XMLRPCTransport.

@@ -87,12 +102,37 @@ class XMLRPCTransport(xmlrpclib.Transport, ClientHelper):
         :param ignoreAbruptClose: ignore the TLSAbruptCloseError on
             unexpected hangup.
         """
-        self._connection = None, None
+        # self._connection is new in python 2.7, since we're using it here,
+        # we'll add this ourselves too, just in case we're pre-2.7
+        self._connection = (None, None)
         xmlrpclib.Transport.__init__(self, use_datetime)
         self.ignoreAbruptClose = ignoreAbruptClose
-        ClientHelper.__init__(self, username, password, certChain,
-            privateKey, checker, settings)
+        ClientHelper.__init__(self,
+                 username, password, 
+                 certChain, privateKey,
+                 checker,
+                 settings)

     def make_connection(self, host):
         """Make a connection to `host`. Reuse keepalive connections."""
-        pass
+        # return an existing connection if possible.  This allows
+        # HTTP/1.1 keep-alive.
+        if self._connection and host == self._connection[0]:
+            http = self._connection[1]
+        else:
+            # create a HTTPS connection object from a host descriptor
+            chost, extra_headers, x509 = self.get_host_info(host)
+
+            http = HTTPTLSConnection(
+                chost, None, username=self.username, password=self.password,
+                certChain=self.certChain, privateKey=self.privateKey,
+                checker=self.checker,
+                settings=self.settings,
+                ignoreAbruptClose=self.ignoreAbruptClose)
+            # store the host argument along with the connection object
+            self._connection = host, http
+        if not self.conn_class_is_http:
+            return http
+        http2 = httplib.HTTP()
+        http2._setup(http)
+        return http2
diff --git a/tlslite/keyexchange.py b/tlslite/keyexchange.py
index 847c510..2242aad 100644
--- a/tlslite/keyexchange.py
+++ b/tlslite/keyexchange.py
@@ -1,19 +1,29 @@
+# Authors:
+#   Hubert Kario (2015)
+#
+# See the LICENSE file for legal information regarding use of this file.
 """Handling of cryptographic operations for key exchange"""
+
 import ecdsa
-from .mathtls import goodGroupParameters, makeK, makeU, makeX, paramStrength, RFC7919_GROUPS, calc_key
-from .errors import TLSInsufficientSecurity, TLSUnknownPSKIdentity, TLSIllegalParameterException, TLSDecryptionFailed, TLSInternalError, TLSDecodeError
+from .mathtls import goodGroupParameters, makeK, makeU, makeX, \
+        paramStrength, RFC7919_GROUPS, calc_key
+from .errors import TLSInsufficientSecurity, TLSUnknownPSKIdentity, \
+        TLSIllegalParameterException, TLSDecryptionFailed, TLSInternalError, \
+        TLSDecodeError
 from .messages import ServerKeyExchange, ClientKeyExchange, CertificateVerify
-from .constants import SignatureAlgorithm, HashAlgorithm, CipherSuite, ExtensionType, GroupName, ECCurveType, SignatureScheme
+from .constants import SignatureAlgorithm, HashAlgorithm, CipherSuite, \
+        ExtensionType, GroupName, ECCurveType, SignatureScheme
 from .utils.ecc import getCurveByName, getPointByteSize
 from .utils.rsakey import RSAKey
-from .utils.cryptomath import bytesToNumber, getRandomBytes, powMod, numBits, numberToByteArray, divceil, numBytes, secureHash
+from .utils.cryptomath import bytesToNumber, getRandomBytes, powMod, \
+        numBits, numberToByteArray, divceil, numBytes, secureHash
 from .utils.lists import getFirstMatching
 from .utils import tlshashlib as hashlib
-from .utils.x25519 import x25519, x448, X25519_G, X448_G, X25519_ORDER_SIZE, X448_ORDER_SIZE
+from .utils.x25519 import x25519, x448, X25519_G, X448_G, X25519_ORDER_SIZE, \
+        X448_ORDER_SIZE
 from .utils.compat import int_types
 from .utils.codec import DecodeError

-
 class KeyExchange(object):
     """
     Common API for calculating Premaster secret
@@ -36,7 +46,7 @@ class KeyExchange(object):
         handshake. If the key exchange method does not send ServerKeyExchange
         (e.g. RSA), it returns None.
         """
-        pass
+        raise NotImplementedError()

     def makeClientKeyExchange(self):
         """
@@ -45,7 +55,8 @@ class KeyExchange(object):
         Returns a ClientKeyExchange for the second flight from client in the
         handshake.
         """
-        pass
+        return ClientKeyExchange(self.cipherSuite,
+                                 self.serverHello.server_version)

     def processClientKeyExchange(self, clientKeyExchange):
         """
@@ -54,23 +65,127 @@ class KeyExchange(object):
         Processes the client's ClientKeyExchange message and returns the
         premaster secret. Raises TLSLocalAlert on error.
         """
-        pass
+        raise NotImplementedError()

-    def processServerKeyExchange(self, srvPublicKey, serverKeyExchange):
+    def processServerKeyExchange(self, srvPublicKey,
+                                 serverKeyExchange):
         """Process the server KEX and return premaster secret"""
-        pass
+        raise NotImplementedError()
+
+    def _tls12_sign_ecdsa_SKE(self, serverKeyExchange, sigHash=None):
+        try:
+            serverKeyExchange.hashAlg, serverKeyExchange.signAlg = \
+                    getattr(SignatureScheme, sigHash)
+            hashName = SignatureScheme.getHash(sigHash)
+        except AttributeError:
+            serverKeyExchange.hashAlg = getattr(HashAlgorithm, sigHash)
+            serverKeyExchange.signAlg = SignatureAlgorithm.ecdsa
+            hashName = sigHash
+
+        hash_bytes = serverKeyExchange.hash(self.clientHello.random,
+                                            self.serverHello.random)
+
+        hash_bytes = hash_bytes[:self.privateKey.private_key.curve.baselen]
+
+        serverKeyExchange.signature = \
+            self.privateKey.sign(hash_bytes, hashAlg=hashName)
+
+        if not serverKeyExchange.signature:
+            raise TLSInternalError("Empty signature")
+
+        if not self.privateKey.verify(serverKeyExchange.signature,
+                                             hash_bytes,
+                                             ecdsa.util.sigdecode_der):
+            raise TLSInternalError("signature validation failure")

     def _tls12_sign_dsa_SKE(self, serverKeyExchange, sigHash=None):
         """Sign a TLSv1.2 SKE message."""
-        pass
+        try:
+            serverKeyExchange.hashAlg, serverKeyExchange.signAlg = \
+                getattr(SignatureScheme, sigHash)
+
+        except AttributeError:
+            serverKeyExchange.signAlg = SignatureAlgorithm.dsa
+            serverKeyExchange.hashAlg = getattr(HashAlgorithm, sigHash)
+
+        hashBytes = serverKeyExchange.hash(self.clientHello.random,
+                                           self.serverHello.random)
+
+        serverKeyExchange.signature = \
+            self.privateKey.sign(hashBytes)
+
+        if not serverKeyExchange.signature:
+            raise TLSInternalError("Empty signature")
+
+        if not self.privateKey.verify(serverKeyExchange.signature,
+                                      hashBytes):
+            raise TLSInternalError("Server Key Exchange signature invalid")

     def _tls12_sign_eddsa_ske(self, server_key_exchange, sig_hash):
         """Sign a TLSv1.2 SKE message."""
-        pass
+        server_key_exchange.hashAlg, server_key_exchange.signAlg = \
+                getattr(SignatureScheme, sig_hash)
+        pad_type = None
+        hash_name = None
+        salt_len = None
+
+        hash_bytes = server_key_exchange.hash(self.clientHello.random,
+                                              self.serverHello.random)
+
+        server_key_exchange.signature = \
+            self.privateKey.hashAndSign(hash_bytes,
+                                        pad_type,
+                                        hash_name,
+                                        salt_len)
+
+        if not server_key_exchange.signature:
+            raise TLSInternalError("Empty signature")
+
+        if not self.privateKey.hashAndVerify(
+                server_key_exchange.signature,
+                hash_bytes,
+                pad_type,
+                hash_name,
+                salt_len):
+            raise TLSInternalError("Server Key Exchange signature invalid")

     def _tls12_signSKE(self, serverKeyExchange, sigHash=None):
         """Sign a TLSv1.2 SKE message."""
-        pass
+        try:
+            serverKeyExchange.hashAlg, serverKeyExchange.signAlg = \
+                    getattr(SignatureScheme, sigHash)
+            keyType = SignatureScheme.getKeyType(sigHash)
+            padType = SignatureScheme.getPadding(sigHash)
+            hashName = SignatureScheme.getHash(sigHash)
+            saltLen = getattr(hashlib, hashName)().digest_size
+        except AttributeError:
+            serverKeyExchange.signAlg = SignatureAlgorithm.rsa
+            serverKeyExchange.hashAlg = getattr(HashAlgorithm, sigHash)
+            keyType = 'rsa'
+            padType = 'pkcs1'
+            hashName = sigHash
+            saltLen = 0
+
+        assert keyType == 'rsa'
+
+        hashBytes = serverKeyExchange.hash(self.clientHello.random,
+                                           self.serverHello.random)
+
+        serverKeyExchange.signature = \
+            self.privateKey.sign(hashBytes,
+                                 padding=padType,
+                                 hashAlg=hashName,
+                                 saltLen=saltLen)
+
+        if not serverKeyExchange.signature:
+            raise TLSInternalError("Empty signature")
+
+        if not self.privateKey.verify(serverKeyExchange.signature,
+                                      hashBytes,
+                                      padding=padType,
+                                      hashAlg=hashName,
+                                      saltLen=saltLen):
+            raise TLSInternalError("Server Key Exchange signature invalid")

     def signServerKeyExchange(self, serverKeyExchange, sigHash=None):
         """
@@ -79,40 +194,223 @@ class KeyExchange(object):
         :type sigHash: str
         :param sigHash: name of the signature hash to be used for signing
         """
-        pass
+        if self.serverHello.server_version < (3, 3):
+            if self.privateKey.key_type == "ecdsa":
+                serverKeyExchange.signAlg = SignatureAlgorithm.ecdsa
+            if self.privateKey.key_type == "dsa":
+                serverKeyExchange.signAlg = SignatureAlgorithm.dsa
+            hashBytes = serverKeyExchange.hash(self.clientHello.random,
+                                               self.serverHello.random)
+
+            serverKeyExchange.signature = self.privateKey.sign(hashBytes)
+
+            if not serverKeyExchange.signature:
+                raise TLSInternalError("Empty signature")
+
+            if not self.privateKey.verify(serverKeyExchange.signature,
+                                          hashBytes):
+                raise TLSInternalError("Server Key Exchange signature invalid")
+        else:
+            if self.privateKey.key_type == "ecdsa":
+                self._tls12_sign_ecdsa_SKE(serverKeyExchange, sigHash)
+            elif self.privateKey.key_type == "dsa":
+                self._tls12_sign_dsa_SKE(serverKeyExchange, sigHash)
+            elif self.privateKey.key_type in ("Ed25519", "Ed448"):
+                self._tls12_sign_eddsa_ske(serverKeyExchange, sigHash)
+            else:
+                self._tls12_signSKE(serverKeyExchange, sigHash)
+
+    @staticmethod
+    def _tls12_verify_ecdsa_SKE(serverKeyExchange, publicKey, clientRandom,
+                                serverRandom, validSigAlgs):
+        hashName = HashAlgorithm.toRepr(serverKeyExchange.hashAlg)
+        if not hashName:
+            raise TLSIllegalParameterException("Unknown hash algorithm")
+
+        hashBytes = serverKeyExchange.hash(clientRandom, serverRandom)
+
+        hashBytes = hashBytes[:publicKey.public_key.curve.baselen]
+
+        if not publicKey.verify(serverKeyExchange.signature, hashBytes,
+                                padding=None,
+                                hashAlg=hashName,
+                                saltLen=None):
+            raise TLSDecryptionFailed("Server Key Exchange signature "
+                                      "invalid")

     @staticmethod
-    def _tls12_verify_eddsa_ske(server_key_exchange, public_key,
-        client_random, server_random, valid_sig_algs):
+    def _tls12_verify_eddsa_ske(server_key_exchange, public_key, client_random,
+                                server_random, valid_sig_algs):
         """Verify SeverKeyExchange messages with EdDSA signatures."""
-        pass
+        del valid_sig_algs
+        sig_bytes = server_key_exchange.signature
+        if not sig_bytes:
+            raise TLSIllegalParameterException("Empty signature")
+
+        hash_bytes = server_key_exchange.hash(client_random, server_random)
+
+        if not public_key.hashAndVerify(sig_bytes,
+                                        hash_bytes):
+            raise TLSDecryptionFailed("Server Key Exchange signature invalid")
+
+    @staticmethod
+    def _tls12_verify_dsa_SKE(serverKeyExchange, publicKey, clientRandom,
+                              serverRandom, validSigAlgs):
+
+        hashBytes = serverKeyExchange.hash(clientRandom, serverRandom)
+
+        if not publicKey.verify(serverKeyExchange.signature, hashBytes):
+            raise TLSDecryptionFailed("Server Key Exchange signature "
+                                      "invalid")

     @staticmethod
     def _tls12_verify_SKE(serverKeyExchange, publicKey, clientRandom,
-        serverRandom, validSigAlgs):
+                          serverRandom, validSigAlgs):
         """Verify TLSv1.2 version of SKE."""
-        pass
+        if (serverKeyExchange.hashAlg, serverKeyExchange.signAlg) not in \
+                validSigAlgs:
+            raise TLSIllegalParameterException("Server selected "
+                                               "invalid signature "
+                                               "algorithm")
+        if (serverKeyExchange.hashAlg, serverKeyExchange.signAlg) in (
+                SignatureScheme.ed25519, SignatureScheme.ed448):
+            return KeyExchange._tls12_verify_eddsa_ske(serverKeyExchange,
+                                                       publicKey,
+                                                       clientRandom,
+                                                       serverRandom,
+                                                       validSigAlgs)
+        if serverKeyExchange.signAlg == SignatureAlgorithm.ecdsa:
+            return KeyExchange._tls12_verify_ecdsa_SKE(serverKeyExchange,
+                                                       publicKey,
+                                                       clientRandom,
+                                                       serverRandom,
+                                                       validSigAlgs)
+
+        elif serverKeyExchange.signAlg == SignatureAlgorithm.dsa:
+            return KeyExchange._tls12_verify_dsa_SKE(serverKeyExchange,
+                                                     publicKey,
+                                                     clientRandom,
+                                                     serverRandom,
+                                                     validSigAlgs)
+
+        schemeID = (serverKeyExchange.hashAlg,
+                    serverKeyExchange.signAlg)
+        scheme = SignatureScheme.toRepr(schemeID)
+        if scheme is not None:
+            keyType = SignatureScheme.getKeyType(scheme)
+            padType = SignatureScheme.getPadding(scheme)
+            hashName = SignatureScheme.getHash(scheme)
+            saltLen = getattr(hashlib, hashName)().digest_size
+        else:
+            if serverKeyExchange.signAlg != SignatureAlgorithm.rsa:
+                raise TLSInternalError("non-RSA sigs are not supported")
+            keyType = 'rsa'
+            padType = 'pkcs1'
+            saltLen = 0
+            hashName = HashAlgorithm.toRepr(serverKeyExchange.hashAlg)
+            if hashName is None:
+                msg = "Unknown hash ID: {0}"\
+                        .format(serverKeyExchange.hashAlg)
+                raise TLSIllegalParameterException(msg)
+        assert keyType == 'rsa'
+
+        hashBytes = serverKeyExchange.hash(clientRandom, serverRandom)
+
+        sigBytes = serverKeyExchange.signature
+        if not sigBytes:
+            raise TLSIllegalParameterException("Empty signature")
+
+        if not publicKey.verify(sigBytes, hashBytes,
+                                padding=padType,
+                                hashAlg=hashName,
+                                saltLen=saltLen):
+            raise TLSDecryptionFailed("Server Key Exchange signature "
+                                      "invalid")

     @staticmethod
     def verifyServerKeyExchange(serverKeyExchange, publicKey, clientRandom,
-        serverRandom, validSigAlgs):
+                                serverRandom, validSigAlgs):
         """Verify signature on the Server Key Exchange message

         the only acceptable signature algorithms are specified by validSigAlgs
         """
-        pass
+        if serverKeyExchange.version < (3, 3):
+            hashBytes = serverKeyExchange.hash(clientRandom, serverRandom)
+            sigBytes = serverKeyExchange.signature
+
+            if not sigBytes:
+                raise TLSIllegalParameterException("Empty signature")
+
+            if not publicKey.verify(sigBytes, hashBytes):
+                raise TLSDecryptionFailed("Server Key Exchange signature "
+                                          "invalid")
+        else:
+            KeyExchange._tls12_verify_SKE(serverKeyExchange, publicKey,
+                                          clientRandom, serverRandom,
+                                          validSigAlgs)

     @staticmethod
     def calcVerifyBytes(version, handshakeHashes, signatureAlg,
-        premasterSecret, clientRandom, serverRandom, prf_name=None,
-        peer_tag=b'client', key_type='rsa'):
+                        premasterSecret, clientRandom, serverRandom,
+                        prf_name = None, peer_tag=b'client', key_type="rsa"):
         """Calculate signed bytes for Certificate Verify"""
-        pass
+        if version == (3, 0):
+            masterSecret = calc_key(version, premasterSecret,
+                                    0, b"master secret",
+                                    client_random=clientRandom,
+                                    server_random=serverRandom,
+                                    output_length=48)
+            verifyBytes = handshakeHashes.digestSSL(masterSecret, b"")
+        elif version in ((3, 1), (3, 2)):
+            if key_type != "ecdsa":
+                verifyBytes = handshakeHashes.digest()
+            else:
+                verifyBytes = handshakeHashes.digest("sha1")
+        elif version == (3, 3):
+            if signatureAlg in (SignatureScheme.ed25519,
+                    SignatureScheme.ed448):
+                hashName = "intrinsic"
+                padding = None
+            elif signatureAlg[1] == SignatureAlgorithm.dsa:
+                hashName = HashAlgorithm.toRepr(signatureAlg[0])
+                padding = None
+            elif signatureAlg[1] != SignatureAlgorithm.ecdsa:
+                scheme = SignatureScheme.toRepr(signatureAlg)
+                if scheme is None:
+                    hashName = HashAlgorithm.toRepr(signatureAlg[0])
+                    padding = 'pkcs1'
+                else:
+                    hashName = SignatureScheme.getHash(scheme)
+                    padding = SignatureScheme.getPadding(scheme)
+            else:
+                padding = None
+                hashName = HashAlgorithm.toRepr(signatureAlg[0])
+            verifyBytes = handshakeHashes.digest(hashName)
+            if padding == 'pkcs1':
+                verifyBytes = RSAKey.addPKCS1Prefix(verifyBytes, hashName)
+        elif version == (3, 4):
+            scheme = SignatureScheme.toRepr(signatureAlg)
+            if scheme:
+                hash_name = SignatureScheme.getHash(scheme)
+            else:
+                # handles negative test cases when we try to pass in
+                # schemes that are not supported in TLS1.3
+                hash_name = HashAlgorithm.toRepr(signatureAlg[0])
+            verifyBytes = bytearray(b'\x20' * 64 +
+                                    b'TLS 1.3, ' + peer_tag +
+                                    b' CertificateVerify' +
+                                    b'\x00') + \
+                          handshakeHashes.digest(prf_name)
+            if hash_name != "intrinsic":
+                verifyBytes = secureHash(verifyBytes, hash_name)
+        else:
+            raise ValueError("Unsupported TLS version {0}".format(version))
+        return verifyBytes

     @staticmethod
     def makeCertificateVerify(version, handshakeHashes, validSigAlgs,
-        privateKey, certificateRequest, premasterSecret, clientRandom,
-        serverRandom):
+                              privateKey, certificateRequest, premasterSecret,
+                              clientRandom, serverRandom):
         """Create a Certificate Verify message

         :param version: protocol version in use
@@ -127,7 +425,71 @@ class KeyExchange(object):
         :param serverRandom: server provided random value, needed only for
             SSLv3
         """
-        pass
+        signatureAlgorithm = None
+        if privateKey.key_type == "ecdsa" and version < (3, 3):
+            signatureAlgorithm = (HashAlgorithm.sha1, SignatureAlgorithm.ecdsa)
+        # in TLS 1.2 we must decide which algorithm to use for signing
+        if version == (3, 3):
+            serverSigAlgs = certificateRequest.supported_signature_algs
+            signatureAlgorithm = getFirstMatching(validSigAlgs, serverSigAlgs)
+            # if none acceptable, do a last resort:
+            if signatureAlgorithm is None:
+                signatureAlgorithm = validSigAlgs[0]
+        verifyBytes = KeyExchange.calcVerifyBytes(version, handshakeHashes,
+                                                  signatureAlgorithm,
+                                                  premasterSecret,
+                                                  clientRandom,
+                                                  serverRandom,
+                                                  key_type=privateKey.key_type)
+        if signatureAlgorithm and signatureAlgorithm in (
+                SignatureScheme.ed25519, SignatureScheme.ed448):
+            padding = None
+            hashName = "intrinsic"
+            saltLen = None
+            sig_func = privateKey.hashAndSign
+            ver_func = privateKey.hashAndVerify
+        elif signatureAlgorithm and \
+                signatureAlgorithm[1] == SignatureAlgorithm.ecdsa:
+            padding = None
+            hashName = HashAlgorithm.toRepr(signatureAlgorithm[0])
+            saltLen = None
+            verifyBytes = verifyBytes[:privateKey.private_key.curve.baselen]
+            sig_func = privateKey.sign
+            ver_func = privateKey.verify
+        elif signatureAlgorithm and \
+                signatureAlgorithm[1] == SignatureAlgorithm.dsa:
+            padding = None
+            hashName = HashAlgorithm.toRepr(signatureAlgorithm[0])
+            saltLen = None
+            sig_func = privateKey.sign
+            ver_func = privateKey.verify
+        else:
+            scheme = SignatureScheme.toRepr(signatureAlgorithm)
+            # for pkcs1 signatures hash is used to add PKCS#1 prefix, but
+            # that was already done by calcVerifyBytes
+            hashName = None
+            saltLen = 0
+            if scheme is None:
+                padding = 'pkcs1'
+            else:
+                padding = SignatureScheme.getPadding(scheme)
+                if padding == 'pss':
+                    hashName = SignatureScheme.getHash(scheme)
+                    saltLen = getattr(hashlib, hashName)().digest_size
+            sig_func = privateKey.sign
+            ver_func = privateKey.verify
+
+        signedBytes = sig_func(verifyBytes,
+                               padding,
+                               hashName,
+                               saltLen)
+        if not ver_func(signedBytes, verifyBytes, padding, hashName,
+                        saltLen):
+            raise TLSInternalError("Certificate Verify signature invalid")
+        certificateVerify = CertificateVerify(version)
+        certificateVerify.create(signedBytes, signatureAlgorithm)
+
+        return certificateVerify


 class AuthenticatedKeyExchange(KeyExchange):
@@ -139,7 +501,9 @@ class AuthenticatedKeyExchange(KeyExchange):

     def makeServerKeyExchange(self, sigHash=None):
         """Prepare server side of key exchange with selected parameters"""
-        pass
+        ske = super(AuthenticatedKeyExchange, self).makeServerKeyExchange()
+        self.signServerKeyExchange(ske, sigHash)
+        return ske


 class RSAKeyExchange(KeyExchange):
@@ -151,24 +515,49 @@ class RSAKeyExchange(KeyExchange):

     def __init__(self, cipherSuite, clientHello, serverHello, privateKey):
         super(RSAKeyExchange, self).__init__(cipherSuite, clientHello,
-            serverHello, privateKey)
+                                             serverHello, privateKey)
         self.encPremasterSecret = None

     def makeServerKeyExchange(self, sigHash=None):
         """Don't create a server key exchange for RSA key exchange"""
-        pass
+        return None

     def processClientKeyExchange(self, clientKeyExchange):
         """Decrypt client key exchange, return premaster secret"""
-        pass
-
-    def processServerKeyExchange(self, srvPublicKey, serverKeyExchange):
+        premasterSecret = self.privateKey.decrypt(\
+            clientKeyExchange.encryptedPreMasterSecret)
+
+        # On decryption failure randomize premaster secret to avoid
+        # Bleichenbacher's "million message" attack
+        randomPreMasterSecret = getRandomBytes(48)
+        if not premasterSecret:
+            premasterSecret = randomPreMasterSecret
+        elif len(premasterSecret) != 48:
+            premasterSecret = randomPreMasterSecret
+        else:
+            versionCheck = (premasterSecret[0], premasterSecret[1])
+            if versionCheck != self.clientHello.client_version:
+                #Tolerate buggy IE clients
+                if versionCheck != self.serverHello.server_version:
+                    premasterSecret = randomPreMasterSecret
+        return premasterSecret
+
+    def processServerKeyExchange(self, srvPublicKey,
+                                 serverKeyExchange):
         """Generate premaster secret for server"""
-        pass
+        del serverKeyExchange # not present in RSA key exchange
+        premasterSecret = getRandomBytes(48)
+        premasterSecret[0] = self.clientHello.client_version[0]
+        premasterSecret[1] = self.clientHello.client_version[1]
+
+        self.encPremasterSecret = srvPublicKey.encrypt(premasterSecret)
+        return premasterSecret

     def makeClientKeyExchange(self):
         """Return a client key exchange with clients key share"""
-        pass
+        clientKeyExchange = super(RSAKeyExchange, self).makeClientKeyExchange()
+        clientKeyExchange.createRSA(self.encPremasterSecret)
+        return clientKeyExchange


 class ADHKeyExchange(KeyExchange):
@@ -178,15 +567,17 @@ class ADHKeyExchange(KeyExchange):
     FFDHE without signing serverKeyExchange useful for anonymous DH
     """

-    def __init__(self, cipherSuite, clientHello, serverHello, dhParams=None,
-        dhGroups=None):
+    def __init__(self, cipherSuite, clientHello, serverHello,
+                 dhParams=None, dhGroups=None):
         super(ADHKeyExchange, self).__init__(cipherSuite, clientHello,
-            serverHello)
+                                             serverHello)
+#pylint: enable = invalid-name
         self.dh_Xs = None
         self.dh_Yc = None
         if dhParams:
             self.dh_g, self.dh_p = dhParams
         else:
+            # 2048-bit MODP Group (RFC 5054, group 3)
             self.dh_g, self.dh_p = goodGroupParameters[2]
         self.dhGroups = dhGroups

@@ -194,28 +585,70 @@ class ADHKeyExchange(KeyExchange):
         """
         Prepare server side of anonymous key exchange with selected parameters
         """
-        pass
+        # Check for RFC 7919 support
+        ext = self.clientHello.getExtension(ExtensionType.supported_groups)
+        if ext and self.dhGroups:
+            commonGroup = getFirstMatching(ext.groups, self.dhGroups)
+            if commonGroup:
+                self.dh_g, self.dh_p = RFC7919_GROUPS[commonGroup - 256]
+            elif getFirstMatching(ext.groups, range(256, 512)):
+                raise TLSInternalError("DHE key exchange attempted despite no "
+                                       "overlap between supported groups")
+
+        # for TLS < 1.3 we need special algorithm to select params (see above)
+        # so do not pass in the group, if we selected one
+        kex = FFDHKeyExchange(None, self.serverHello.server_version,
+                              self.dh_g, self.dh_p)
+        self.dh_Xs = kex.get_random_private_key()
+        dh_Ys = kex.calc_public_value(self.dh_Xs)
+
+        version = self.serverHello.server_version
+        serverKeyExchange = ServerKeyExchange(self.cipherSuite, version)
+        serverKeyExchange.createDH(self.dh_p, self.dh_g, dh_Ys)
+        # No sign for anonymous ServerKeyExchange.
+        return serverKeyExchange

     def processClientKeyExchange(self, clientKeyExchange):
         """Use client provided parameters to establish premaster secret"""
-        pass
+        dh_Yc = clientKeyExchange.dh_Yc
+
+        kex = FFDHKeyExchange(None, self.serverHello.server_version,
+                              self.dh_g, self.dh_p)
+        return kex.calc_shared_key(self.dh_Xs, dh_Yc)

     def processServerKeyExchange(self, srvPublicKey, serverKeyExchange):
         """Process the server key exchange, return premaster secret."""
-        pass
+        del srvPublicKey
+        dh_p = serverKeyExchange.dh_p
+        # TODO make the minimum changeable
+        if dh_p < 2**1023:
+            raise TLSInsufficientSecurity("DH prime too small")
+        dh_g = serverKeyExchange.dh_g
+        dh_Ys = serverKeyExchange.dh_Ys
+
+        kex = FFDHKeyExchange(None, self.serverHello.server_version,
+                              dh_g, dh_p)
+
+        dh_Xc = kex.get_random_private_key()
+        self.dh_Yc = kex.calc_public_value(dh_Xc)
+        return kex.calc_shared_key(dh_Xc, dh_Ys)

     def makeClientKeyExchange(self):
         """Create client key share for the key exchange"""
-        pass
+        cke = super(ADHKeyExchange, self).makeClientKeyExchange()
+        cke.createDH(self.dh_Yc)
+        return cke


+# the DHE_RSA part comes from IETF ciphersuite names, we want to keep it
+#pylint: disable = invalid-name
 class DHE_RSAKeyExchange(AuthenticatedKeyExchange, ADHKeyExchange):
     """
     Handling of authenticated ephemeral Diffe-Hellman Key exchange.
     """

     def __init__(self, cipherSuite, clientHello, serverHello, privateKey,
-        dhParams=None, dhGroups=None):
+                 dhParams=None, dhGroups=None):
         """
         Create helper object for Diffie-Hellamn key exchange.

@@ -226,7 +659,9 @@ class DHE_RSAKeyExchange(AuthenticatedKeyExchange, ADHKeyExchange):
         :type dhParams: 2-element tuple of int
         """
         super(DHE_RSAKeyExchange, self).__init__(cipherSuite, clientHello,
-            serverHello, dhParams, dhGroups)
+                                                 serverHello, dhParams,
+                                                 dhGroups)
+#pylint: enable = invalid-name
         self.privateKey = privateKey


@@ -237,10 +672,10 @@ class AECDHKeyExchange(KeyExchange):
     ECDHE without signing serverKeyExchange useful for anonymous ECDH
     """

-    def __init__(self, cipherSuite, clientHello, serverHello,
-        acceptedCurves, defaultCurve=GroupName.secp256r1):
+    def __init__(self, cipherSuite, clientHello, serverHello, acceptedCurves,
+                 defaultCurve=GroupName.secp256r1):
         super(AECDHKeyExchange, self).__init__(cipherSuite, clientHello,
-            serverHello)
+                                               serverHello)
         self.ecdhXs = None
         self.acceptedCurves = acceptedCurves
         self.group_id = None
@@ -249,28 +684,100 @@ class AECDHKeyExchange(KeyExchange):

     def makeServerKeyExchange(self, sigHash=None):
         """Create AECDHE version of Server Key Exchange"""
-        pass
+        #Get client supported groups
+        client_curves = self.clientHello.getExtension(
+                ExtensionType.supported_groups)
+        if client_curves is None:
+            # in case there is no extension, we can pick any curve,
+            # use the configured one
+            client_curves = [self.defaultCurve]
+        elif not client_curves.groups:
+            # extension should have been validated before
+            raise TLSInternalError("Can't do ECDHE with no client curves")
+        else:
+            client_curves = client_curves.groups
+
+        #Pick first client preferred group we support
+        self.group_id = getFirstMatching(client_curves, self.acceptedCurves)
+        if self.group_id is None:
+            raise TLSInsufficientSecurity("No mutual groups")
+
+        kex = ECDHKeyExchange(self.group_id, self.serverHello.server_version)
+        self.ecdhXs = kex.get_random_private_key()
+
+        if isinstance(self.ecdhXs, ecdsa.keys.SigningKey):
+            ecdhYs = bytearray(
+                self.ecdhXs.get_verifying_key().to_string(
+                    encoding = 'uncompressed'
+                    )
+                )
+        else:
+            ecdhYs = kex.calc_public_value(self.ecdhXs)
+
+        version = self.serverHello.server_version
+        serverKeyExchange = ServerKeyExchange(self.cipherSuite, version)
+        serverKeyExchange.createECDH(ECCurveType.named_curve,
+                                     named_curve=self.group_id,
+                                     point=ecdhYs)
+        # No sign for anonymous ServerKeyExchange
+        return serverKeyExchange

     def processClientKeyExchange(self, clientKeyExchange):
         """Calculate premaster secret from previously generated SKE and CKE"""
-        pass
+        ecdhYc = clientKeyExchange.ecdh_Yc
+
+        if not ecdhYc:
+            raise TLSDecodeError("No key share")
+
+        kex = ECDHKeyExchange(self.group_id, self.serverHello.server_version)
+        return kex.calc_shared_key(self.ecdhXs, ecdhYc)

     def processServerKeyExchange(self, srvPublicKey, serverKeyExchange):
         """Process the server key exchange, return premaster secret"""
-        pass
+        del srvPublicKey
+
+        if serverKeyExchange.curve_type != ECCurveType.named_curve \
+            or serverKeyExchange.named_curve not in self.acceptedCurves:
+            raise TLSIllegalParameterException("Server picked curve we "
+                                               "didn't advertise")
+
+        ecdh_Ys = serverKeyExchange.ecdh_Ys
+        if not ecdh_Ys:
+            raise TLSDecodeError("Empty server key share")
+
+        kex = ECDHKeyExchange(serverKeyExchange.named_curve,
+                              self.serverHello.server_version)
+        ecdhXc = kex.get_random_private_key()
+        if isinstance(ecdhXc, ecdsa.keys.SigningKey):
+            self.ecdhYc = bytearray(
+                ecdhXc.get_verifying_key().to_string(
+                    encoding = 'uncompressed'
+                    )
+                )
+        else:
+            self.ecdhYc = kex.calc_public_value(ecdhXc)
+        return kex.calc_shared_key(ecdhXc, ecdh_Ys)

     def makeClientKeyExchange(self):
         """Make client key exchange for ECDHE"""
-        pass
+        cke = super(AECDHKeyExchange, self).makeClientKeyExchange()
+        cke.createECDH(self.ecdhYc)
+        return cke


+# The ECDHE_RSA part comes from the IETF names of ciphersuites, so we want to
+# keep it
+#pylint: disable = invalid-name
 class ECDHE_RSAKeyExchange(AuthenticatedKeyExchange, AECDHKeyExchange):
     """Helper class for conducting ECDHE key exchange"""

     def __init__(self, cipherSuite, clientHello, serverHello, privateKey,
-        acceptedCurves, defaultCurve=GroupName.secp256r1):
+                 acceptedCurves, defaultCurve=GroupName.secp256r1):
         super(ECDHE_RSAKeyExchange, self).__init__(cipherSuite, clientHello,
-            serverHello, acceptedCurves, defaultCurve)
+                                                   serverHello,
+                                                   acceptedCurves,
+                                                   defaultCurve)
+#pylint: enable = invalid-name
         self.privateKey = privateKey


@@ -278,10 +785,10 @@ class SRPKeyExchange(KeyExchange):
     """Helper class for conducting SRP key exchange"""

     def __init__(self, cipherSuite, clientHello, serverHello, privateKey,
-        verifierDB, srpUsername=None, password=None, settings=None):
+                 verifierDB, srpUsername=None, password=None, settings=None):
         """Link Key Exchange options with verifierDB for SRP"""
         super(SRPKeyExchange, self).__init__(cipherSuite, clientHello,
-            serverHello, privateKey)
+                                             serverHello, privateKey)
         self.N = None
         self.v = None
         self.b = None
@@ -292,25 +799,86 @@ class SRPKeyExchange(KeyExchange):
         self.password = password
         self.settings = settings
         if srpUsername is not None and not isinstance(srpUsername, bytearray):
-            raise TypeError('srpUsername must be a bytearray object')
+            raise TypeError("srpUsername must be a bytearray object")
         if password is not None and not isinstance(password, bytearray):
-            raise TypeError('password must be a bytearray object')
+            raise TypeError("password must be a bytearray object")

     def makeServerKeyExchange(self, sigHash=None):
         """Create SRP version of Server Key Exchange"""
-        pass
+        srpUsername = bytes(self.clientHello.srp_username)
+        #Get parameters from username
+        try:
+            entry = self.verifierDB[srpUsername]
+        except KeyError:
+            raise TLSUnknownPSKIdentity("Unknown identity")
+        (self.N, g, s, self.v) = entry
+
+        #Calculate server's ephemeral DH values (b, B)
+        self.b = bytesToNumber(getRandomBytes(32))
+        k = makeK(self.N, g)
+        self.B = (powMod(g, self.b, self.N) + (k * self.v)) % self.N
+
+        #Create ServerKeyExchange, signing it if necessary
+        serverKeyExchange = ServerKeyExchange(self.cipherSuite,
+                                              self.serverHello.server_version)
+        serverKeyExchange.createSRP(self.N, g, s, self.B)
+        if self.cipherSuite in CipherSuite.srpCertSuites:
+            self.signServerKeyExchange(serverKeyExchange, sigHash)
+        return serverKeyExchange

     def processClientKeyExchange(self, clientKeyExchange):
         """Calculate premaster secret from Client Key Exchange and sent SKE"""
-        pass
+        A = clientKeyExchange.srp_A
+        if A % self.N == 0:
+            raise TLSIllegalParameterException("Invalid SRP A value")
+
+        #Calculate u
+        u = makeU(self.N, A, self.B)
+
+        #Calculate premaster secret
+        S = powMod((A * powMod(self.v, u, self.N)) % self.N, self.b, self.N)
+        return numberToByteArray(S)

     def processServerKeyExchange(self, srvPublicKey, serverKeyExchange):
         """Calculate premaster secret from ServerKeyExchange"""
-        pass
+        del srvPublicKey # irrelevant for SRP
+        N = serverKeyExchange.srp_N
+        g = serverKeyExchange.srp_g
+        s = serverKeyExchange.srp_s
+        B = serverKeyExchange.srp_B
+
+        if (g, N) not in goodGroupParameters:
+            raise TLSInsufficientSecurity("Unknown group parameters")
+        if numBits(N) < self.settings.minKeySize:
+            raise TLSInsufficientSecurity("N value is too small: {0}".\
+                                          format(numBits(N)))
+        if numBits(N) > self.settings.maxKeySize:
+            raise TLSInsufficientSecurity("N value is too large: {0}".\
+                                          format(numBits(N)))
+        if B % N == 0:
+            raise TLSIllegalParameterException("Suspicious B value")
+
+        #Client ephemeral value
+        a = bytesToNumber(getRandomBytes(32))
+        self.A = powMod(g, a, N)
+
+        #Calculate client's static DH values (x, v)
+        x = makeX(s, self.srpUsername, self.password)
+        v = powMod(g, x, N)
+
+        #Calculate u
+        u = makeU(N, self.A, B)
+
+        #Calculate premaster secret
+        k = makeK(N, g)
+        S = powMod((B - (k*v)) % N, a+(u*x), N)
+        return numberToByteArray(S)

     def makeClientKeyExchange(self):
         """Create ClientKeyExchange"""
-        pass
+        cke = super(SRPKeyExchange, self).makeClientKeyExchange()
+        cke.createSRP(self.A)
+        return cke


 class RawDHKeyExchange(object):
@@ -333,15 +901,15 @@ class RawDHKeyExchange(object):
         """
         Generate a random value suitable for use as the private value of KEX.
         """
-        pass
+        raise NotImplementedError("Abstract class")

     def calc_public_value(self, private):
         """Calculate the public value from the provided private value."""
-        pass
+        raise NotImplementedError("Abstract class")

     def calc_shared_key(self, private, peer_share):
         """Calcualte the shared key given our private and remote share value"""
-        pass
+        raise NotImplementedError("Abstract class")


 class FFDHKeyExchange(RawDHKeyExchange):
@@ -350,16 +918,16 @@ class FFDHKeyExchange(RawDHKeyExchange):
     def __init__(self, group, version, generator=None, prime=None):
         super(FFDHKeyExchange, self).__init__(group, version)
         if prime and group:
-            raise ValueError(
-                "Can't set the RFC7919 group and custom params at the same time"
-                )
+            raise ValueError("Can't set the RFC7919 group and custom params"
+                             " at the same time")
         if group:
-            self.generator, self.prime = RFC7919_GROUPS[group - 256]
+            self.generator, self.prime = RFC7919_GROUPS[group-256]
         else:
             self.prime = prime
             self.generator = generator
+
         if not 1 < self.generator < self.prime:
-            raise TLSIllegalParameterException('Invalid DH generator')
+            raise TLSIllegalParameterException("Invalid DH generator")

     def get_random_private_key(self):
         """
@@ -367,7 +935,10 @@ class FFDHKeyExchange(RawDHKeyExchange):

         :rtype: int
         """
-        pass
+        # Per RFC 3526, Section 1, the exponent should have double the entropy
+        # of the strength of the group.
+        needed_bytes = divceil(paramStrength(self.prime) * 2, 8)
+        return bytesToNumber(getRandomBytes(needed_bytes))

     def calc_public_value(self, private):
         """
@@ -375,19 +946,45 @@ class FFDHKeyExchange(RawDHKeyExchange):

         :rtype: int
         """
-        pass
+        dh_Y = powMod(self.generator, private, self.prime)
+        if dh_Y in (1, self.prime - 1):
+            raise TLSIllegalParameterException("Small subgroup capture")
+        if self.version < (3, 4):
+            return dh_Y
+        else:
+            return numberToByteArray(dh_Y, numBytes(self.prime))

     def _normalise_peer_share(self, peer_share):
         """Convert the peer_share to number if necessary."""
-        pass
+        if isinstance(peer_share, (int_types)):
+            return peer_share
+
+        if numBytes(self.prime) != len(peer_share):
+            raise TLSIllegalParameterException(
+                "Key share does not match FFDH prime")
+        return bytesToNumber(peer_share)

     def calc_shared_key(self, private, peer_share):
         """Calculate the shared key."""
-        pass
+        peer_share = self._normalise_peer_share(peer_share)
+        # First half of RFC 2631, Section 2.1.5. Validate the client's public
+        # key.
+        # use of safe primes also means that the p-1 is invalid
+        if not 2 <= peer_share < self.prime - 1:
+            raise TLSIllegalParameterException("Invalid peer key share")
+
+        S = powMod(peer_share, private, self.prime)
+        if S in (1, self.prime - 1):
+            raise TLSIllegalParameterException("Small subgroup capture")
+        if self.version < (3, 4):
+            return numberToByteArray(S)
+        else:
+            return numberToByteArray(S, numBytes(self.prime))


 class ECDHKeyExchange(RawDHKeyExchange):
     """Implementation of the Elliptic Curve Diffie-Hellman key exchange."""
+
     _x_groups = set((GroupName.x25519, GroupName.x448))

     @staticmethod
@@ -397,23 +994,71 @@ class ECDHKeyExchange(RawDHKeyExchange):

         :raises TLSIllegalParameterException: if the value is all zero
         """
-        pass
+        summa = 0
+        for i in value:
+            summa |= i
+        if summa == 0:
+            raise TLSIllegalParameterException("Invalid key share")

     def __init__(self, group, version):
         super(ECDHKeyExchange, self).__init__(group, version)

     def get_random_private_key(self):
         """Return random private key value for the selected curve."""
-        pass
+        if self.group in self._x_groups:
+            if self.group == GroupName.x25519:
+                return getRandomBytes(X25519_ORDER_SIZE)
+            else:
+                return getRandomBytes(X448_ORDER_SIZE)
+        else:
+            curve = getCurveByName(GroupName.toStr(self.group))
+            return ecdsa.keys.SigningKey.generate(curve)

     def _get_fun_gen_size(self):
         """Return the function and generator for X25519/X448 KEX."""
-        pass
+        if self.group == GroupName.x25519:
+            return x25519, bytearray(X25519_G), X25519_ORDER_SIZE
+        else:
+            return x448, bytearray(X448_G), X448_ORDER_SIZE

     def calc_public_value(self, private):
         """Calculate public value for given private key."""
-        pass
+        if isinstance(private, ecdsa.keys.SigningKey):
+            return private.verifying_key.to_string('uncompressed')
+        if self.group in self._x_groups:
+            fun, generator, _ = self._get_fun_gen_size()
+            return fun(private, generator)
+        else:
+            curve = getCurveByName(GroupName.toStr(self.group))
+            point = curve.generator * private
+            return bytearray(point.to_bytes('uncompressed'))

     def calc_shared_key(self, private, peer_share):
         """Calculate the shared key,"""
-        pass
+
+        if self.group in self._x_groups:
+            fun, _, size = self._get_fun_gen_size()
+            if len(peer_share) != size:
+                raise TLSIllegalParameterException("Invalid key share")
+            if isinstance(private, ecdsa.keys.SigningKey):
+                private = bytesToNumber(private.to_string())
+            S = fun(private, peer_share)
+            self._non_zero_check(S)
+            return S
+
+        curve = getCurveByName(GroupName.toRepr(self.group))
+        try:
+            abstractPoint = ecdsa.ellipticcurve.AbstractPoint()
+            point = abstractPoint.from_bytes(curve.curve, peer_share)
+            ecdhYc = ecdsa.ellipticcurve.Point(
+                curve.curve, point[0], point[1])
+
+        except (AssertionError, DecodeError):
+            raise TLSIllegalParameterException("Invalid ECC point")
+        if isinstance(private, ecdsa.keys.SigningKey):
+            ecdh = ecdsa.ecdh.ECDH(curve=curve, private_key=private)
+            ecdh.load_received_public_key_bytes(peer_share)
+            return bytearray(ecdh.generate_sharedsecret_bytes())
+        S = ecdhYc * private
+
+        return numberToByteArray(S.x(), getPointByteSize(ecdhYc))
diff --git a/tlslite/mathtls.py b/tlslite/mathtls.py
index abd3d46..8406ff7 100644
--- a/tlslite/mathtls.py
+++ b/tlslite/mathtls.py
@@ -1,10 +1,21 @@
+# Authors: 
+#   Trevor Perrin
+#   Dave Baggett (Arcode Corporation) - MD5 support for MAC_SSL
+#   Yngve Pettersen (ported by Paul Sokolovsky) - TLS 1.2
+#   Hubert Kario - SHA384 PRF
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """Miscellaneous helper functions."""
+
 from .utils.compat import *
 from .utils.cryptomath import *
 from .constants import CipherSuite
 from .utils import tlshashlib as hashlib
 from .utils import tlshmac as hmac
 from .utils.deprecations import deprecated_method
+
+
 FFDHE_PARAMETERS = {}
 """
 Listing of all well known FFDH parameters.
@@ -14,26 +25,36 @@ Please note that this dictionary includes all groups that are well-known

 You should use RFC7919_GROUPS for well-known secure groups.
 """
-RFC2409_GROUP1 = 2, int(remove_whitespace(
-    """
+
+
+# RFC 2409 section 6.1, First Oakley Group, 768 bit MODP
+RFC2409_GROUP1 = (
+    2,
+    int(remove_whitespace("""
          FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
          29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
          EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
-         E485B576 625E7EC6 F44C42E9 A63A3620 FFFFFFFF FFFFFFFF"""
-    ), 16)
-FFDHE_PARAMETERS['RFC2409 group 1'] = RFC2409_GROUP1
-RFC2409_GROUP2 = 2, int(remove_whitespace(
-    """
+         E485B576 625E7EC6 F44C42E9 A63A3620 FFFFFFFF FFFFFFFF"""), 16))
+FFDHE_PARAMETERS["RFC2409 group 1"] = RFC2409_GROUP1
+
+
+# RFC 2409 section 6.2, Second Oakley Group, 1024 bit MODP
+RFC2409_GROUP2 = (
+    2,
+    int(remove_whitespace("""
          FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
          29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
          EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
          E485B576 625E7EC6 F44C42E9 A637ED6B 0BFF5CB6 F406B7ED
          EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE65381
-         FFFFFFFF FFFFFFFF"""
-    ), 16)
-FFDHE_PARAMETERS['RFC2409 group 2'] = RFC2409_GROUP2
-RFC3526_GROUP5 = 2, int(remove_whitespace(
-    """
+         FFFFFFFF FFFFFFFF"""), 16))
+FFDHE_PARAMETERS["RFC2409 group 2"] = RFC2409_GROUP2
+
+
+# RFC 3526 section 2, 1536 bit MODP
+RFC3526_GROUP5 = (
+    2,
+    int(remove_whitespace("""
       FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
       29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
       EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
@@ -41,11 +62,14 @@ RFC3526_GROUP5 = 2, int(remove_whitespace(
       EE386BFB 5A899FA5 AE9F2411 7C4B1FE6 49286651 ECE45B3D
       C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
       83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
-      670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF"""
-    ), 16)
-FFDHE_PARAMETERS['RFC3526 group 5'] = RFC3526_GROUP5
-RFC3526_GROUP14 = 2, int(remove_whitespace(
-    """
+      670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF"""), 16))
+FFDHE_PARAMETERS["RFC3526 group 5"] = RFC3526_GROUP5
+
+
+# RFC 3526 section 3, 2048 bit MODP
+RFC3526_GROUP14 = (
+    2,
+    int(remove_whitespace("""
       FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
       29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
       EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
@@ -56,11 +80,14 @@ RFC3526_GROUP14 = 2, int(remove_whitespace(
       670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B
       E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9
       DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510
-      15728E5A 8AACAA68 FFFFFFFF FFFFFFFF"""
-    ), 16)
-FFDHE_PARAMETERS['RFC3526 group 14'] = RFC3526_GROUP14
-RFC3526_GROUP15 = 2, int(remove_whitespace(
-    """
+      15728E5A 8AACAA68 FFFFFFFF FFFFFFFF"""), 16))
+FFDHE_PARAMETERS["RFC3526 group 14"] = RFC3526_GROUP14
+
+
+# RFC 3526 section 4, 3072 bit MODP
+RFC3526_GROUP15 = (
+    2,
+    int(remove_whitespace("""
       FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
       29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
       EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
@@ -76,11 +103,14 @@ RFC3526_GROUP15 = 2, int(remove_whitespace(
       ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B
       F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
       BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31
-      43DB5BFC E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF"""
-    ), 16)
-FFDHE_PARAMETERS['RFC3526 group 15'] = RFC3526_GROUP15
-RFC3526_GROUP16 = 2, int(remove_whitespace(
-    """
+      43DB5BFC E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF"""), 16))
+FFDHE_PARAMETERS["RFC3526 group 15"] = RFC3526_GROUP15
+
+
+# RFC 3526 section 5, 4096 bit MODP
+RFC3526_GROUP16 = (
+    2,
+    int(remove_whitespace("""
       FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
       29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
       EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
@@ -102,11 +132,14 @@ RFC3526_GROUP16 = 2, int(remove_whitespace(
       287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED
       1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9
       93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34063199
-      FFFFFFFF FFFFFFFF"""
-    ), 16)
-FFDHE_PARAMETERS['RFC3526 group 16'] = RFC3526_GROUP16
-RFC3526_GROUP17 = 2, int(remove_whitespace(
-    """
+      FFFFFFFF FFFFFFFF"""), 16))
+FFDHE_PARAMETERS["RFC3526 group 16"] = RFC3526_GROUP16
+
+
+# RFC 3526 section 6, 6144 bit MODP
+RFC3526_GROUP17 = (
+    2,
+    int(remove_whitespace("""
    FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
    8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B
    302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9
@@ -134,11 +167,14 @@ RFC3526_GROUP17 = 2, int(remove_whitespace(
    CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328 06A1D58B
    B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C DA56C9EC 2EF29632
    387FE8D7 6E3C0468 043E8F66 3F4860EE 12BF2D5B 0B7474D6 E694F91E
-   6DCC4024 FFFFFFFF FFFFFFFF"""
-    ), 16)
-FFDHE_PARAMETERS['RFC3526 group 17'] = RFC3526_GROUP17
-RFC3526_GROUP18 = 2, int(remove_whitespace(
-    """
+   6DCC4024 FFFFFFFF FFFFFFFF"""), 16))
+FFDHE_PARAMETERS["RFC3526 group 17"] = RFC3526_GROUP17
+
+
+# RFC 3526 section 7, 8192 bit MODP
+RFC3526_GROUP18 = (
+    2,
+    int(remove_whitespace("""
       FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1
       29024E08 8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD
       EF9519B3 CD3A431B 302B0A6D F25F1437 4FE1356D 6D51C245
@@ -181,27 +217,34 @@ RFC3526_GROUP18 = 2, int(remove_whitespace(
       B1D510BD 7EE74D73 FAF36BC3 1ECFA268 359046F4 EB879F92
       4009438B 481C6CD7 889A002E D5EE382B C9190DA6 FC026E47
       9558E447 5677E9AA 9E3050E2 765694DF C81F56E8 80B96E71
-      60C980DD 98EDD3DF FFFFFFFF FFFFFFFF"""
-    ), 16)
-FFDHE_PARAMETERS['RFC3526 group 18'] = RFC3526_GROUP18
-goodGroupParameters = [(2, int(remove_whitespace(
-    """
+      60C980DD 98EDD3DF FFFFFFFF FFFFFFFF"""), 16))
+FFDHE_PARAMETERS["RFC3526 group 18"] = RFC3526_GROUP18
+
+
+# 1024, 1536, 2048, 3072, 4096, 6144, and 8192 bit groups from RFC 5054
+# Formatted as in the RFC
+goodGroupParameters = [
+    # RFC 5054, 1, 1024-bit Group
+    (2, int(remove_whitespace(
+        """
           EEAF0AB9 ADB38DD6 9C33F80A FA8FC5E8 60726187 75FF3C0B 9EA2314C
           9C256576 D674DF74 96EA81D3 383B4813 D692C6E0 E0D5D8E2 50B98BE4
           8E495C1D 6089DAD1 5DC7D7B4 6154D6B6 CE8EF4AD 69B15D49 82559B29
           7BCF1885 C529F566 660E57EC 68EDBC3C 05726CC0 2FD4CBF4 976EAA9A
-          FD5138FE 8376435B 9FC61D2F C0EB06E3"""
-    ), 16)), (2, int(remove_whitespace(
-    """
+          FD5138FE 8376435B 9FC61D2F C0EB06E3"""), 16)),
+    # RFC 5054, 2, 1536-bit Group
+    (2, int(remove_whitespace(
+        """
           9DEF3CAF B939277A B1F12A86 17A47BBB DBA51DF4 99AC4C80 BEEEA961
           4B19CC4D 5F4F5F55 6E27CBDE 51C6A94B E4607A29 1558903B A0D0F843
           80B655BB 9A22E8DC DF028A7C EC67F0D0 8134B1C8 B9798914 9B609E0B
           E3BAB63D 47548381 DBC5B1FC 764E3F4B 53DD9DA1 158BFD3E 2B9C8CF5
           6EDF0195 39349627 DB2FD53D 24B7C486 65772E43 7D6C7F8C E442734A
           F7CCB7AE 837C264A E3A9BEB8 7F8A2FE9 B8B5292E 5A021FFF 5E91479E
-          8CE7A28C 2442C6F3 15180F93 499A234D CF76E3FE D135F9BB"""
-    ), 16)), (2, int(remove_whitespace(
-    """
+          8CE7A28C 2442C6F3 15180F93 499A234D CF76E3FE D135F9BB"""), 16)),
+    # RFC 5054, 3, 2048-bit Group
+    (2, int(remove_whitespace(
+        """
           AC6BDB41 324A9A9B F166DE5E 1389582F AF72B665 1987EE07 FC319294
           3DB56050 A37329CB B4A099ED 8193E075 7767A13D D52312AB 4B03310D
           CD7F48A9 DA04FD50 E8083969 EDB767B0 CF609517 9A163AB3 661A05FB
@@ -211,9 +254,10 @@ goodGroupParameters = [(2, int(remove_whitespace(
           5EA77A27 75D2ECFA 032CFBDB F52FB378 61602790 04E57AE6 AF874E73
           03CE5329 9CCC041C 7BC308D8 2A5698F3 A8D0C382 71AE35F8 E9DBFBB6
           94B5C803 D89F7AE4 35DE236D 525F5475 9B65E372 FCD68EF2 0FA7111F
-          9E4AFF73"""
-    ), 16)), (5, int(remove_whitespace(
-    """
+          9E4AFF73"""), 16)),
+    # RFC 5054, 4, 3072-bit Group
+    (5, int(remove_whitespace(
+        """
           FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
           8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B
           302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9
@@ -227,9 +271,10 @@ goodGroupParameters = [(2, int(remove_whitespace(
           B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226
           1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
           BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC
-          E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF"""
-    ), 16)), (5, int(remove_whitespace(
-    """
+          E0FD108E 4B82D120 A93AD2CA FFFFFFFF FFFFFFFF"""), 16)),
+    # RFC 5054, 5, 4096-bit Group
+    (5, int(remove_whitespace(
+        """
           FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
           8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B
           302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9
@@ -248,9 +293,10 @@ goodGroupParameters = [(2, int(remove_whitespace(
           04DE8EF9 2E8EFC14 1FBECAA6 287C5947 4E6BC05D 99B2964F A090C3A2
           233BA186 515BE7ED 1F612970 CEE2D7AF B81BDD76 2170481C D0069127
           D5B05AA9 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34063199
-          FFFFFFFF FFFFFFFF"""
-    ), 16)), (5, int(remove_whitespace(
-    """
+          FFFFFFFF FFFFFFFF"""), 16)),
+    # RFC 5054, 6, 6144-bit Group
+    (5, int(remove_whitespace(
+        """
           FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
           8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B
           302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9
@@ -278,9 +324,10 @@ goodGroupParameters = [(2, int(remove_whitespace(
           CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328 06A1D58B
           B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C DA56C9EC 2EF29632
           387FE8D7 6E3C0468 043E8F66 3F4860EE 12BF2D5B 0B7474D6 E694F91E
-          6DCC4024 FFFFFFFF FFFFFFFF"""
-    ), 16)), (19, int(remove_whitespace(
-    """
+          6DCC4024 FFFFFFFF FFFFFFFF"""), 16)),
+    # RFC 5054, 7, 8192-bit Group
+    (19, int(remove_whitespace(
+        """
           FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
           8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B
           302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9
@@ -317,32 +364,47 @@ goodGroupParameters = [(2, int(remove_whitespace(
           0846851D F9AB4819 5DED7EA1 B1D510BD 7EE74D73 FAF36BC3 1ECFA268
           359046F4 EB879F92 4009438B 481C6CD7 889A002E D5EE382B C9190DA6
           FC026E47 9558E447 5677E9AA 9E3050E2 765694DF C81F56E8 80B96E71
-          60C980DD 98EDD3DF FFFFFFFF FFFFFFFF"""
-    ), 16))]
+          60C980DD 98EDD3DF FFFFFFFF FFFFFFFF"""), 16))]
+
+
 for num, group in enumerate(goodGroupParameters, 1):
-    FFDHE_PARAMETERS['RFC5054 group {0}'.format(num)] = group
+    FFDHE_PARAMETERS["RFC5054 group {0}".format(num)] = group
+
+
+# old versions of tlslite had an incorrect generator for 3072 bit group
+# from RFC 5054. Since the group is a safe prime, the generator of "2" is
+# cryptographically safe, so we don't have reason to reject connections
+# from old tlslite, so add the old invalid value to the "known good" list
 goodGroupParameters.append((2, goodGroupParameters[3][1]))
+# we had a bad generator for group 7 (8192 bit) - 5 - while it needs to be 19
+# same as above, any generator but 1 and p-1 are ok, cryptographically speaking
 goodGroupParameters.append((5, goodGroupParameters[6][1]))
-RFC5114_GROUP22 = int(remove_whitespace(
-    """
+
+
+# INSECURE groups from RFC 5114, do NOT use
+# RFC 5114, section 2.1, 1024 bit MODP with 160-bit Prime Order Subgroup
+RFC5114_GROUP22 = (
+    int(remove_whitespace("""
        A4D1CBD5 C3FD3412 6765A442 EFB99905 F8104DD2 58AC507F
        D6406CFF 14266D31 266FEA1E 5C41564B 777E690F 5504F213
        160217B4 B01B886A 5E91547F 9E2749F4 D7FBD7D3 B9A92EE1
        909D0D22 63F80A76 A6A24C08 7A091F53 1DBF0A01 69B6A28A
        D662A4D1 8E73AFA3 2D779D59 18D08BC8 858F4DCE F97C2A24
-       855E6EEB 22B3B2E5"""
-    ), 16), int(remove_whitespace(
-    """
+       855E6EEB 22B3B2E5"""), 16),
+    int(remove_whitespace("""
        B10B8F96 A080E01D DE92DE5E AE5D54EC 52C99FBC FB06A3C6
        9A6A9DCA 52D23B61 6073E286 75A23D18 9838EF1E 2EE652C0
        13ECB4AE A9061123 24975C3C D49B83BF ACCBDD7D 90C4BD70
        98488E9C 219A7372 4EFFD6FA E5644738 FAA31A4F F55BCCC0
        A151AF5F 0DC8B4BD 45BF37DF 365C1A65 E68CFDA7 6D4DA708
-       DF1FB2BC 2E4A4371"""
-    ), 16)
-FFDHE_PARAMETERS['RFC5114 group 22'] = RFC5114_GROUP22
-RFC5114_GROUP23 = int(remove_whitespace(
-    """
+       DF1FB2BC 2E4A4371"""), 16))
+FFDHE_PARAMETERS["RFC5114 group 22"] = RFC5114_GROUP22
+
+
+# RFC 5114, section 2.2, 2048 bit MODP with 224-bit Prime Order Subgroup
+# INSECURE, do not use
+RFC5114_GROUP23 = (
+    int(remove_whitespace("""
         AC4032EF 4F2D9AE3 9DF30B5C 8FFDAC50 6CDEBE7B 89998CAF
         74866A08 CFE4FFE3 A6824A4E 10B9A6F0 DD921F01 A70C4AFA
         AB739D77 00C29F52 C57DB17C 620A8652 BE5E9001 A8D66AD7
@@ -353,9 +415,8 @@ RFC5114_GROUP23 = int(remove_whitespace(
         10E183ED D19963DD D9E263E4 770589EF 6AA21E7F 5F2FF381
         B539CCE3 409D13CD 566AFBB4 8D6C0191 81E1BCFE 94B30269
         EDFE72FE 9B6AA4BD 7B5A0F1C 71CFFF4C 19C418E1 F6EC0179
-        81BC087F 2A7065B3 84B890D3 191F2BFA"""
-    ), 16), int(remove_whitespace(
-    """
+        81BC087F 2A7065B3 84B890D3 191F2BFA"""), 16),
+    int(remove_whitespace("""
         AD107E1E 9123A9D0 D660FAA7 9559C51F A20D64E5 683B9FD1
         B54B1597 B61D0A75 E6FA141D F95A56DB AF9A3C40 7BA1DF15
         EB3D688A 309C180E 1DE6B85A 1274A0A6 6D3F8152 AD6AC212
@@ -366,11 +427,14 @@ RFC5114_GROUP23 = int(remove_whitespace(
         CDF93ACC 44328387 315D75E1 98C641A4 80CD86A1 B9E587E8
         BE60E69C C928B2B9 C52172E4 13042E9B 23F10B0E 16E79763
         C9B53DCF 4BA80A29 E3FB73C1 6B8E75B9 7EF363E2 FFA31F71
-        CF9DE538 4E71B81C 0AC4DFFE 0C10E64F"""
-    ), 16)
-FFDHE_PARAMETERS['RFC5114 group 23'] = RFC5114_GROUP23
-RFC5114_GROUP24 = int(remove_whitespace(
-    """
+        CF9DE538 4E71B81C 0AC4DFFE 0C10E64F"""), 16))
+FFDHE_PARAMETERS["RFC5114 group 23"] = RFC5114_GROUP23
+
+
+# RFC 5114, section 2.3, 2048 bit MODP with 256-bit Prime Order Subgroup
+# INSECURE, do not use
+RFC5114_GROUP24 = (
+    int(remove_whitespace("""
        3FB32C9B 73134D0B 2E775066 60EDBD48 4CA7B18F 21EF2054
        07F4793A 1A0BA125 10DBC150 77BE463F FF4FED4A AC0BB555
        BE3A6C1B 0C6B47B1 BC3773BF 7E8C6F62 901228F8 C28CBB18
@@ -381,9 +445,8 @@ RFC5114_GROUP24 = int(remove_whitespace(
        C8484B1E 052588B9 B7D2BBD2 DF016199 ECD06E15 57CD0915
        B3353BBB 64E0EC37 7FD02837 0DF92B52 C7891428 CDC67EB6
        184B523D 1DB246C3 2F630784 90F00EF8 D647D148 D4795451
-       5E2327CF EF98C582 664B4C0F 6CC41659"""
-    ), 16), int(remove_whitespace(
-    """
+       5E2327CF EF98C582 664B4C0F 6CC41659"""), 16),
+    int(remove_whitespace("""
        87A8E61D B4B6663C FFBBD19C 65195999 8CEEF608 660DD0F2
        5D2CEED4 435E3B00 E00DF8F1 D61957D4 FAF7DF45 61B2AA30
        16C3D911 34096FAA 3BF4296D 830E9A7C 209E0C64 97517ABD
@@ -394,17 +457,22 @@ RFC5114_GROUP24 = int(remove_whitespace(
        67E144E5 14056425 1CCACB83 E6B486F6 B3CA3F79 71506026
        C0B857F6 89962856 DED4010A BD0BE621 C3A3960A 54E710C3
        75F26375 D7014103 A4B54330 C198AF12 6116D227 6E11715F
-       693877FA D7EF09CA DB094AE9 1E1A1597"""
-    ), 16)
-FFDHE_PARAMETERS['RFC5114 group 24'] = RFC5114_GROUP24
+       693877FA D7EF09CA DB094AE9 1E1A1597"""), 16))
+FFDHE_PARAMETERS["RFC5114 group 24"] = RFC5114_GROUP24
+
+
 RFC7919_GROUPS = []
 """
 All DH parameters specified in RFC 7919.

 Those are the parameters recommended for use in TLS.
 """
-FFDHE2048 = 2, int(remove_whitespace(
-    """
+
+
+# RFC 7919 ffdhe2048 bit group
+FFDHE2048 = (
+    2,
+    int(remove_whitespace("""
     FFFFFFFF FFFFFFFF ADF85458 A2BB4A9A AFDC5620 273D3CF1
     D8B9C583 CE2D3695 A9E13641 146433FB CC939DCE 249B3EF9
     7D2FE363 630C75D8 F681B202 AEC4617A D3DF1ED5 D5FD6561
@@ -415,13 +483,16 @@ FFDHE2048 = 2, int(remove_whitespace(
     0B07A7C8 EE0A6D70 9E02FCE1 CDF7E2EC C03404CD 28342F61
     9172FE9C E98583FF 8E4F1232 EEF28183 C3FE3B1B 4C6FAD73
     3BB5FCBC 2EC22005 C58EF183 7D1683B2 C6F34A26 C1B2EFFA
-    886B4238 61285C97 FFFFFFFF FFFFFFFF"""
-    ), 16)
+    886B4238 61285C97 FFFFFFFF FFFFFFFF"""), 16))
 goodGroupParameters.append(FFDHE2048)
 RFC7919_GROUPS.append(FFDHE2048)
-FFDHE_PARAMETERS['RFC7919 ffdhe2048'] = FFDHE2048
-FFDHE3072 = 2, int(remove_whitespace(
-    """
+FFDHE_PARAMETERS["RFC7919 ffdhe2048"] = FFDHE2048
+
+
+# RFC 7919 ffdhe3072 bit group
+FFDHE3072 = (
+    2,
+    int(remove_whitespace("""
     FFFFFFFF FFFFFFFF ADF85458 A2BB4A9A AFDC5620 273D3CF1
     D8B9C583 CE2D3695 A9E13641 146433FB CC939DCE 249B3EF9
     7D2FE363 630C75D8 F681B202 AEC4617A D3DF1ED5 D5FD6561
@@ -437,13 +508,16 @@ FFDHE3072 = 2, int(remove_whitespace(
     AEFE1309 85139270 B4130C93 BC437944 F4FD4452 E2D74DD3
     64F2E21E 71F54BFF 5CAE82AB 9C9DF69E E86D2BC5 22363A0D
     ABC52197 9B0DEADA 1DBF9A42 D5C4484E 0ABCD06B FA53DDEF
-    3C1B20EE 3FD59D7C 25E41D2B 66C62E37 FFFFFFFF FFFFFFFF"""
-    ), 16)
+    3C1B20EE 3FD59D7C 25E41D2B 66C62E37 FFFFFFFF FFFFFFFF"""), 16))
 goodGroupParameters.append(FFDHE3072)
 RFC7919_GROUPS.append(FFDHE3072)
-FFDHE_PARAMETERS['RFC7919 ffdhe3072'] = FFDHE3072
-FFDHE4096 = 2, int(remove_whitespace(
-    """
+FFDHE_PARAMETERS["RFC7919 ffdhe3072"] = FFDHE3072
+
+
+# RFC 7919 ffdhe4096 bit group
+FFDHE4096 = (
+    2,
+    int(remove_whitespace("""
     FFFFFFFF FFFFFFFF ADF85458 A2BB4A9A AFDC5620 273D3CF1
     D8B9C583 CE2D3695 A9E13641 146433FB CC939DCE 249B3EF9
     7D2FE363 630C75D8 F681B202 AEC4617A D3DF1ED5 D5FD6561
@@ -465,13 +539,16 @@ FFDHE4096 = 2, int(remove_whitespace(
     A907600A 918130C4 6DC778F9 71AD0038 092999A3 33CB8B7A
     1A1DB93D 7140003C 2A4ECEA9 F98D0ACC 0A8291CD CEC97DCF
     8EC9B55A 7F88A46B 4DB5A851 F44182E1 C68A007E 5E655F6A
-    FFFFFFFF FFFFFFFF"""
-    ), 16)
+    FFFFFFFF FFFFFFFF"""), 16))
 goodGroupParameters.append(FFDHE4096)
 RFC7919_GROUPS.append(FFDHE4096)
-FFDHE_PARAMETERS['RFC7919 ffdhe4096'] = FFDHE4096
-FFDHE6144 = 2, int(remove_whitespace(
-    """
+FFDHE_PARAMETERS["RFC7919 ffdhe4096"] = FFDHE4096
+
+
+# RFC 7919 ffdhe6144 bit group
+FFDHE6144 = (
+    2,
+    int(remove_whitespace("""
     FFFFFFFF FFFFFFFF ADF85458 A2BB4A9A AFDC5620 273D3CF1
     D8B9C583 CE2D3695 A9E13641 146433FB CC939DCE 249B3EF9
     7D2FE363 630C75D8 F681B202 AEC4617A D3DF1ED5 D5FD6561
@@ -503,13 +580,16 @@ FFDHE6144 = 2, int(remove_whitespace(
     D72B0374 6AE77F5E 62292C31 1562A846 505DC82D B854338A
     E49F5235 C95B9117 8CCF2DD5 CACEF403 EC9D1810 C6272B04
     5B3B71F9 DC6B80D6 3FDD4A8E 9ADB1E69 62A69526 D43161C1
-    A41D570D 7938DAD4 A40E329C D0E40E65 FFFFFFFF FFFFFFFF"""
-    ), 16)
+    A41D570D 7938DAD4 A40E329C D0E40E65 FFFFFFFF FFFFFFFF"""), 16))
 goodGroupParameters.append(FFDHE6144)
 RFC7919_GROUPS.append(FFDHE6144)
-FFDHE_PARAMETERS['RFC7919 ffdhe6144'] = FFDHE6144
-FFDHE8192 = 2, int(remove_whitespace(
-    """
+FFDHE_PARAMETERS["RFC7919 ffdhe6144"] = FFDHE6144
+
+
+# RFC 7919 ffdhe8192 bit group
+FFDHE8192 = (
+    2,
+    int(remove_whitespace("""
     FFFFFFFF FFFFFFFF ADF85458 A2BB4A9A AFDC5620 273D3CF1
     D8B9C583 CE2D3695 A9E13641 146433FB CC939DCE 249B3EF9
     7D2FE363 630C75D8 F681B202 AEC4617A D3DF1ED5 D5FD6561
@@ -552,11 +632,10 @@ FFDHE8192 = 2, int(remove_whitespace(
     1EFC8CE0 BA8A4FE8 B6855DFE 72B0A66E DED2FBAB FBE58A30
     FAFABE1C 5D71A87E 2F741EF8 C1FE86FE A6BBFDE5 30677F0D
     97D11D49 F7A8443D 0822E506 A9F4614E 011E2A94 838FF88C
-    D68C8BB7 C5C6424C FFFFFFFF FFFFFFFF"""
-    ), 16)
+    D68C8BB7 C5C6424C FFFFFFFF FFFFFFFF"""), 16))
 goodGroupParameters.append(FFDHE8192)
 RFC7919_GROUPS.append(FFDHE8192)
-FFDHE_PARAMETERS['RFC7919 ffdhe8192'] = FFDHE8192
+FFDHE_PARAMETERS["RFC7919 ffdhe8192"] = FFDHE8192


 def paramStrength(param):
@@ -570,41 +649,144 @@ def paramStrength(param):
     :param param: prime or modulus
     :type param: int
     """
-    pass
+    size = numBits(param)
+    if size < 512:
+        return 48
+    elif size < 768:
+        return 56
+    elif size < 816:
+        return 64
+    elif size < 1023:
+        return 72
+    elif size < 1535:
+        return 80  # NIST SP 800-57
+    elif size < 2047:
+        return 88  # rounded RFC 3526
+    elif size < 3071:
+        return 112  # NIST SP 800-57
+    elif size < 4095:
+        return 128  # NIST SP 800-57
+    elif size < 6144:
+        return 152  # rounded RFC 3526
+    elif size < 7679:
+        return 168  # rounded RFC 3526
+    elif size < 15359:
+        return 192  # NIST SP 800-57
+    else:
+        return 256  # NIST SP 800-57


 def P_hash(mac_name, secret, seed, length):
     """Internal method for calculation the PRF in TLS."""
-    pass
+    ret = bytearray(length)
+    seed = compatHMAC(seed)
+    A = seed
+    index = 0
+    mac = hmac.HMAC(compatHMAC(secret), digestmod=mac_name)
+    while index < length:
+        a_fun = mac.copy()
+        a_fun.update(A)
+        A = a_fun.digest()
+        out_fun = mac.copy()
+        out_fun.update(A)
+        out_fun.update(seed)
+        output = out_fun.digest()
+
+        how_many = min(length - index, len(output))
+        ret[index:index+how_many] = output[:how_many]
+        index += how_many
+    return ret
+

+def PRF(secret, label, seed, length):
+    #Split the secret into left and right halves
+    # which may share a byte if len is odd
+    S1 = secret[ : int(math.ceil(len(secret)/2.0))]
+    S2 = secret[ int(math.floor(len(secret)/2.0)) : ]
+
+    #Run the left half through P_MD5 and the right half through P_SHA1
+    p_md5 = P_hash("md5", S1, label + seed, length)
+    p_sha1 = P_hash("sha1", S2, label + seed, length)
+
+    #XOR the output values and return the result
+    for x in range(length):
+        p_md5[x] ^= p_sha1[x]
+    return p_md5

 def PRF_1_2(secret, label, seed, length):
     """Pseudo Random Function for TLS1.2 ciphers that use SHA256"""
-    pass
-
+    return P_hash("sha256", secret, label + seed, length)

 def PRF_1_2_SHA384(secret, label, seed, length):
     """Pseudo Random Function for TLS1.2 ciphers that use SHA384"""
-    pass
+    return P_hash("sha384", secret, label + seed, length)

+def PRF_SSL(secret, seed, length):
+    bytes = bytearray(length)
+    index = 0
+    for x in range(26):
+        A = bytearray([ord('A')+x] * (x+1)) # 'A', 'BB', 'CCC', etc..
+        input = secret + SHA1(A + secret + seed)
+        output = MD5(input)
+        for c in output:
+            if index >= length:
+                return bytes
+            bytes[index] = c
+            index += 1
+    return bytes

-@deprecated_method('Please use calc_key function instead.')
+@deprecated_method("Please use calc_key function instead.")
 def calcExtendedMasterSecret(version, cipherSuite, premasterSecret,
-    handshakeHashes):
+                             handshakeHashes):
     """Derive Extended Master Secret from premaster and handshake msgs"""
-    pass
+    assert version in ((3, 1), (3, 2), (3, 3))
+    if version in ((3, 1), (3, 2)):
+        masterSecret = PRF(premasterSecret, b"extended master secret",
+                           handshakeHashes.digest('md5') +
+                           handshakeHashes.digest('sha1'),
+                           48)
+    else:
+        if cipherSuite in CipherSuite.sha384PrfSuites:
+            masterSecret = PRF_1_2_SHA384(premasterSecret,
+                                          b"extended master secret",
+                                          handshakeHashes.digest('sha384'),
+                                          48)
+        else:
+            masterSecret = PRF_1_2(premasterSecret,
+                                   b"extended master secret",
+                                   handshakeHashes.digest('sha256'),
+                                   48)
+    return masterSecret


-@deprecated_method('Please use calc_key function instead.')
+@deprecated_method("Please use calc_key function instead.")
 def calcMasterSecret(version, cipherSuite, premasterSecret, clientRandom,
-    serverRandom):
+                     serverRandom):
     """Derive Master Secret from premaster secret and random values"""
-    pass
+    if version == (3,0):
+        masterSecret = PRF_SSL(premasterSecret,
+                            clientRandom + serverRandom, 48)
+    elif version in ((3,1), (3,2)):
+        masterSecret = PRF(premasterSecret, b"master secret",
+                            clientRandom + serverRandom, 48)
+    elif version == (3,3):
+        if cipherSuite in CipherSuite.sha384PrfSuites:
+            masterSecret = PRF_1_2_SHA384(premasterSecret,
+                                          b"master secret",
+                                          clientRandom + serverRandom,
+                                          48)
+        else:
+            masterSecret = PRF_1_2(premasterSecret,
+                                   b"master secret",
+                                   clientRandom + serverRandom,
+                                   48)
+    else:
+        raise AssertionError()
+    return masterSecret

-
-@deprecated_method('Please use calc_key function instead.')
-def calcFinished(version, masterSecret, cipherSuite, handshakeHashes, isClient
-    ):
+@deprecated_method("Please use calc_key function instead.")
+def calcFinished(version, masterSecret, cipherSuite, handshakeHashes,
+                 isClient):
     """Calculate the Handshake protocol Finished value

     :param version: TLS protocol version tuple
@@ -614,11 +796,36 @@ def calcFinished(version, masterSecret, cipherSuite, handshakeHashes, isClient
     :param isClient: whether the calculation should be performed for message
         sent by client (True) or by server (False) side of connection
     """
-    pass
+    assert version in ((3, 0), (3, 1), (3, 2), (3, 3))
+    if version == (3,0):
+        if isClient:
+            senderStr = b"\x43\x4C\x4E\x54"
+        else:
+            senderStr = b"\x53\x52\x56\x52"
+
+        verifyData = handshakeHashes.digestSSL(masterSecret, senderStr)
+    else:
+        if isClient:
+            label = b"client finished"
+        else:
+            label = b"server finished"
+
+        if version in ((3,1), (3,2)):
+            handshakeHash = handshakeHashes.digest()
+            verifyData = PRF(masterSecret, label, handshakeHash, 12)
+        else: # version == (3,3):
+            if cipherSuite in CipherSuite.sha384PrfSuites:
+                handshakeHash = handshakeHashes.digest('sha384')
+                verifyData = PRF_1_2_SHA384(masterSecret, label,
+                                            handshakeHash, 12)
+            else:
+                handshakeHash = handshakeHashes.digest('sha256')
+                verifyData = PRF_1_2(masterSecret, label, handshakeHash, 12)

+    return verifyData

 def calc_key(version, secret, cipher_suite, label, handshake_hashes=None,
-    client_random=None, server_random=None, output_length=None):
+            client_random=None, server_random=None, output_length=None):
     """
     Method for calculating different keys depending on input.
     It can be used to calculate finished value, master secret,
@@ -640,8 +847,136 @@ def calc_key(version, secret, cipher_suite, label, handshake_hashes=None,
         master secret or key expansion.
     :param int output_length: Number of bytes to output.
     """
-    pass
+
+
+    # SSL3 calculations.
+    if version == (3, 0):
+        # Calculating Finished value, either for message sent
+        # by server or by client
+        if label == b"client finished":
+            senderStr = b"\x43\x4C\x4E\x54"
+            return handshake_hashes.digestSSL(secret, senderStr)
+        elif label == b"server finished":
+            senderStr = b"\x53\x52\x56\x52"
+            return handshake_hashes.digestSSL(secret, senderStr)
+        else:
+            assert label in [b"key expansion", b"master secret"]
+            func = PRF_SSL
+
+    # TLS1.0 or TLS1.1 calculations.
+    elif version in ((3, 1), (3, 2)):
+        func = PRF
+        # Seed needed for calculating extended master secret
+        if label == b"extended master secret":
+            seed = handshake_hashes.digest('md5') + \
+                   handshake_hashes.digest('sha1')
+        # Seed needed for calculating Finished value
+        elif label in [b"server finished", b"client finished"]:
+            seed = handshake_hashes.digest()
+        else:
+            assert label in [b"key expansion", b"master secret"]
+
+    # TLS1.2 calculations.
+    else:
+        assert version == (3, 3)
+        if cipher_suite in CipherSuite.sha384PrfSuites:
+            func = PRF_1_2_SHA384
+            # Seed needed for calculating Finished value or extended master
+            # secret
+            if label in [b"extended master secret", b"server finished",
+                    b"client finished"]:
+                seed = handshake_hashes.digest('sha384')
+            else:
+                assert label in [b"key expansion", b"master secret"]
+        else:
+            # Same as above, just using sha256
+            func = PRF_1_2
+            if label in [b"extended master secret", b"server finished",
+                    b"client finished"]:
+                seed = handshake_hashes.digest('sha256')
+            else:
+                assert label in [b"key expansion", b"master secret"]
+
+    # Seed needed for calculating key expansion or master secret
+    if label == b"key expansion":
+        seed = server_random + client_random
+    if label == b"master secret":
+        seed = client_random + server_random
+
+    if func == PRF_SSL:
+        return func(secret, seed, output_length)
+    return func(secret, label, seed, output_length)
+
+def makeX(salt, username, password):
+    if len(username)>=256:
+        raise ValueError("username too long")
+    if len(salt)>=256:
+        raise ValueError("salt too long")
+    innerHashResult = SHA1(username + bytearray(b":") + password)
+    outerHashResult = SHA1(salt + innerHashResult)
+    return bytesToNumber(outerHashResult)
+
+#This function is used by VerifierDB.makeVerifier
+def makeVerifier(username, password, bits):
+    bitsIndex = {1024:0, 1536:1, 2048:2, 3072:3, 4096:4, 6144:5, 8192:6}[bits]
+    g,N = goodGroupParameters[bitsIndex]
+    salt = getRandomBytes(16)
+    x = makeX(salt, username, password)
+    verifier = powMod(g, x, N)
+    return N, g, salt, verifier
+
+def PAD(n, x):
+    nLength = len(numberToByteArray(n))
+    b = numberToByteArray(x)
+    if len(b) < nLength:
+        b = (b"\0" * (nLength-len(b))) + b
+    return b
+
+def makeU(N, A, B):
+  return bytesToNumber(SHA1(PAD(N, A) + PAD(N, B)))
+
+def makeK(N, g):
+  return bytesToNumber(SHA1(numberToByteArray(N) + PAD(N, g)))
+
+def createHMAC(k, digestmod=hashlib.sha1):
+    h = hmac.HMAC(k, digestmod=digestmod)
+    if not hasattr(h, 'block_size'):
+        h.block_size = digestmod().block_size
+    assert h.block_size == digestmod().block_size
+    return h
+
+def createMAC_SSL(k, digestmod=None):
+    mac = MAC_SSL()
+    mac.create(k, digestmod=digestmod)
+    return mac


 class MAC_SSL(object):
-    pass
+    def create(self, k, digestmod=None):
+        self.digestmod = digestmod or hashlib.sha1
+        self.block_size = self.digestmod().block_size
+        # Repeat pad bytes 48 times for MD5; 40 times for other hash functions.
+        self.digest_size = 16 if (self.digestmod is hashlib.md5) else 20
+        repeat = 40 if self.digest_size == 20 else 48
+        opad = b"\x5C" * repeat
+        ipad = b"\x36" * repeat
+
+        self.ohash = self.digestmod(k + opad)
+        self.ihash = self.digestmod(k + ipad)
+
+    def update(self, m):
+        self.ihash.update(m)
+
+    def copy(self):
+        new = MAC_SSL()
+        new.ihash = self.ihash.copy()
+        new.ohash = self.ohash.copy()
+        new.digestmod = self.digestmod
+        new.digest_size = self.digest_size
+        new.block_size = self.block_size
+        return new
+
+    def digest(self):
+        ohash2 = self.ohash.copy()
+        ohash2.update(self.ihash.digest())
+        return bytearray(ohash2.digest())
diff --git a/tlslite/messages.py b/tlslite/messages.py
index d4ceb65..1354cd1 100644
--- a/tlslite/messages.py
+++ b/tlslite/messages.py
@@ -1,4 +1,15 @@
+# Authors:
+#   Trevor Perrin
+#   Google - handling CertificateRequest.certificate_types
+#   Google (adapted by Sam Rushing and Marcelo Fernandez) - NPN support
+#   Dimitris Moraitis - Anon ciphersuites
+#   Yngve Pettersen (ported by Paul Sokolovsky) - TLS 1.2
+#   Hubert Kario - 'extensions' cleanup
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """Classes representing TLS messages."""
+
 from .utils.compat import *
 from .utils.cryptomath import *
 from .errors import *
@@ -18,7 +29,7 @@ class RecordHeader(object):
     def __init__(self, ssl2):
         """Define instance variables."""
         self.type = 0
-        self.version = 0, 0
+        self.version = (0, 0)
         self.length = 0
         self.ssl2 = ssl2

@@ -32,24 +43,46 @@ class RecordHeader3(RecordHeader):

     def create(self, version, type, length):
         """Set object values for writing (serialisation)."""
-        pass
+        self.type = type
+        self.version = version
+        self.length = length
+        return self

     def write(self):
         """Serialise object to bytearray."""
-        pass
+        writer = Writer()
+        writer.add(self.type, 1)
+        writer.add(self.version[0], 1)
+        writer.add(self.version[1], 1)
+        writer.add(self.length, 2)
+        return writer.bytes

     def parse(self, parser):
         """Deserialise object from Parser."""
-        pass
+        self.type = parser.get(1)
+        self.version = (parser.get(1), parser.get(1))
+        self.length = parser.get(2)
+        self.ssl2 = False
+        return self
+
+    @property
+    def typeName(self):
+        matching = [x[0] for x in ContentType.__dict__.items()
+                    if x[1] == self.type]
+        if len(matching) == 0:
+            return "unknown(" + str(self.type) + ")"
+        else:
+            return str(matching[0])

     def __str__(self):
-        return (
-            'SSLv3 record,version({0[0]}.{0[1]}),content type({1}),length({2})'
-            .format(self.version, self.typeName, self.length))
+        return "SSLv3 record,version({0[0]}.{0[1]}),"\
+                "content type({1}),length({2})".format(self.version,
+                                                       self.typeName,
+                                                       self.length)

     def __repr__(self):
-        return ('RecordHeader3(type={0}, version=({1[0]}.{1[1]}), length={2})'
-            .format(self.type, self.version, self.length))
+        return "RecordHeader3(type={0}, version=({1[0]}.{1[1]}), length={2})".\
+                format(self.type, self.version, self.length)


 class RecordHeader2(RecordHeader):
@@ -71,15 +104,50 @@ class RecordHeader2(RecordHeader):

     def parse(self, parser):
         """Deserialise object from Parser."""
-        pass
+        firstByte = parser.get(1)
+        secondByte = parser.get(1)
+        if firstByte & 0x80:
+            self.length = ((firstByte & 0x7f) << 8) | secondByte
+        else:
+            self.length = ((firstByte & 0x3f) << 8) | secondByte
+            self.securityEscape = firstByte & 0x40 != 0
+            self.padding = parser.get(1)
+
+        self.type = ContentType.handshake
+        self.version = (2, 0)
+        return self

     def create(self, length, padding=0, securityEscape=False):
         """Set object's values."""
-        pass
+        self.length = length
+        self.padding = padding
+        self.securityEscape = securityEscape
+        return self

     def write(self):
         """Serialise object to bytearray."""
-        pass
+        writer = Writer()
+
+        shortHeader = not (self.padding or self.securityEscape)
+
+        if ((shortHeader and self.length >= 0x8000) or
+                (not shortHeader and self.length >= 0x4000)):
+            raise ValueError("length too large")
+
+        firstByte = 0
+        if shortHeader:
+            firstByte |= 0x80
+        if self.securityEscape:
+            firstByte |= 0x40
+        firstByte |= self.length >> 8
+        secondByte = self.length & 0xff
+
+        writer.add(firstByte, 1)
+        writer.add(secondByte, 1)
+        if not shortHeader:
+            writer.add(self.padding, 1)
+
+        return writer.bytes


 class Message(object):
@@ -99,34 +167,60 @@ class Message(object):

     def write(self):
         """Return serialised object data."""
-        pass
+        return self.data


 class Alert(object):
-
     def __init__(self):
         self.contentType = ContentType.alert
         self.level = 0
         self.description = 0

+    def create(self, description, level=AlertLevel.fatal):
+        self.level = level
+        self.description = description
+        return self
+
+    def parse(self, p):
+        p.setLengthCheck(2)
+        self.level = p.get(1)
+        self.description = p.get(1)
+        p.stopLengthCheck()
+        return self
+
+    def write(self):
+        w = Writer()
+        w.add(self.level, 1)
+        w.add(self.description, 1)
+        return w.bytes
+
+    @property
+    def levelName(self):
+        return none_as_unknown(AlertLevel.toRepr(self.level),
+                             self.level)
+
+    @property
+    def descriptionName(self):
+        return none_as_unknown(AlertDescription.toRepr(self.description),
+                             self.description)
+
     def __str__(self):
-        return 'Alert, level:{0}, description:{1}'.format(self.levelName,
-            self.descriptionName)
+        return "Alert, level:{0}, description:{1}".format(self.levelName,
+                                                          self.descriptionName)

     def __repr__(self):
-        return 'Alert(level={0}, description={1})'.format(self.level, self.
-            description)
+        return "Alert(level={0}, description={1})".format(self.level,
+                                                          self.description)


 class HandshakeMsg(object):
-
     def __init__(self, handshakeType):
         self.contentType = ContentType.handshake
         self.handshakeType = handshakeType

     def __eq__(self, other):
         """Check if other object represents the same data as this object."""
-        if hasattr(self, 'write') and hasattr(other, 'write'):
+        if hasattr(self, "write") and hasattr(other, "write"):
             return self.write() == other.write()
         else:
             return False
@@ -135,6 +229,12 @@ class HandshakeMsg(object):
         """Check if other object represents different data as this object."""
         return not self.__eq__(other)

+    def postWrite(self, w):
+        headerWriter = Writer()
+        headerWriter.add(self.handshakeType, 1)
+        headerWriter.add(len(w.bytes), 3)
+        return headerWriter.bytes + w.bytes
+

 class HelloMessage(HandshakeMsg):
     """
@@ -155,7 +255,17 @@ class HelloMessage(HandshakeMsg):
         :raises TLSInternalError: when there are multiple extensions of the
             same type
         """
-        pass
+        if self.extensions is None:
+            return None
+
+        exts = [ext for ext in self.extensions if ext.extType == extType]
+        if len(exts) > 1:
+            raise TLSInternalError(
+                "Multiple extensions of the same type present")
+        elif len(exts) == 1:
+            return exts[0]
+        else:
+            return None

     def addExtension(self, ext):
         """
@@ -164,15 +274,24 @@ class HelloMessage(HandshakeMsg):
         :type ext: TLSExtension
         :param ext: extension object to add to list
         """
-        pass
+        if self.extensions is None:
+            self.extensions = []
+
+        self.extensions.append(ext)

     def _addExt(self, extType):
         """Add en empty extension of given type, if not already present"""
-        pass
+        ext = self.getExtension(extType)
+        if ext is None:
+            ext = TLSExtension(extType=extType).create(bytearray(0))
+            self.addExtension(ext)

     def _removeExt(self, extType):
         """Remove extension of given type"""
-        pass
+        if self.extensions is not None:
+            self.extensions[:] = (i for i in self.extensions
+                                  if i.extType != extType)
+

     def _addOrRemoveExt(self, extType, add):
         """
@@ -183,7 +302,10 @@ class HelloMessage(HandshakeMsg):
         :type add: boolean
         :param add: whether to add (True) or remove (False) the extension
         """
-        pass
+        if add:
+            self._addExt(extType)
+        else:
+            self._removeExt(extType)


 class ClientHello(HelloMessage):
@@ -214,11 +336,17 @@ class ClientHello(HelloMessage):
     def __init__(self, ssl2=False):
         super(ClientHello, self).__init__(HandshakeType.client_hello)
         self.ssl2 = ssl2
-        self.client_version = 0, 0
+        self.client_version = (0, 0)
         self.random = bytearray(32)
         self.session_id = bytearray(0)
-        self.cipher_suites = []
-        self.compression_methods = []
+        self.cipher_suites = []         # a list of 16-bit values
+        self.compression_methods = []   # a list of 8-bit values
+
+    def _ciphers_to_str(self):
+        ciphers = ", ".join(
+            CipherSuite.ietfNames.get(i, str(i))
+            for i in self.cipher_suites)
+        return "[{0}]".format(ciphers)

     def __str__(self):
         """
@@ -226,17 +354,20 @@ class ClientHello(HelloMessage):

         :rtype: str
         """
-        if self.session_id.count(bytearray(b'\x00')) == len(self.session_id
-            ) and len(self.session_id) != 0:
+        if self.session_id.count(bytearray(b'\x00')) == len(self.session_id)\
+                and len(self.session_id) != 0:
             session = "bytearray(b'\\x00'*{0})".format(len(self.session_id))
         else:
             session = repr(self.session_id)
-        ret = (
-            'client_hello,version({0[0]}.{0[1]}),random(...),session ID({1!s}),cipher suites({2}),compression methods({3!r})'
-            .format(self.client_version, session, self._ciphers_to_str(),
-            self.compression_methods))
+        ret = "client_hello,version({0[0]}.{0[1]}),random(...),"\
+              "session ID({1!s}),cipher suites({2}),"\
+              "compression methods({3!r})".format(
+                  self.client_version, session,
+                  self._ciphers_to_str(), self.compression_methods)
+
         if self.extensions is not None:
-            ret += ',extensions({0!r})'.format(self.extensions)
+            ret += ",extensions({0!r})".format(self.extensions)
+
         return ret

     def __repr__(self):
@@ -245,11 +376,12 @@ class ClientHello(HelloMessage):

         :rtype: str
         """
-        return (
-            'ClientHello(ssl2={0}, client_version=({1[0]}.{1[1]}), random={2!r}, session_id={3!r}, cipher_suites={4}, compression_methods={5}, extensions={6})'
-            .format(self.ssl2, self.client_version, self.random, self.
-            session_id, self._ciphers_to_str(), self.compression_methods,
-            self.extensions))
+        return "ClientHello(ssl2={0}, client_version=({1[0]}.{1[1]}), "\
+               "random={2!r}, session_id={3!r}, cipher_suites={4}, "\
+               "compression_methods={5}, extensions={6})".format(
+                   self.ssl2, self.client_version, self.random,
+                   self.session_id, self._ciphers_to_str(),
+                   self.compression_methods, self.extensions)

     @property
     def certificate_types(self):
@@ -259,7 +391,13 @@ class ClientHello(HelloMessage):
         .. deprecated:: 0.5
             use extensions field to get the extension for inspection
         """
-        pass
+        cert_type = self.getExtension(ExtensionType.cert_type)
+        if cert_type is None:
+            # XXX backwards compatibility: TLSConnection
+            # depends on a default value of this property
+            return [CertificateType.x509]
+        else:
+            return cert_type.certTypes

     @certificate_types.setter
     def certificate_types(self, val):
@@ -274,7 +412,13 @@ class ClientHello(HelloMessage):
         :param val: list of supported certificate types by client encoded as
             single byte integers
         """
-        pass
+        cert_type = self.getExtension(ExtensionType.cert_type)
+
+        if cert_type is None:
+            ext = ClientCertTypeExtension().create(val)
+            self.addExtension(ext)
+        else:
+            cert_type.certTypes = val

     @property
     def srp_username(self):
@@ -284,7 +428,12 @@ class ClientHello(HelloMessage):
         .. deprecated:: 0.5
             use extensions field to get the extension for inspection
         """
-        pass
+        srp_ext = self.getExtension(ExtensionType.srp)
+
+        if srp_ext is None:
+            return None
+        else:
+            return srp_ext.identity

     @srp_username.setter
     def srp_username(self, name):
@@ -294,7 +443,13 @@ class ClientHello(HelloMessage):
         :type name: bytearray
         :param name: UTF-8 encoded username
         """
-        pass
+        srp_ext = self.getExtension(ExtensionType.srp)
+
+        if srp_ext is None:
+            ext = SRPExtension().create(name)
+            self.addExtension(ext)
+        else:
+            srp_ext.identity = name

     @property
     def tack(self):
@@ -306,7 +461,7 @@ class ClientHello(HelloMessage):

         :rtype: boolean
         """
-        pass
+        return self.getExtension(ExtensionType.tack) is not None

     @tack.setter
     def tack(self, present):
@@ -317,7 +472,7 @@ class ClientHello(HelloMessage):
         :param present: True will create extension while False will remove
             extension from client hello
         """
-        pass
+        self._addOrRemoveExt(ExtensionType.tack, present)

     @property
     def supports_npn(self):
@@ -329,7 +484,7 @@ class ClientHello(HelloMessage):

         :rtype: boolean
         """
-        pass
+        return self.getExtension(ExtensionType.supports_npn) is not None

     @supports_npn.setter
     def supports_npn(self, present):
@@ -340,7 +495,7 @@ class ClientHello(HelloMessage):
         :param present: selects whatever to create or remove the extension
             from list of supported ones
         """
-        pass
+        self._addOrRemoveExt(ExtensionType.supports_npn, present)

     @property
     def server_name(self):
@@ -352,7 +507,14 @@ class ClientHello(HelloMessage):

         :rtype: bytearray
         """
-        pass
+        sni_ext = self.getExtension(ExtensionType.server_name)
+        if sni_ext is None:
+            return bytearray(0)
+        else:
+            if len(sni_ext.hostNames) > 0:
+                return sni_ext.hostNames[0]
+            else:
+                return bytearray(0)

     @server_name.setter
     def server_name(self, hostname):
@@ -362,11 +524,19 @@ class ClientHello(HelloMessage):
         :type hostname: bytearray
         :param hostname: name of the host_name to set
         """
-        pass
+        sni_ext = self.getExtension(ExtensionType.server_name)
+        if sni_ext is None:
+            sni_ext = SNIExtension().create(hostname)
+            self.addExtension(sni_ext)
+        else:
+            names = list(sni_ext.hostNames)
+            names[0] = hostname
+            sni_ext.hostNames = names

     def create(self, version, random, session_id, cipher_suites,
-        certificate_types=None, srpUsername=None, tack=False, supports_npn=
-        None, serverName=None, extensions=None):
+               certificate_types=None, srpUsername=None,
+               tack=False, supports_npn=None, serverName=None,
+               extensions=None):
         """
         Create a ClientHello message for sending.

@@ -409,19 +579,102 @@ class ClientHello(HelloMessage):
         :type extensions: list of :py:class:`~.extensions.TLSExtension`
         :param extensions: list of extensions to advertise
         """
-        pass
+        self.client_version = version
+        self.random = random
+        self.session_id = session_id
+        self.cipher_suites = cipher_suites
+        self.compression_methods = [0]
+        if extensions is not None:
+            self.extensions = extensions
+        if certificate_types is not None:
+            self.certificate_types = certificate_types
+        if srpUsername is not None:
+            if not isinstance(srpUsername, bytearray):
+                raise TypeError("srpUsername must be a bytearray object")
+            self.srp_username = srpUsername
+        self.tack = tack
+        if supports_npn is not None:
+            self.supports_npn = supports_npn
+        if serverName is not None:
+            self.server_name = bytearray(serverName, "utf-8")
+        return self

     def parse(self, p):
         """Deserialise object from on the wire data."""
-        pass
+        if self.ssl2:
+            self.client_version = (p.get(1), p.get(1))
+            cipherSpecsLength = p.get(2)
+            sessionIDLength = p.get(2)
+            randomLength = p.get(2)
+            p.setLengthCheck(cipherSpecsLength +
+                             sessionIDLength +
+                             randomLength)
+            self.cipher_suites = p.getFixList(3, cipherSpecsLength//3)
+            self.session_id = p.getFixBytes(sessionIDLength)
+            self.random = p.getFixBytes(randomLength)
+            if len(self.random) < 32:
+                zeroBytes = 32-len(self.random)
+                self.random = bytearray(zeroBytes) + self.random
+            self.compression_methods = [0]  # Fake this value
+            p.stopLengthCheck()
+        else:
+            p.startLengthCheck(3)
+            self.client_version = (p.get(1), p.get(1))
+            self.random = p.getFixBytes(32)
+            self.session_id = p.getVarBytes(1)
+            if len(self.session_id) > 32:
+                raise DecodeError("session_id too long")
+            self.cipher_suites = p.getVarList(2, 2)
+            self.compression_methods = p.getVarList(1, 1)
+            if not p.atLengthCheck():
+                self.extensions = []
+                totalExtLength = p.get(2)
+                p2 = Parser(p.getFixBytes(totalExtLength))
+                while p2.getRemainingLength() > 0:
+                    ext = TLSExtension().parse(p2)
+                    self.extensions += [ext]
+            p.stopLengthCheck()
+        return self

     def _writeSSL2(self):
         """Serialise SSLv2 object to on the wire data."""
-        pass
+        writer = Writer()
+        writer.add(self.handshakeType, 1)
+        writer.add(self.client_version[0], 1)
+        writer.add(self.client_version[1], 1)
+
+        ciphersWriter = Writer()
+        ciphersWriter.addFixSeq(self.cipher_suites, 3)
+
+        writer.add(len(ciphersWriter.bytes), 2)
+        writer.add(len(self.session_id), 2)
+        writer.add(len(self.random), 2)
+
+        writer.bytes += ciphersWriter.bytes
+        writer.bytes += self.session_id
+        writer.bytes += self.random
+
+        # postWrite() is necessary only for SSLv3/TLS
+        return writer.bytes

     def _write(self):
         """Serialise SSLv3 or TLS object to on the wire data."""
-        pass
+        w = Writer()
+        w.add(self.client_version[0], 1)
+        w.add(self.client_version[1], 1)
+        w.bytes += self.random
+        w.addVarSeq(self.session_id, 1, 1)
+        w.addVarSeq(self.cipher_suites, 2, 2)
+        w.addVarSeq(self.compression_methods, 1, 1)
+
+        if self.extensions is not None:
+            w2 = Writer()
+            for ext in self.extensions:
+                w2.bytes += ext.write()
+
+            w.add(len(w2.bytes), 2)
+            w.bytes += w2.bytes
+        return self.postWrite(w)

     def psk_truncate(self):
         """Return a truncated encoding of message without binders.
@@ -434,11 +687,24 @@ class ClientHello(HelloMessage):

         :rtype: bytearray
         """
-        pass
+        ext = self.extensions[-1]
+        if not isinstance(ext, PreSharedKeyExtension):
+            raise ValueError("Last extension must be the pre_shared_key "
+                             "extension")
+        bts = self.write()
+        # every binder has 1 byte long header and the list of them
+        # has a 2 byte header
+        length = sum(len(i) + 1 for i in ext.binders) + 2
+
+        return bts[:-length]
+

     def write(self):
         """Serialise object to on the wire data."""
-        pass
+        if self.ssl2:
+            return self._writeSSL2()
+        else:
+            return self._write()


 class HelloRequest(HandshakeMsg):
@@ -449,6 +715,19 @@ class HelloRequest(HandshakeMsg):
     def __init__(self):
         super(HelloRequest, self).__init__(HandshakeType.hello_request)

+    def create(self):
+        return self
+
+    def write(self):
+        return self.postWrite(Writer())
+
+    def parse(self, parser):
+        # verify that the message is empty (the buffer will just contain
+        # the length from header)
+        parser.startLengthCheck(3)
+        parser.stopLengthCheck()
+        return self
+

 class ServerHello(HelloMessage):
     """
@@ -487,7 +766,7 @@ class ServerHello(HelloMessage):
     def __init__(self):
         """Initialise ServerHello object."""
         super(ServerHello, self).__init__(HandshakeType.server_hello)
-        self.server_version = 0, 0
+        self.server_version = (0, 0)
         self.random = bytearray(32)
         self.session_id = bytearray(0)
         self.cipher_suite = 0
@@ -495,33 +774,47 @@ class ServerHello(HelloMessage):
         self._tack_ext = None

     def __str__(self):
-        base = (
-            'server_hello,length({0}),version({1[0]}.{1[1]}),random(...),session ID({2!r}),cipher({3:#x}),compression method({4})'
-            .format(len(self.write()) - 4, self.server_version, self.
-            session_id, self.cipher_suite, self.compression_method))
+        base = "server_hello,length({0}),version({1[0]}.{1[1]}),random(...),"\
+                "session ID({2!r}),cipher({3:#x}),compression method({4})"\
+                .format(len(self.write())-4, self.server_version,
+                        self.session_id, self.cipher_suite,
+                        self.compression_method)
+
         if self.extensions is None:
             return base
-        ret = ',extensions['
-        ret += ','.join(repr(x) for x in self.extensions)
-        ret += ']'
+
+        ret = ",extensions["
+        ret += ",".join(repr(x) for x in self.extensions)
+        ret += "]"
         return base + ret

     def __repr__(self):
-        return (
-            'ServerHello(server_version=({0[0]}, {0[1]}), random={1!r}, session_id={2!r}, cipher_suite={3}, compression_method={4}, _tack_ext={5}, extensions={6!r})'
-            .format(self.server_version, self.random, self.session_id, self
-            .cipher_suite, self.compression_method, self._tack_ext, self.
-            extensions))
+        return "ServerHello(server_version=({0[0]}, {0[1]}), random={1!r}, "\
+                "session_id={2!r}, cipher_suite={3}, compression_method={4}, "\
+                "_tack_ext={5}, extensions={6!r})".format(
+                    self.server_version, self.random, self.session_id,
+                    self.cipher_suite, self.compression_method, self._tack_ext,
+                    self.extensions)

     @property
     def tackExt(self):
         """Return the TACK extension."""
-        pass
+        if self._tack_ext is None:
+            ext = self.getExtension(ExtensionType.tack)
+            if ext is None or not tackpyLoaded:
+                return None
+            else:
+                self._tack_ext = TackExtension(ext.extData)
+        return self._tack_ext

     @tackExt.setter
     def tackExt(self, val):
         """Set the TACK extension."""
-        pass
+        self._tack_ext = val
+        # makes sure that extensions are included in the on the wire encoding
+        if val is not None:
+            if self.extensions is None:
+                self.extensions = []

     @property
     def certificate_type(self):
@@ -530,7 +823,12 @@ class ServerHello(HelloMessage):

         :rtype: int
         """
-        pass
+        cert_type = self.getExtension(ExtensionType.cert_type)
+        if cert_type is None:
+            # XXX backwards compatibility, TLSConnection expects the default
+            # value to be that
+            return CertificateType.x509
+        return cert_type.cert_type

     @certificate_type.setter
     def certificate_type(self, val):
@@ -540,7 +838,17 @@ class ServerHello(HelloMessage):
         :type val: int
         :param val: type of certificate
         """
-        pass
+        if val == CertificateType.x509 or val is None:
+            # XXX backwards compatibility, x509 value should not be sent
+            self._removeExt(ExtensionType.cert_type)
+            return
+
+        cert_type = self.getExtension(ExtensionType.cert_type)
+        if cert_type is None:
+            ext = ServerCertTypeExtension().create(val)
+            self.addExtension(ext)
+        else:
+            cert_type.cert_type = val

     @property
     def next_protos(self):
@@ -549,7 +857,12 @@ class ServerHello(HelloMessage):

         :rtype: list of bytearrays
         """
-        pass
+        npn_ext = self.getExtension(ExtensionType.supports_npn)
+
+        if npn_ext is None:
+            return None
+        else:
+            return npn_ext.protocols

     @next_protos.setter
     def next_protos(self, val):
@@ -559,7 +872,21 @@ class ServerHello(HelloMessage):
         :type val: list
         :param val: list of protocols to advertise as UTF-8 encoded names
         """
-        pass
+        if val is None:
+            # XXX: do not send empty extension
+            self._removeExt(ExtensionType.supports_npn)
+            return
+        else:
+            # convinience function, make sure the values are properly encoded
+            val = [bytearray(x) for x in val]
+
+        npn_ext = self.getExtension(ExtensionType.supports_npn)
+
+        if npn_ext is None:
+            ext = NPNExtension().create(val)
+            self.addExtension(ext)
+        else:
+            npn_ext.protocols = val

     @property
     def next_protos_advertised(self):
@@ -568,7 +895,7 @@ class ServerHello(HelloMessage):

         :rtype: list of bytearrays
         """
-        pass
+        return self.next_protos

     @next_protos_advertised.setter
     def next_protos_advertised(self, val):
@@ -578,13 +905,68 @@ class ServerHello(HelloMessage):
         :type val: list
         :param val: list of protocols to advertise as UTF-8 encoded names
         """
-        pass
+        self.next_protos = val

     def create(self, version, random, session_id, cipher_suite,
-        certificate_type=None, tackExt=None, next_protos_advertised=None,
-        extensions=None):
+               certificate_type=None, tackExt=None,
+               next_protos_advertised=None,
+               extensions=None):
         """Initialize the object for deserialisation."""
-        pass
+        self.extensions = extensions
+        self.server_version = version
+        self.random = random
+        self.session_id = session_id
+        self.cipher_suite = cipher_suite
+        self.certificate_type = certificate_type
+        self.compression_method = 0
+        if tackExt is not None:
+            self.tackExt = tackExt
+        self.next_protos_advertised = next_protos_advertised
+        return self
+
+    def parse(self, p):
+        p.startLengthCheck(3)
+        self.server_version = (p.get(1), p.get(1))
+        self.random = p.getFixBytes(32)
+        self.session_id = p.getVarBytes(1)
+        self.cipher_suite = p.get(2)
+        self.compression_method = p.get(1)
+        if not p.atLengthCheck():
+            self.extensions = []
+            totalExtLength = p.get(2)
+            p2 = Parser(p.getFixBytes(totalExtLength))
+            while p2.getRemainingLength() > 0:
+                if self.random == TLS_1_3_HRR:
+                    ext = TLSExtension(hrr=True).parse(p2)
+                else:
+                    ext = TLSExtension(server=True).parse(p2)
+                self.extensions += [ext]
+        p.stopLengthCheck()
+        return self
+
+    def write(self):
+        w = Writer()
+        w.add(self.server_version[0], 1)
+        w.add(self.server_version[1], 1)
+        w.bytes += self.random
+        w.addVarSeq(self.session_id, 1, 1)
+        w.add(self.cipher_suite, 2)
+        w.add(self.compression_method, 1)
+
+        if self.extensions is not None:
+            w2 = Writer()
+            for ext in self.extensions:
+                w2.bytes += ext.write()
+
+            if self.tackExt:
+                b = self.tackExt.serialize()
+                w2.add(ExtensionType.tack, 2)
+                w2.add(len(b), 2)
+                w2.bytes += b
+
+            w.add(len(w2.bytes), 2)
+            w.bytes += w2.bytes
+        return self.postWrite(w)


 class ServerHello2(HandshakeMsg):
@@ -615,23 +997,62 @@ class ServerHello2(HandshakeMsg):
         super(ServerHello2, self).__init__(SSL2HandshakeType.server_hello)
         self.session_id_hit = 0
         self.certificate_type = 0
-        self.server_version = 0, 0
+        self.server_version = (0, 0)
         self.certificate = bytearray(0)
         self.ciphers = []
         self.session_id = bytearray(0)

     def create(self, session_id_hit, certificate_type, server_version,
-        certificate, ciphers, session_id):
+               certificate, ciphers, session_id):
         """Initialize fields of the SERVER-HELLO message."""
-        pass
+        self.session_id_hit = session_id_hit
+        self.certificate_type = certificate_type
+        self.server_version = server_version
+        self.certificate = certificate
+        self.ciphers = ciphers
+        self.session_id = session_id
+        return self

     def write(self):
         """Serialise object to on the wire data."""
-        pass
+        writer = Writer()
+        writer.add(self.handshakeType, 1)
+        writer.add(self.session_id_hit, 1)
+        writer.add(self.certificate_type, 1)
+        if len(self.server_version) != 2:
+            raise ValueError("server version must be a 2-element tuple")
+        writer.addFixSeq(self.server_version, 1)
+        writer.add(len(self.certificate), 2)
+
+        ciphersWriter = Writer()
+        ciphersWriter.addFixSeq(self.ciphers, 3)
+
+        writer.add(len(ciphersWriter.bytes), 2)
+        writer.add(len(self.session_id), 2)
+
+        writer.bytes += self.certificate
+        writer.bytes += ciphersWriter.bytes
+        writer.bytes += self.session_id
+
+        # postWrite() is necessary only for SSLv3/TLS
+        return writer.bytes

     def parse(self, parser):
         """Deserialise object from on the wire data."""
-        pass
+        self.session_id_hit = parser.get(1)
+        self.certificate_type = parser.get(1)
+        self.server_version = (parser.get(1), parser.get(1))
+        certificateLength = parser.get(2)
+        ciphersLength = parser.get(2)
+        sessionIDLength = parser.get(2)
+        parser.setLengthCheck(certificateLength +
+                              ciphersLength +
+                              sessionIDLength)
+        self.certificate = parser.getFixBytes(certificateLength)
+        self.ciphers = parser.getFixList(3, ciphersLength // 3)
+        self.session_id = parser.getFixBytes(sessionIDLength)
+        parser.stopLengthCheck()
+        return self


 class CertificateEntry(object):
@@ -650,24 +1071,53 @@ class CertificateEntry(object):

     def create(self, certificate, extensions):
         """Set all values of the certificate entry."""
-        pass
+        self.certificate = certificate
+        self.extensions = extensions
+        return self

     def write(self):
         """Serialise the object."""
-        pass
+        writer = Writer()
+        if self.certificateType == CertificateType.x509:
+            writer.addVarSeq(self.certificate.writeBytes(), 1, 3)
+        else:
+            raise ValueError("Set certificate type ({0}) unsupported"
+                             .format(self.certificateType))
+
+        if self.extensions is not None:
+            writer2 = Writer()
+            for ext in self.extensions:
+                writer2.bytes += ext.write()
+            writer.addVarSeq(writer2.bytes, 1, 2)
+
+        return writer.bytes

     def parse(self, parser):
         """Deserialise the object from on the wire data."""
-        pass
+        if self.certificateType == CertificateType.x509:
+            certBytes = parser.getVarBytes(3)
+            x509 = X509()
+            x509.parseBinary(certBytes)
+            self.certificate = x509
+        else:
+            raise ValueError("Set certificate type ({0}) unsupported"
+                             .format(self.certificateType))
+
+        self.extensions = []
+        parser.startLengthCheck(2)
+        while not parser.atLengthCheck():
+            ext = TLSExtension(cert=True).parse(parser)
+            self.extensions.append(ext)
+        parser.stopLengthCheck()
+        return self

     def __repr__(self):
-        return 'CertificateEntry(certificate={0!r}, extensions={1!r})'.format(
-            self.certificate, self.extensions)
+        return "CertificateEntry(certificate={0!r}, extensions={1!r})".format(
+                self.certificate, self.extensions)


-@deprecated_attrs({'cert_chain': 'certChain'})
+@deprecated_attrs({"cert_chain": "certChain"})
 class Certificate(HandshakeMsg):
-
     def __init__(self, certificateType, version=(3, 2)):
         HandshakeMsg.__init__(self, HandshakeType.certificate)
         self.certificateType = certificateType
@@ -679,31 +1129,129 @@ class Certificate(HandshakeMsg):
     @property
     def cert_chain(self):
         """Getter for the cert_chain property."""
-        pass
+        if self._cert_chain:
+            return self._cert_chain
+        elif self.certificate_list:
+            return X509CertChain([i.certificate
+                                  for i in self.certificate_list])
+        else:
+            return None

     @cert_chain.setter
     def cert_chain(self, cert_chain):
         """Setter for the cert_chain property."""
-        pass
+        if isinstance(cert_chain, X509CertChain):
+            self._cert_chain = cert_chain
+            self.certificate_list = [CertificateEntry(self.certificateType)
+                                     .create(i, []) for i
+                                     in cert_chain.x509List]
+        elif cert_chain is None:
+            self.certificate_list = []
+        else:
+            self.certificate_list = cert_chain

-    @deprecated_params({'cert_chain': 'certChain'})
+    @deprecated_params({"cert_chain": "certChain"})
     def create(self, cert_chain, context=b''):
         """Initialise fields of the class."""
-        pass
+        self.cert_chain = cert_chain
+        self.certificate_request_context = context
+        return self
+
+    def _parse_certificate_list(self, parser):
+        self.certificate_list = []
+        while parser.getRemainingLength():
+            entry = CertificateEntry(self.certificateType)
+            self.certificate_list.append(entry.parse(parser))
+
+    def _parse_tls13(self, parser):
+        parser.startLengthCheck(3)
+        self.certificate_request_context = parser.getVarBytes(1)
+        self._parse_certificate_list(Parser(parser.getVarBytes(3)))
+        parser.stopLengthCheck()
+        return self
+
+    def _parse_tls12(self, p):
+        p.startLengthCheck(3)
+        if self.certificateType == CertificateType.x509:
+            chainLength = p.get(3)
+            index = 0
+            certificate_list = []
+            while index != chainLength:
+                certBytes = p.getVarBytes(3)
+                if not certBytes:
+                    raise DecodeError("Client certificate is empty")
+                x509 = X509()
+                try:
+                    x509.parseBinary(certBytes)
+                except SyntaxError:
+                    raise BadCertificateError("Certificate could not be parsed")
+                certificate_list.append(x509)
+                index += len(certBytes)+3
+            if certificate_list:
+                self._cert_chain = X509CertChain(certificate_list)
+        else:
+            raise AssertionError()
+
+        p.stopLengthCheck()
+        return self
+
+    def parse(self, p):
+        if self.version <= (3, 3):
+            return self._parse_tls12(p)
+        else:
+            return self._parse_tls13(p)
+
+    def _write_tls13(self):
+        w = Writer()
+        w.addVarSeq(self.certificate_request_context, 1, 1)
+        w2 = Writer()
+        for entry in self.certificate_list:
+            w2.bytes += entry.write()
+        w.addVarSeq(w2.bytes, 1, 3)
+        return w
+
+    def _write_tls12(self):
+        w = Writer()
+        if self.certificateType == CertificateType.x509:
+            chainLength = 0
+            if self._cert_chain:
+                certificate_list = self._cert_chain.x509List
+            else:
+                certificate_list = []
+            # determine length
+            for cert in certificate_list:
+                bytes = cert.writeBytes()
+                chainLength += len(bytes)+3
+            # add bytes
+            w.add(chainLength, 3)
+            for cert in certificate_list:
+                bytes = cert.writeBytes()
+                w.addVarSeq(bytes, 1, 3)
+        else:
+            raise AssertionError()
+        return w
+
+    def write(self):
+        if self.version <= (3, 3):
+            writer = self._write_tls12()
+        else:
+            writer = self._write_tls13()
+        return self.postWrite(writer)

     def __repr__(self):
         if self.version <= (3, 3):
-            return 'Certificate(cert_chain={0!r})'.format(self.cert_chain.
-                x509List)
-        return ('Certificate(request_context={0!r}, certificate_list={1!r})'
-            .format(self.certificate_request_context, self.certificate_list))
+            return "Certificate(cert_chain={0!r})"\
+                   .format(self.cert_chain.x509List)
+        return "Certificate(request_context={0!r}, "\
+               "certificate_list={1!r})"\
+               .format(self.certificate_request_context,
+                       self.certificate_list)


 class CertificateRequest(HelloMessage):
-
     def __init__(self, version):
-        super(CertificateRequest, self).__init__(HandshakeType.
-            certificate_request)
+        super(CertificateRequest, self).__init__(
+                HandshakeType.certificate_request)
         self.certificate_types = []
         self.certificate_authorities = []
         self.version = version
@@ -719,10 +1267,20 @@ class CertificateRequest(HelloMessage):
         Extensions are used/valid only for TLS 1.3 but they are a good
         unified storage mechanism for all versions.
         """
-        pass
+        ext = self.getExtension(ExtensionType.signature_algorithms)
+        if ext:
+            return ext.sigalgs
+        return None
+
+    @supported_signature_algs.setter
+    def supported_signature_algs(self, val):
+        self._removeExt(ExtensionType.signature_algorithms)
+        if val is not None:
+            ext = SignatureAlgorithmsExtension().create(val)
+            self.addExtension(ext)

     def create(self, certificate_types=None, certificate_authorities=None,
-        sig_algs=None, context=b'', extensions=None):
+               sig_algs=None, context=b'', extensions=None):
         """
             Creates a Certificate Request message.
             For TLS 1.3 only the context and extensions parameters should be
@@ -730,7 +1288,81 @@ class CertificateRequest(HelloMessage):
             For TLS versions below 1.3 instead only the first three parameters
             are considered.
         """
-        pass
+        self.certificate_types = certificate_types
+        self.certificate_authorities = certificate_authorities
+        self.certificate_request_context = context
+        self.extensions = extensions
+        # do this after setting extensions, or it will be overwritten
+        if sig_algs is not None:
+            self.supported_signature_algs = sig_algs
+        return self
+
+    def _parse_tls13(self, parser):
+        parser.startLengthCheck(3)
+        self.certificate_request_context = parser.getVarBytes(1)
+        if not parser.getRemainingLength():
+            raise SyntaxError("No list of extensions")
+        else:
+            self.extensions = []
+            sub_parser = Parser(parser.getVarBytes(2))
+            while sub_parser.getRemainingLength():
+                # We care only for universal extensions so far
+                ext = TLSExtension().parse(sub_parser)
+                self.extensions.append(ext)
+
+        parser.stopLengthCheck()
+        return self
+
+    def _parse_tls12(self, p):
+        p.startLengthCheck(3)
+        self.certificate_types = p.getVarList(1, 1)
+        if self.version == (3, 3):
+            self.supported_signature_algs = p.getVarTupleList(1, 2, 2)
+        ca_list_length = p.get(2)
+        index = 0
+        self.certificate_authorities = []
+        while index != ca_list_length:
+            ca_bytes = p.getVarBytes(2)
+            self.certificate_authorities.append(ca_bytes)
+            index += len(ca_bytes)+2
+        p.stopLengthCheck()
+        return self
+
+    def parse(self, parser):
+        if self.version <= (3, 3):
+            return self._parse_tls12(parser)
+        return self._parse_tls13(parser)
+
+    def _write_tls13(self):
+        writer = Writer()
+        writer.addVarSeq(self.certificate_request_context, 1, 1)
+        sub_writer = Writer()
+        for ext in self.extensions or []:
+            sub_writer.bytes += ext.write()
+        writer.addVarSeq(sub_writer.bytes, 1, 2)
+        return writer
+
+    def _write_tls12(self):
+        w = Writer()
+        w.addVarSeq(self.certificate_types, 1, 1)
+        if self.version >= (3, 3):
+            w.addVarTupleSeq(self.supported_signature_algs, 1, 2)
+        caLength = 0
+        # determine length
+        for ca_dn in self.certificate_authorities:
+            caLength += len(ca_dn)+2
+        w.add(caLength, 2)
+        # add bytes
+        for ca_dn in self.certificate_authorities:
+            w.addVarSeq(ca_dn, 1, 2)
+        return w
+
+    def write(self):
+        if self.version <= (3, 3):
+            writer = self._write_tls12()
+        else:
+            writer = self._write_tls13()
+        return self.postWrite(writer)


 class ServerKeyExchange(HandshakeMsg):
@@ -796,47 +1428,69 @@ class ServerKeyExchange(HandshakeMsg):
         self.srp_s = bytearray(0)
         self.srp_B = 0
         self.srp_B_len = None
+        # Anon DH params:
         self.dh_p = 0
         self.dh_p_len = None
         self.dh_g = 0
         self.dh_g_len = None
         self.dh_Ys = 0
         self.dh_Ys_len = None
+        # EC settings
         self.curve_type = None
         self.named_curve = None
         self.ecdh_Ys = bytearray(0)
+        # signature for certificate authenticated ciphersuites
         self.signature = bytearray(0)
+        # signature hash algorithm and signing algorithm for TLSv1.2
         self.hashAlg = 0
         self.signAlg = 0

     def __repr__(self):
-        ret = ('ServerKeyExchange(cipherSuite=CipherSuite.{0}, version={1}'
-            .format(CipherSuite.ietfNames[self.cipherSuite], self.version))
+        ret = "ServerKeyExchange(cipherSuite=CipherSuite.{0}, version={1}"\
+              "".format(CipherSuite.ietfNames[self.cipherSuite], self.version)
+
         if self.srp_N != 0:
-            ret += ', srp_N={0}, srp_g={1}, srp_s={2!r}, srp_B={3}'.format(self
-                .srp_N, self.srp_g, self.srp_s, self.srp_B)
+            ret += ", srp_N={0}, srp_g={1}, srp_s={2!r}, srp_B={3}".format(
+                self.srp_N, self.srp_g, self.srp_s, self.srp_B)
         if self.dh_p != 0:
-            ret += ', dh_p={0}, dh_g={1}, dh_Ys={2}'.format(self.dh_p, self
-                .dh_g, self.dh_Ys)
+            ret += ", dh_p={0}, dh_g={1}, dh_Ys={2}".format(
+                self.dh_p, self.dh_g, self.dh_Ys)
         if self.signAlg != 0:
-            ret += ', hashAlg={0}, signAlg={1}'.format(self.hashAlg, self.
-                signAlg)
+            ret += ", hashAlg={0}, signAlg={1}".format(
+                self.hashAlg, self.signAlg)
         if self.signature != bytearray(0):
-            ret += ', signature={0!r}'.format(self.signature)
-        ret += ')'
+            ret += ", signature={0!r}".format(self.signature)
+        ret += ")"
+
         return ret

     def createSRP(self, srp_N, srp_g, srp_s, srp_B):
         """Set SRP protocol parameters."""
-        pass
+        self.srp_N = srp_N
+        self.srp_N_len = None
+        self.srp_g = srp_g
+        self.srp_g_len = None
+        self.srp_s = srp_s
+        self.srp_B = srp_B
+        self.srp_B_len = None
+        return self

     def createDH(self, dh_p, dh_g, dh_Ys):
         """Set FFDH protocol parameters."""
-        pass
+        self.dh_p = dh_p
+        self.dh_p_len = None
+        self.dh_g = dh_g
+        self.dh_g_len = None
+        self.dh_Ys = dh_Ys
+        self.dh_Ys_len = None
+        return self

     def createECDH(self, curve_type, named_curve=None, point=None):
         """Set ECDH protocol parameters."""
-        pass
+        self.curve_type = curve_type
+        self.named_curve = named_curve
+        self.ecdh_Ys = point
+        return self

     def parse(self, parser):
         """
@@ -845,7 +1499,41 @@ class ServerKeyExchange(HandshakeMsg):
         :type parser: Parser
         :param parser: parser to read data from
         """
-        pass
+        parser.startLengthCheck(3)
+        if self.cipherSuite in CipherSuite.srpAllSuites:
+            self.srp_N_len = parser.get(2)
+            self.srp_N = bytesToNumber(parser.getFixBytes(self.srp_N_len))
+            self.srp_g_len = parser.get(2)
+            self.srp_g = bytesToNumber(parser.getFixBytes(self.srp_g_len))
+            self.srp_s = parser.getVarBytes(1)
+            self.srp_B_len = parser.get(2)
+            self.srp_B = bytesToNumber(parser.getFixBytes(self.srp_B_len))
+        elif self.cipherSuite in CipherSuite.dhAllSuites:
+            self.dh_p_len = parser.get(2)
+            self.dh_p = bytesToNumber(parser.getFixBytes(self.dh_p_len))
+            self.dh_g_len = parser.get(2)
+            self.dh_g = bytesToNumber(parser.getFixBytes(self.dh_g_len))
+            self.dh_Ys_len = parser.get(2)
+            self.dh_Ys = bytesToNumber(parser.getFixBytes(self.dh_Ys_len))
+        elif self.cipherSuite in CipherSuite.ecdhAllSuites:
+            self.curve_type = parser.get(1)
+            # only named curves supported
+            assert self.curve_type == 3
+            self.named_curve = parser.get(2)
+            self.ecdh_Ys = parser.getVarBytes(1)
+        else:
+            raise AssertionError()
+
+        if self.cipherSuite in CipherSuite.certAllSuites or\
+                self.cipherSuite in CipherSuite.ecdheEcdsaSuites or\
+                self.cipherSuite in CipherSuite.dheDsaSuites:
+            if self.version == (3, 3):
+                self.hashAlg = parser.get(1)
+                self.signAlg = parser.get(1)
+            self.signature = parser.getVarBytes(2)
+
+        parser.stopLengthCheck()
+        return self

     def writeParams(self):
         """
@@ -853,7 +1541,30 @@ class ServerKeyExchange(HandshakeMsg):

         :rtype: bytearray
         """
-        pass
+        writer = Writer()
+        if self.cipherSuite in CipherSuite.srpAllSuites:
+            writer.addVarSeq(numberToByteArray(self.srp_N, self.srp_N_len),
+                             1, 2)
+            writer.addVarSeq(numberToByteArray(self.srp_g, self.srp_g_len),
+                             1, 2)
+            writer.addVarSeq(self.srp_s, 1, 1)
+            writer.addVarSeq(numberToByteArray(self.srp_B, self.srp_B_len),
+                             1, 2)
+        elif self.cipherSuite in CipherSuite.dhAllSuites:
+            writer.addVarSeq(numberToByteArray(self.dh_p, self.dh_p_len),
+                             1, 2)
+            writer.addVarSeq(numberToByteArray(self.dh_g, self.dh_g_len),
+                             1, 2)
+            writer.addVarSeq(numberToByteArray(self.dh_Ys, self.dh_Ys_len),
+                             1, 2)
+        elif self.cipherSuite in CipherSuite.ecdhAllSuites:
+            writer.add(self.curve_type, 1)
+            assert self.curve_type == 3
+            writer.add(self.named_curve, 2)
+            writer.addVarSeq(self.ecdh_Ys, 1, 1)
+        else:
+            assert(False)
+        return writer.bytes

     def write(self):
         """
@@ -861,7 +1572,17 @@ class ServerKeyExchange(HandshakeMsg):

         :rtype: bytearray
         """
-        pass
+        writer = Writer()
+        writer.bytes += self.writeParams()
+        if self.cipherSuite in CipherSuite.certAllSuites or \
+                self.cipherSuite in CipherSuite.ecdheEcdsaSuites or \
+                self.cipherSuite in CipherSuite.dheDsaSuites:
+            if self.version >= (3, 3):
+                assert self.hashAlg != 0 and self.signAlg != 0
+                writer.add(self.hashAlg, 1)
+                writer.add(self.signAlg, 1)
+            writer.addVarSeq(self.signature, 1, 2)
+        return self.postWrite(writer)

     def hash(self, clientRandom, serverRandom):
         """
@@ -869,17 +1590,46 @@ class ServerKeyExchange(HandshakeMsg):

         :rtype: bytearray
         """
-        pass
+        bytesToHash = clientRandom + serverRandom + self.writeParams()
+        if self.version >= (3, 3):
+            sigScheme = SignatureScheme.toRepr((self.hashAlg, self.signAlg))
+            if sigScheme is None:
+                hashAlg = HashAlgorithm.toRepr(self.hashAlg)
+                if hashAlg is None:
+                    raise AssertionError("Unknown hash algorithm: {0}".
+                                         format(self.hashAlg))
+            else:
+                hashAlg = SignatureScheme.getHash(sigScheme)
+            if hashAlg == "intrinsic":
+                return bytesToHash
+            return secureHash(bytesToHash, hashAlg)
+        # DSA and ECDSA ciphers in TLS 1.1 and earlier sign the messages using
+        # SHA-1 only
+        if self.cipherSuite in CipherSuite.ecdheEcdsaSuites or\
+                self.cipherSuite in CipherSuite.dheDsaSuites:
+            return SHA1(bytesToHash)
+        return MD5(bytesToHash) + SHA1(bytesToHash)


 class ServerHelloDone(HandshakeMsg):
-
     def __init__(self):
         HandshakeMsg.__init__(self, HandshakeType.server_hello_done)

+    def create(self):
+        return self
+
+    def parse(self, p):
+        p.startLengthCheck(3)
+        p.stopLengthCheck()
+        return self
+
+    def write(self):
+        w = Writer()
+        return self.postWrite(w)
+
     def __repr__(self):
         """Human readable representation of object."""
-        return 'ServerHelloDone()'
+        return "ServerHelloDone()"


 class ClientKeyExchange(HandshakeMsg):
@@ -928,7 +1678,8 @@ class ClientKeyExchange(HandshakeMsg):
         :param srp_A: client SRP answer
         :rtype: ClientKeyExchange
         """
-        pass
+        self.srp_A = srp_A
+        return self

     def createRSA(self, encryptedPreMasterSecret):
         """
@@ -939,7 +1690,8 @@ class ClientKeyExchange(HandshakeMsg):
         :type encryptedPreMasterSecret: bytearray
         :rtype: ClientKeyExchange
         """
-        pass
+        self.encryptedPreMasterSecret = encryptedPreMasterSecret
+        return self

     def createDH(self, dh_Yc):
         """
@@ -950,7 +1702,8 @@ class ClientKeyExchange(HandshakeMsg):
         :type dh_Yc: int
         :rtype: ClientKeyExchange
         """
-        pass
+        self.dh_Yc = dh_Yc
+        return self

     def createECDH(self, ecdh_Yc):
         """
@@ -961,7 +1714,8 @@ class ClientKeyExchange(HandshakeMsg):
         :type ecdh_Yc: bytearray
         :rtype: ClientKeyExchange
         """
-        pass
+        self.ecdh_Yc = ecdh_Yc
+        return self

     def parse(self, parser):
         """
@@ -972,7 +1726,25 @@ class ClientKeyExchange(HandshakeMsg):
         :type parser: Parser
         :rtype: ClientKeyExchange
         """
-        pass
+        parser.startLengthCheck(3)
+        if self.cipherSuite in CipherSuite.srpAllSuites:
+            self.srp_A = bytesToNumber(parser.getVarBytes(2))
+        elif self.cipherSuite in CipherSuite.certSuites:
+            if self.version in ((3, 1), (3, 2), (3, 3)):
+                self.encryptedPreMasterSecret = parser.getVarBytes(2)
+            elif self.version == (3, 0):
+                self.encryptedPreMasterSecret = \
+                    parser.getFixBytes(parser.getRemainingLength())
+            else:
+                raise AssertionError()
+        elif self.cipherSuite in CipherSuite.dhAllSuites:
+            self.dh_Yc = bytesToNumber(parser.getVarBytes(2))
+        elif self.cipherSuite in CipherSuite.ecdhAllSuites:
+            self.ecdh_Yc = parser.getVarBytes(1)
+        else:
+            raise AssertionError()
+        parser.stopLengthCheck()
+        return self

     def write(self):
         """
@@ -980,7 +1752,23 @@ class ClientKeyExchange(HandshakeMsg):

         :rtype: bytearray
         """
-        pass
+        w = Writer()
+        if self.cipherSuite in CipherSuite.srpAllSuites:
+            w.addVarSeq(numberToByteArray(self.srp_A), 1, 2)
+        elif self.cipherSuite in CipherSuite.certSuites:
+            if self.version in ((3, 1), (3, 2), (3, 3)):
+                w.addVarSeq(self.encryptedPreMasterSecret, 1, 2)
+            elif self.version == (3, 0):
+                w.bytes += self.encryptedPreMasterSecret
+            else:
+                raise AssertionError()
+        elif self.cipherSuite in CipherSuite.dhAllSuites:
+            w.addVarSeq(numberToByteArray(self.dh_Yc), 1, 2)
+        elif self.cipherSuite in CipherSuite.ecdhAllSuites:
+            w.addVarSeq(self.ecdh_Yc, 1, 1)
+        else:
+            raise AssertionError()
+        return self.postWrite(w)


 class ClientMasterKey(HandshakeMsg):
@@ -1002,8 +1790,8 @@ class ClientMasterKey(HandshakeMsg):
     """

     def __init__(self):
-        super(ClientMasterKey, self).__init__(SSL2HandshakeType.
-            client_master_key)
+        super(ClientMasterKey,
+              self).__init__(SSL2HandshakeType.client_master_key)
         self.cipher = 0
         self.clear_key = bytearray(0)
         self.encrypted_key = bytearray(0)
@@ -1011,15 +1799,39 @@ class ClientMasterKey(HandshakeMsg):

     def create(self, cipher, clear_key, encrypted_key, key_argument):
         """Set values of the CLIENT-MASTER-KEY object."""
-        pass
+        self.cipher = cipher
+        self.clear_key = clear_key
+        self.encrypted_key = encrypted_key
+        self.key_argument = key_argument
+        return self

     def write(self):
         """Serialise the object to on the wire data."""
-        pass
+        writer = Writer()
+        writer.add(self.handshakeType, 1)
+        writer.add(self.cipher, 3)
+        writer.add(len(self.clear_key), 2)
+        writer.add(len(self.encrypted_key), 2)
+        writer.add(len(self.key_argument), 2)
+        writer.bytes += self.clear_key
+        writer.bytes += self.encrypted_key
+        writer.bytes += self.key_argument
+        return writer.bytes

     def parse(self, parser):
         """Deserialise object from on the wire data."""
-        pass
+        self.cipher = parser.get(3)
+        clear_key_length = parser.get(2)
+        encrypted_key_length = parser.get(2)
+        key_argument_length = parser.get(2)
+        parser.setLengthCheck(clear_key_length +
+                              encrypted_key_length +
+                              key_argument_length)
+        self.clear_key = parser.getFixBytes(clear_key_length)
+        self.encrypted_key = parser.getFixBytes(encrypted_key_length)
+        self.key_argument = parser.getFixBytes(key_argument_length)
+        parser.stopLengthCheck()
+        return self


 class CertificateVerify(HandshakeMsg):
@@ -1044,7 +1856,9 @@ class CertificateVerify(HandshakeMsg):
         :param signatureAlgorithm: signature algorithm used to make the
             signature (TLSv1.2 only)
         """
-        pass
+        self.signatureAlgorithm = signatureAlgorithm
+        self.signature = signature
+        return self

     def parse(self, parser):
         """
@@ -1052,7 +1866,12 @@ class CertificateVerify(HandshakeMsg):

         :param parser: parser with data to read
         """
-        pass
+        parser.startLengthCheck(3)
+        if self.version >= (3, 3):
+            self.signatureAlgorithm = (parser.get(1), parser.get(1))
+        self.signature = parser.getVarBytes(2)
+        parser.stopLengthCheck()
+        return self

     def write(self):
         """
@@ -1060,46 +1879,117 @@ class CertificateVerify(HandshakeMsg):

         :rtype: bytearray
         """
-        pass
+        writer = Writer()
+        if self.version >= (3, 3):
+            writer.add(self.signatureAlgorithm[0], 1)
+            writer.add(self.signatureAlgorithm[1], 1)
+        writer.addVarSeq(self.signature, 1, 2)
+        return self.postWrite(writer)


 class ChangeCipherSpec(object):
-
     def __init__(self):
         self.contentType = ContentType.change_cipher_spec
         self.type = 1

+    def create(self):
+        self.type = 1
+        return self

-class NextProtocol(HandshakeMsg):
+    def parse(self, p):
+        p.setLengthCheck(1)
+        self.type = p.get(1)
+        p.stopLengthCheck()
+        if p.getRemainingLength():
+            raise DecodeError("Multi-byte CCS message")
+        return self

+    def write(self):
+        w = Writer()
+        w.add(self.type, 1)
+        return w.bytes
+
+
+class NextProtocol(HandshakeMsg):
     def __init__(self):
         HandshakeMsg.__init__(self, HandshakeType.next_protocol)
         self.next_proto = None

+    def create(self, next_proto):
+        self.next_proto = next_proto
+        return self
+
+    def parse(self, p):
+        p.startLengthCheck(3)
+        self.next_proto = p.getVarBytes(1)
+        _ = p.getVarBytes(1)
+        p.stopLengthCheck()
+        return self
+
+    def write(self, trial=False):
+        w = Writer()
+        w.addVarSeq(self.next_proto, 1, 1)
+        paddingLen = 32 - ((len(self.next_proto) + 2) % 32)
+        w.addVarSeq(bytearray(paddingLen), 1, 1)
+        return self.postWrite(w)

-class Finished(HandshakeMsg):

+class Finished(HandshakeMsg):
     def __init__(self, version, hash_length=None):
         HandshakeMsg.__init__(self, HandshakeType.finished)
         self.version = version
         self.verify_data = bytearray(0)
         self.hash_length = hash_length

+    def create(self, verify_data):
+        self.verify_data = verify_data
+        return self
+
+    def parse(self, p):
+        p.startLengthCheck(3)
+        if self.version == (3, 0):
+            self.verify_data = p.getFixBytes(36)
+        elif self.version in ((3, 1), (3, 2), (3, 3)):
+            self.verify_data = p.getFixBytes(12)
+        elif self.version > (3, 3):
+            self.verify_data = p.getFixBytes(self.hash_length)
+        else:
+            raise AssertionError()
+        p.stopLengthCheck()
+        return self
+
+    def write(self):
+        w = Writer()
+        w.bytes += self.verify_data
+        return self.postWrite(w)
+

 class EncryptedExtensions(HelloMessage):
     """Handling of the TLS1.3 Encrypted Extensions message."""

     def __init__(self):
-        super(EncryptedExtensions, self).__init__(HandshakeType.
-            encrypted_extensions)
+        super(EncryptedExtensions, self).__init__(
+                HandshakeType.encrypted_extensions)

     def create(self, extensions):
         """Set the extensions in the message."""
-        pass
+        self.extensions = extensions
+        return self

     def parse(self, parser):
         """Parse the extensions from on the wire data."""
-        pass
+        parser.startLengthCheck(3)
+
+        if not parser.getRemainingLength():
+            raise SyntaxError("No list of extensions")
+        else:
+            self.extensions = []
+            p2 = Parser(parser.getVarBytes(2))
+            while p2.getRemainingLength():
+                self.extensions.append(TLSExtension(encExt=True).parse(p2))
+
+        parser.stopLengthCheck()
+        return self

     def write(self):
         """
@@ -1107,7 +1997,15 @@ class EncryptedExtensions(HelloMessage):

         :rtype: bytearray
         """
-        pass
+        w = Writer()
+        w2 = Writer()
+        for ext in self.extensions:
+            w2.bytes += ext.write()
+
+        w.add(len(w2.bytes), 2)
+        w.bytes += w2.bytes
+
+        return self.postWrite(w)


 class NewSessionTicket(HelloMessage):
@@ -1115,19 +2013,26 @@ class NewSessionTicket(HelloMessage):

     def __init__(self):
         """Create New Session Ticket object."""
-        super(NewSessionTicket, self).__init__(HandshakeType.new_session_ticket
-            )
+        super(NewSessionTicket, self).__init__(HandshakeType
+                                               .new_session_ticket)
         self.ticket_lifetime = 0
         self.ticket_age_add = 0
         self.ticket_nonce = bytearray(0)
         self.ticket = bytearray(0)
         self.extensions = []
+        # time at which the ticket was received, not sent on the wire
+        # in seconds in Unix Epoch
         self.time = None

     def create(self, ticket_lifetime, ticket_age_add, ticket_nonce, ticket,
-        extensions):
+               extensions):
         """Initialise a New Session Ticket."""
-        pass
+        self.ticket_lifetime = ticket_lifetime
+        self.ticket_age_add = ticket_age_add
+        self.ticket_nonce = ticket_nonce
+        self.ticket = ticket
+        self.extensions = extensions
+        return self

     def write(self):
         """
@@ -1135,11 +2040,34 @@ class NewSessionTicket(HelloMessage):

         :rtype: bytearray
         """
-        pass
+        w = Writer()
+        w.add(self.ticket_lifetime, 4)
+        w.add(self.ticket_age_add, 4)
+        w.addVarSeq(self.ticket_nonce, 1, 1)
+        w.addVarSeq(self.ticket, 1, 2)
+        w2 = Writer()
+        for ext in self.extensions:
+            w2.bytes += ext.write()
+        w.add(len(w2.bytes), 2)
+        w.bytes += w2.bytes
+
+        return self.postWrite(w)

     def parse(self, parser):
         """Parse the object from on the wire data."""
-        pass
+        parser.startLengthCheck(3)
+
+        self.ticket_lifetime = parser.get(4)
+        self.ticket_age_add = parser.get(4)
+        self.ticket_nonce = parser.getVarBytes(1)
+        self.ticket = parser.getVarBytes(2)
+        self.extensions = []
+        ext_parser = Parser(parser.getVarBytes(2))
+        while ext_parser.getRemainingLength():
+            self.extensions.append(TLSExtension().parse(ext_parser))
+
+        parser.stopLengthCheck()
+        return self


 class NewSessionTicket1_0(HelloMessage):
@@ -1147,14 +2075,16 @@ class NewSessionTicket1_0(HelloMessage):

     def __init__(self):
         """Create New Session Ticket object."""
-        super(NewSessionTicket1_0, self).__init__(HandshakeType.
-            new_session_ticket)
+        super(NewSessionTicket1_0, self).__init__(HandshakeType
+                                                  .new_session_ticket)
         self.ticket_lifetime = 0
         self.ticket = bytearray(0)

     def create(self, ticket_lifetime, ticket):
         """Initialise a New Session Ticket."""
-        pass
+        self.ticket_lifetime = ticket_lifetime
+        self.ticket = ticket
+        return self

     def write(self):
         """
@@ -1162,11 +2092,22 @@ class NewSessionTicket1_0(HelloMessage):

         :rtype: bytearray
         """
-        pass
+        w = Writer()
+        w.add(self.ticket_lifetime, 4)
+        w.addVarSeq(self.ticket, 1, 2)
+        w2 = Writer()
+        w.bytes += w2.bytes
+
+        return self.postWrite(w)

     def parse(self, parser):
         """Parse the object from on the wire data."""
-        pass
+        parser.startLengthCheck(3)
+        self.ticket_lifetime = parser.get(4)
+        self.ticket = parser.getVarBytes(2)
+        parser.stopLengthCheck()
+
+        return self


 class SessionTicketPayload(object):
@@ -1211,19 +2152,89 @@ class SessionTicketPayload(object):
     @property
     def client_cert_chain(self):
         """Getter for the client_cert_chain property."""
-        pass
+        if self._cert_chain:
+            return X509CertChain([i.certificate
+                                  for i in self._cert_chain])
+        return None

     @client_cert_chain.setter
     def client_cert_chain(self, client_cert_chain):
         """Setter for the cert_chain property."""
-        pass
+        self._cert_chain = [CertificateEntry(CertificateType.x509)
+                            .create(i, []) for i in client_cert_chain.x509List]

     def create(self, master_secret, protocol_version, cipher_suite,
-        creation_time, nonce=bytearray(), client_cert_chain=None,
-        encrypt_then_mac=False, extended_master_secret=False, server_name=
-        bytearray()):
+               creation_time, nonce=bytearray(), client_cert_chain=None,
+               encrypt_then_mac=False, extended_master_secret=False,
+               server_name=bytearray()):
         """Initialise the object with cryptographic data."""
-        pass
+        self.master_secret = master_secret
+        self.protocol_version = protocol_version
+        self.cipher_suite = cipher_suite
+        self.creation_time = creation_time
+        self.nonce = nonce
+        if client_cert_chain:
+            self.version = 1
+            self.client_cert_chain = client_cert_chain
+        if encrypt_then_mac or extended_master_secret or server_name:
+            if self.client_cert_chain is None:
+                self._cert_chain = []
+            self.version = 2
+            self.encrypt_then_mac = encrypt_then_mac
+            self.extended_master_secret = extended_master_secret
+            if server_name is None:
+                self.server_name = bytearray()
+            else:
+                self.server_name = server_name
+        return self
+
+    def _parse_cert_chain(self, parser):
+        self._cert_chain = []
+        while parser.getRemainingLength():
+            entry = CertificateEntry(CertificateType.x509)
+            self._cert_chain.append(entry.parse(parser))
+
+    def parse(self, parser):
+        self.version = parser.get(2)
+        if self.version > 2:
+            raise ValueError("Unrecognised version number")
+        self.master_secret = parser.getVarBytes(2)
+        self.protocol_version = (parser.get(1), parser.get(1))
+        self.cipher_suite = parser.get(2)
+        self.nonce = parser.getVarBytes(1)
+        self.creation_time = parser.get(8)
+        if self.version >= 1:
+            self._parse_cert_chain(Parser(parser.getVarBytes(3)))
+        if self.version >= 2:
+            self.encrypt_then_mac = bool(parser.get(1))
+            self.extended_master_secret = bool(parser.get(1))
+            self.server_name = parser.getVarBytes(2)
+        if parser.getRemainingLength():
+            raise ValueError("Malformed ticket")
+        return self
+
+    def write(self):
+        writer = Writer()
+        writer.addTwo(self.version)
+        writer.addTwo(len(self.master_secret))
+        writer.bytes += self.master_secret
+        writer.addOne(self.protocol_version[0])
+        writer.addOne(self.protocol_version[1])
+        writer.addTwo(self.cipher_suite)
+        writer.addOne(len(self.nonce))
+        writer.bytes += self.nonce
+        writer.add(self.creation_time, 8)
+        if self.version >= 1:
+            wcert = Writer()
+            for entry in self._cert_chain:
+                wcert.bytes += entry.write()
+            writer.addVarSeq(wcert.bytes, 1, 3)
+        if self.version >= 2:
+            writer.addOne(int(self.encrypt_then_mac))
+            writer.addOne(int(self.extended_master_secret))
+            writer.addTwo(len(self.server_name))
+            writer.bytes += self.server_name
+        return writer.bytes


 class SSL2Finished(HandshakeMsg):
@@ -1235,15 +2246,21 @@ class SSL2Finished(HandshakeMsg):

     def create(self, verify_data):
         """Set the message payload."""
-        pass
+        self.verify_data = verify_data
+        return self

     def parse(self, parser):
         """Deserialise the message from on the wire data."""
-        pass
+        self.verify_data = parser.getFixBytes(parser.getRemainingLength())
+        return self

     def write(self):
         """Serialise the message to on the wire data."""
-        pass
+        writer = Writer()
+        writer.add(self.handshakeType, 1)
+        writer.bytes += self.verify_data
+        # does not use postWrite() as it's a SSLv2 message
+        return writer.bytes


 class ClientFinished(SSL2Finished):
@@ -1285,30 +2302,55 @@ class CertificateStatus(HandshakeMsg):

     def __init__(self):
         """Create the objet, set its type."""
-        super(CertificateStatus, self).__init__(HandshakeType.
-            certificate_status)
+        super(CertificateStatus, self).__init__(
+                HandshakeType.certificate_status)
         self.status_type = None
         self.ocsp = bytearray()

     def create(self, status_type, ocsp):
         """Set up message payload."""
-        pass
+        self.status_type = status_type
+        self.ocsp = ocsp
+        return self

     def parse(self, parser):
         """Deserialise the message from one the wire data."""
-        pass
+        parser.startLengthCheck(3)
+        self.status_type = parser.get(1)
+        self.ocsp = parser.getVarBytes(3)
+        parser.stopLengthCheck()
+        return self

     def write(self):
         """Serialise the message."""
-        pass
+        writer = Writer()
+        writer.add(self.status_type, 1)
+        writer.add(len(self.ocsp), 3)
+        writer.bytes += self.ocsp
+        return self.postWrite(writer)


 class ApplicationData(object):
-
     def __init__(self):
         self.contentType = ContentType.application_data
         self.bytes = bytearray(0)

+    def create(self, bytes):
+        self.bytes = bytes
+        return self
+
+    def splitFirstByte(self):
+        newMsg = ApplicationData().create(self.bytes[:1])
+        self.bytes = self.bytes[1:]
+        return newMsg
+
+    def parse(self, p):
+        self.bytes = p.bytes
+        return self
+
+    def write(self):
+        return self.bytes
+

 class Heartbeat(object):
     """
@@ -1332,11 +2374,18 @@ class Heartbeat(object):

     def create(self, message_type, payload, padding_length):
         """Create heartbeat request or response with selected parameters"""
-        pass
+        self.message_type = message_type
+        self.payload = payload
+        self.padding = getRandomBytes(padding_length)
+        return self

     def create_response(self):
         """Creates heartbeat response based on request."""
-        pass
+        heartbeat_response = Heartbeat().create(
+            HeartbeatMessageType.heartbeat_response,
+            self.payload,
+            16)
+        return heartbeat_response

     def parse(self, p):
         """
@@ -1345,20 +2394,29 @@ class Heartbeat(object):
         We are reading only message type and payload, ignoring
         leftover bytes (padding).
         """
-        pass
+        self.message_type = p.get(1)
+        self.payload = p.getVarBytes(2)
+        self.padding = p.getFixBytes(p.getRemainingLength())
+        return self

     def write(self):
         """Serialise heartbeat message."""
-        pass
+        w = Writer()
+        w.add(self.message_type, 1)
+        w.add(len(self.payload), 2)
+        w.bytes += self.payload
+        w.bytes += self.padding
+        return w.bytes

     @property
     def _message_type(self):
         """Format heartbeat message to human readable representation."""
-        pass
+        return none_as_unknown(HeartbeatMessageType.toRepr(self.message_type),
+                               self.message_type)

     def __str__(self):
         """Return human readable representation of heartbeat message."""
-        return 'heartbeat {0}'.format(self._message_type)
+        return "heartbeat {0}".format(self._message_type)


 class KeyUpdate(HandshakeMsg):
@@ -1376,12 +2434,18 @@ class KeyUpdate(HandshakeMsg):

     def create(self, message_type):
         """Create KeyUpdate message with selected parameter."""
-        pass
+        self.message_type = message_type
+        return self

     def parse(self, p):
         """Deserialize keyupdate message from parser."""
-        pass
+        p.startLengthCheck(3)
+        self.message_type = p.get(1)
+        p.stopLengthCheck()
+        return self

     def write(self):
         """Serialise keyupdate message."""
-        pass
+        writer = Writer()
+        writer.add(self.message_type, 1)
+        return self.postWrite(writer)
diff --git a/tlslite/messagesocket.py b/tlslite/messagesocket.py
index 0cb49ef..7a38598 100644
--- a/tlslite/messagesocket.py
+++ b/tlslite/messagesocket.py
@@ -1,11 +1,18 @@
+# vim: set fileencoding=utf8
+#
+# Copyright © 2015, Hubert Kario
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """Wrapper of TLS RecordLayer providing message-level abstraction"""
+
 from .recordlayer import RecordLayer
 from .constants import ContentType
 from .messages import RecordHeader3, Message
 from .utils.codec import Parser

-
 class MessageSocket(RecordLayer):
+
     """TLS Record Layer socket that provides Message level abstraction

     Because the record layer has a hard size limit on sent messages, they need
@@ -43,13 +50,16 @@ class MessageSocket(RecordLayer):
         :param defragmenter: defragmenter to apply on the records read
         """
         super(MessageSocket, self).__init__(sock)
+
         self.defragmenter = defragmenter
         self.unfragmentedDataTypes = (ContentType.application_data,
-            ContentType.heartbeat)
-        self._lastRecordVersion = 0, 0
+                                      ContentType.heartbeat)
+        self._lastRecordVersion = (0, 0)
+
         self._sendBuffer = bytearray(0)
         self._sendBufferType = None
-        self.recordSize = 2 ** 14
+
+        self.recordSize = 2**14

     def recvMessage(self):
         """
@@ -61,11 +71,39 @@ class MessageSocket(RecordLayer):

         :rtype: generator
         """
-        pass
+        while True:
+            while True:
+                ret = self.defragmenter.get_message()
+                if ret is None:
+                    break
+                header = RecordHeader3().create(self._lastRecordVersion,
+                                                ret[0],
+                                                0)
+                yield header, Parser(ret[1])
+
+            for ret in self.recvRecord():
+                if ret in (0, 1):
+                    yield ret
+                else:
+                    break
+
+            header, parser = ret
+            if header.type in self.unfragmentedDataTypes:
+                yield ret
+            # TODO probably needs a bit better handling...
+            if header.ssl2:
+                yield ret
+
+            self.defragmenter.add_data(header.type, parser.bytes)
+            self._lastRecordVersion = header.version

     def recvMessageBlocking(self):
         """Blocking variant of :py:meth:`recvMessage`."""
-        pass
+        for res in self.recvMessage():
+            if res in (0, 1):
+                pass
+            else:
+                return res

     def flush(self):
         """
@@ -76,11 +114,20 @@ class MessageSocket(RecordLayer):

         :rtype: generator
         """
-        pass
+        while len(self._sendBuffer) > 0:
+            recordPayload = self._sendBuffer[:self.recordSize]
+            self._sendBuffer = self._sendBuffer[self.recordSize:]
+            msg = Message(self._sendBufferType, recordPayload)
+            for res in self.sendRecord(msg):
+                yield res
+
+        assert len(self._sendBuffer) == 0
+        self._sendBufferType = None

     def flushBlocking(self):
         """Blocking variant of :py:meth:`flush`."""
-        pass
+        for _ in self.flush():
+            pass

     def queueMessage(self, msg):
         """
@@ -94,11 +141,24 @@ class MessageSocket(RecordLayer):

         :rtype: generator
         """
-        pass
+        if self._sendBufferType is None:
+            self._sendBufferType = msg.contentType
+
+        if msg.contentType == self._sendBufferType:
+            self._sendBuffer += msg.write()
+            return
+
+        for res in self.flush():
+            yield res
+
+        assert self._sendBufferType is None
+        self._sendBufferType = msg.contentType
+        self._sendBuffer += msg.write()

     def queueMessageBlocking(self, msg):
         """Blocking variant of :py:meth:`queueMessage`."""
-        pass
+        for _ in self.queueMessage(msg):
+            pass

     def sendMessage(self, msg):
         """
@@ -115,8 +175,13 @@ class MessageSocket(RecordLayer):

         :rtype: generator
         """
-        pass
+        for res in self.queueMessage(msg):
+            yield res
+
+        for res in self.flush():
+            yield res

     def sendMessageBlocking(self, msg):
         """Blocking variant of :py:meth:`sendMessage`."""
-        pass
+        for _ in self.sendMessage(msg):
+            pass
diff --git a/tlslite/ocsp.py b/tlslite/ocsp.py
index 0745404..78955cf 100644
--- a/tlslite/ocsp.py
+++ b/tlslite/ocsp.py
@@ -1,17 +1,17 @@
 """Class for handling primary OCSP responses"""
+
 from .utils.asn1parser import ASN1Parser
 from .utils.cryptomath import bytesToNumber, numBytes, secureHash
 from .x509 import X509
 from .signed import SignedObject
 from .errors import TLSIllegalParameterException

-
 class OCSPRespStatus(object):
     """ OCSP response status codes (RFC 2560) """
     successful = 0
     malformedRequest = 1
     internalError = 2
-    tryLater = 3
+    tryLater = 3    # 4 is not used to match RFC2560 specification
     sigRequired = 5
     unauthorized = 6

@@ -23,7 +23,6 @@ class CertStatus(object):

 class SingleResponse(object):
     """ This class represents SingleResponse ASN1 type (defined in RFC2560) """
-
     def __init__(self, value):
         self.value = value
         self.cert_hash_alg = None
@@ -34,16 +33,63 @@ class SingleResponse(object):
         self.this_update = None
         self.next_update = None
         self.parse(value)
-    _hash_algs_OIDs = {tuple([42, 134, 72, 134, 247, 13, 2, 5]): 'md5',
-        tuple([43, 14, 3, 2, 26]): 'sha1', tuple([96, 134, 72, 1, 101, 3, 4,
-        2, 4]): 'sha224', tuple([96, 134, 72, 1, 101, 3, 4, 2, 1]):
-        'sha256', tuple([96, 134, 72, 1, 101, 3, 4, 2, 2]): 'sha384', tuple
-        ([96, 134, 72, 1, 101, 3, 4, 2, 3]): 'sha512'}
+
+    _hash_algs_OIDs = {
+        tuple([0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x2, 0x5]): 'md5',
+        tuple([0x2b, 0xe, 0x3, 0x2, 0x1a]): 'sha1',
+        tuple([0x60, 0x86, 0x48, 0x1, 0x65, 0x3, 0x4, 0x2, 0x4]): 'sha224',
+        tuple([0x60, 0x86, 0x48, 0x1, 0x65, 0x3, 0x4, 0x2, 0x1]): 'sha256',
+        tuple([0x60, 0x86, 0x48, 0x1, 0x65, 0x3, 0x4, 0x2, 0x2]): 'sha384',
+        tuple([0x60, 0x86, 0x48, 0x1, 0x65, 0x3, 0x4, 0x2, 0x3]): 'sha512'
+    }
+
+    def parse(self, value):
+        cert_id = value.getChild(0)
+        self.cert_hash_alg = cert_id.getChild(0).getChild(0).value
+        self.cert_issuer_name_hash = cert_id.getChild(1).value
+        self.cert_issuer_key_hash = cert_id.getChild(2).value
+        self.cert_serial_num = bytesToNumber(cert_id.getChild(3).value)
+        self.cert_status = value.getChild(1).value
+        self.this_update = value.getChild(2).value
+        # next_update is optional
+        try:
+            fld = value.getChild(3)
+            if fld.type.tag_id == 0:
+                self.next_update = fld.value
+        except SyntaxError:
+            self.next_update = None
+
+    def verify_cert_match(self, server_cert, issuer_cert):
+        # extact subject public key
+        issuer_key = issuer_cert.subject_public_key
+
+        # extract issuer DN
+        issuer_name = issuer_cert.subject
+
+        try:
+            alg = self._hash_algs_OIDs[tuple(self.cert_hash_alg)]
+        except KeyError as e:
+            raise TLSIllegalParameterException("Unknown hash algorithm: {0}".format(
+                            list(self.cert_hash_alg)))
+
+        # hash issuer key
+        hashed_key = secureHash(issuer_key, alg)
+        if hashed_key != self.cert_issuer_key_hash:
+            raise ValueError("Could not verify certificate public key")
+
+        # hash issuer name
+        hashed_name = secureHash(issuer_name, alg)
+        if hashed_name != self.cert_issuer_name_hash:
+            raise ValueError("Could not verify certificate DN")
+
+        # serial number
+        if server_cert.serial_number != self.cert_serial_num:
+            raise ValueError("Could not verify certificate serial number")
+        return True


 class OCSPResponse(SignedObject):
     """ This class represents an OCSP response. """
-
     def __init__(self, value):
         super(OCSPResponse, self).__init__()
         self.bytes = None
@@ -63,7 +109,34 @@ class OCSPResponse(SignedObject):
         :type value: stream of bytes
         :param value: An DER-encoded OCSP response
         """
-        pass
+        self.bytes = bytearray(value)
+        parser = ASN1Parser(self.bytes)
+        resp_status = parser.getChild(0)
+        self.resp_status = resp_status.value[0]
+        # if the response status is not successsful, abort parsing other fields
+        if self.resp_status != OCSPRespStatus.successful:
+            return self
+        resp_bytes = parser.getChild(1).getChild(0)
+        self.resp_type = resp_bytes.getChild(0).value
+        response = resp_bytes.getChild(1)
+        # check if response is id-pkix-ocsp-basic
+        if list(self.resp_type) != [43, 6, 1, 5, 5, 7, 48, 1, 1]:
+            raise SyntaxError()
+        basic_resp = response.getChild(0)
+        # parsing tbsResponseData fields
+        self._tbsdataparse(basic_resp.getChild(0))
+        self.tbs_data = basic_resp.getChildBytes(0)
+        self.signature_alg = basic_resp.getChild(1).getChild(0).value
+        self.signature = basic_resp.getChild(2).value
+        # test if certs field is present
+        if basic_resp.getChildCount() > 3:
+            certs = basic_resp.getChild(3)
+            cnt = certs.getChildCount()
+            for i in range(cnt):
+                certificate = X509()
+                certificate.parseBinary(certs.getChild(i).value)
+                self.certs.append(certificate)
+        return self

     def _tbsdataparse(self, value):
         """
@@ -72,4 +145,20 @@ class OCSPResponse(SignedObject):
         :type value: stream of bytes
         :param value: TBS data
         """
-        pass
+        # test if version is ommited
+        field = value.getChild(0)
+        cnt = 0
+        if field.type.tag_id == 0:
+            # version is not omitted
+            cnt += 1
+            self.version = field.value
+        else:
+            self.version = 1
+        self.resp_id = value.getChild(cnt).value
+        self.produced_at = value.getChild(cnt+1).value
+        responses = value.getChild(cnt+2)
+        resp_cnt = responses.getChildCount()
+        for i in range(resp_cnt):
+            resp = responses.getChild(i)
+            parsed_resp = SingleResponse(resp)
+            self.responses.append(parsed_resp)
diff --git a/tlslite/recordlayer.py b/tlslite/recordlayer.py
index 72658db..5b21a1e 100644
--- a/tlslite/recordlayer.py
+++ b/tlslite/recordlayer.py
@@ -1,27 +1,37 @@
+# Copyright (c) 2014, Hubert Kario
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """Implementation of the TLS Record Layer protocol"""
+
 import socket
 import errno
 import copy
 try:
+    # in python 3 the native zip() returns iterator
     from itertools import izip
 except ImportError:
     izip = zip
 try:
+    # in python 3 the native range() returns an object/iterator
     xrange
 except NameError:
     xrange = range
+
 from .utils import tlshashlib as hashlib
 from .constants import ContentType, CipherSuite
 from .messages import RecordHeader3, RecordHeader2, Message
-from .utils.cipherfactory import createAESCCM, createAESCCM_8, createAESGCM, createAES, createRC4, createTripleDES, createCHACHA20
+from .utils.cipherfactory import createAESCCM, createAESCCM_8, createAESGCM,\
+        createAES, createRC4, createTripleDES, createCHACHA20
 from .utils.codec import Parser, Writer
 from .utils.compat import compatHMAC
 from .utils.cryptomath import getRandomBytes, MD5, HKDF_expand_label
 from .utils.constanttime import ct_compare_digest, ct_check_cbc_mac_and_pad
-from .errors import TLSRecordOverflow, TLSIllegalParameterException, TLSAbruptCloseError, TLSDecryptionFailed, TLSBadRecordMAC, TLSUnexpectedMessage
+from .errors import TLSRecordOverflow, TLSIllegalParameterException,\
+        TLSAbruptCloseError, TLSDecryptionFailed, TLSBadRecordMAC, \
+        TLSUnexpectedMessage
 from .mathtls import createMAC_SSL, createHMAC, calc_key

-
 class RecordSocket(object):
     """
     Socket wrapper for reading and writing TLS Records.
@@ -41,9 +51,9 @@ class RecordSocket(object):
         :type sock: socket.socket
         """
         self.sock = sock
-        self.version = 0, 0
+        self.version = (0, 0)
         self.tls13record = False
-        self.recv_record_limit = 2 ** 14
+        self.recv_record_limit = 2**14

     def _sockSendAll(self, data):
         """
@@ -53,7 +63,19 @@ class RecordSocket(object):
         :param data: data to send
         :raises socket.error: when write to socket failed
         """
-        pass
+        while 1:
+            try:
+                bytesSent = self.sock.send(data)
+            except socket.error as why:
+                if why.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN):
+                    yield 1
+                    continue
+                raise
+
+            if bytesSent == len(data):
+                return
+            data = data[bytesSent:]
+            yield 1

     def send(self, msg, padding=0):
         """
@@ -65,7 +87,20 @@ class RecordSocket(object):
         :param padding: amount of padding to specify for SSLv2
         :raises socket.error: when write to socket failed
         """
-        pass
+        data = msg.write()
+
+        if self.version in ((2, 0), (0, 2)):
+            header = RecordHeader2().create(len(data),
+                                            padding)
+        else:
+            header = RecordHeader3().create(self.version,
+                                            msg.contentType,
+                                            len(data))
+
+        data = header.write() + data
+
+        for result in self._sockSendAll(data):
+            yield result

     def _sockRecvAll(self, length):
         """
@@ -76,11 +111,84 @@ class RecordSocket(object):
             blocking and would block and bytearray in case the read finished
         :raises TLSAbruptCloseError: when the socket closed
         """
-        pass
+        buf = bytearray(0)
+
+        if length == 0:
+            yield buf
+
+        while True:
+            try:
+                socketBytes = self.sock.recv(length - len(buf))
+            except socket.error as why:
+                if why.args[0] in (errno.EWOULDBLOCK, errno.EAGAIN):
+                    yield 0
+                    continue
+                else:
+                    raise
+
+            #if the connection closed, raise socket error
+            if len(socketBytes) == 0:
+                raise TLSAbruptCloseError()
+
+            buf += bytearray(socketBytes)
+            if len(buf) == length:
+                yield buf

     def _recvHeader(self):
         """Read a single record header from socket"""
-        pass
+        #Read the next record header
+        buf = bytearray(0)
+        ssl2 = False
+
+        result = None
+        for result in self._sockRecvAll(1):
+            if result in (0, 1):
+                yield result
+            else: break
+        assert result is not None
+
+        buf += result
+
+        if buf[0] in ContentType.all:
+            ssl2 = False
+            # SSLv3 record layer header is 5 bytes long, we already read 1
+            result = None
+            for result in self._sockRecvAll(4):
+                if result in (0, 1):
+                    yield result
+                else: break
+            assert result is not None
+            buf += result
+        else:
+            # if header has no pading the header is 2 bytes long, 3 otherwise
+            # at the same time we already read 1 byte
+            ssl2 = True
+            if buf[0] & 0x80:
+                readLen = 1
+            else:
+                readLen = 2
+            result = None
+            for result in self._sockRecvAll(readLen):
+                if result in (0, 1):
+                    yield result
+                else: break
+            assert result is not None
+            buf += result
+
+
+        #Parse the record header
+        if ssl2:
+            record = RecordHeader2().parse(Parser(buf))
+            # padding can't be longer than overall length and if it is present
+            # the overall size must be a multiple of cipher block size
+            if ((record.padding > record.length) or
+                    (record.padding and record.length % 8)):
+                raise TLSIllegalParameterException(\
+                        "Malformed record layer header")
+        else:
+            record = RecordHeader3().parse(Parser(buf))
+
+        yield record

     def recv(self):
         """
@@ -98,10 +206,38 @@ class RecordSocket(object):
         :raises TLSIllegalParameterException: When the record header was
             malformed
         """
-        pass
+        record = None
+        for record in self._recvHeader():
+            if record in (0, 1):
+                yield record
+            else: break
+        assert record is not None
+
+        #Check the record header fields
+        # 18432 = 2**14 (default record size limit) + 1024 (maximum compression
+        # overhead) + 1024 (maximum encryption overhead)
+        if record.length > self.recv_record_limit + 1024 + 1024:
+            raise TLSRecordOverflow()
+        if self.tls13record and record.length > self.recv_record_limit + 256:
+            raise TLSRecordOverflow()
+
+        #Read the record contents
+        buf = bytearray(0)
+
+        result = None
+        for result in self._sockRecvAll(record.length):
+            if result in (0, 1):
+                yield result
+            else: break
+        assert result is not None
+
+        buf += result
+
+        yield (record, buf)


 class ConnectionState(object):
+
     """Preserve the connection state for reading and writing data to records"""

     def __init__(self):
@@ -114,7 +250,10 @@ class ConnectionState(object):

     def getSeqNumBytes(self):
         """Return encoded sequence number and increment it."""
-        pass
+        writer = Writer()
+        writer.add(self.seqnum, 8)
+        self.seqnum += 1
+        return writer.bytes

     def __copy__(self):
         """Return a copy of the object."""
@@ -128,6 +267,7 @@ class ConnectionState(object):


 class RecordLayer(object):
+
     """
     Implementation of TLS record layer protocol

@@ -155,25 +295,34 @@ class RecordLayer(object):
     def __init__(self, sock):
         self.sock = sock
         self._recordSocket = RecordSocket(sock)
-        self._version = 0, 0
+        self._version = (0, 0)
         self._tls13record = False
+
         self.client = True
+
         self._writeState = ConnectionState()
         self._readState = ConnectionState()
         self._pendingWriteState = ConnectionState()
         self._pendingReadState = ConnectionState()
         self.fixedIVBlock = None
+
         self.handshake_finished = False
+
         self.padding_cb = None
+
         self._early_data_ok = False
         self.max_early_data = 0
         self._early_data_processed = 0
-        self.send_record_limit = 2 ** 14
+        self.send_record_limit = 2**14

     @property
     def recv_record_limit(self):
         """Maximum record size that is permitted for receiving."""
-        pass
+        return self._recordSocket.recv_record_limit
+
+    @recv_record_limit.setter
+    def recv_record_limit(self, value):
+        self._recordSocket.recv_record_limit = value

     @property
     def early_data_ok(self):
@@ -188,7 +337,12 @@ class RecordLayer(object):
         Use max_early_data to set the limit of the total size of records
         that will be processed like this.
         """
-        pass
+        return self._early_data_ok
+
+    @early_data_ok.setter
+    def early_data_ok(self, val):
+        self._early_data_processed = 0
+        self._early_data_ok = val

     @property
     def encryptThenMAC(self):
@@ -199,47 +353,60 @@ class RecordLayer(object):
         integrity for next parameter change (after CCS),
         gets current state
         """
-        pass
+        return self._writeState.encryptThenMAC
+
+    @encryptThenMAC.setter
+    def encryptThenMAC(self, value):
+        self._pendingWriteState.encryptThenMAC = value
+        self._pendingReadState.encryptThenMAC = value

     def _get_pending_state_etm(self):
         """
         Return the state of encrypt then MAC for the connection after
         CCS will be exchanged
         """
-        pass
+        return self._pendingWriteState.encryptThenMAC

     @property
     def blockSize(self):
         """Return the size of block used by current symmetric cipher (R/O)"""
-        pass
+        return self._writeState.encContext.block_size

     @property
     def tls13record(self):
         """Return the value of the tls13record state."""
-        pass
+        return self._tls13record

     @tls13record.setter
     def tls13record(self, val):
         """Change the record layer to TLS1.3-like operation, if applicable."""
-        pass
+        self._tls13record = val
+        self._recordSocket.tls13record = val
+        self._handle_tls13_record()

     def _is_tls13_plus(self):
         """Returns True if we're doing real TLS 1.3."""
-        pass
+        return self._version > (3, 3) and self._tls13record

     def _handle_tls13_record(self):
         """Make sure that the version and tls13record setting is consistent."""
-        pass
+        if self._is_tls13_plus():
+            # in TLS 1.3 all records need to be sent with the generic version
+            # which is the same as TLS 1.2
+            self._recordSocket.version = (3, 3)
+        else:
+            self._recordSocket.version = self._version

     @property
     def version(self):
         """Return the TLS version used by record layer"""
-        pass
+        return self._version

     @version.setter
     def version(self, val):
         """Set the TLS version used by record layer"""
-        pass
+        self._version = val
+        self._handle_tls13_record()

     def getCipherName(self):
         """
@@ -248,7 +415,9 @@ class RecordLayer(object):
         :rtype: str
         :returns: The name of the cipher, like 'aes128', 'rc4', etc.
         """
-        pass
+        if self._writeState.encContext is None:
+            return None
+        return self._writeState.encContext.name

     def getCipherImplementation(self):
         """
@@ -259,43 +428,166 @@ class RecordLayer(object):
         :rtype: str
         :returns: Name of cipher implementation used, None if not initialised
         """
-        pass
+        if self._writeState.encContext is None:
+            return None
+        return self._writeState.encContext.implementation

     def shutdown(self):
         """Clear read and write states"""
-        pass
+        self._writeState = ConnectionState()
+        self._readState = ConnectionState()
+        self._pendingWriteState = ConnectionState()
+        self._pendingReadState = ConnectionState()

     def isCBCMode(self):
         """Returns true if cipher uses CBC mode"""
-        pass
+        if self._writeState and self._writeState.encContext and \
+                self._writeState.encContext.isBlockCipher:
+            return True
+        else:
+            return False
+    #
+    # sending messages
+    #

     def addPadding(self, data):
         """Add padding to data so that it is multiple of block size"""
-        pass
+        currentLength = len(data)
+        blockLength = self.blockSize
+        paddingLength = blockLength - 1 - (currentLength % blockLength)
+
+        paddingBytes = bytearray([paddingLength] * (paddingLength+1))
+        data += paddingBytes
+        return data

     def calculateMAC(self, mac, seqnumBytes, contentType, data):
         """Calculate the SSL/TLS version of a MAC"""
-        pass
+        mac.update(compatHMAC(seqnumBytes))
+        mac.update(compatHMAC(bytearray([contentType])))
+        assert self.version in ((3, 0), (3, 1), (3, 2), (3, 3))
+        if self.version != (3, 0):
+            mac.update(compatHMAC(bytearray([self.version[0]])))
+            mac.update(compatHMAC(bytearray([self.version[1]])))
+        mac.update(compatHMAC(bytearray([len(data)//256])))
+        mac.update(compatHMAC(bytearray([len(data)%256])))
+        mac.update(compatHMAC(data))
+        return bytearray(mac.digest())

     def _macThenEncrypt(self, data, contentType):
         """MAC, pad then encrypt data"""
-        pass
+        if self._writeState.macContext:
+            seqnumBytes = self._writeState.getSeqNumBytes()
+            mac = self._writeState.macContext.copy()
+            macBytes = self.calculateMAC(mac, seqnumBytes, contentType, data)
+            data += macBytes
+
+        #Encrypt for Block or Stream Cipher
+        if self._writeState.encContext:
+            #Add padding (for Block Cipher):
+            if self._writeState.encContext.isBlockCipher:
+
+                #Add TLS 1.1 fixed block
+                if self.version >= (3, 2):
+                    data = self.fixedIVBlock + data
+
+                data = self.addPadding(data)
+
+            #Encrypt
+            data = self._writeState.encContext.encrypt(data)
+
+        return data

     def _encryptThenMAC(self, buf, contentType):
         """Pad, encrypt and then MAC the data"""
-        pass
+        if self._writeState.encContext:
+            # add IV for TLS1.1+
+            if self.version >= (3, 2):
+                buf = self.fixedIVBlock + buf
+
+            buf = self.addPadding(buf)
+
+            buf = self._writeState.encContext.encrypt(buf)
+
+        # add MAC
+        if self._writeState.macContext:
+            seqnumBytes = self._writeState.getSeqNumBytes()
+            mac = self._writeState.macContext.copy()
+
+            # append MAC
+            macBytes = self.calculateMAC(mac, seqnumBytes, contentType, buf)
+            buf += macBytes
+
+        return buf

     def _getNonce(self, state, seqnum):
         """Calculate a nonce for a given enc/dec context"""
-        pass
+        # ChaCha is using the draft-TLS1.3-like nonce derivation
+        if (state.encContext.name == "chacha20-poly1305" and
+                len(state.fixedNonce) == 12) or self._is_tls13_plus():
+            # 4 byte nonce is used by the draft cipher
+            pad = bytearray(len(state.fixedNonce) - len(seqnum))
+            nonce = bytearray(i ^ j for i, j in zip(pad + seqnum,
+                                                    state.fixedNonce))
+        else:
+            nonce = state.fixedNonce + seqnum
+        return nonce
+

     def _encryptThenSeal(self, buf, contentType):
         """Encrypt with AEAD cipher"""
-        pass
+        #Assemble the authenticated data.
+        seqNumBytes = self._writeState.getSeqNumBytes()
+        if not self._is_tls13_plus():
+            authData = seqNumBytes + bytearray([contentType,
+                                                self.version[0],
+                                                self.version[1],
+                                                len(buf)//256,
+                                                len(buf)%256])
+        else:  # TLS 1.3
+            out_len = len(buf) + self._writeState.encContext.tagLength
+            # this is just recreated Record Layer header
+            authData = bytearray([contentType,
+                                  self._recordSocket.version[0],
+                                  self._recordSocket.version[1],
+                                  out_len // 256, out_len % 256])
+
+        nonce = self._getNonce(self._writeState, seqNumBytes)
+
+        assert len(nonce) == self._writeState.encContext.nonceLength
+
+        buf = self._writeState.encContext.seal(nonce, buf, authData)
+
+        #AES-GCM, has an explicit variable nonce.
+        if "aes" in self._writeState.encContext.name and \
+                not self._is_tls13_plus():
+            buf = seqNumBytes + buf
+
+        return buf

     def _ssl2Encrypt(self, data):
         """Encrypt in SSL2 mode"""
-        pass
+        # in SSLv2 sequence numbers are incremented for plaintext records too
+        seqnumBytes = self._writeState.getSeqNumBytes()
+
+        if (self._writeState.encContext and
+                self._writeState.encContext.isBlockCipher):
+            plaintext_len = len(data)
+            data = self.addPadding(data)
+            padding = len(data) - plaintext_len
+        else:
+            padding = 0
+
+        if self._writeState.macContext:
+            mac = self._writeState.macContext.copy()
+            mac.update(compatHMAC(data))
+            mac.update(compatHMAC(seqnumBytes[-4:]))
+
+            data = bytearray(mac.digest()) + data
+
+        if self._writeState.encContext:
+            data = self._writeState.encContext.encrypt(data)
+
+        return data, padding

     def sendRecord(self, msg):
         """
@@ -307,15 +599,124 @@ class RecordLayer(object):
         :param msg: TLS message to send
         :type msg: ApplicationData, HandshakeMessage, etc.
         """
-        pass
+        data = msg.write()
+        contentType = msg.contentType
+
+        # TLS 1.3 hides the content type of messages
+        # but CCS is always not encrypted
+        if self._is_tls13_plus() and self._writeState.encContext and \
+                contentType != ContentType.change_cipher_spec:
+            data += bytearray([contentType])
+            if self.padding_cb:
+                max_padding = self.send_record_limit - len(data) - 1
+                # add number of zero bytes specified by padding_cb()
+                data += bytearray(self.padding_cb(len(data),
+                                                  contentType,
+                                                  max_padding))
+            # in TLS 1.3 contentType is ignored by _encryptThenSeal
+            contentType = ContentType.application_data
+
+        padding = 0
+        if self.version in ((0, 2), (2, 0)):
+            data, padding = self._ssl2Encrypt(data)
+        elif self.version > (3, 3) and \
+                contentType == ContentType.change_cipher_spec:
+            # TLS 1.3 does not encrypt CCS messages
+            pass
+        elif self._writeState.encContext and \
+                self._writeState.encContext.isAEAD:
+            data = self._encryptThenSeal(data, contentType)
+        elif self._writeState.encryptThenMAC:
+            data = self._encryptThenMAC(data, contentType)
+        else:
+            data = self._macThenEncrypt(data, contentType)
+
+        encryptedMessage = Message(contentType, data)
+
+        for result in self._recordSocket.send(encryptedMessage, padding):
+            yield result
+
+    #
+    # receiving messages
+    #

     def _decryptStreamThenMAC(self, recordType, data):
         """Decrypt a stream cipher and check MAC"""
-        pass
+        if self._readState.encContext:
+            assert self.version in ((3, 0), (3, 1), (3, 2), (3, 3))
+
+            data = self._readState.encContext.decrypt(data)
+
+        if self._readState.macContext:
+            #Check MAC
+            macGood = True
+            macLength = self._readState.macContext.digest_size
+            endLength = macLength
+            if endLength > len(data):
+                macGood = False
+            else:
+                #Read MAC
+                startIndex = len(data) - endLength
+                endIndex = startIndex + macLength
+                checkBytes = data[startIndex : endIndex]
+
+                #Calculate MAC
+                seqnumBytes = self._readState.getSeqNumBytes()
+                data = data[:-endLength]
+                mac = self._readState.macContext.copy()
+                macBytes = self.calculateMAC(mac, seqnumBytes, recordType,
+                                             data)
+
+                #Compare MACs
+                if not ct_compare_digest(macBytes, checkBytes):
+                    macGood = False
+
+            if not macGood:
+                raise TLSBadRecordMAC()
+
+        return data
+

     def _decryptThenMAC(self, recordType, data):
         """Decrypt data, check padding and MAC"""
-        pass
+        if self._readState.encContext:
+            assert self.version in ((3, 0), (3, 1), (3, 2), (3, 3))
+            assert self._readState.encContext.isBlockCipher
+            assert self._readState.macContext
+
+            #
+            # decrypt the record
+            #
+            blockLength = self._readState.encContext.block_size
+            if len(data) % blockLength != 0:
+                raise TLSDecryptionFailed()
+            data = self._readState.encContext.decrypt(data)
+            if self.version >= (3, 2): #For TLS 1.1, remove explicit IV
+                data = data[self._readState.encContext.block_size : ]
+
+            #
+            # check padding and MAC
+            #
+            seqnumBytes = self._readState.getSeqNumBytes()
+
+            if not ct_check_cbc_mac_and_pad(
+                    data,
+                    self._readState.macContext,
+                    seqnumBytes,
+                    recordType,
+                    self.version,
+                    self._readState.encContext.block_size):
+                raise TLSBadRecordMAC()
+
+            #
+            # strip padding and MAC
+            #
+
+            endLength = data[-1] + 1 + self._readState.macContext.digest_size
+
+            data = data[:-endLength]
+
+        return data

     def _macThenDecrypt(self, recordType, buf):
         """
@@ -324,15 +725,139 @@ class RecordLayer(object):
         :raises TLSBadRecordMAC: when the mac value is invalid
         :raises TLSDecryptionFailed: when the data to decrypt has invalid size
         """
-        pass
+        if self._readState.macContext:
+            macLength = self._readState.macContext.digest_size
+            if len(buf) < macLength:
+                raise TLSBadRecordMAC("Truncated data")
+
+            checkBytes = buf[-macLength:]
+            buf = buf[:-macLength]
+
+            seqnumBytes = self._readState.getSeqNumBytes()
+            mac = self._readState.macContext.copy()
+
+            macBytes = self.calculateMAC(mac, seqnumBytes, recordType, buf)
+
+            if not ct_compare_digest(macBytes, checkBytes):
+                raise TLSBadRecordMAC("MAC mismatch")
+
+        if self._readState.encContext:
+            blockLength = self._readState.encContext.block_size
+            if len(buf) % blockLength != 0:
+                raise TLSDecryptionFailed("data length not multiple of "\
+                                          "block size")
+
+            buf = self._readState.encContext.decrypt(buf)
+
+            # remove explicit IV
+            if self.version >= (3, 2):
+                buf = buf[blockLength:]
+
+            if len(buf) == 0:
+                raise TLSBadRecordMAC("No data left after IV removal")
+
+            # check padding
+            paddingLength = buf[-1]
+            if paddingLength + 1 > len(buf):
+                raise TLSBadRecordMAC("Invalid padding length")
+
+            paddingGood = True
+            totalPaddingLength = paddingLength+1
+            if self.version != (3, 0):
+                paddingBytes = buf[-totalPaddingLength:-1]
+                for byte in paddingBytes:
+                    if byte != paddingLength:
+                        paddingGood = False
+
+            if not paddingGood:
+                raise TLSBadRecordMAC("Invalid padding byte values")
+
+            # remove padding
+            buf = buf[:-totalPaddingLength]
+
+        return buf

     def _decryptAndUnseal(self, header, buf):
         """Decrypt AEAD encrypted data"""
-        pass
+        seqnumBytes = self._readState.getSeqNumBytes()
+        # AES-GCM has an explicit variable nonce in TLS 1.2
+        if "aes" in self._readState.encContext.name and \
+                not self._is_tls13_plus():
+            explicitNonceLength = 8
+            if explicitNonceLength > len(buf):
+                #Publicly invalid.
+                raise TLSBadRecordMAC("Truncated nonce")
+            nonce = self._readState.fixedNonce + buf[:explicitNonceLength]
+            buf = buf[8:]
+        else:
+            # for TLS 1.3 and Chacha20 in TLS 1.2 share nonce generation
+            # algorithm
+            nonce = self._getNonce(self._readState, seqnumBytes)
+
+        if self._readState.encContext.tagLength > len(buf):
+            #Publicly invalid.
+            raise TLSBadRecordMAC("Truncated tag")
+
+        if not self._is_tls13_plus():
+            plaintextLen = len(buf) - self._readState.encContext.tagLength
+            authData = seqnumBytes + bytearray([header.type, self.version[0],
+                                                self.version[1],
+                                                plaintextLen//256,
+                                                plaintextLen%256])
+        else:  # TLS 1.3
+            # enforce the checks for encrypted records
+            if header.type != ContentType.application_data:
+                raise TLSUnexpectedMessage(
+                    "Invalid ContentType for encrypted record: {0}"
+                    .format(ContentType.toStr(header.type)))
+            if header.version != (3, 3):
+                raise TLSIllegalParameterException(
+                    "Unexpected version in encrypted record: {0}"
+                    .format(header.version))
+            if header.length != len(buf):
+                raise TLSBadRecordMAC("Length mismatch")
+            authData = header.write()
+
+        buf = self._readState.encContext.open(nonce, buf, authData)
+        if buf is None:
+            raise TLSBadRecordMAC("Invalid tag, decryption failure")
+        return buf

     def _decryptSSL2(self, data, padding):
         """Decrypt SSL2 encrypted data"""
-        pass
+        # sequence numbers are incremented for plaintext records too
+        seqnumBytes = self._readState.getSeqNumBytes()
+
+        #
+        # decrypt
+        #
+        if self._readState.encContext:
+            if self._readState.encContext.isBlockCipher:
+                blockLength = self._readState.encContext.block_size
+                if len(data) % blockLength:
+                    raise TLSDecryptionFailed()
+            data = self._readState.encContext.decrypt(data)
+
+        #
+        # strip and check MAC
+        #
+        if self._readState.macContext:
+            macBytes = data[:16]
+            data = data[16:]
+
+            mac = self._readState.macContext.copy()
+            mac.update(compatHMAC(data))
+            mac.update(compatHMAC(seqnumBytes[-4:]))
+            calcMac = bytearray(mac.digest())
+            if macBytes != calcMac:
+                raise TLSBadRecordMAC()
+
+        #
+        # strip padding
+        #
+        if padding:
+            data = data[:-padding]
+        return data

     @staticmethod
     def _tls13_de_pad(data):
@@ -344,7 +869,19 @@ class RecordLayer(object):

         :rtype: tuple
         """
-        pass
+        # the padding is at the end and the first non-zero byte is the
+        # padding
+        # could be reversed(enumerate(data)), if that worked at all
+        # could be reversed(list(enumerate(data))), if that didn't double
+        # memory usage
+        for pos, value in izip(reversed(xrange(len(data))), reversed(data)):
+            if value != 0:
+                break
+        else:
+            raise TLSUnexpectedMessage("Malformed record layer inner plaintext"
+                                       " - content type missing")
+
+        return data[:pos], value

     def recvRecord(self):
         """
@@ -358,7 +895,96 @@ class RecordLayer(object):
         :raises TLSRecordOverflow: when the received record was longer than
             allowed by negotiated version of TLS
         """
-        pass
+        while True:
+            result = None
+            for result in self._recordSocket.recv():
+                if result in (0, 1):
+                    yield result
+                else: break
+            assert result is not None
+
+            (header, data) = result
+            # as trying decryption increments sequence number, we need to
+            # keep the old one (we do copy of the whole object in case
+            # some cipher has an internal state itself)
+            read_state_copy = None
+            if self.early_data_ok:
+                # do the copy only when needed
+                read_state_copy = copy.copy(self._readState)
+
+            try:
+                if isinstance(header, RecordHeader2):
+                    data = self._decryptSSL2(data, header.padding)
+                    if self.handshake_finished:
+                        header.type = ContentType.application_data
+                # in TLS 1.3, the other party may send an unprotected CCS
+                # message at any point in connection
+                elif self._is_tls13_plus() and \
+                        header.type == ContentType.change_cipher_spec:
+                    pass
+                # when we're in the early handshake, then unencrypted alerts
+                # are fine too
+                elif self._is_tls13_plus() and \
+                        header.type == ContentType.alert and \
+                        len(data) < 3 and \
+                        self._readState and \
+                        self._readState.encContext and \
+                        self._readState.seqnum == 0:
+                    pass
+                elif self._readState and \
+                    self._readState.encContext and \
+                    self._readState.encContext.isAEAD:
+                    data = self._decryptAndUnseal(header, data)
+                elif self._readState and self._readState.encryptThenMAC:
+                    data = self._macThenDecrypt(header.type, data)
+                elif self._readState and \
+                        self._readState.encContext and \
+                        self._readState.encContext.isBlockCipher:
+                    data = self._decryptThenMAC(header.type, data)
+                else:
+                    data = self._decryptStreamThenMAC(header.type, data)
+                # if we don't have an encryption context established
+                # and early data is ok, that means we have received
+                # encrypted record in case the type of record is
+                # application_data (from TLS 1.3)
+                if not self._readState.encContext \
+                        and not self._readState.macContext \
+                        and self.early_data_ok and \
+                        header.type == ContentType.application_data:
+                    raise TLSBadRecordMAC("early data received")
+            except TLSBadRecordMAC:
+                if self.early_data_ok and (
+                        self._early_data_processed + len(data)
+                        < self.max_early_data):
+                    # ignore exception, retry reading
+                    self._early_data_processed += len(data)
+                    # reload state for decryption
+                    self._readState = read_state_copy
+                    continue
+                raise
+            # as soon as we're able to decrypt messages again, we must
+            # start checking the MACs
+            self.early_data_ok = False
+
+            # TLS 1.3 encrypts the type, CCS and Alerts are not encrypted
+            if self._is_tls13_plus() and self._readState and \
+                    self._readState.encContext and\
+                    header.type == ContentType.application_data:
+                # check if plaintext is not too big, RFC 8446, section 5.4
+                if len(data) > self.recv_record_limit + 1:
+                    raise TLSRecordOverflow()
+                data, contentType = self._tls13_de_pad(data)
+                header = RecordHeader3().create((3, 4), contentType, len(data))
+
+            # RFC 5246, section 6.2.1
+            if len(data) > self.recv_record_limit:
+                raise TLSRecordOverflow()
+
+            yield (header, Parser(data))
+
+    #
+    # cryptography state methods
+    #

     def changeWriteState(self):
         """
@@ -369,7 +995,12 @@ class RecordLayer(object):
         performed and directly after sending a :py:class:`ChangeCipherSpec`
         message.
         """
-        pass
+        if self.version in ((0, 2), (2, 0)):
+            # in SSLv2 sequence numbers carry over from plaintext to encrypted
+            # context
+            self._pendingWriteState.seqnum = self._writeState.seqnum
+        self._writeState = self._pendingWriteState
+        self._pendingWriteState = ConnectionState()

     def changeReadState(self):
         """
@@ -380,25 +1011,109 @@ class RecordLayer(object):
         performed and directly after receiving a :py:class:`ChangeCipherSpec`
         message.
         """
-        pass
+        if self.version in ((0, 2), (2, 0)):
+            # in SSLv2 sequence numbers carry over from plaintext to encrypted
+            # context
+            self._pendingReadState.seqnum = self._readState.seqnum
+        self._readState = self._pendingReadState
+        self._pendingReadState = ConnectionState()

     @staticmethod
     def _getCipherSettings(cipherSuite):
         """Get the settings for cipher suite used"""
-        pass
+        if cipherSuite in CipherSuite.aes256GcmSuites:
+            keyLength = 32
+            ivLength = 4
+            createCipherFunc = createAESGCM
+        elif cipherSuite in CipherSuite.aes128GcmSuites:
+            keyLength = 16
+            ivLength = 4
+            createCipherFunc = createAESGCM
+        elif cipherSuite in CipherSuite.aes256Ccm_8Suites:
+            keyLength = 32
+            ivLength = 4
+            createCipherFunc = createAESCCM_8
+        elif cipherSuite in CipherSuite.aes256CcmSuites:
+            keyLength = 32
+            ivLength = 4
+            createCipherFunc = createAESCCM
+        elif cipherSuite in CipherSuite.aes128Ccm_8Suites:
+            keyLength = 16
+            ivLength = 4
+            createCipherFunc = createAESCCM_8
+        elif cipherSuite in CipherSuite.aes128CcmSuites:
+            keyLength = 16
+            ivLength = 4
+            createCipherFunc = createAESCCM
+        elif cipherSuite in CipherSuite.chacha20Suites:
+            keyLength = 32
+            ivLength = 12
+            createCipherFunc = createCHACHA20
+        elif cipherSuite in CipherSuite.chacha20draft00Suites:
+            keyLength = 32
+            ivLength = 4
+            createCipherFunc = createCHACHA20
+        elif cipherSuite in CipherSuite.aes128Suites:
+            keyLength = 16
+            ivLength = 16
+            createCipherFunc = createAES
+        elif cipherSuite in CipherSuite.aes256Suites:
+            keyLength = 32
+            ivLength = 16
+            createCipherFunc = createAES
+        elif cipherSuite in CipherSuite.rc4Suites:
+            keyLength = 16
+            ivLength = 0
+            createCipherFunc = createRC4
+        elif cipherSuite in CipherSuite.tripleDESSuites:
+            keyLength = 24
+            ivLength = 8
+            createCipherFunc = createTripleDES
+        elif cipherSuite in CipherSuite.nullSuites:
+            keyLength = 0
+            ivLength = 0
+            createCipherFunc = None
+        else:
+            raise AssertionError()
+
+        return (keyLength, ivLength, createCipherFunc)

     @staticmethod
     def _getMacSettings(cipherSuite):
         """Get settings for HMAC used"""
-        pass
+        if cipherSuite in CipherSuite.aeadSuites:
+            macLength = 0
+            digestmod = None
+        elif cipherSuite in CipherSuite.shaSuites:
+            macLength = 20
+            digestmod = hashlib.sha1
+        elif cipherSuite in CipherSuite.sha256Suites:
+            macLength = 32
+            digestmod = hashlib.sha256
+        elif cipherSuite in CipherSuite.sha384Suites:
+            macLength = 48
+            digestmod = hashlib.sha384
+        elif cipherSuite in CipherSuite.md5Suites:
+            macLength = 16
+            digestmod = hashlib.md5
+        else:
+            raise AssertionError()
+
+        return macLength, digestmod

     @staticmethod
     def _getHMACMethod(version):
         """Get the HMAC method"""
-        pass
+        assert version in ((3, 0), (3, 1), (3, 2), (3, 3))
+        if version == (3, 0):
+            createMACFunc = createMAC_SSL
+        elif version in ((3, 1), (3, 2), (3, 3)):
+            createMACFunc = createHMAC
+
+        return createMACFunc

     def calcSSL2PendingStates(self, cipherSuite, masterSecret, clientRandom,
-        serverRandom, implementations):
+                              serverRandom, implementations):
         """
         Create the keys for encryption and decryption in SSLv2

@@ -406,15 +1121,153 @@ class RecordLayer(object):
         key-arg data for the server that needs to be passed up to handshake
         protocol.
         """
-        pass
+        if cipherSuite in CipherSuite.ssl2_128Key:
+            key_length = 16
+        elif cipherSuite in CipherSuite.ssl2_192Key:
+            key_length = 24
+        elif cipherSuite in CipherSuite.ssl2_64Key:
+            key_length = 8
+        else:
+            raise ValueError("Unknown cipher specified")
+
+        key_material = bytearray(key_length * 2)
+        md5_output_size = 16
+        for i, pos in enumerate(range(0, key_length * 2, md5_output_size)):
+            key_material[pos:pos+md5_output_size] = MD5(\
+                    masterSecret +
+                    bytearray(str(i), "ascii") +
+                    clientRandom + serverRandom)
+
+        serverWriteKey = key_material[:key_length]
+        clientWriteKey = key_material[key_length:]
+
+        # specification draft says that DES key should not use the
+        # incrementing label but all implementations use it anyway
+        #elif cipherSuite in CipherSuite.ssl2_64Key:
+        #    key_material = MD5(masterSecret + clientRandom + serverRandom)
+        #    serverWriteKey = key_material[0:8]
+        #    clientWriteKey = key_material[8:16]
+
+        # RC4 cannot use initialisation vector
+        if cipherSuite not in CipherSuite.ssl2rc4:
+            iv = getRandomBytes(8)
+        else:
+            iv = bytearray(0)
+
+        clientPendingState = ConnectionState()
+        serverPendingState = ConnectionState()
+
+        # MAC
+        clientPendingState.macContext = hashlib.md5()
+        clientPendingState.macContext.update(compatHMAC(clientWriteKey))
+        serverPendingState.macContext = hashlib.md5()
+        serverPendingState.macContext.update(compatHMAC(serverWriteKey))
+
+        # ciphers
+        if cipherSuite in CipherSuite.ssl2rc4:
+            cipherMethod = createRC4
+        elif cipherSuite in CipherSuite.ssl2_3des:
+            cipherMethod = createTripleDES
+        else:
+            raise NotImplementedError("Unknown cipher")
+
+        clientPendingState.encContext = cipherMethod(clientWriteKey, iv,
+                                                     implementations)
+        serverPendingState.encContext = cipherMethod(serverWriteKey, iv,
+                                                     implementations)
+
+        # Assign new connection states to pending states
+        if self.client:
+            self._pendingWriteState = clientPendingState
+            self._pendingReadState = serverPendingState
+        else:
+            self._pendingWriteState = serverPendingState
+            self._pendingReadState = clientPendingState
+
+        return iv

     def calcPendingStates(self, cipherSuite, masterSecret, clientRandom,
-        serverRandom, implementations):
+                          serverRandom, implementations):
         """Create pending states for encryption and decryption."""
-        pass
+        keyLength, ivLength, createCipherFunc = \
+                self._getCipherSettings(cipherSuite)
+
+        macLength, digestmod = self._getMacSettings(cipherSuite)
+
+        if not digestmod:
+            createMACFunc = None
+        else:
+            createMACFunc = self._getHMACMethod(self.version)
+
+        outputLength = (macLength*2) + (keyLength*2) + (ivLength*2)
+
+        #Calculate Keying Material from Master Secret
+        keyBlock = calc_key(self.version, masterSecret, cipherSuite,
+                            b"key expansion", client_random=clientRandom,
+                            server_random=serverRandom,
+                            output_length=outputLength)
+
+        #Slice up Keying Material
+        clientPendingState = ConnectionState()
+        serverPendingState = ConnectionState()
+        parser = Parser(keyBlock)
+        clientMACBlock = parser.getFixBytes(macLength)
+        serverMACBlock = parser.getFixBytes(macLength)
+        clientKeyBlock = parser.getFixBytes(keyLength)
+        serverKeyBlock = parser.getFixBytes(keyLength)
+        clientIVBlock = parser.getFixBytes(ivLength)
+        serverIVBlock = parser.getFixBytes(ivLength)
+
+        if digestmod:
+            # Legacy cipher
+            clientPendingState.macContext = createMACFunc(
+                compatHMAC(clientMACBlock), digestmod=digestmod)
+            serverPendingState.macContext = createMACFunc(
+                compatHMAC(serverMACBlock), digestmod=digestmod)
+            if createCipherFunc is not None:
+                clientPendingState.encContext = \
+                                            createCipherFunc(clientKeyBlock,
+                                                             clientIVBlock,
+                                                             implementations)
+                serverPendingState.encContext = \
+                                            createCipherFunc(serverKeyBlock,
+                                                             serverIVBlock,
+                                                             implementations)
+        else:
+            # AEAD
+            clientPendingState.macContext = None
+            serverPendingState.macContext = None
+            clientPendingState.encContext = createCipherFunc(clientKeyBlock,
+                                                             implementations)
+            serverPendingState.encContext = createCipherFunc(serverKeyBlock,
+                                                             implementations)
+            clientPendingState.fixedNonce = clientIVBlock
+            serverPendingState.fixedNonce = serverIVBlock
+
+        #Assign new connection states to pending states
+        if self.client:
+            clientPendingState.encryptThenMAC = \
+                    self._pendingWriteState.encryptThenMAC
+            self._pendingWriteState = clientPendingState
+            serverPendingState.encryptThenMAC = \
+                    self._pendingReadState.encryptThenMAC
+            self._pendingReadState = serverPendingState
+        else:
+            serverPendingState.encryptThenMAC = \
+                    self._pendingWriteState.encryptThenMAC
+            self._pendingWriteState = serverPendingState
+            clientPendingState.encryptThenMAC = \
+                    self._pendingReadState.encryptThenMAC
+            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 calcTLS1_3PendingState(self, cipherSuite, cl_traffic_secret,
-        sr_traffic_secret, implementations):
+                               sr_traffic_secret,
+                               implementations):
         """
         Create pending state for encryption in TLS 1.3.

@@ -427,4 +1280,96 @@ class RecordLayer(object):
         :param list implementations: list of names of implementations that
             are permitted for the connection
         """
-        pass
+        prf_name = 'sha384' if cipherSuite \
+                   in CipherSuite.sha384PrfSuites \
+                   else 'sha256'
+
+        key_length, iv_length, cipher_func = \
+            self._getCipherSettings(cipherSuite)
+        iv_length = 12
+
+        clientPendingState = ConnectionState()
+        serverPendingState = ConnectionState()
+
+        clientPendingState.macContext = None
+        clientPendingState.encContext = \
+            cipher_func(HKDF_expand_label(cl_traffic_secret,
+                                          b"key", b"",
+                                          key_length,
+                                          prf_name),
+                        implementations)
+        clientPendingState.fixedNonce = HKDF_expand_label(cl_traffic_secret,
+                                                          b"iv", b"",
+                                                          iv_length,
+                                                          prf_name)
+
+        serverPendingState.macContext = None
+        serverPendingState.encContext = \
+            cipher_func(HKDF_expand_label(sr_traffic_secret,
+                                          b"key", b"",
+                                          key_length,
+                                          prf_name),
+                        implementations)
+        serverPendingState.fixedNonce = HKDF_expand_label(sr_traffic_secret,
+                                                          b"iv", b"",
+                                                          iv_length,
+                                                          prf_name)
+
+        if self.client:
+            self._pendingWriteState = clientPendingState
+            self._pendingReadState = serverPendingState
+        else:
+            self._pendingWriteState = serverPendingState
+            self._pendingReadState = clientPendingState
+
+    def _calcTLS1_3KeyUpdate(self, cipherSuite, app_secret):
+        prf_name, prf_length = ('sha384', 48) if cipherSuite \
+                                in CipherSuite.sha384PrfSuites \
+                                else ('sha256', 32)
+        key_length, iv_length, cipher_func = \
+            self._getCipherSettings(cipherSuite)
+        iv_length = 12
+
+        new_app_secret = HKDF_expand_label(app_secret,
+                                           b"traffic upd", b"",
+                                           prf_length,
+                                           prf_name)
+        new_state = ConnectionState()
+        new_state.macContext = None
+        new_state.encContext = \
+            cipher_func(HKDF_expand_label(new_app_secret,
+                                          b"key", b"",
+                                          key_length,
+                                          prf_name),
+                        None)
+        new_state.fixedNonce = HKDF_expand_label(new_app_secret,
+                                                 b"iv", b"",
+                                                 iv_length,
+                                                 prf_name)
+        return new_app_secret, new_state
+
+    def calcTLS1_3KeyUpdate_sender(self, cipherSuite, cl_app_secret,
+                                   sr_app_secret):
+        if self.client:
+            new_sr_app_secret, server_state = self._calcTLS1_3KeyUpdate(
+                cipherSuite, sr_app_secret)
+            self._readState = server_state
+            return cl_app_secret, new_sr_app_secret
+        else:
+            new_cl_app_secret, client_state = self._calcTLS1_3KeyUpdate(
+                cipherSuite, cl_app_secret)
+            self._readState = client_state
+            return new_cl_app_secret, sr_app_secret
+
+    def calcTLS1_3KeyUpdate_reciever(self, cipherSuite, cl_app_secret,
+                                     sr_app_secret):
+        if self.client:
+            new_cl_app_secret, client_state = self._calcTLS1_3KeyUpdate(
+                cipherSuite, cl_app_secret)
+            self._writeState = client_state
+            return new_cl_app_secret, sr_app_secret
+        else:
+            new_sr_app_secret, server_state = self._calcTLS1_3KeyUpdate(
+                cipherSuite, sr_app_secret)
+            self._writeState = server_state
+            return cl_app_secret, new_sr_app_secret
diff --git a/tlslite/session.py b/tlslite/session.py
index 3df1059..0e310b7 100644
--- a/tlslite/session.py
+++ b/tlslite/session.py
@@ -1,9 +1,15 @@
+# Authors: 
+#   Trevor Perrin
+#   Dave Baggett (Arcode Corporation) - canonicalCipherName
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """Class representing a TLS session."""
+
 from .utils.compat import *
 from .mathtls import *
 from .constants import *

-
 class Session(object):
     """
     This class represents a TLS session.
@@ -72,12 +78,12 @@ class Session(object):
         self.masterSecret = bytearray(0)
         self.sessionID = bytearray(0)
         self.cipherSuite = 0
-        self.srpUsername = ''
+        self.srpUsername = ""
         self.clientCertChain = None
         self.serverCertChain = None
         self.tackExt = None
         self.tackInHelloExt = False
-        self.serverName = ''
+        self.serverName = ""
         self.resumable = False
         self.encryptThenMAC = False
         self.extendedMasterSecret = False
@@ -89,13 +95,84 @@ class Session(object):
         self.tickets = None
         self.tls_1_0_tickets = None

+    def create(self, masterSecret, sessionID, cipherSuite,
+               srpUsername, clientCertChain, serverCertChain,
+               tackExt, tackInHelloExt, serverName, resumable=True,
+               encryptThenMAC=False, extendedMasterSecret=False,
+               appProto=bytearray(0), cl_app_secret=bytearray(0),
+               sr_app_secret=bytearray(0), exporterMasterSecret=bytearray(0),
+               resumptionMasterSecret=bytearray(0), tickets=None,
+               tls_1_0_tickets=None):
+        self.masterSecret = masterSecret
+        self.sessionID = sessionID
+        self.cipherSuite = cipherSuite
+        self.srpUsername = srpUsername
+        self.clientCertChain = clientCertChain
+        self.serverCertChain = serverCertChain
+        self.tackExt = tackExt
+        self.tackInHelloExt = tackInHelloExt  
+        self.serverName = serverName
+        self.resumable = resumable
+        self.encryptThenMAC = encryptThenMAC
+        self.extendedMasterSecret = extendedMasterSecret
+        self.appProto = appProto
+        self.cl_app_secret = cl_app_secret
+        self.sr_app_secret = sr_app_secret
+        self.exporterMasterSecret = exporterMasterSecret
+        self.resumptionMasterSecret = resumptionMasterSecret
+        # NOTE we need a reference copy not a copy of object here!
+        self.tickets = tickets
+        self.tls_1_0_tickets = tls_1_0_tickets
+
+    def _clone(self):
+        other = Session()
+        other.masterSecret = self.masterSecret
+        other.sessionID = self.sessionID
+        other.cipherSuite = self.cipherSuite
+        other.srpUsername = self.srpUsername
+        other.clientCertChain = self.clientCertChain
+        other.serverCertChain = self.serverCertChain
+        other.tackExt = self.tackExt
+        other.tackInHelloExt = self.tackInHelloExt
+        other.serverName = self.serverName
+        other.resumable = self.resumable
+        other.encryptThenMAC = self.encryptThenMAC
+        other.extendedMasterSecret = self.extendedMasterSecret
+        other.appProto = self.appProto
+        other.cl_app_secret = self.cl_app_secret
+        other.sr_app_secret = self.sr_app_secret
+        other.exporterMasterSecret = self.exporterMasterSecret
+        other.resumptionMasterSecret = self.resumptionMasterSecret
+        other.tickets = self.tickets
+        other.tls_1_0_tickets = self.tls_1_0_tickets
+        return other
+
     def valid(self):
         """If this session can be used for session resumption.

         :rtype: bool
         :returns: If this session can be used for session resumption.
         """
-        pass
+        # TODO add checks for tickets received from server (freshness etc.)
+        return self.resumable and (self.sessionID or self.tickets or
+                                   self.tls_1_0_tickets)
+
+    def _setResumable(self, boolean):
+        #Only let it be set to True if the sessionID is non-null
+        if (not boolean) or (boolean and self.sessionID):
+            self.resumable = boolean
+
+    def getTackId(self):
+        if self.tackExt and self.tackExt.tack:
+            return self.tackExt.tack.getTackId()
+        else:
+            return None
+        
+    def getBreakSigs(self):
+        if self.tackExt and self.tackExt.break_sigs:
+            return self.tackExt.break_sigs
+        else:
+            return None

     def getCipherName(self):
         """Get the name of the cipher used with this connection.
@@ -103,15 +180,15 @@ class Session(object):
         :rtype: str
         :returns: The name of the cipher used with this connection.
         """
-        pass
-
+        return CipherSuite.canonicalCipherName(self.cipherSuite)
+        
     def getMacName(self):
         """Get the name of the HMAC hash algo used with this connection.

         :rtype: str
         :returns: The name of the HMAC hash algo used with this connection.
         """
-        pass
+        return CipherSuite.canonicalMacName(self.cipherSuite)


 class Ticket(object):
@@ -144,3 +221,6 @@ class Ticket(object):
         self.master_secret = master_secret
         self.cipher_suite = cipher_suite
         self.time_received = time.time()
+
+    def valid(self):
+        return time.time() < self.time_received + self.ticket_lifetime
diff --git a/tlslite/sessioncache.py b/tlslite/sessioncache.py
index a40c490..ac54e49 100644
--- a/tlslite/sessioncache.py
+++ b/tlslite/sessioncache.py
@@ -1,8 +1,15 @@
+# Authors: 
+#   Trevor Perrin
+#   Martin von Loewis - python 3 port
+#   Mirko Dziadzka - bugfix
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """Class for caching TLS sessions."""
+
 import threading
 import time

-
 class SessionCache(object):
     """This class is used by the server to cache TLS sessions.

@@ -14,6 +21,11 @@ class SessionCache(object):
     This class is thread-safe.
     """

+    #References to these instances
+    #are also held by the caller, who may change the 'resumable'
+    #flag, so the SessionCache must return the same instances
+    #it was passed in.
+
     def __init__(self, maxEntries=10000, maxAge=14400):
         """Create a new SessionCache.

@@ -25,9 +37,15 @@ class SessionCache(object):
         :type maxAge: int
         :param maxAge:  The number of seconds before a session expires
             from the cache.  The default is 14400 (i.e. 4 hours)."""
+
         self.lock = threading.Lock()
+
+        # Maps sessionIDs to sessions
         self.entriesDict = {}
-        self.entriesList = [(None, None)] * maxEntries
+
+        #Circular list of (sessionID, timestamp) pairs
+        self.entriesList = [(None,None)] * maxEntries
+
         self.firstIndex = 0
         self.lastIndex = 0
         self.maxAge = maxAge
@@ -35,8 +53,14 @@ class SessionCache(object):
     def __getitem__(self, sessionID):
         self.lock.acquire()
         try:
-            self._purge()
+            self._purge() #Delete old items, so we're assured of a new one
             session = self.entriesDict[bytes(sessionID)]
+
+            #When we add sessions they're resumable, but it's possible
+            #for the session to be invalidated later on (if a fatal alert
+            #is returned), so we have to check for resumability before
+            #returning the session.
+
             if session.valid():
                 return session
             else:
@@ -44,14 +68,36 @@ class SessionCache(object):
         finally:
             self.lock.release()

+
     def __setitem__(self, sessionID, session):
         self.lock.acquire()
         try:
+            #Add the new element
             self.entriesDict[bytes(sessionID)] = session
-            self.entriesList[self.lastIndex] = bytes(sessionID), time.time()
-            self.lastIndex = (self.lastIndex + 1) % len(self.entriesList)
+            self.entriesList[self.lastIndex] = (bytes(sessionID), time.time())
+            self.lastIndex = (self.lastIndex+1) % len(self.entriesList)
+
+            #If the cache is full, we delete the oldest element to make an
+            #empty space
             if self.lastIndex == self.firstIndex:
-                del self.entriesDict[self.entriesList[self.firstIndex][0]]
-                self.firstIndex = (self.firstIndex + 1) % len(self.entriesList)
+                del(self.entriesDict[self.entriesList[self.firstIndex][0]])
+                self.firstIndex = (self.firstIndex+1) % len(self.entriesList)
         finally:
             self.lock.release()
+
+    #Delete expired items
+    def _purge(self):
+        currentTime = time.time()
+
+        #Search through the circular list, deleting expired elements until
+        #we reach a non-expired element.  Since elements in list are
+        #ordered in time, we can break once we reach the first non-expired
+        #element
+        index = self.firstIndex
+        while index != self.lastIndex:
+            if currentTime - self.entriesList[index][1] > self.maxAge:
+                del(self.entriesDict[self.entriesList[index][0]])
+                index = (index+1) % len(self.entriesList)
+            else:
+                break
+        self.firstIndex = index
diff --git a/tlslite/signed.py b/tlslite/signed.py
index 20048da..97c1fc2 100644
--- a/tlslite/signed.py
+++ b/tlslite/signed.py
@@ -1,34 +1,87 @@
 """Base class that represents any signed object"""
+
 from .utils.cryptomath import numBytes
-RSA_SIGNATURE_HASHES = ['sha512', 'sha384', 'sha256', 'sha224', 'sha1']
-ALL_RSA_SIGNATURE_HASHES = RSA_SIGNATURE_HASHES + ['md5']
-RSA_SCHEMES = ['pss', 'pkcs1']

+RSA_SIGNATURE_HASHES = ["sha512", "sha384", "sha256", "sha224", "sha1"]
+ALL_RSA_SIGNATURE_HASHES = RSA_SIGNATURE_HASHES + ["md5"]
+RSA_SCHEMES = ["pss", "pkcs1"]

-class SignatureSettings(object):

-    def __init__(self, min_key_size=None, max_key_size=None, rsa_sig_hashes
-        =None, rsa_schemes=None):
+class SignatureSettings(object):
+    def __init__(self, min_key_size=None, max_key_size=None,
+                 rsa_sig_hashes=None, rsa_schemes=None):
         """Create default variables for key-related settings."""
         self.min_key_size = min_key_size or 1023
         self.max_key_size = max_key_size or 8193
         self.rsa_sig_hashes = rsa_sig_hashes or list(RSA_SIGNATURE_HASHES)
         self.rsa_schemes = rsa_schemes or list(RSA_SCHEMES)

+    def _copy_settings(self, other):
+        other.min_key_size = self.min_key_size
+        other.max_key_size = self.max_key_size
+        other.rsa_sig_hashes = self.rsa_sig_hashes
+        other.rsa_schemes = self.rsa_schemes

-class SignedObject(object):
+    @staticmethod
+    def _sanityCheckKeySizes(other):
+        if other.min_key_size < 512:
+            raise ValueError("min_key_size too small")
+        if other.min_key_size > 16384:
+            raise ValueError("min_key_size too large")
+        if other.max_key_size < 512:
+            raise ValueError("max_key_size too small")
+        if other.max_key_size > 16384:
+            raise ValueError("max_key_size too large")
+        if other.max_key_size < other.min_key_size:
+            raise ValueError("max_key_size smaller than min_key_size")
+
+    @staticmethod
+    def _sanityCheckSignatureAlgs(other):
+        not_allowed = [alg for alg in other.rsa_sig_hashes
+                       if alg not in ALL_RSA_SIGNATURE_HASHES]
+        if len(not_allowed) > 0:
+            raise ValueError("Following signature algorithms are not allowed: "
+                             "{0}".format(", ".join(not_allowed)))
+
+    def validate(self):
+        other = SignatureSettings()
+        self._copy_settings(other)
+        self._sanityCheckKeySizes(other)
+        self._sanityCheckSignatureAlgs(other)
+        return other

+
+class SignedObject(object):
     def __init__(self):
         self.tbs_data = None
         self.signature = None
         self.signature_alg = None
-    _hash_algs_OIDs = {tuple([42, 134, 72, 134, 247, 13, 1, 1, 4]): 'md5',
-        tuple([42, 134, 72, 134, 247, 13, 1, 1, 5]): 'sha1', tuple([42, 134,
-        72, 134, 247, 13, 1, 1, 14]): 'sha224', tuple([42, 134, 72, 134, 
-        247, 13, 1, 1, 12]): 'sha384', tuple([42, 134, 72, 134, 247, 13, 1,
-        1, 11]): 'sha256', tuple([42, 134, 72, 134, 247, 13, 1, 1, 13]):
-        'sha512'}
+
+    _hash_algs_OIDs = {
+        tuple([0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0x4]): 'md5',
+        tuple([0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0x5]): 'sha1',
+        tuple([0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0xe]): 'sha224',
+        tuple([0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0xc]): 'sha384',
+        tuple([0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0xb]): 'sha256',
+        tuple([0x2a, 0x86, 0x48, 0x86, 0xf7, 0xd, 0x1, 0x1, 0xd]): 'sha512'
+    }

     def verify_signature(self, publicKey, settings=None):
         """ Verify signature in a reponse"""
-        pass
+        offset = 0
+        settings = settings or SignatureSettings()
+
+        # workaround as some signature encodings could be zero left-padded
+        if (self.signature[0] == 0 and
+                numBytes(publicKey.n) + 1 == len(self.signature)):
+            offset = 1
+
+        alg = self._hash_algs_OIDs[tuple(self.signature_alg)]
+        if alg not in settings.rsa_sig_hashes:
+            raise ValueError("Invalid signature algorithm: {0}".format(alg))
+        verified = publicKey.hashAndVerify(self.signature[offset:],
+                                           self.tbs_data, hAlg=alg)
+        if not verified:
+            raise ValueError("Signature could not be verified for {0}"
+                             .format(alg))
+        return True
diff --git a/tlslite/tlsconnection.py b/tlslite/tlsconnection.py
index 73f8e07..587556f 100644
--- a/tlslite/tlsconnection.py
+++ b/tlslite/tlsconnection.py
@@ -1,6 +1,20 @@
+# Authors:
+#   Trevor Perrin
+#   Google - added reqCAs parameter
+#   Google (adapted by Sam Rushing and Marcelo Fernandez) - NPN support
+#   Google - FALLBACK_SCSV
+#   Dimitris Moraitis - Anon ciphersuites
+#   Martin von Loewis - python 3 port
+#   Yngve Pettersen (ported by Paul Sokolovsky) - TLS 1.2
+#   Hubert Kario - complete refactoring of key exchange methods, addition
+#          of ECDH support
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """
 MAIN CLASS FOR TLS LITE (START HERE!).
 """
+
 from __future__ import division
 import time
 import socket
@@ -19,10 +33,12 @@ from .handshakesettings import HandshakeSettings, KNOWN_VERSIONS, CURVE_ALIASES
 from .handshakehashes import HandshakeHashes
 from .utils.tackwrapper import *
 from .utils.deprecations import deprecated_params
-from .keyexchange import KeyExchange, RSAKeyExchange, DHE_RSAKeyExchange, ECDHE_RSAKeyExchange, SRPKeyExchange, ADHKeyExchange, AECDHKeyExchange, FFDHKeyExchange, ECDHKeyExchange
+from .keyexchange import KeyExchange, RSAKeyExchange, DHE_RSAKeyExchange, \
+        ECDHE_RSAKeyExchange, SRPKeyExchange, ADHKeyExchange, \
+        AECDHKeyExchange, FFDHKeyExchange, ECDHKeyExchange
 from .handshakehelpers import HandshakeHelpers
-from .utils.cipherfactory import createAESCCM, createAESCCM_8, createAESGCM, createCHACHA20
-
+from .utils.cipherfactory import createAESCCM, createAESCCM_8, \
+        createAESGCM, createCHACHA20

 class TLSConnection(TLSRecordLayer):
     """
@@ -64,7 +80,10 @@ class TLSConnection(TLSRecordLayer):
         self._clientRandom = bytearray(0)
         self._serverRandom = bytearray(0)
         self.next_proto = None
+        # whether the CCS was already sent in the connection (for hello retry)
         self._ccs_sent = False
+        # if and how big is the limit on records peer is willing to accept
+        # used only for TLS 1.2 and earlier
         self._peer_record_size_limit = None
         self._pha_supported = False

@@ -77,12 +96,45 @@ class TLSConnection(TLSRecordLayer):
         :type length: int
         :param length: number of bytes of the keying material to export
         """
-        pass
+        if label in (b'server finished', b'client finished',
+                     b'master secret', b'key expansion'):
+            raise ValueError("Forbidden label value")
+        if self.version < (3, 1):
+            raise ValueError("Supported only in TLSv1.0 and later")
+        elif self.version < (3, 3):
+            return PRF(self.session.masterSecret, label,
+                       self._clientRandom + self._serverRandom,
+                       length)
+        elif self.version == (3, 3):
+            if self.session.cipherSuite in CipherSuite.sha384PrfSuites:
+                return PRF_1_2_SHA384(self.session.masterSecret, label,
+                                      self._clientRandom + self._serverRandom,
+                                      length)
+            else:
+                return PRF_1_2(self.session.masterSecret, label,
+                               self._clientRandom + self._serverRandom,
+                               length)
+        elif self.version == (3, 4):
+            prf = 'sha256'
+            if self.session.cipherSuite in CipherSuite.sha384PrfSuites:
+                prf = 'sha384'
+            secret = derive_secret(self.session.exporterMasterSecret, label,
+                                   None, prf)
+            ctxhash = secureHash(bytearray(b''), prf)
+            return HKDF_expand_label(secret, b"exporter", ctxhash, length, prf)
+        else:
+            raise AssertionError("Unknown protocol version")
+
+    #*********************************************************
+    # Client Handshake Functions
+    #*********************************************************

-    @deprecated_params({'async_': 'async'},
-        "'{old_name}' is a keyword in Python 3.7, use'{new_name}'")
-    def handshakeClientAnonymous(self, session=None, settings=None, checker
-        =None, serverName=None, async_=False):
+    @deprecated_params({"async_": "async"},
+                       "'{old_name}' is a keyword in Python 3.7, use"
+                       "'{new_name}'")
+    def handshakeClientAnonymous(self, session=None, settings=None,
+                                 checker=None, serverName=None,
+                                 async_=False):
         """Perform an anonymous handshake in the role of client.

         This function performs an SSL or TLS handshake using an
@@ -135,12 +187,23 @@ class TLSConnection(TLSRecordLayer):
         :raises tlslite.errors.TLSAuthenticationError: If the checker
             doesn't like the other party's authentication credentials.
         """
-        pass
+        handshaker = self._handshakeClientAsync(anonParams=(True),
+                                                session=session,
+                                                settings=settings,
+                                                checker=checker,
+                                                serverName=serverName)
+        if async_:
+            return handshaker
+        for result in handshaker:
+            pass

-    @deprecated_params({'async_': 'async'},
-        "'{old_name}' is a keyword in Python 3.7, use'{new_name}'")
-    def handshakeClientSRP(self, username, password, session=None, settings
-        =None, checker=None, reqTack=True, serverName=None, async_=False):
+    @deprecated_params({"async_": "async"},
+                       "'{old_name}' is a keyword in Python 3.7, use"
+                       "'{new_name}'")
+    def handshakeClientSRP(self, username, password, session=None,
+                           settings=None, checker=None,
+                           reqTack=True, serverName=None,
+                           async_=False):
         """Perform an SRP handshake in the role of client.

         This function performs a TLS/SRP handshake.  SRP mutually
@@ -204,13 +267,33 @@ class TLSConnection(TLSRecordLayer):
         :raises tlslite.errors.TLSAuthenticationError: If the checker
             doesn't like the other party's authentication credentials.
         """
-        pass
+        # TODO add deprecation warning
+        if isinstance(username, str):
+            username = bytearray(username, 'utf-8')
+        if isinstance(password, str):
+            password = bytearray(password, 'utf-8')
+        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

-    @deprecated_params({'async_': 'async'},
-        "'{old_name}' is a keyword in Python 3.7, use'{new_name}'")
-    def handshakeClientCert(self, certChain=None, privateKey=None, session=
-        None, settings=None, checker=None, nextProtos=None, reqTack=True,
-        serverName=None, async_=False, alpn=None):
+    @deprecated_params({"async_": "async"},
+                       "'{old_name}' is a keyword in Python 3.7, use"
+                       "'{new_name}'")
+    def handshakeClientCert(self, certChain=None, privateKey=None,
+                            session=None, settings=None, checker=None,
+                            nextProtos=None, reqTack=True, serverName=None,
+                            async_=False, alpn=None):
         """Perform a certificate-based handshake in the role of client.

         This function performs an SSL or TLS handshake.  The server
@@ -291,34 +374,1502 @@ class TLSConnection(TLSRecordLayer):
         :raises tlslite.errors.TLSAuthenticationError: If the checker
             doesn't like the other party's authentication credentials.
         """
-        pass
+        handshaker = \
+                self._handshakeClientAsync(certParams=(certChain, privateKey),
+                                           session=session, settings=settings,
+                                           checker=checker,
+                                           serverName=serverName,
+                                           nextProtos=nextProtos,
+                                           reqTack=reqTack,
+                                           alpn=alpn)
+        # 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 _handshakeClientAsync(self, srpParams=(), certParams=(), anonParams=(),
+                              session=None, settings=None, checker=None,
+                              nextProtos=None, serverName=None, reqTack=True,
+                              alpn=None):
+
+        handshaker = self._handshakeClientAsyncHelper(srpParams=srpParams,
+                certParams=certParams,
+                anonParams=anonParams,
+                session=session,
+                settings=settings,
+                serverName=serverName,
+                nextProtos=nextProtos,
+                reqTack=reqTack,
+                alpn=alpn)
+        for result in self._handshakeWrapperAsync(handshaker, checker):
+            yield result
+
+
+    def _handshakeClientAsyncHelper(self, srpParams, certParams, anonParams,
+                               session, settings, serverName, nextProtos,
+                               reqTack, alpn):
+
+        self._handshakeStart(client=True)
+
+        #Unpack parameters
+        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
+        if certParams:
+            assert(not srpParams)
+            assert(not anonParams)
+            clientCertChain, privateKey = certParams
+        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 cert_chain but no privateKey")
+        if privateKey and not clientCertChain:
+            raise ValueError("Caller passed a privateKey but no cert_chain")
+        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")
+        if alpn is not None and not alpn:
+            raise ValueError("Caller passed empty alpn list")
+        # reject invalid hostnames but accept empty/None ones
+        if serverName and not is_valid_hostname(serverName):
+            raise ValueError("Caller provided invalid server host name: {0}"
+                             .format(serverName))
+
+        # Validates the settings and filters out any unsupported ciphers
+        # or crypto libraries that were requested
+        if not settings:
+            settings = HandshakeSettings()
+        settings = settings.validate()
+        self.sock.padding_cb = settings.padding_cb
+
+        if clientCertChain:
+            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:
+                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 += bytearray(b"GARBAGE")
+        if password and self.fault == Fault.badPassword:
+            password += bytearray(b"GARBAGE")
+
+        # Tentatively set the client's record 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
+        # in TLS 1.3 it always needs to be set to TLS 1.0
+        self.version = \
+            (3, 1) if settings.maxVersion > (3, 3) else settings.maxVersion
+
+        # OK Start sending messages!
+        # *****************************
+
+        # Send the ClientHello.
+        for result in self._clientSendClientHello(settings, session,
+                                        srpUsername, srpParams, certParams,
+                                        anonParams, serverName, nextProtos,
+                                        reqTack, alpn):
+            if result in (0,1): yield result
+            else: break
+        clientHello = result
+
+        #Get the ServerHello.
+        for result in self._clientGetServerHello(settings, session,
+                                                 clientHello):
+            if result in (0,1): yield result
+            else: break
+        serverHello = result
+        cipherSuite = serverHello.cipher_suite
+
+        # Check the serverHello.random  if it includes the downgrade protection
+        # values as described in RFC8446 section 4.1.3
+
+        # For TLS1.3
+        if (settings.maxVersion > (3, 3) and self.version <= (3, 3)) and \
+                (serverHello.random[-8:] == TLS_1_2_DOWNGRADE_SENTINEL or
+                 serverHello.random[-8:] == TLS_1_1_DOWNGRADE_SENTINEL):
+            for result in self._sendError(AlertDescription.illegal_parameter,
+                                          "Connection terminated because "
+                                          "of downgrade protection."):
+                yield result
+
+        # For TLS1.2
+        if settings.maxVersion == (3, 3) and self.version < (3, 3) and \
+                serverHello.random[-8:] == TLS_1_1_DOWNGRADE_SENTINEL:
+            for result in self._sendError(AlertDescription.illegal_parameter,
+                                          "Connection terminated because "
+                                          "of downgrade protection."):
+                yield result
+
+        # if we're doing tls1.3, use the new code as the negotiation is much
+        # different
+        ext = serverHello.getExtension(ExtensionType.supported_versions)
+        if ext and ext.version > (3, 3):
+            for result in self._clientTLS13Handshake(settings, session,
+                                                     clientHello,
+                                                     clientCertChain,
+                                                     privateKey,
+                                                     serverHello):
+                if result in (0, 1):
+                    yield result
+                else:
+                    break
+            if result in ["finished", "resumed_and_finished"]:
+                self._handshakeDone(resumed=(result == "resumed_and_finished"))
+                self._serverRandom = serverHello.random
+                self._clientRandom = clientHello.random
+                return
+            else:
+                raise Exception("unexpected return")
+
+        # Choose a matching Next Protocol from server list against ours
+        # (string or None)
+        nextProto = self._clientSelectNextProto(nextProtos, serverHello)
+
+        # Check if server selected encrypt-then-MAC
+        if serverHello.getExtension(ExtensionType.encrypt_then_mac):
+            self._recordLayer.encryptThenMAC = True
+
+        if serverHello.getExtension(ExtensionType.extended_master_secret):
+            self.extendedMasterSecret = True
+
+        #If the server elected to resume the session, it is handled here.
+        for result in self._clientResume(session, serverHello,
+                        clientHello.random,
+                        nextProto, settings):
+            if result in (0,1): yield result
+            else: break
+
+        if result == "resumed_and_finished":
+            self._handshakeDone(resumed=True)
+            self._serverRandom = serverHello.random
+            self._clientRandom = clientHello.random
+            # alpn protocol is independent of resumption and renegotiation
+            # and needs to be negotiated every time
+            alpnExt = serverHello.getExtension(ExtensionType.alpn)
+            if alpnExt:
+                session.appProto = alpnExt.protocol_names[0]
+            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:
+            keyExchange = SRPKeyExchange(cipherSuite, clientHello,
+                                         serverHello, None, None,
+                                         srpUsername=srpUsername,
+                                         password=password,
+                                         settings=settings)
+
+        #If the server selected an anonymous ciphersuite, the client
+        #finishes reading the post-ServerHello messages.
+        elif cipherSuite in CipherSuite.dhAllSuites:
+            keyExchange = DHE_RSAKeyExchange(cipherSuite, clientHello,
+                                             serverHello, None)
+
+        elif cipherSuite in CipherSuite.ecdhAllSuites:
+            acceptedCurves = self._curveNamesToList(settings)
+            keyExchange = ECDHE_RSAKeyExchange(cipherSuite, clientHello,
+                                               serverHello, None,
+                                               acceptedCurves)
+
+        #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:
+            keyExchange = RSAKeyExchange(cipherSuite, clientHello,
+                                         serverHello, None)
+
+        # we'll send few messages here, send them in single TCP packet
+        self.sock.buffer_writes = True
+        for result in self._clientKeyExchange(settings, cipherSuite,
+                                              clientCertChain,
+                                              privateKey,
+                                              serverHello.certificate_type,
+                                              serverHello.tackExt,
+                                              clientHello.random,
+                                              serverHello.random,
+                                              keyExchange):
+            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.
+        # socket buffering is turned off in _clientFinished
+        for result in self._clientFinished(premasterSecret,
+                            clientHello.random,
+                            serverHello.random,
+                            cipherSuite, settings.cipherImplementations,
+                            nextProto, settings):
+                if result in (0,1): yield result
+                else: break
+        masterSecret = result
+
+        # check if an application layer protocol was negotiated
+        alpnProto = None
+        alpnExt = serverHello.getExtension(ExtensionType.alpn)
+        if alpnExt:
+            alpnProto = alpnExt.protocol_names[0]
+
+        # 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 is not None),
+                            serverName,
+                            encryptThenMAC=self._recordLayer.encryptThenMAC,
+                            extendedMasterSecret=self.extendedMasterSecret,
+                            appProto=alpnProto,
+                            # NOTE it must be a reference not a copy
+                            tickets=self.tickets,
+                            tls_1_0_tickets=self.tls_1_0_tickets)
+        self._handshakeDone(resumed=False)
+        self._serverRandom = serverHello.random
+        self._clientRandom = clientHello.random
+
+
+    def _clientSendClientHello(self, settings, session, srpUsername,
+                                srpParams, certParams, anonParams,
+                                serverName, nextProtos, reqTack, alpn):
+        #Initialize acceptable ciphersuites
+        cipherSuites = [CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV]
+        if srpParams:
+            cipherSuites += CipherSuite.getSrpAllSuites(settings)
+        elif certParams:
+            cipherSuites += CipherSuite.getTLS13Suites(settings)
+            cipherSuites += CipherSuite.getEcdsaSuites(settings)
+            cipherSuites += CipherSuite.getEcdheCertSuites(settings)
+            cipherSuites += CipherSuite.getDheCertSuites(settings)
+            cipherSuites += CipherSuite.getCertSuites(settings)
+            cipherSuites += CipherSuite.getDheDsaSuites(settings)
+        elif anonParams:
+            cipherSuites += CipherSuite.getEcdhAnonSuites(settings)
+            cipherSuites += CipherSuite.getAnonSuites(settings)
+        else:
+            assert False
+
+        #Add any SCSVs. These are not real cipher suites, but signaling
+        #values which reuse the cipher suite field in the ClientHello.
+        wireCipherSuites = list(cipherSuites)
+        if settings.sendFallbackSCSV:
+            wireCipherSuites.append(CipherSuite.TLS_FALLBACK_SCSV)
+
+        #Initialize acceptable certificate types
+        certificateTypes = settings.getCertificateTypes()
+
+        extensions = []
+
+        #Initialize TLS extensions
+        if settings.useEncryptThenMAC:
+            extensions.append(TLSExtension().\
+                              create(ExtensionType.encrypt_then_mac,
+                                     bytearray(0)))
+        if settings.useExtendedMasterSecret:
+            extensions.append(TLSExtension().create(ExtensionType.
+                                                    extended_master_secret,
+                                                    bytearray(0)))
+        # In TLS1.2 advertise support for additional signature types
+        if settings.maxVersion >= (3, 3):
+            sigList = self._sigHashesToList(settings)
+            assert len(sigList) > 0
+            extensions.append(SignatureAlgorithmsExtension().\
+                              create(sigList))
+        # if we know any protocols for ALPN, advertise them
+        if alpn:
+            extensions.append(ALPNExtension().create(alpn))
+
+        session_id = bytearray()
+        # when TLS 1.3 advertised, add key shares, set fake session_id
+        shares = None
+        if next((i for i in settings.versions if i > (3, 3)), None):
+            # if we have a client cert configured, do indicate we're willing
+            # to perform Post Handshake Authentication
+            if certParams and certParams[1]:
+                extensions.append(TLSExtension(
+                    extType=ExtensionType.post_handshake_auth).
+                    create(bytearray(b'')))
+                self._client_keypair = certParams
+
+            # fake session_id for middlebox compatibility mode
+            session_id = getRandomBytes(32)
+
+            extensions.append(SupportedVersionsExtension().
+                              create(settings.versions))
+
+            shares = []
+            for group_name in settings.keyShares:
+                group_id = getattr(GroupName, group_name)
+                key_share = self._genKeyShareEntry(group_id, (3, 4))
+
+                shares.append(key_share)
+            # if TLS 1.3 is enabled, key_share must always be sent
+            # (unless only static PSK is used)
+            extensions.append(ClientKeyShareExtension().create(shares))
+
+            # add info on types of PSKs supported (also used for
+            # NewSessionTicket so send basically always)
+            ext = PskKeyExchangeModesExtension().create(
+                [getattr(PskKeyExchangeMode, i) for i in settings.psk_modes])
+            extensions.append(ext)
+
+        groups = []
+        #Send the ECC extensions only if we advertise ECC ciphers
+        if next((cipher for cipher in cipherSuites \
+                if cipher in CipherSuite.ecdhAllSuites), None) is not None:
+            groups.extend(self._curveNamesToList(settings))
+            extensions.append(ECPointFormatsExtension().\
+                              create([ECPointFormat.uncompressed]))
+        # Advertise FFDHE groups if we have DHE ciphers
+        if next((cipher for cipher in cipherSuites
+                 if cipher in CipherSuite.dhAllSuites), None) is not None:
+            groups.extend(self._groupNamesToList(settings))
+        # Send the extension only if it will be non empty
+        if groups:
+            if shares:
+                # put the groups used for key shares first, and in order
+                # (req. from RFC 8446, section 4.2.8)
+                share_ids = [i.group for i in shares]
+                diff = set(groups) - set(share_ids)
+                groups = share_ids + [i for i in groups if i in diff]
+            extensions.append(SupportedGroupsExtension().create(groups))
+
+        if settings.use_heartbeat_extension:
+            extensions.append(HeartbeatExtension().create(
+                HeartbeatMode.PEER_ALLOWED_TO_SEND))
+            self.heartbeat_can_receive = True
+
+        if settings.record_size_limit:
+            extensions.append(RecordSizeLimitExtension().create(
+                settings.record_size_limit))
+
+        # If SessionTicket support is enabled and we have a valid ticket, we
+        # send it in an attempt to resume the session, if SessionTicket support
+        # is enabled but we don't have a valid ticket, we send an empty ext
+        # to indicate support for the feaure
+        if session and session.tls_1_0_tickets:
+            # first get rid of expired tickets
+            session.tls_1_0_tickets[:] = [
+                i for i in session.tls_1_0_tickets if i.valid()]
+            # then send first ticket
+            for cached_ticket in session.tls_1_0_tickets:
+                extensions.append(SessionTicketExtension().create(
+                    cached_ticket.ticket))
+                break
+            else:
+                # or just advertise that we support session resumption
+                extensions.append(SessionTicketExtension().create(
+                    bytearray(0)))
+        else:
+            extensions.append(SessionTicketExtension().create(
+                bytearray(0)))
+
+        # don't send empty list of extensions or extensions in SSLv3
+        if not extensions or settings.maxVersion == (3, 0):
+            extensions = None
+
+        sent_version = min(settings.maxVersion, (3, 3))
+
+        #Either send ClientHello (with a resumable session)...
+        if session and session.sessionID:
+            #If it's resumable, then its
+            #ciphersuite must be one of the acceptable ciphersuites
+            if session.cipherSuite not in cipherSuites:
+                raise ValueError("Session's cipher suite not consistent "\
+                                 "with parameters")
+            else:
+                clientHello = ClientHello()
+                clientHello.create(sent_version, getRandomBytes(32),
+                                   session.sessionID, wireCipherSuites,
+                                   certificateTypes,
+                                   session.srpUsername,
+                                   reqTack, nextProtos is not None,
+                                   session.serverName,
+                                   extensions=extensions)
+
+        #Or send ClientHello (without)
+        else:
+            clientHello = ClientHello()
+            clientHello.create(sent_version, getRandomBytes(32),
+                               session_id, wireCipherSuites,
+                               certificateTypes,
+                               srpUsername,
+                               reqTack, nextProtos is not None,
+                               serverName,
+                               extensions=extensions)
+
+        # Check if padding extension should be added
+        # we want to add extensions even when using just SSLv3
+        if settings.usePaddingExtension:
+            HandshakeHelpers.alignClientHelloPadding(clientHello)
+
+        # because TLS 1.3 PSK is sent in ClientHello and signs the ClientHello
+        # we need to send it as the last extension
+        if (settings.pskConfigs or (session and session.tickets)) \
+                and settings.maxVersion >= (3, 4):
+            ext = PreSharedKeyExtension()
+            idens = []
+            binders = []
+            # if we have a previous session, include it in PSKs too
+            if session and session.tickets:
+                now = time.time()
+                # clean the list from obsolete ones
+                # RFC says that the tickets MUST NOT be cached longer than
+                # 7 days
+                session.tickets[:] = (i for i in session.tickets if
+                                      i.time + i.ticket_lifetime > now and
+                                      i.time + 7 * 24 * 60 * 60 > now)
+                if session.tickets:
+                    ticket = session.tickets[0]
+
+                    # ticket.time is in seconds while the obfuscated time
+                    # is in ms
+                    ticket_time = int(
+                        time.time() * 1000 -
+                        ticket.time * 1000 +
+                        ticket.ticket_age_add) % 2**32
+                    idens.append(PskIdentity().create(ticket.ticket,
+                                                      ticket_time))
+                    binder_len = 48 if session.cipherSuite in \
+                        CipherSuite.sha384PrfSuites else 32
+                    binders.append(bytearray(binder_len))
+            for psk in settings.pskConfigs:
+                # skip PSKs with no identities as they're TLS1.3 incompatible
+                if not psk[0]:
+                    continue
+                idens.append(PskIdentity().create(psk[0], 0))
+                psk_hash = psk[2] if len(psk) > 2 else 'sha256'
+                assert psk_hash in set(['sha256', 'sha384'])
+                # create fake binder values to create correct length fields
+                binders.append(bytearray(32 if psk_hash == 'sha256' else 48))
+
+            if idens:
+                ext.create(idens, binders)
+                clientHello.extensions.append(ext)
+
+                # for HRR case we'll need 1st CH and HRR in handshake hashes,
+                # so pass them in, truncated CH will be added by the helpers to
+                # the copy of the hashes
+                HandshakeHelpers.update_binders(clientHello,
+                                                self._handshake_hash,
+                                                settings.pskConfigs,
+                                                session.tickets if session
+                                                else None,
+                                                session.resumptionMasterSecret
+                                                if session else None)
+
+        for result in self._sendMsg(clientHello):
+            yield result
+        yield clientHello
+
+    def _clientGetServerHello(self, settings, session, clientHello):
+        client_hello_hash = self._handshake_hash.copy()
+        for result in self._getMsg(ContentType.handshake,
+                                   HandshakeType.server_hello):
+            if result in (0,1): yield result
+            else: break
+
+        hello_retry = None
+        ext = result.getExtension(ExtensionType.supported_versions)
+        if result.random == TLS_1_3_HRR and ext and ext.version > (3, 3):
+            self.version = ext.version
+            hello_retry = result
+
+            # create synthetic handshake hash
+            prf_name, prf_size = self._getPRFParams(hello_retry.cipher_suite)
+
+            self._handshake_hash = HandshakeHashes()
+            writer = Writer()
+            writer.add(HandshakeType.message_hash, 1)
+            writer.addVarSeq(client_hello_hash.digest(prf_name), 1, 3)
+            self._handshake_hash.update(writer.bytes)
+            self._handshake_hash.update(hello_retry.write())
+
+            # check if all extensions in the HRR were present in client hello
+            ch_ext_types = set(i.extType for i in clientHello.extensions)
+            ch_ext_types.add(ExtensionType.cookie)
+
+            bad_ext = next((i for i in hello_retry.extensions
+                            if i.extType not in ch_ext_types), None)
+            if bad_ext:
+                bad_ext = ExtensionType.toStr(bad_ext)
+                for result in self._sendError(AlertDescription
+                                              .unsupported_extension,
+                                              ("Unexpected extension in HRR: "
+                                               "{0}").format(bad_ext)):
+                    yield result
+
+            # handle cookie extension
+            cookie = hello_retry.getExtension(ExtensionType.cookie)
+            if cookie:
+                clientHello.addExtension(cookie)
+
+            # handle key share extension
+            sr_key_share_ext = hello_retry.getExtension(ExtensionType
+                                                        .key_share)
+            if sr_key_share_ext:
+                group_id = sr_key_share_ext.selected_group
+                # check if group selected by server is valid
+                groups_ext = clientHello.getExtension(ExtensionType
+                                                      .supported_groups)
+                if group_id not in groups_ext.groups:
+                    for result in self._sendError(AlertDescription
+                                                  .illegal_parameter,
+                                                  "Server selected group we "
+                                                  "did not advertise"):
+                        yield result
+
+                cl_key_share_ext = clientHello.getExtension(ExtensionType
+                                                            .key_share)
+                # check if the server didn't ask for a group we already sent
+                if next((entry for entry in cl_key_share_ext.client_shares
+                         if entry.group == group_id), None):
+                    for result in self._sendError(AlertDescription
+                                                  .illegal_parameter,
+                                                  "Server selected group we "
+                                                  "did sent the key share "
+                                                  "for"):
+                        yield result
+
+                key_share = self._genKeyShareEntry(group_id, (3, 4))
+
+                # old key shares need to be removed
+                cl_key_share_ext.client_shares = [key_share]
+
+            if not cookie and not sr_key_share_ext:
+                # HRR did not result in change to Client Hello
+                for result in self._sendError(AlertDescription.
+                                              illegal_parameter,
+                                              "Received HRR did not cause "
+                                              "update to Client Hello"):
+                    yield result
+
+            if clientHello.session_id != hello_retry.session_id:
+                for result in self._sendError(
+                        AlertDescription.illegal_parameter,
+                        "Received HRR session_id does not match the one in "
+                        "ClientHello"):
+                    yield result
+
+            ext = clientHello.getExtension(ExtensionType.pre_shared_key)
+            if ext:
+                # move the extension to end (in case extension like cookie was
+                # added
+                clientHello.extensions.remove(ext)
+                clientHello.extensions.append(ext)
+                HandshakeHelpers.update_binders(clientHello,
+                                                self._handshake_hash,
+                                                settings.pskConfigs,
+                                                session.tickets if session
+                                                else None,
+                                                session.resumptionMasterSecret
+                                                if session else None)
+
+            # resend the client hello with performed changes
+            msgs = []
+            if clientHello.session_id:
+                ccs = ChangeCipherSpec().create()
+                msgs.append(ccs)
+            msgs.append(clientHello)
+            for result in self._sendMsgs(msgs):
+                yield result
+            self._ccs_sent = True
+
+            # retry getting server hello
+            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
+        real_version = serverHello.server_version
+        if serverHello.server_version >= (3, 3):
+            ext = serverHello.getExtension(ExtensionType.supported_versions)
+            if ext:
+                real_version = ext.version
+        self.version = real_version
+
+        #Check ServerHello
+        if hello_retry and \
+                hello_retry.cipher_suite != serverHello.cipher_suite:
+            for result in self._sendError(AlertDescription.illegal_parameter,
+                                          "server selected different cipher "
+                                          "in HRR and Server Hello"):
+                yield result
+        if real_version < settings.minVersion:
+            for result in self._sendError(
+                    AlertDescription.protocol_version,
+                    "Too old version: {0} (min: {1})"
+                    .format(real_version, settings.minVersion)):
+                yield result
+        if real_version > settings.maxVersion and \
+                real_version not in settings.versions:
+            for result in self._sendError(
+                    AlertDescription.protocol_version,
+                    "Too new version: {0} (max: {1})"
+                    .format(real_version, settings.maxVersion)):
+                yield result
+        if real_version > (3, 3) and \
+                serverHello.session_id != clientHello.session_id:
+            for result in self._sendError(
+                    AlertDescription.illegal_parameter,
+                    "Received ServerHello session_id does not match the one "
+                    "in ClientHello"):
+                yield result
+        cipherSuites = CipherSuite.filterForVersion(clientHello.cipher_suites,
+                                                    minVersion=real_version,
+                                                    maxVersion=real_version)
+        if serverHello.cipher_suite not in cipherSuites:
+            for result in self._sendError(\
+                AlertDescription.illegal_parameter,
+                "Server responded with incorrect ciphersuite"):
+                yield result
+        if serverHello.certificate_type not in clientHello.certificate_types:
+            for result in self._sendError(\
+                AlertDescription.illegal_parameter,
+                "Server responded with incorrect certificate type"):
+                yield result
+        if serverHello.compression_method != 0:
+            for result in self._sendError(\
+                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 not serverHello.tackExt.verifySignatures():
+                for result in self._sendError(\
+                    AlertDescription.decrypt_error,
+                    "TackExtension contains an invalid signature"):
+                    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.getExtension(ExtensionType.extended_master_secret)\
+            and settings.requireExtendedMasterSecret:
+            for result in self._sendError(
+                    AlertDescription.insufficient_security,
+                    "Negotiation of Extended master Secret failed"):
+                yield result
+        alpnExt = serverHello.getExtension(ExtensionType.alpn)
+        if alpnExt:
+            if not alpnExt.protocol_names or \
+                    len(alpnExt.protocol_names) != 1:
+                for result in self._sendError(
+                        AlertDescription.illegal_parameter,
+                        "Server responded with invalid ALPN extension"):
+                    yield result
+            clntAlpnExt = clientHello.getExtension(ExtensionType.alpn)
+            if not clntAlpnExt:
+                for result in self._sendError(
+                        AlertDescription.unsupported_extension,
+                        "Server sent ALPN extension without one in "
+                        "client hello"):
+                    yield result
+            if alpnExt.protocol_names[0] not in clntAlpnExt.protocol_names:
+                for result in self._sendError(
+                        AlertDescription.illegal_parameter,
+                        "Server selected ALPN protocol we did not advertise"):
+                    yield result
+        heartbeat_ext = serverHello.getExtension(ExtensionType.heartbeat)
+        if heartbeat_ext:
+            if not settings.use_heartbeat_extension:
+                for result in self._sendError(
+                        AlertDescription.unsupported_extension,
+                        "Server sent Heartbeat extension without one in "
+                        "client hello"):
+                    yield result
+            if heartbeat_ext.mode == HeartbeatMode.PEER_ALLOWED_TO_SEND and \
+                    settings.heartbeat_response_callback:
+                self.heartbeat_can_send = True
+                self.heartbeat_response_callback = settings.\
+                    heartbeat_response_callback
+            elif heartbeat_ext.mode == HeartbeatMode.\
+                    PEER_NOT_ALLOWED_TO_SEND or not settings.\
+                    heartbeat_response_callback:
+                self.heartbeat_can_send = False
+            else:
+                for result in self._sendError(
+                        AlertDescription.illegal_parameter,
+                        "Server responded with invalid Heartbeat extension"):
+                    yield result
+            self.heartbeat_supported = True
+        size_limit_ext = serverHello.getExtension(
+            ExtensionType.record_size_limit)
+        if size_limit_ext:
+            if size_limit_ext.record_size_limit is None:
+                for result in self._sendError(
+                        AlertDescription.decode_error,
+                        "Malformed record_size_limit extension"):
+                    yield result
+            # if we got the extension in ServerHello it means we're doing
+            # TLS 1.2 so the max value for extension is 2^14
+            if not 64 <= size_limit_ext.record_size_limit <= 2**14:
+                for result in self._sendError(
+                        AlertDescription.illegal_parameter,
+                        "Server responed with invalid value in "
+                        "record_size_limit extension"):
+                    yield result
+            self._peer_record_size_limit = size_limit_ext.record_size_limit
+        yield serverHello

     @staticmethod
     def _getKEX(group, version):
         """Get object for performing key exchange."""
-        pass
+        if group in GroupName.allFF:
+            return FFDHKeyExchange(group, version)
+        return ECDHKeyExchange(group, version)

     @classmethod
     def _genKeyShareEntry(cls, group, version):
         """Generate KeyShareEntry object from randomly selected private value.
         """
-        pass
+        kex = cls._getKEX(group, version)
+        private = kex.get_random_private_key()
+        share = kex.calc_public_value(private)
+        return KeyShareEntry().create(group, share, private)

     @staticmethod
     def _getPRFParams(cipher_suite):
         """Return name of hash used for PRF and the hash output size."""
-        pass
+        if cipher_suite in CipherSuite.sha384PrfSuites:
+            return 'sha384', 48
+        return 'sha256', 32

     def _clientTLS13Handshake(self, settings, session, clientHello,
-        clientCertChain, privateKey, serverHello):
+                              clientCertChain, privateKey, serverHello):
         """Perform TLS 1.3 handshake as a client."""
-        pass
+        prfName, prf_size = self._getPRFParams(serverHello.cipher_suite)
+
+        # we have client and server hello in TLS 1.3 so we have the necessary
+        # key shares to derive the handshake receive key
+        sr_kex = serverHello.getExtension(ExtensionType.key_share)
+        sr_psk = serverHello.getExtension(ExtensionType.pre_shared_key)
+        if not sr_kex and not sr_psk:
+            raise TLSIllegalParameterException("Server did not select PSK nor "
+                                               "an (EC)DH group")
+        if sr_kex:
+            sr_kex = sr_kex.server_share
+            self.ecdhCurve = sr_kex.group
+            cl_key_share_ex = clientHello.getExtension(ExtensionType.key_share)
+            cl_kex = next((i for i in cl_key_share_ex.client_shares
+                           if i.group == sr_kex.group), None)
+            if cl_kex is None:
+                raise TLSIllegalParameterException("Server selected not "
+                                                   "advertised group.")
+            kex = self._getKEX(sr_kex.group, self.version)
+
+            shared_sec = kex.calc_shared_key(cl_kex.private,
+                                             sr_kex.key_exchange)
+        else:
+            shared_sec = bytearray(prf_size)
+
+        # if server agreed to perform resumption, find the matching secret key
+        resuming = False
+        if sr_psk:
+            clPSK = clientHello.getExtension(ExtensionType.pre_shared_key)
+            ident = clPSK.identities[sr_psk.selected]
+            psk = [i[1] for i in settings.pskConfigs if i[0] == ident.identity]
+            if psk:
+                psk = psk[0]
+            else:
+                resuming = True
+                psk = HandshakeHelpers.calc_res_binder_psk(
+                    ident, session.resumptionMasterSecret,
+                    session.tickets)
+        else:
+            psk = bytearray(prf_size)
+
+        secret = bytearray(prf_size)
+        # Early Secret
+        secret = secureHMAC(secret, psk, prfName)
+
+        # Handshake Secret
+        secret = derive_secret(secret, bytearray(b'derived'),
+                               None, prfName)
+        secret = secureHMAC(secret, shared_sec, prfName)
+
+        sr_handshake_traffic_secret = derive_secret(secret,
+                                                    bytearray(b's hs traffic'),
+                                                    self._handshake_hash,
+                                                    prfName)
+        cl_handshake_traffic_secret = derive_secret(secret,
+                                                    bytearray(b'c hs traffic'),
+                                                    self._handshake_hash,
+                                                    prfName)
+
+        # prepare for reading encrypted messages
+        self._recordLayer.calcTLS1_3PendingState(
+            serverHello.cipher_suite,
+            cl_handshake_traffic_secret,
+            sr_handshake_traffic_secret,
+            settings.cipherImplementations)
+
+        self._changeReadState()
+
+        for result in self._getMsg(ContentType.handshake,
+                                   HandshakeType.encrypted_extensions):
+            if result in (0, 1):
+                yield result
+            else:
+                break
+        encrypted_extensions = result
+        assert isinstance(encrypted_extensions, EncryptedExtensions)
+
+        size_limit_ext = encrypted_extensions.getExtension(
+            ExtensionType.record_size_limit)
+        if size_limit_ext and not settings.record_size_limit:
+            for result in self._sendError(
+                    AlertDescription.illegal_parameter,
+                    "Server sent record_size_limit extension despite us not "
+                    "advertising it"):
+                yield result
+        if size_limit_ext:
+            if size_limit_ext.record_size_limit is None:
+                for result in self._sendError(
+                        AlertDescription.decode_error,
+                        "Malformed record_size_limit extension"):
+                    yield result
+            if not 64 <= size_limit_ext.record_size_limit <= 2**14+1:
+                for result in self._sendError(
+                        AlertDescription.illegal_parameter,
+                        "Invalid valid in record_size_limit extension"):
+                    yield result
+            # the record layer code expects a limit that excludes content type
+            # from the value while extension is defined including it
+            self._send_record_limit = size_limit_ext.record_size_limit - 1
+            self._recv_record_limit = min(2**14, settings.record_size_limit - 1)
+
+        # if we negotiated PSK then Certificate is not sent
+        certificate_request = None
+        certificate = None
+        if not sr_psk:
+            for result in self._getMsg(ContentType.handshake,
+                                       (HandshakeType.certificate_request,
+                                        HandshakeType.certificate),
+                                       CertificateType.x509):
+                if result in (0, 1):
+                    yield result
+                else:
+                    break
+
+            if isinstance(result, CertificateRequest):
+                certificate_request = result
+
+                # we got CertificateRequest so now we'll get Certificate
+                for result in self._getMsg(ContentType.handshake,
+                                           HandshakeType.certificate,
+                                           CertificateType.x509):
+                    if result in (0, 1):
+                        yield result
+                    else:
+                        break
+
+            certificate = result
+            assert isinstance(certificate, Certificate)
+
+            srv_cert_verify_hh = self._handshake_hash.copy()
+
+            for result in self._getMsg(ContentType.handshake,
+                                       HandshakeType.certificate_verify):
+                if result in (0, 1):
+                    yield result
+                else:
+                    break
+            certificate_verify = result
+            assert isinstance(certificate_verify, CertificateVerify)
+
+            signature_scheme = certificate_verify.signatureAlgorithm
+            self.serverSigAlg = signature_scheme
+
+            signature_context = KeyExchange.calcVerifyBytes((3, 4),
+                                                            srv_cert_verify_hh,
+                                                            signature_scheme,
+                                                            None, None, None,
+                                                            prfName, b'server')
+
+            for result in self._clientGetKeyFromChain(certificate, settings):
+                if result in (0, 1):
+                    yield result
+                else:
+                    break
+            publicKey, serverCertChain, tackExt = result
+
+            if signature_scheme in (SignatureScheme.ed25519,
+                                    SignatureScheme.ed448):
+                pad_type = None
+                hash_name = "intrinsic"
+                salt_len = None
+                method = publicKey.hashAndVerify
+            elif signature_scheme[1] == SignatureAlgorithm.ecdsa:
+                pad_type = None
+                hash_name = HashAlgorithm.toRepr(signature_scheme[0])
+                matching_hash = self._curve_name_to_hash_name(
+                    publicKey.curve_name)
+                if hash_name != matching_hash:
+                    raise TLSIllegalParameterException(
+                        "server selected signature method invalid for the "
+                        "certificate it presented (curve mismatch)")
+
+                salt_len = None
+                method = publicKey.verify
+            else:
+                scheme = SignatureScheme.toRepr(signature_scheme)
+                pad_type = SignatureScheme.getPadding(scheme)
+                hash_name = SignatureScheme.getHash(scheme)
+                salt_len = getattr(hashlib, hash_name)().digest_size
+                method = publicKey.verify
+
+            if not method(certificate_verify.signature,
+                          signature_context,
+                          pad_type,
+                          hash_name,
+                          salt_len):
+                raise TLSDecryptionFailed("server Certificate Verify "
+                                          "signature "
+                                          "verification failed")
+
+        transcript_hash = self._handshake_hash.digest(prfName)
+
+        for result in self._getMsg(ContentType.handshake,
+                                   HandshakeType.finished,
+                                   prf_size):
+            if result in (0, 1):
+                yield result
+            else:
+                break
+        finished = result
+
+        server_finish_hs = self._handshake_hash.copy()
+
+        assert isinstance(finished, Finished)
+
+        finished_key = HKDF_expand_label(sr_handshake_traffic_secret,
+                                         b"finished", b'', prf_size, prfName)
+        verify_data = secureHMAC(finished_key, transcript_hash, prfName)
+
+        if finished.verify_data != verify_data:
+            raise TLSDecryptionFailed("Finished value is not valid")
+
+        # now send client set of messages
+        self._changeWriteState()
+
+        # Master secret
+        secret = derive_secret(secret, bytearray(b'derived'), None, prfName)
+        secret = secureHMAC(secret, bytearray(prf_size), prfName)
+
+        cl_app_traffic = derive_secret(secret, bytearray(b'c ap traffic'),
+                                       server_finish_hs, prfName)
+        sr_app_traffic = derive_secret(secret, bytearray(b's ap traffic'),
+                                       server_finish_hs, prfName)
+
+        if certificate_request:
+            client_certificate = Certificate(serverHello.certificate_type,
+                                             self.version)
+            if clientCertChain:
+                # Check to make sure we have the same type of certificates the
+                # server requested
+                if serverHello.certificate_type == CertificateType.x509 \
+                    and not isinstance(clientCertChain, X509CertChain):
+                    for result in self._sendError(
+                            AlertDescription.handshake_failure,
+                            "Client certificate is of wrong type"):
+                        yield result

-    def _clientKeyExchange(self, settings, cipherSuite, clientCertChain,
-        privateKey, certificateType, tackExt, clientRandom, serverRandom,
-        keyExchange):
+            client_certificate.create(clientCertChain)
+            # we need to send the message even if we don't have a certificate
+            for result in self._sendMsg(client_certificate):
+                yield result
+
+            if clientCertChain and privateKey:
+                valid_sig_algs = certificate_request.supported_signature_algs
+                if not valid_sig_algs:
+                    for result in self._sendError(
+                            AlertDescription.missing_extension,
+                            "No Signature Algorithms found"):
+                        yield result
+
+                availSigAlgs = self._sigHashesToList(settings, privateKey,
+                                                     clientCertChain,
+                                                     version=(3, 4))
+                signature_scheme = getFirstMatching(availSigAlgs,
+                                                    valid_sig_algs)
+                scheme = SignatureScheme.toRepr(signature_scheme)
+                signature_scheme = getattr(SignatureScheme, scheme)
+
+                signature_context = \
+                    KeyExchange.calcVerifyBytes((3, 4), self._handshake_hash,
+                                                signature_scheme, None, None,
+                                                None, prfName, b'client')
+
+                if signature_scheme in (SignatureScheme.ed25519,
+                        SignatureScheme.ed448):
+                    pad_type = None
+                    hash_name = "intrinsic"
+                    salt_len = None
+                    sig_func = privateKey.hashAndSign
+                    ver_func = privateKey.hashAndVerify
+                elif signature_scheme[1] == SignatureAlgorithm.ecdsa:
+                    pad_type = None
+                    hash_name = HashAlgorithm.toRepr(signature_scheme[0])
+                    salt_len = None
+                    sig_func = privateKey.sign
+                    ver_func = privateKey.verify
+                else:
+                    pad_type = SignatureScheme.getPadding(scheme)
+                    hash_name = SignatureScheme.getHash(scheme)
+                    salt_len = getattr(hashlib, hash_name)().digest_size
+                    sig_func = privateKey.sign
+                    ver_func = privateKey.verify
+
+                signature = sig_func(signature_context,
+                                     pad_type,
+                                     hash_name,
+                                     salt_len)
+                if not ver_func(signature, signature_context,
+                                pad_type,
+                                hash_name,
+                                salt_len):
+                    for result in self._sendError(
+                            AlertDescription.internal_error,
+                            "Certificate Verify signature failed"):
+                        yield result
+
+                certificate_verify = CertificateVerify(self.version)
+                certificate_verify.create(signature, signature_scheme)
+
+                for result in self._sendMsg(certificate_verify):
+                    yield result
+
+        # Do after client cert and verify messages has been sent.
+        exporter_master_secret = derive_secret(secret,
+                                               bytearray(b'exp master'),
+                                               self._handshake_hash, prfName)
+
+        self._recordLayer.calcTLS1_3PendingState(
+            serverHello.cipher_suite,
+            cl_app_traffic,
+            sr_app_traffic,
+            settings.cipherImplementations)
+        # be ready to process alert messages from the server, which
+        # MUST be encrypted with ap traffic secret when they are sent after
+        # Finished
+        self._changeReadState()
+
+        cl_finished_key = HKDF_expand_label(cl_handshake_traffic_secret,
+                                            b"finished", b'',
+                                            prf_size, prfName)
+        cl_verify_data = secureHMAC(
+            cl_finished_key,
+            self._handshake_hash.digest(prfName),
+            prfName)
+
+        cl_finished = Finished(self.version, prf_size)
+        cl_finished.create(cl_verify_data)
+
+        if not self._ccs_sent and clientHello.session_id:
+            ccs = ChangeCipherSpec().create()
+            msgs = [ccs, cl_finished]
+        else:
+            msgs = [cl_finished]
+
+        for result in self._sendMsgs(msgs):
+            yield result
+
+        # CCS messages are not allowed in post handshake authentication
+        self._middlebox_compat_mode = False
+
+        # fully switch to application data
+        self._changeWriteState()
+
+        self._first_handshake_hashes = self._handshake_hash.copy()
+
+        resumption_master_secret = derive_secret(secret,
+                                                 bytearray(b'res master'),
+                                                 self._handshake_hash, prfName)
+
+        self.session = Session()
+        self.extendedMasterSecret = True
+
+        serverName = None
+        if clientHello.server_name:
+            serverName = clientHello.server_name.decode("utf-8")
+
+        appProto = None
+        alpnExt = encrypted_extensions.getExtension(ExtensionType.alpn)
+        if alpnExt:
+            appProto = alpnExt.protocol_names[0]
+
+        heartbeat_ext = encrypted_extensions.getExtension(ExtensionType.heartbeat)
+        if heartbeat_ext:
+            if not settings.use_heartbeat_extension:
+                for result in self._sendError(
+                        AlertDescription.unsupported_extension,
+                        "Server sent Heartbeat extension without one in "
+                        "client hello"):
+                    yield result
+            if heartbeat_ext.mode == HeartbeatMode.PEER_ALLOWED_TO_SEND and \
+                    settings.heartbeat_response_callback:
+                self.heartbeat_can_send = True
+                self.heartbeat_response_callback = settings.\
+                    heartbeat_response_callback
+            elif heartbeat_ext.mode == HeartbeatMode.\
+                    PEER_NOT_ALLOWED_TO_SEND or not settings.\
+                    heartbeat_response_callback:
+                self.heartbeat_can_send = False
+            else:
+                for result in self._sendError(
+                        AlertDescription.illegal_parameter,
+                        "Server responded with invalid Heartbeat extension"):
+                    yield result
+            self.heartbeat_supported = True
+
+        self.session.create(secret,
+                            bytearray(b''),  # no session_id in TLS 1.3
+                            serverHello.cipher_suite,
+                            None,  # no SRP
+                            clientCertChain,
+                            certificate.cert_chain if certificate else None,
+                            None,  # no TACK
+                            False,  # no TACK in hello
+                            serverName,
+                            encryptThenMAC=False,  # all ciphers are AEAD
+                            extendedMasterSecret=True,  # all TLS1.3 are EMS
+                            appProto=appProto,
+                            cl_app_secret=cl_app_traffic,
+                            sr_app_secret=sr_app_traffic,
+                            exporterMasterSecret=exporter_master_secret,
+                            resumptionMasterSecret=resumption_master_secret,
+                            # NOTE it must be a reference, not a copy!
+                            tickets=self.tickets)
+
+        yield "finished" if not resuming else "resumed_and_finished"
+
+    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,
+                      nextProto, settings):
+
+        if session and ((session.sessionID and \
+            serverHello.session_id == session.sessionID) or
+            session.tls_1_0_tickets):
+
+            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,
+                                    settings.cipherImplementations)
+
+            #Exchange ChangeCipherSpec and Finished messages
+            for result in self._getFinished(session.masterSecret,
+                                            session.cipherSuite):
+                yield result
+            # buffer writes so that CCS and Finished go out in one TCP packet
+            self.sock.buffer_writes = True
+            for result in self._sendFinished(session.masterSecret,
+                                             session.cipherSuite,
+                                             nextProto,
+                                             settings=settings):
+                yield result
+            self.sock.flush()
+            self.sock.buffer_writes = False
+
+            #Set the session for this connection
+            self.session = session
+            yield "resumed_and_finished"
+
+    def _clientKeyExchange(self, settings, cipherSuite,
+                           clientCertChain, privateKey,
+                           certificateType,
+                           tackExt, clientRandom, serverRandom,
+                           keyExchange):
         """Perform the client side of key exchange"""
-        pass
+        # if server chose cipher suite with authentication, get the certificate
+        if cipherSuite in CipherSuite.certAllSuites or \
+                cipherSuite in CipherSuite.ecdheEcdsaSuites or \
+                cipherSuite in CipherSuite.dheDsaSuites:
+            for result in self._getMsg(ContentType.handshake,
+                                       HandshakeType.certificate,
+                                       certificateType):
+                if result in (0, 1):
+                    yield result
+                else: break
+            serverCertificate = result
+        else:
+            serverCertificate = None
+        # if server chose RSA key exchange, we need to skip SKE message
+        if cipherSuite not in CipherSuite.certSuites:
+            for result in self._getMsg(ContentType.handshake,
+                                       HandshakeType.server_key_exchange,
+                                       cipherSuite):
+                if result in (0, 1):
+                    yield result
+                else: break
+            serverKeyExchange = result
+        else:
+            serverKeyExchange = None
+
+        for result in self._getMsg(ContentType.handshake,
+                                   (HandshakeType.certificate_request,
+                                    HandshakeType.server_hello_done)):
+            if result in (0, 1):
+                yield result
+            else: break
+
+        certificateRequest = None
+        if isinstance(result, CertificateRequest):
+            certificateRequest = result
+
+            #abort if Certificate Request with inappropriate ciphersuite
+            if cipherSuite not in CipherSuite.certAllSuites \
+                and cipherSuite not in CipherSuite.ecdheEcdsaSuites \
+                and cipherSuite not in CipherSuite.dheDsaSuites\
+                or cipherSuite in CipherSuite.srpAllSuites:
+                for result in self._sendError(\
+                        AlertDescription.unexpected_message,
+                        "Certificate Request with incompatible cipher suite"):
+                    yield result
+
+            # we got CertificateRequest so now we'll get ServerHelloDone
+            for result in self._getMsg(ContentType.handshake,
+                                       HandshakeType.server_hello_done):
+                if result in (0, 1):
+                    yield result
+                else: break
+        serverHelloDone = result
+
+        serverCertChain = None
+        publicKey = None
+        if cipherSuite in CipherSuite.certAllSuites or \
+                cipherSuite in CipherSuite.ecdheEcdsaSuites or \
+                cipherSuite in CipherSuite.dheDsaSuites:
+            # get the certificate
+            for result in self._clientGetKeyFromChain(serverCertificate,
+                                                      settings,
+                                                      tackExt):
+                if result in (0, 1):
+                    yield result
+                else: break
+            publicKey, serverCertChain, tackExt = result
+
+            #Check the server's signature, if the server chose an authenticated
+            # PFS-enabled ciphersuite
+
+            if serverKeyExchange:
+                valid_sig_algs = \
+                    self._sigHashesToList(settings,
+                                          certList=serverCertChain)
+                try:
+                    KeyExchange.verifyServerKeyExchange(serverKeyExchange,
+                                                        publicKey,
+                                                        clientRandom,
+                                                        serverRandom,
+                                                        valid_sig_algs)
+                except TLSIllegalParameterException:
+                    for result in self._sendError(AlertDescription.\
+                                                  illegal_parameter):
+                        yield result
+                except TLSDecryptionFailed:
+                    for result in self._sendError(\
+                            AlertDescription.decrypt_error):
+                        yield result
+
+        if serverKeyExchange:
+            # store key exchange metadata for user applications
+            if self.version >= (3, 3) \
+                    and (cipherSuite in CipherSuite.certAllSuites or
+                         cipherSuite in CipherSuite.ecdheEcdsaSuites) \
+                    and cipherSuite not in CipherSuite.certSuites:
+                self.serverSigAlg = (serverKeyExchange.hashAlg,
+                                     serverKeyExchange.signAlg)
+
+            if cipherSuite in CipherSuite.dhAllSuites:
+                self.dhGroupSize = numBits(serverKeyExchange.dh_p)
+            if cipherSuite in CipherSuite.ecdhAllSuites:
+                self.ecdhCurve = serverKeyExchange.named_curve
+
+        #Send Certificate if we were asked for it
+        if certificateRequest:
+            # if a peer doesn't advertise support for any algorithm in TLSv1.2,
+            # support for SHA1+RSA can be assumed
+            if self.version == (3, 3)\
+                and not [sig for sig in \
+                         certificateRequest.supported_signature_algs\
+                         if sig[1] == SignatureAlgorithm.rsa]:
+                for result in self._sendError(\
+                        AlertDescription.handshake_failure,
+                        "Server doesn't accept any sigalgs we support: " +
+                        str(certificateRequest.supported_signature_algs)):
+                    yield result
+            clientCertificate = Certificate(certificateType)
+
+            if clientCertChain:
+                #Check to make sure we have the same type of
+                #certificates the server requested
+                if certificateType == CertificateType.x509 \
+                    and not isinstance(clientCertChain, X509CertChain):
+                    for result in self._sendError(\
+                            AlertDescription.handshake_failure,
+                            "Client certificate is of wrong type"):
+                        yield result
+
+                clientCertificate.create(clientCertChain)
+            # we need to send the message even if we don't have a certificate
+            for result in self._sendMsg(clientCertificate):
+                yield result
+        else:
+            #Server didn't ask for cer, zeroise so session doesn't store them
+            privateKey = None
+            clientCertChain = None
+
+        try:
+            ske = serverKeyExchange
+            premasterSecret = keyExchange.processServerKeyExchange(publicKey,
+                                                                   ske)
+        except TLSInsufficientSecurity as e:
+            for result in self._sendError(\
+                    AlertDescription.insufficient_security, e):
+                yield result
+        except TLSIllegalParameterException as e:
+            for result in self._sendError(\
+                    AlertDescription.illegal_parameter, e):
+                yield result
+
+        clientKeyExchange = keyExchange.makeClientKeyExchange()
+
+        #Send ClientKeyExchange
+        for result in self._sendMsg(clientKeyExchange):
+            yield result
+
+        # the Extended Master Secret calculation uses the same handshake
+        # hashes as the Certificate Verify calculation so we need to
+        # make a copy of it
+        self._certificate_verify_handshake_hash = self._handshake_hash.copy()
+
+        #if client auth was requested and we have a private key, send a
+        #CertificateVerify
+        if certificateRequest and privateKey:
+            valid_sig_algs = self._sigHashesToList(settings, privateKey,
+                                                   clientCertChain)
+            try:
+                certificateVerify = KeyExchange.makeCertificateVerify(
+                    self.version,
+                    self._certificate_verify_handshake_hash,
+                    valid_sig_algs,
+                    privateKey,
+                    certificateRequest,
+                    premasterSecret,
+                    clientRandom,
+                    serverRandom)
+            except TLSInternalError as exception:
+                for result in self._sendError(
+                        AlertDescription.internal_error, exception):
+                    yield result
+            for result in self._sendMsg(certificateVerify):
+                yield result
+
+        yield (premasterSecret, serverCertChain, clientCertChain, tackExt)
+
+    def _clientFinished(self, premasterSecret, clientRandom, serverRandom,
+                        cipherSuite, cipherImplementations, nextProto,
+                        settings):
+
+        masterSecret = self._calculate_master_secret(premasterSecret,
+                                                     cipherSuite,
+                                                     clientRandom,
+                                                     serverRandom)
+        self._calcPendingStates(cipherSuite, masterSecret,
+                                clientRandom, serverRandom,
+                                cipherImplementations)
+
+        #Exchange ChangeCipherSpec and Finished messages
+        for result in self._sendFinished(masterSecret, cipherSuite, nextProto,
+                settings=settings):
+            yield result
+        self.sock.flush()
+        self.sock.buffer_writes = False
+        for result in self._getFinished(masterSecret,
+                                        cipherSuite,
+                                        nextProto=nextProto):
+            yield result
+        yield masterSecret

     def _check_certchain_with_settings(self, cert_chain, settings):
         """
@@ -327,12 +1878,120 @@ class TLSConnection(TLSRecordLayer):
         Checks if the certificate key size matches the minimum and maximum
         sizes set or that it uses curves enabled in settings
         """
-        pass
+        #Get and check public key from the cert chain
+        publicKey = cert_chain.getEndEntityPublicKey()
+        cert_type = cert_chain.x509List[0].certAlg
+        if cert_type == "ecdsa":
+            curve_name = publicKey.curve_name
+            for name, aliases in CURVE_ALIASES.items():
+                if curve_name in aliases:
+                    curve_name = name
+                    break
+
+            if self.version <= (3, 3) and curve_name not in settings.eccCurves:
+                for result in self._sendError(
+                        AlertDescription.handshake_failure,
+                        "Peer sent certificate with curve we did not "
+                        "advertise support for: {0}".format(curve_name)):
+                    yield result
+            if self.version >= (3, 4):
+                if curve_name not in ('secp256r1', 'secp384r1', 'secp521r1'):
+                    for result in self._sendError(
+                            AlertDescription.illegal_parameter,
+                            "Peer sent certificate with curve not supported "
+                            "in TLS 1.3: {0}".format(curve_name)):
+                        yield result
+                if curve_name == 'secp256r1':
+                    sig_alg_for_curve = 'sha256'
+                elif curve_name == 'secp384r1':
+                    sig_alg_for_curve = 'sha384'
+                else:
+                    assert curve_name == 'secp521r1'
+                    sig_alg_for_curve = 'sha512'
+                if sig_alg_for_curve not in settings.ecdsaSigHashes:
+                    for result in self._sendError(
+                            AlertDescription.illegal_parameter,
+                            "Peer selected certificate with ECDSA curve we "
+                            "did not advertise support for: {0}"
+                            .format(curve_name)):
+                        yield result
+        elif cert_type in ("Ed25519", "Ed448"):
+            if self.version < (3, 3):
+                for result in self._sendError(
+                        AlertDescription.illegal_parameter,
+                        "Peer sent certificate incompatible with negotiated "
+                        "TLS version"):
+                    yield result
+            if cert_type not in settings.more_sig_schemes:
+                for result in self._sendError(
+                        AlertDescription.handshake_failure,
+                        "Peer sent certificate we did not advertise support "
+                        "for: {0}".format(cert_type)):
+                    yield result
+
+        else:
+            # for RSA and DSA keys
+            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
+
+    def _clientGetKeyFromChain(self, certificate, settings, tack_ext=None):
+        #Get and check cert chain from the Certificate message
+        cert_chain = certificate.cert_chain
+        if not cert_chain or cert_chain.getNumCerts() == 0:
+            for result in self._sendError(
+                    AlertDescription.illegal_parameter,
+                    "Other party sent a Certificate message without "\
+                    "certificates"):
+                yield result
+
+        for result in self._check_certchain_with_settings(
+                cert_chain,
+                settings):
+            if result in (0, 1):
+                yield result
+            else: break
+        public_key = result
+
+        # If there's no TLS Extension, look for a TACK cert
+        if tackpyLoaded:
+            if not tack_ext:
+                tack_ext = cert_chain.getTackExt()
+
+            # If there's a TACK (whether via TLS or TACK Cert), check that it
+            # matches the cert chain
+            if tack_ext and tack_ext.tacks:
+                for tack in tack_ext.tacks:
+                    if not cert_chain.checkTack(tack):
+                        for result in self._sendError(
+                                AlertDescription.illegal_parameter,
+                                "Other party's TACK doesn't match their public key"):
+                                yield result

-    def handshakeServer(self, verifierDB=None, certChain=None, privateKey=
-        None, reqCert=False, sessionCache=None, settings=None, checker=None,
-        reqCAs=None, tacks=None, activationFlags=0, nextProtos=None, anon=
-        False, alpn=None, sni=None):
+        yield public_key, cert_chain, tack_ext
+
+
+    #*********************************************************
+    # Server Handshake Functions
+    #*********************************************************
+
+
+    def handshakeServer(self, verifierDB=None,
+                        certChain=None, privateKey=None, reqCert=False,
+                        sessionCache=None, settings=None, checker=None,
+                        reqCAs=None,
+                        tacks=None, activationFlags=0,
+                        nextProtos=None, anon=False, alpn=None, sni=None):
         """Perform a handshake in the role of server.

         This function performs an SSL or TLS handshake.  Depending on
@@ -420,12 +2079,21 @@ class TLSConnection(TLSRecordLayer):
         :raises tlslite.errors.TLSAuthenticationError: If the checker
             doesn't like the other party's authentication credentials.
         """
-        pass
+        for result in self.handshakeServerAsync(verifierDB,
+                certChain, privateKey, reqCert, sessionCache, settings,
+                checker, reqCAs,
+                tacks=tacks, activationFlags=activationFlags,
+                nextProtos=nextProtos, anon=anon, alpn=alpn, sni=sni):
+            pass

-    def handshakeServerAsync(self, verifierDB=None, certChain=None,
-        privateKey=None, reqCert=False, sessionCache=None, settings=None,
-        checker=None, reqCAs=None, tacks=None, activationFlags=0,
-        nextProtos=None, anon=False, alpn=None, sni=None):
+
+    def handshakeServerAsync(self, verifierDB=None,
+                             certChain=None, privateKey=None, reqCert=False,
+                             sessionCache=None, settings=None, checker=None,
+                             reqCAs=None,
+                             tacks=None, activationFlags=0,
+                             nextProtos=None, anon=False, alpn=None, sni=None
+                             ):
         """Start a server handshake operation on the TLS connection.

         This function returns a generator which behaves similarly to
@@ -437,7 +2105,345 @@ class TLSConnection(TLSRecordLayer):
         :rtype: iterable
         :returns: A generator; see above for details.
         """
-        pass
+        handshaker = self._handshakeServerAsyncHelper(\
+            verifierDB=verifierDB, cert_chain=certChain,
+            privateKey=privateKey, reqCert=reqCert,
+            sessionCache=sessionCache, settings=settings,
+            reqCAs=reqCAs,
+            tacks=tacks, activationFlags=activationFlags,
+            nextProtos=nextProtos, anon=anon, alpn=alpn, sni=sni)
+        for result in self._handshakeWrapperAsync(handshaker, checker):
+            yield result
+
+
+    def _handshakeServerAsyncHelper(self, verifierDB,
+                                    cert_chain, privateKey, reqCert,
+                                    sessionCache, settings, reqCAs, tacks,
+                                    activationFlags, nextProtos, anon, alpn,
+                                    sni):
+
+        self._handshakeStart(client=False)
+
+        if not settings:
+            settings = HandshakeSettings()
+        settings = settings.validate()
+
+        if (not verifierDB) and (not cert_chain) and not anon and \
+                not settings.pskConfigs and not settings.virtual_hosts:
+            raise ValueError("Caller passed no authentication credentials")
+        if cert_chain and not privateKey:
+            raise ValueError("Caller passed a cert_chain but no privateKey")
+        if privateKey and not cert_chain:
+            raise ValueError("Caller passed a privateKey but no cert_chain")
+        if reqCAs and not reqCert:
+            raise ValueError("Caller passed reqCAs but not reqCert")
+        if cert_chain and not isinstance(cert_chain, 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.useExperimentalTackExtension:
+                raise ValueError("useExperimentalTackExtension not enabled")
+        if alpn is not None and not alpn:
+            raise ValueError("Empty list of ALPN protocols")
+
+        self.sock.padding_cb = settings.padding_cb
+
+        # OK Start exchanging messages
+        # ******************************
+
+        # Handle ClientHello and resumption
+        for result in self._serverGetClientHello(settings, privateKey,
+                                                 cert_chain,
+                                                 verifierDB, sessionCache,
+                                                 anon, alpn, sni):
+            if result in (0,1): yield result
+            elif result == None:
+                self._handshakeDone(resumed=True)
+                return # Handshake was resumed, we're done
+            else: break
+        (clientHello, version, cipherSuite, sig_scheme, privateKey,
+            cert_chain) = result
+
+        # in TLS 1.3 the handshake is completely different
+        # (extensions go into different messages, format of messages is
+        # different, etc.)
+        if version > (3, 3):
+            for result in self._serverTLS13Handshake(settings, clientHello,
+                                                     cipherSuite,
+                                                     privateKey, cert_chain,
+                                                     version, sig_scheme,
+                                                     alpn, reqCert):
+                if result in (0, 1):
+                    yield result
+                else:
+                    break
+            if result == "finished":
+                self._handshakeDone(resumed=False)
+            return
+
+        #If not a resumption...
+
+        # Create the ServerHello message
+        if sessionCache:
+            sessionID = getRandomBytes(32)
+        else:
+            sessionID = bytearray(0)
+
+        if not clientHello.supports_npn:
+            nextProtos = None
+
+        alpnExt = clientHello.getExtension(ExtensionType.alpn)
+        if alpnExt and alpn:
+            # if there's ALPN, don't do NPN
+            nextProtos = None
+
+        # If not doing a certificate-based suite, discard the TACK
+        if not cipherSuite in CipherSuite.certAllSuites and \
+                not cipherSuite in CipherSuite.ecdheEcdsaSuites:
+            tacks = None
+
+        # Prepare a TACK Extension if requested
+        if clientHello.tack:
+            tackExt = TackExtension.create(tacks, activationFlags)
+        else:
+            tackExt = None
+
+        extensions = []
+        # Prepare other extensions if requested
+        if settings.useEncryptThenMAC and \
+                clientHello.getExtension(ExtensionType.encrypt_then_mac) and \
+                cipherSuite not in CipherSuite.streamSuites and \
+                cipherSuite not in CipherSuite.aeadSuites:
+            extensions.append(TLSExtension().create(ExtensionType.
+                                                    encrypt_then_mac,
+                                                    bytearray(0)))
+            self._recordLayer.encryptThenMAC = True
+
+        if settings.useExtendedMasterSecret:
+            if clientHello.getExtension(ExtensionType.extended_master_secret):
+                extensions.append(TLSExtension().create(ExtensionType.
+                                                        extended_master_secret,
+                                                        bytearray(0)))
+                self.extendedMasterSecret = True
+            elif settings.requireExtendedMasterSecret:
+                for result in self._sendError(
+                        AlertDescription.insufficient_security,
+                        "Failed to negotiate Extended Master Secret"):
+                    yield result
+
+        selectedALPN = None
+        if alpnExt and alpn:
+            for protoName in alpnExt.protocol_names:
+                if protoName in alpn:
+                    selectedALPN = protoName
+                    ext = ALPNExtension().create([protoName])
+                    extensions.append(ext)
+                    break
+            else:
+                for result in self._sendError(
+                        AlertDescription.no_application_protocol,
+                        "No mutually supported application layer protocols"):
+                    yield result
+        # notify client that we understood its renegotiation info extension
+        # or SCSV
+        secureRenego = False
+        renegoExt = clientHello.getExtension(ExtensionType.renegotiation_info)
+        if renegoExt:
+            if renegoExt.renegotiated_connection:
+                for result in self._sendError(
+                        AlertDescription.handshake_failure,
+                        "Non empty renegotiation info extension in "
+                        "initial Client Hello"):
+                    yield result
+            secureRenego = True
+        elif CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV in \
+                clientHello.cipher_suites:
+            secureRenego = True
+        if secureRenego:
+            extensions.append(RenegotiationInfoExtension()
+                              .create(bytearray(0)))
+
+        # tell the client what point formats we support
+        if clientHello.getExtension(ExtensionType.ec_point_formats):
+            # even though the selected cipher may not use ECC, client may want
+            # to send a CA certificate with ECDSA...
+            extensions.append(ECPointFormatsExtension().create(
+                [ECPointFormat.uncompressed]))
+
+        # if client sent Heartbeat extension
+        if clientHello.getExtension(ExtensionType.heartbeat):
+            # and we want to accept it
+            if settings.use_heartbeat_extension:
+                extensions.append(HeartbeatExtension().create(
+                    HeartbeatMode.PEER_ALLOWED_TO_SEND))
+
+        if clientHello.getExtension(ExtensionType.record_size_limit) and \
+                settings.record_size_limit:
+            # in TLS 1.2 and earlier we can select at most 2^14B records
+            extensions.append(RecordSizeLimitExtension().create(
+                min(2**14, settings.record_size_limit)))
+
+        # If the client indicates that it supports resumption using
+        # session_ticket extension, we send a zero len extension to indicate
+        # that we are going to
+        # send a new ticket in a NewSessionTicket message
+        send_session_ticket = False
+        session_ticket = clientHello.getExtension(ExtensionType.session_ticket)
+        enable_ticket = settings.ticket_count > 0 and settings.ticketKeys
+        if session_ticket and len(session_ticket.ticket) == 0 \
+                and enable_ticket:
+            send_session_ticket = True
+            extensions.append(SessionTicketExtension().create(
+                bytearray(0)))
+
+        # don't send empty list of extensions
+        if not extensions:
+            extensions = None
+
+        serverHello = ServerHello()
+        # RFC 8446, section 4.1.3
+        random = getRandomBytes(32)
+        if version == (3, 3) and settings.maxVersion > (3, 3):
+            random[-8:] = TLS_1_2_DOWNGRADE_SENTINEL
+        if version < (3, 3) and settings.maxVersion >= (3, 3):
+            random[-8:] = TLS_1_1_DOWNGRADE_SENTINEL
+        serverHello.create(self.version, random, sessionID,
+                           cipherSuite, CertificateType.x509, tackExt,
+                           nextProtos, extensions=extensions)
+
+        # Perform the SRP key exchange
+        clientCertChain = None
+        if cipherSuite in CipherSuite.srpAllSuites:
+            for result in self._serverSRPKeyExchange(clientHello, serverHello,
+                                                     verifierDB, cipherSuite,
+                                                     privateKey, cert_chain,
+                                                     settings):
+                if result in (0, 1):
+                    yield result
+                else: break
+            premasterSecret, privateKey, cert_chain = result
+
+        # Perform a certificate-based key exchange
+        elif (cipherSuite in CipherSuite.certSuites or
+              cipherSuite in CipherSuite.dheCertSuites or
+              cipherSuite in CipherSuite.dheDsaSuites or
+              cipherSuite in CipherSuite.ecdheCertSuites or
+              cipherSuite in CipherSuite.ecdheEcdsaSuites):
+            try:
+                sig_hash_alg, cert_chain, privateKey = \
+                    self._pickServerKeyExchangeSig(settings,
+                                                   clientHello,
+                                                   cert_chain,
+                                                   privateKey)
+            except TLSHandshakeFailure as alert:
+                for result in self._sendError(
+                        AlertDescription.handshake_failure,
+                        str(alert)):
+                    yield result
+
+            if cipherSuite in CipherSuite.certSuites:
+                keyExchange = RSAKeyExchange(cipherSuite,
+                                             clientHello,
+                                             serverHello,
+                                             privateKey)
+            elif cipherSuite in CipherSuite.dheCertSuites or \
+                    cipherSuite in CipherSuite.dheDsaSuites:
+                dhGroups = self._groupNamesToList(settings)
+                keyExchange = DHE_RSAKeyExchange(cipherSuite,
+                                                 clientHello,
+                                                 serverHello,
+                                                 privateKey,
+                                                 settings.dhParams,
+                                                 dhGroups)
+            elif cipherSuite in CipherSuite.ecdheCertSuites or \
+                    cipherSuite in CipherSuite.ecdheEcdsaSuites:
+                acceptedCurves = self._curveNamesToList(settings)
+                defaultCurve = getattr(GroupName, settings.defaultCurve)
+                keyExchange = ECDHE_RSAKeyExchange(cipherSuite,
+                                                   clientHello,
+                                                   serverHello,
+                                                   privateKey,
+                                                   acceptedCurves,
+                                                   defaultCurve)
+            else:
+                assert(False)
+            for result in self._serverCertKeyExchange(clientHello, serverHello,
+                                        sig_hash_alg, cert_chain, keyExchange,
+                                        reqCert, reqCAs, cipherSuite,
+                                        settings):
+                if result in (0,1): yield result
+                else: break
+            (premasterSecret, clientCertChain) = result
+
+        # Perform anonymous Diffie Hellman key exchange
+        elif (cipherSuite in CipherSuite.anonSuites or
+              cipherSuite in CipherSuite.ecdhAnonSuites):
+            if cipherSuite in CipherSuite.anonSuites:
+                dhGroups = self._groupNamesToList(settings)
+                keyExchange = ADHKeyExchange(cipherSuite, clientHello,
+                                             serverHello, settings.dhParams,
+                                             dhGroups)
+            else:
+                acceptedCurves = self._curveNamesToList(settings)
+                defaultCurve = getattr(GroupName, settings.defaultCurve)
+                keyExchange = AECDHKeyExchange(cipherSuite, clientHello,
+                                               serverHello, acceptedCurves,
+                                               defaultCurve)
+            for result in self._serverAnonKeyExchange(serverHello, keyExchange,
+                                                      cipherSuite):
+                if result in (0,1): yield result
+                else: break
+            premasterSecret = result
+
+        else:
+            assert(False)
+
+        # Create the session object
+        self.session = Session()
+        if cipherSuite in CipherSuite.certAllSuites or \
+                cipherSuite in CipherSuite.ecdheEcdsaSuites:
+            serverCertChain = cert_chain
+        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")
+
+        # We'll update the session master secret once it is calculated
+        # in _serverFinished
+        self.session.create(b"", serverHello.session_id, cipherSuite,
+                            srpUsername, clientCertChain, serverCertChain,
+                            tackExt, (serverHello.tackExt is not None),
+                            serverName,
+                            encryptThenMAC=
+                            self._recordLayer._get_pending_state_etm(),
+                            extendedMasterSecret=self.extendedMasterSecret,
+                            appProto=selectedALPN,
+                            # NOTE it must be a reference, not a copy!
+                            tickets=self.tickets)
+
+        # Exchange Finished messages
+        for result in self._serverFinished(premasterSecret,
+                                clientHello.random, serverHello.random,
+                                cipherSuite, settings.cipherImplementations,
+                                nextProtos, settings, send_session_ticket,
+                                clientCertChain):
+                if result in (0,1): yield result
+                else: break
+
+        #Add the session object to the session cache
+        if sessionCache and sessionID:
+            sessionCache[sessionID] = self.session
+
+        self._handshakeDone(resumed=False)
+        self._serverRandom = serverHello.random
+        self._clientRandom = clientHello.random

     def request_post_handshake_auth(self, settings=None):
         """
@@ -448,56 +2454,2309 @@ class TLSConnection(TLSRecordLayer):
         will only request for the new identity of client, it will not wait for
         it.
         """
-        pass
+        if self.version != (3, 4):
+            raise ValueError("PHA is supported only in TLS 1.3")
+        if self._client:
+            raise ValueError("PHA can only be requested by server")
+        if not self._pha_supported:
+            raise ValueError("PHA not supported by client")
+
+        settings = settings or HandshakeSettings()
+        settings = settings.validate()
+
+        valid_sig_algs = self._sigHashesToList(settings)
+        if not valid_sig_algs:
+            raise ValueError("No signature algorithms enabled in "
+                             "HandshakeSettings")
+
+        context = bytes(getRandomBytes(32))
+
+        certificate_request = CertificateRequest(self.version)
+        certificate_request.create(context=context, sig_algs=valid_sig_algs)
+
+        self._cert_requests[context] = certificate_request
+
+        for result in self._sendMsg(certificate_request):
+            yield result

     @staticmethod
     def _derive_key_iv(nonce, user_key, settings):
         """Derive the IV and key for session ticket encryption."""
-        pass
+        if settings.ticketCipher == "aes128gcm":
+            prf_name = "sha256"
+            prf_size = 32
+        else:
+            prf_name = "sha384"
+            prf_size = 48
+
+        # mix the nonce with the key set by user
+        secret = bytearray(prf_size)
+        secret = secureHMAC(secret, nonce, prf_name)
+        secret = derive_secret(secret, bytearray(b'derived'), None, prf_name)
+        secret = secureHMAC(secret, user_key, prf_name)
+
+        ticket_secret = derive_secret(secret,
+                                      bytearray(b'SessionTicket secret'),
+                                      None, prf_name)
+
+        key = HKDF_expand_label(ticket_secret, b"key", b"", len(user_key),
+                                prf_name)
+        # all AEADs use 12 byte long IV
+        iv = HKDF_expand_label(ticket_secret, b"iv", b"", 12, prf_name)
+        return key, iv

     def _serverSendTickets(self, settings):
         """Send session tickets to client."""
-        pass
+        if not settings.ticketKeys:
+            return
+
+        if self.version < (3, 4):
+            secret = self.session.masterSecret
+        else:
+            secret = self.session.resumptionMasterSecret
+
+        # make sure we send at most one ticket in TLS 1.2 and earlier
+        for _ in range(settings.ticket_count if self.version > (3, 3) else
+                       int(bool(settings.ticket_count))):
+            # prepare the ticket
+            ticket = SessionTicketPayload()
+            ticket.create(secret,
+                          self.version,
+                          self.session.cipherSuite,
+                          int(time.time()),
+                          getRandomBytes(len(settings.ticketKeys[0])),
+                          client_cert_chain=self.session.clientCertChain,
+                          encrypt_then_mac=
+                          self._recordLayer._get_pending_state_etm(),
+                          extended_master_secret=self.extendedMasterSecret,
+                          server_name=self.session.serverName.encode("utf-8")
+                          if self.session.serverName else bytearray())
+
+            # encrypt the ticket
+
+            # generate keys for the encryption
+            nonce = getRandomBytes(32)
+            key, iv = self._derive_key_iv(nonce, settings.ticketKeys[0],
+                                          settings)
+
+            if settings.ticketCipher in ("aes128gcm", "aes256gcm"):
+                cipher = createAESGCM(key,
+                                      settings.cipherImplementations)
+            elif settings.ticketCipher in ("aes128ccm", "aes256ccm"):
+                cipher = createAESCCM(key, settings.cipherImplementations)
+            elif settings.ticketCipher in ("aes128ccm_8", "aes256ccm_8"):
+                cipher = createAESCCM_8(key, settings.cipherImplementations)
+            else:
+                assert settings.ticketCipher == "chacha20-poly1305"
+                cipher = createCHACHA20(key,
+                                        settings.cipherImplementations)
+
+            encrypted_ticket = cipher.seal(iv, ticket.write(), b'')
+
+            # encapsulate the ticket and send to client
+            if self.version < (3, 4):
+                new_ticket = NewSessionTicket1_0()
+                new_ticket.create(settings.ticketLifetime,
+                                  nonce + encrypted_ticket)
+                self.tls_1_0_tickets.append(encrypted_ticket)
+            else:
+                new_ticket = NewSessionTicket()
+                new_ticket.create(settings.ticketLifetime,
+                                  getRandomNumber(1, 8**4),
+                                  ticket.nonce,
+                                  nonce + encrypted_ticket,
+                                  [])
+            self._queue_message(new_ticket)
+
+        # send tickets to client
+        if settings.ticket_count:
+            for result in self._queue_flush():
+                yield result
+
+    def _tryDecrypt(self, settings, identity=None, ticket=None):
+        if not settings.ticketKeys:
+            return None, None
+
+        if self.version < (3, 4):
+            assert ticket
+            nonce, encrypted_ticket = ticket[:32], ticket[32:]
+        else:
+            assert identity
+            if len(identity.identity) < 33:
+                # too small for an encrypted ticket
+                return None, None
+            nonce, encrypted_ticket = identity.identity[:32], identity.identity[32:]
+
+        for user_key in settings.ticketKeys:
+            key, iv = self._derive_key_iv(nonce, user_key, settings)
+            if settings.ticketCipher in ("aes128gcm", "aes256gcm"):
+                cipher = createAESGCM(key, settings.cipherImplementations)
+            elif settings.ticketCipher in ("aes128ccm", "aes256ccm"):
+                cipher = createAESCCM(key, settings.cipherImplementations)
+            elif settings.ticketCipher in ("aes128ccm_8", "aes256ccm_8"):
+                cipher = createAESCCM_8(key, settings.cipherImplementations)
+            else:
+                assert settings.ticketCipher == "chacha20-poly1305"
+                cipher = createCHACHA20(key, settings.cipherImplementations)
+
+            ticket = cipher.open(iv, encrypted_ticket, b'')
+            if not ticket:
+                continue
+
+            parser = Parser(ticket)
+            try:
+                ticket = SessionTicketPayload().parse(parser)
+            except ValueError:
+                continue
+
+            if self.version < (3, 4):
+                return None, ticket
+
+            prf = 'sha384' if ticket.cipher_suite \
+                in CipherSuite.sha384PrfSuites else 'sha256'
+
+            new_sess_ticket = NewSessionTicket()
+            new_sess_ticket.ticket_nonce = ticket.nonce
+            new_sess_ticket.ticket = identity.identity
+
+            psk = HandshakeHelpers.calc_res_binder_psk(identity,
+                                                       ticket.master_secret,
+                                                       [new_sess_ticket])
+
+            return ((identity.identity, psk, prf), ticket)
+
+        # no working keys
+        return None, None

     def _serverTLS13Handshake(self, settings, clientHello, cipherSuite,
-        privateKey, serverCertChain, version, scheme, srv_alpns, reqCert):
+                              privateKey, serverCertChain, version, scheme,
+                              srv_alpns, reqCert):
         """Perform a TLS 1.3 handshake"""
-        pass
+        prf_name, prf_size = self._getPRFParams(cipherSuite)
+
+        secret = bytearray(prf_size)
+
+        share = clientHello.getExtension(ExtensionType.key_share)
+        if share:
+            share_ids = [i.group for i in share.client_shares]
+            for group_name in chain(settings.keyShares, settings.eccCurves,
+                                    settings.dhGroups):
+                selected_group = getattr(GroupName, group_name)
+                if selected_group in share_ids:
+                    cl_key_share = next(i for i in share.client_shares
+                                        if i.group == selected_group)
+                    break
+            else:
+                for result in self._sendError(AlertDescription.internal_error,
+                                              "HRR did not work?!"):
+                    yield result
+
+        psk = None
+        selected_psk = None
+        resumed_client_cert_chain = None
+        psks = clientHello.getExtension(ExtensionType.pre_shared_key)
+        psk_types = clientHello.getExtension(
+            ExtensionType.psk_key_exchange_modes)
+        if psks and (PskKeyExchangeMode.psk_dhe_ke in psk_types.modes or
+                     PskKeyExchangeMode.psk_ke in psk_types.modes) and \
+                (settings.pskConfigs or settings.ticketKeys):
+            for i, ident in enumerate(psks.identities):
+                ticket = None
+                external = True
+                match = [j for j in settings.pskConfigs
+                         if j[0] == ident.identity]
+                if not match:
+                    (match, ticket) = self._tryDecrypt(settings, ident)
+                    external = False
+                    if not match:
+                        continue
+                    match = [match]
+
+                # check if the ticket version matches
+                # but with PSK we don't have a ticket, but we still can have a
+                # binder value, so `match` will be non-null
+                if ticket and self.version != ticket.protocol_version:
+                    continue
+                # check if PSK can be used with selected cipher suite
+                psk_hash = match[0][2] if len(match[0]) > 2 else 'sha256'
+                if psk_hash != prf_name:
+                    continue
+
+                psk = match[0][1]
+                selected_psk = i
+                if ticket:
+                    resumed_client_cert_chain = ticket.client_cert_chain
+                try:
+                    HandshakeHelpers.verify_binder(
+                        clientHello,
+                        self._pre_client_hello_handshake_hash,
+                        selected_psk,
+                        psk,
+                        psk_hash,
+                        external)
+                except TLSIllegalParameterException as e:
+                    for result in self._sendError(
+                            AlertDescription.illegal_parameter,
+                            str(e)):
+                        yield result
+                break
+
+        sh_extensions = []
+
+        # we need to gen key share either when we selected psk_dhe_ke or
+        # regular certificate authenticated key exchange (the default)
+        if (psk and
+                PskKeyExchangeMode.psk_dhe_ke in psk_types.modes and
+                "psk_dhe_ke" in settings.psk_modes) or\
+                (psk is None and privateKey):
+            self.ecdhCurve = selected_group
+            kex = self._getKEX(selected_group, version)
+            key_share = self._genKeyShareEntry(selected_group, version)
+
+            try:
+                shared_sec = kex.calc_shared_key(key_share.private,
+                                                 cl_key_share.key_exchange)
+            except TLSIllegalParameterException as alert:
+                for result in self._sendError(
+                        AlertDescription.illegal_parameter,
+                        str(alert)):
+                    yield result
+
+            sh_extensions.append(ServerKeyShareExtension().create(key_share))
+        elif (psk is not None and
+              PskKeyExchangeMode.psk_ke in psk_types.modes and
+              "psk_ke" in settings.psk_modes):
+            shared_sec = bytearray(prf_size)
+        else:
+            for result in self._sendError(
+                    AlertDescription.handshake_failure,
+                    "Could not find acceptable PSK identity nor certificate"):
+                yield result
+
+        if psk is None:
+            psk = bytearray(prf_size)
+
+        sh_extensions.append(SrvSupportedVersionsExtension().create(version))
+        if selected_psk is not None:
+            sh_extensions.append(SrvPreSharedKeyExtension()
+                                 .create(selected_psk))
+
+        serverHello = ServerHello()
+        # in TLS1.3 the version selected is sent in extension, (3, 3) is
+        # just dummy value to workaround broken middleboxes
+        serverHello.create((3, 3), getRandomBytes(32),
+                           clientHello.session_id,
+                           cipherSuite, extensions=sh_extensions)
+
+        msgs = []
+        msgs.append(serverHello)
+        if not self._ccs_sent and clientHello.session_id:
+            ccs = ChangeCipherSpec().create()
+            msgs.append(ccs)
+        for result in self._sendMsgs(msgs):
+            yield result
+
+        # Early secret
+        secret = secureHMAC(secret, psk, prf_name)
+
+        # Handshake Secret
+        secret = derive_secret(secret, bytearray(b'derived'), None, prf_name)
+        secret = secureHMAC(secret, shared_sec, prf_name)
+
+        sr_handshake_traffic_secret = derive_secret(secret,
+                                                    bytearray(b's hs traffic'),
+                                                    self._handshake_hash,
+                                                    prf_name)
+        cl_handshake_traffic_secret = derive_secret(secret,
+                                                    bytearray(b'c hs traffic'),
+                                                    self._handshake_hash,
+                                                    prf_name)
+        self.version = version
+        self._recordLayer.calcTLS1_3PendingState(
+            cipherSuite,
+            cl_handshake_traffic_secret,
+            sr_handshake_traffic_secret,
+            settings.cipherImplementations)
+
+        self._changeWriteState()
+
+        ee_extensions = []
+
+        if clientHello.getExtension(ExtensionType.record_size_limit) and \
+                settings.record_size_limit:
+            ee_extensions.append(RecordSizeLimitExtension().create(
+                min(2**14+1, settings.record_size_limit)))
+
+        # a bit of a hack to detect if the HRR was sent
+        # as that means that original key share didn't match what we wanted
+        # send the client updated list of shares we support,
+        # preferred ones first
+        if clientHello.getExtension(ExtensionType.cookie):
+            ext = SupportedGroupsExtension()
+            groups = [getattr(GroupName, i) for i in settings.keyShares]
+            groups += [getattr(GroupName, i) for i in settings.eccCurves
+                       if getattr(GroupName, i) not in groups]
+            groups += [getattr(GroupName, i) for i in settings.dhGroups
+                       if getattr(GroupName, i) not in groups]
+            if groups:
+                ext.create(groups)
+                ee_extensions.append(ext)
+
+        alpn_ext = clientHello.getExtension(ExtensionType.alpn)
+        if alpn_ext:
+            # error handling was done when receiving ClientHello
+            matched = [i for i in alpn_ext.protocol_names if i in srv_alpns]
+            if matched:
+                ext = ALPNExtension().create([matched[0]])
+                ee_extensions.append(ext)
+
+        if clientHello.getExtension(ExtensionType.heartbeat):
+            if settings.use_heartbeat_extension:
+                ee_extensions.append(HeartbeatExtension().create(
+                    HeartbeatMode.PEER_ALLOWED_TO_SEND))
+
+        encryptedExtensions = EncryptedExtensions().create(ee_extensions)
+        self._queue_message(encryptedExtensions)
+
+        if selected_psk is None:
+
+            # optionally send the client a certificate request
+            if reqCert:
+
+                # the context SHALL be zero length except in post-handshake
+                ctx = b''
+
+                # Get list of valid Signing Algorithms
+                # DSA is not supported for TLS 1.3
+                cr_settings = settings.validate()
+                cr_settings.dsaSigHashes = []
+                valid_sig_algs = self._sigHashesToList(cr_settings)
+                assert valid_sig_algs
+
+                certificate_request = CertificateRequest(self.version)
+                certificate_request.create(context=ctx, sig_algs=valid_sig_algs)
+                self._queue_message(certificate_request)
+
+            certificate = Certificate(CertificateType.x509, self.version)
+            certificate.create(serverCertChain, bytearray())
+            self._queue_message(certificate)
+
+            certificate_verify = CertificateVerify(self.version)
+
+            signature_scheme = getattr(SignatureScheme, scheme)
+
+            signature_context = \
+                KeyExchange.calcVerifyBytes((3, 4), self._handshake_hash,
+                                            signature_scheme, None, None, None,
+                                            prf_name, b'server')
+
+            if signature_scheme in (SignatureScheme.ed25519,
+                    SignatureScheme.ed448):
+                hashName = "intrinsic"
+                padType = None
+                saltLen = None
+                sig_func = privateKey.hashAndSign
+                ver_func = privateKey.hashAndVerify
+            elif signature_scheme[1] == SignatureAlgorithm.ecdsa:
+                hashName = HashAlgorithm.toRepr(signature_scheme[0])
+                padType = None
+                saltLen = None
+                sig_func = privateKey.sign
+                ver_func = privateKey.verify
+            else:
+                padType = SignatureScheme.getPadding(scheme)
+                hashName = SignatureScheme.getHash(scheme)
+                saltLen = getattr(hashlib, hashName)().digest_size
+                sig_func = privateKey.sign
+                ver_func = privateKey.verify
+
+            signature = sig_func(signature_context,
+                                 padType,
+                                 hashName,
+                                 saltLen)
+            if not ver_func(signature, signature_context,
+                            padType,
+                            hashName,
+                            saltLen):
+                for result in self._sendError(
+                        AlertDescription.internal_error,
+                        "Certificate Verify signature failed"):
+                    yield result
+            certificate_verify.create(signature, signature_scheme)
+
+            self._queue_message(certificate_verify)
+
+        finished_key = HKDF_expand_label(sr_handshake_traffic_secret,
+                                         b"finished", b'', prf_size, prf_name)
+        verify_data = secureHMAC(finished_key,
+                                 self._handshake_hash.digest(prf_name),
+                                 prf_name)
+
+        finished = Finished(self.version, prf_size).create(verify_data)
+
+        self._queue_message(finished)
+        for result in self._queue_flush():
+            yield result
+
+        self._changeReadState()
+
+        # Master secret
+        secret = derive_secret(secret, bytearray(b'derived'), None, prf_name)
+        secret = secureHMAC(secret, bytearray(prf_size), prf_name)
+
+        cl_app_traffic = derive_secret(secret, bytearray(b'c ap traffic'),
+                                       self._handshake_hash, prf_name)
+        sr_app_traffic = derive_secret(secret, bytearray(b's ap traffic'),
+                                       self._handshake_hash, prf_name)
+        self._recordLayer.calcTLS1_3PendingState(serverHello.cipher_suite,
+                                                 cl_app_traffic,
+                                                 sr_app_traffic,
+                                                 settings
+                                                 .cipherImplementations)
+
+        # all the messages sent by the server after the Finished message
+        # MUST be encrypted with ap traffic secret, even if they regard
+        # problems in processing client Certificate, CertificateVerify or
+        # Finished messages
+        self._changeWriteState()
+
+        client_cert_chain = None
+        #Get [Certificate,] (if was requested)
+        if reqCert and selected_psk is None:
+            for result in self._getMsg(ContentType.handshake,
+                                       HandshakeType.certificate,
+                                       CertificateType.x509):
+                if result in (0, 1):
+                    yield result
+                else:
+                    break
+            client_certificate = result
+            assert isinstance(client_certificate, Certificate)
+            client_cert_chain = client_certificate.cert_chain
+
+        #Get and check CertificateVerify, if relevant
+        cli_cert_verify_hh = self._handshake_hash.copy()
+        if client_cert_chain and client_cert_chain.getNumCerts():
+            for result in self._getMsg(ContentType.handshake,
+                                       HandshakeType.certificate_verify):
+                if result in (0, 1):
+                    yield result
+                else: break
+            certificate_verify = result
+            assert isinstance(certificate_verify, CertificateVerify)
+
+            signature_scheme = certificate_verify.signatureAlgorithm
+
+            valid_sig_algs = self._sigHashesToList(settings,
+                                                   certList=client_cert_chain,
+                                                   version=(3, 4))
+            if signature_scheme not in valid_sig_algs:
+                for result in self._sendError(
+                        AlertDescription.illegal_parameter,
+                        "Invalid signature on Certificate Verify"):
+                    yield result
+
+            signature_context = \
+                KeyExchange.calcVerifyBytes((3, 4), cli_cert_verify_hh,
+                                            signature_scheme, None, None, None,
+                                            prf_name, b'client')
+
+            public_key = client_cert_chain.getEndEntityPublicKey()
+
+            if signature_scheme in (SignatureScheme.ed25519,
+                    SignatureScheme.ed448):
+                hash_name = "intrinsic"
+                pad_type = None
+                salt_len = None
+                ver_func = public_key.hashAndVerify
+            elif signature_scheme[1] == SignatureAlgorithm.ecdsa:
+                hash_name = HashAlgorithm.toRepr(signature_scheme[0])
+                pad_type = None
+                salt_len = None
+                ver_func = public_key.verify
+            else:
+                scheme = SignatureScheme.toRepr(signature_scheme)
+                pad_type = SignatureScheme.getPadding(scheme)
+                hash_name = SignatureScheme.getHash(scheme)
+                salt_len = getattr(hashlib, hash_name)().digest_size
+                ver_func = public_key.verify
+
+            if not ver_func(certificate_verify.signature,
+                            signature_context,
+                            pad_type,
+                            hash_name,
+                            salt_len):
+                for result in self._sendError(
+                        AlertDescription.decrypt_error,
+                        "signature verification failed"):
+                    yield result
+
+        # as both exporter and resumption master secrets include handshake
+        # transcript, we need to derive them early
+        exporter_master_secret = derive_secret(secret,
+                                               bytearray(b'exp master'),
+                                               self._handshake_hash,
+                                               prf_name)
+
+        # verify Finished of client
+        cl_finished_key = HKDF_expand_label(cl_handshake_traffic_secret,
+                                            b"finished", b'',
+                                            prf_size, prf_name)
+        cl_verify_data = secureHMAC(cl_finished_key,
+                                    self._handshake_hash.digest(prf_name),
+                                    prf_name)
+        for result in self._getMsg(ContentType.handshake,
+                                   HandshakeType.finished,
+                                   prf_size):
+            if result in (0, 1):
+                yield result
+            else:
+                break
+        cl_finished = result
+        assert isinstance(cl_finished, Finished)
+        if cl_finished.verify_data != cl_verify_data:
+            for result in self._sendError(
+                    AlertDescription.decrypt_error,
+                    "Finished value is not valid"):
+                yield result
+
+        # disallow CCS messages after handshake
+        self._middlebox_compat_mode = False
+
+        resumption_master_secret = derive_secret(secret,
+                                                 bytearray(b'res master'),
+                                                 self._handshake_hash,
+                                                 prf_name)
+
+        self._first_handshake_hashes = self._handshake_hash.copy()
+
+        self.session = Session()
+        self.extendedMasterSecret = True
+        server_name = None
+        if clientHello.server_name:
+            server_name = clientHello.server_name.decode('utf-8')
+
+        app_proto = None
+        alpnExt = encryptedExtensions.getExtension(ExtensionType.alpn)
+        if alpnExt:
+            app_proto = alpnExt.protocol_names[0]
+
+        if not client_cert_chain and resumed_client_cert_chain:
+            client_cert_chain = resumed_client_cert_chain
+
+        self.session.create(secret,
+                            bytearray(b''),  # no session_id
+                            serverHello.cipher_suite,
+                            bytearray(b''),  # no SRP
+                            client_cert_chain,
+                            serverCertChain,
+                            None,
+                            False,
+                            server_name,
+                            encryptThenMAC=False,
+                            extendedMasterSecret=True,
+                            appProto=app_proto,
+                            cl_app_secret=cl_app_traffic,
+                            sr_app_secret=sr_app_traffic,
+                            exporterMasterSecret=exporter_master_secret,
+                            resumptionMasterSecret=resumption_master_secret,
+                            # NOTE it must be a reference, not a copy
+                            tickets=self.tickets)
+
+        # switch to application_traffic_secret for client packets
+        self._changeReadState()
+
+        for result in self._serverSendTickets(settings):
+            yield result
+
+        yield "finished"
+
+    def _ticket_to_session(self, settings, ticket_ext):
+        if not ticket_ext.ticket:
+            return None
+        _, ticket = self._tryDecrypt(settings, ticket=ticket_ext.ticket)
+        if not ticket:
+            return None
+
+        if ticket.creation_time + settings.ticketLifetime < time.time():
+            return None
+
+        session = Session()
+        session.create(ticket.master_secret,
+                       b'',  # no session_id
+                       ticket.cipher_suite,
+                       '',  # not SRP
+                       ticket.client_cert_chain,
+                       None,  # no server cert chain
+                       None,  # no TACK
+                       False,  # no TACK
+                       serverName=ticket.server_name.decode("utf-8") if
+                       ticket.server_name else "",
+                       encryptThenMAC=ticket.encrypt_then_mac,
+                       extendedMasterSecret=ticket.extended_master_secret)
+        return session
+
+    def _serverGetClientHello(self, settings, private_key, cert_chain,
+                              verifierDB,
+                              sessionCache, anon, alpn, sni):
+        # Tentatively set version to most-desirable version, so if an error
+        # occurs parsing the ClientHello, this will be the version we'll use
+        # for the error alert
+        # If TLS 1.3 is enabled, use the "compatible" TLS 1.2 version
+        self.version = min(settings.maxVersion, (3, 3))
+
+        self._pre_client_hello_handshake_hash = self._handshake_hash.copy()
+        #Get ClientHello
+        for result in self._getMsg(ContentType.handshake,
+                                   HandshakeType.client_hello):
+            if result in (0,1): yield result
+            else: break
+        clientHello = result
+
+        # check if the ClientHello and its extensions are well-formed
+
+        #If client's version is too low, reject it
+        real_version = clientHello.client_version
+        if real_version >= (3, 3):
+            ext = clientHello.getExtension(ExtensionType.supported_versions)
+            if ext:
+                for v in ext.versions:
+                    if v in KNOWN_VERSIONS and v > real_version:
+                        real_version = v
+        if real_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
+
+        # there MUST be at least one value in both of those
+        if not clientHello.cipher_suites or \
+                not clientHello.compression_methods:
+            for result in self._sendError(
+                    AlertDescription.decode_error,
+                    "Malformed Client Hello message"):
+                yield result
+
+        # client hello MUST advertise uncompressed method
+        if 0 not in clientHello.compression_methods:
+            for result in self._sendError(
+                    AlertDescription.illegal_parameter,
+                    "Client Hello missing uncompressed method"):
+                yield result
+
+        # the list of signatures methods is defined as <2..2^16-2>, which
+        # means it can't be empty, but it's only applicable to TLSv1.2 protocol
+        ext = clientHello.getExtension(ExtensionType.signature_algorithms)
+        if clientHello.client_version >= (3, 3) and ext and not ext.sigalgs:
+            for result in self._sendError(
+                    AlertDescription.decode_error,
+                    "Malformed signature_algorithms extension"):
+                yield result
+
+        # Sanity check the ALPN extension
+        alpnExt = clientHello.getExtension(ExtensionType.alpn)
+        if alpnExt:
+            if not alpnExt.protocol_names:
+                for result in self._sendError(
+                        AlertDescription.decode_error,
+                        "Client sent empty list of ALPN names"):
+                    yield result
+            for protocolName in alpnExt.protocol_names:
+                if not protocolName:
+                    for result in self._sendError(
+                            AlertDescription.decode_error,
+                            "Client sent empty name in ALPN extension"):
+                        yield result
+
+        # Sanity check the SNI extension
+        sniExt = clientHello.getExtension(ExtensionType.server_name)
+        # check if extension is well formed
+        if sniExt and (not sniExt.extData or not sniExt.serverNames):
+            for result in self._sendError(
+                    AlertDescription.decode_error,
+                    "Recevived SNI extension is malformed"):
+                yield result
+        if sniExt and sniExt.hostNames:
+            # RFC 6066 limitation
+            if len(sniExt.hostNames) > 1:
+                for result in self._sendError(
+                        AlertDescription.illegal_parameter,
+                        "Client sent multiple host names in SNI extension"):
+                    yield result
+            if not sniExt.hostNames[0]:
+                for result in self._sendError(
+                        AlertDescription.decode_error,
+                        "Received SNI extension is malformed"):
+                    yield result
+            try:
+                name = sniExt.hostNames[0].decode('ascii', 'strict')
+            except UnicodeDecodeError:
+                for result in self._sendError(
+                        AlertDescription.illegal_parameter,
+                        "Host name in SNI is not valid ASCII"):
+                    yield result
+            if not is_valid_hostname(name):
+                for result in self._sendError(
+                        AlertDescription.illegal_parameter,
+                        "Host name in SNI is not valid DNS name"):
+                    yield result
+
+        # sanity check the EMS extension
+        emsExt = clientHello.getExtension(ExtensionType.extended_master_secret)
+        if emsExt and emsExt.extData:
+            for result in self._sendError(
+                    AlertDescription.decode_error,
+                    "Non empty payload of the Extended "
+                    "Master Secret extension"):
+                yield result
+
+        # sanity check the TLS 1.3 extensions
+        ver_ext = clientHello.getExtension(ExtensionType.supported_versions)
+        if ver_ext and (3, 4) in ver_ext.versions:
+            psk = clientHello.getExtension(ExtensionType.pre_shared_key)
+            psk_modes = clientHello.getExtension(
+                ExtensionType.psk_key_exchange_modes)
+            key_share = clientHello.getExtension(ExtensionType.key_share)
+            sup_groups = clientHello.getExtension(
+                ExtensionType.supported_groups)
+
+            pha = clientHello.getExtension(ExtensionType.post_handshake_auth)
+            if pha:
+                if pha.extData:
+                    for result in self._sendError(
+                            AlertDescription.decode_error,
+                            "Invalid encoding of post_handshake_auth extension"
+                            ):
+                        yield result
+                self._pha_supported = True
+
+            key_exchange = None
+
+            if psk_modes:
+                if not psk_modes.modes:
+                    for result in self._sendError(
+                            AlertDescription.decode_error,
+                            "Empty psk_key_exchange_modes extension"):
+                        yield result
+            # psk_ke
+            if psk:
+                if not psk.identities:
+                    for result in self._sendError(
+                            AlertDescription.decode_error,
+                            "No identities in PSK extension"):
+                        yield result
+                if not psk.binders:
+                    for result in self._sendError(
+                            AlertDescription.decode_error,
+                            "No binders in PSK extension"):
+                        yield result
+                if len(psk.identities) != len(psk.binders):
+                    for result in self._sendError(
+                            AlertDescription.illegal_parameter,
+                            "Number of identities does not match number of "
+                            "binders in PSK extension"):
+                        yield result
+                if any(not i.identity for i in psk.identities):
+                    for result in self._sendError(
+                            AlertDescription.decoder_error,
+                            "Empty identity in PSK extension"):
+                        yield result
+                if any(not i for i in psk.binders):
+                    for result in self._sendError(
+                            AlertDescription.decoder_error,
+                            "Empty binder in PSK extension"):
+                        yield result
+                if psk is not clientHello.extensions[-1]:
+                    for result in self._sendError(
+                            AlertDescription.illegal_parameter,
+                            "PSK extension not last in client hello"):
+                        yield result
+                if not psk_modes:
+                    for result in self._sendError(
+                            AlertDescription.missing_extension,
+                            "PSK extension without psk_key_exchange_modes "
+                            "extension"):
+                        yield result
+
+                if PskKeyExchangeMode.psk_dhe_ke not in psk_modes.modes:
+                    key_exchange = "psk_ke"
+            # cert
+            if not key_exchange:
+                if not sup_groups:
+                    for result in self._sendError(
+                            AlertDescription.missing_extension,
+                            "Missing supported_groups extension"):
+                        yield result
+                if not key_share:
+                    for result in self._sendError(
+                            AlertDescription.missing_extension,
+                            "Missing key_share extension"):
+                        yield result
+
+                if not sup_groups.groups:
+                    for result in self._sendError(
+                            AlertDescription.decode_error,
+                            "Empty supported_groups extension"):
+                        yield result
+                if key_share.client_shares is None:
+                    for result in self._sendError(
+                            AlertDescription.decode_error,
+                            "Empty key_share extension"):
+                        yield result
+
+                # check supported_groups
+                if TLS_1_3_FORBIDDEN_GROUPS.intersection(sup_groups.groups):
+                    for result in self._sendError(
+                            AlertDescription.illegal_parameter,
+                            "Client advertised in TLS 1.3 Client Hello a key "
+                            "exchange group forbidden in TLS 1.3"):
+                        yield result
+
+                # Check key_share
+                mismatch = next((i for i in key_share.client_shares
+                                 if i.group not in sup_groups.groups), None)
+                if mismatch:
+                    for result in self._sendError(
+                            AlertDescription.illegal_parameter,
+                            "Client sent key share for "
+                            "group it did not advertise "
+                            "support for: {0}"
+                            .format(GroupName.toStr(mismatch))):
+                        yield result
+
+                key_share_ids = [i.group for i in key_share.client_shares]
+                if len(set(key_share_ids)) != len(key_share_ids):
+                    for result in self._sendError(
+                            AlertDescription.illegal_parameter,
+                            "Client sent multiple key shares for the same "
+                            "group"):
+                        yield result
+
+                group_ids = sup_groups.groups
+                diff = set(group_ids) - set(key_share_ids)
+                if key_share_ids != [i for i in group_ids if i not in diff]:
+                    for result in self._sendError(
+                            AlertDescription.illegal_parameter,
+                            "Client sent key shares in different order than "
+                            "the advertised groups."):
+                        yield result
+
+                sig_algs = clientHello.getExtension(
+                    ExtensionType.signature_algorithms)
+                if (not psk_modes or not psk) and sig_algs:
+                    key_exchange = "cert"
+
+            # psk_dhe_ke
+            if not key_exchange and psk:
+                key_exchange = "psk_dhe_ke"
+
+            if not key_exchange:
+                for result in self._sendError(
+                        AlertDescription.missing_extension,
+                        "Missing extension"):
+                    yield result
+
+            early_data = clientHello.getExtension(ExtensionType.early_data)
+            if early_data:
+                if early_data.extData:
+                    for result in self._sendError(
+                            AlertDescription.decode_error,
+                            "malformed early_data extension"):
+                        yield result
+                if not psk:
+                    for result in self._sendError(
+                            AlertDescription.illegal_parameter,
+                            "early_data without PSK extension"):
+                        yield result
+                # if early data comes from version we don't support, client
+                # MUST (section D.3 draft 28) abort the connection so we
+                # enable early data tolerance only when versions match
+                self._recordLayer.max_early_data = settings.max_early_data
+                self._recordLayer.early_data_ok = True
+
+        # negotiate the protocol version for the connection
+        high_ver = None
+        if ver_ext:
+            high_ver = getFirstMatching(settings.versions,
+                                        ver_ext.versions)
+            if not high_ver:
+                for result in self._sendError(
+                        AlertDescription.protocol_version,
+                        "supported_versions did not include version we "
+                        "support"):
+                    yield result
+        if high_ver:
+            # when we selected TLS 1.3, we cannot set the record layer to
+            # it as well as that also switches it to a mode where the
+            # content type is encrypted
+            # use the backwards compatible TLS 1.2 version instead
+            self.version = min((3, 3), high_ver)
+            version = high_ver
+        elif clientHello.client_version > settings.maxVersion:
+            # in TLS 1.3 the version is negotiatied with extension,
+            # but the settings use the (3, 4) as the max version
+            self.version = min(settings.maxVersion, (3, 3))
+            version = self.version
+        else:
+            #Set the version to the client's version
+            self.version = min(clientHello.client_version, (3, 3))
+            version = self.version
+
+        #Detect if the client performed an inappropriate fallback.
+        if version < settings.maxVersion and \
+                CipherSuite.TLS_FALLBACK_SCSV in clientHello.cipher_suites:
+            for result in self._sendError(
+                    AlertDescription.inappropriate_fallback):
+                yield result
+
+        # TODO when TLS 1.3 is final, check the client hello random for
+        # downgrade too
+
+        # start negotiating the parameters of the connection
+
+        sni_ext = clientHello.getExtension(ExtensionType.server_name)
+        if sni_ext:
+            name = sni_ext.hostNames[0].decode('ascii', 'strict')
+            # warn the client if the name didn't match the expected value
+            if sni and sni != name:
+                alert = Alert().create(AlertDescription.unrecognized_name,
+                                       AlertLevel.warning)
+                for result in self._sendMsg(alert):
+                    yield result
+
+        #Check if there's intersection between supported curves by client and
+        #server
+        clientGroups = clientHello.getExtension(ExtensionType.supported_groups)
+        # in case the client didn't advertise any curves, we can pick any so
+        # enable ECDHE
+        ecGroupIntersect = True
+        # if there is no extension, then enable DHE
+        ffGroupIntersect = True
+        if clientGroups is not None:
+            clientGroups = clientGroups.groups
+            if not clientGroups:
+                for result in self._sendError(
+                        AlertDescription.decode_error,
+                        "Received malformed supported_groups extension"):
+                    yield result
+            serverGroups = self._curveNamesToList(settings)
+            ecGroupIntersect = getFirstMatching(clientGroups, serverGroups)
+            # RFC 7919 groups
+            serverGroups = self._groupNamesToList(settings)
+            ffGroupIntersect = getFirstMatching(clientGroups, serverGroups)
+            # if there is no overlap, but there are no FFDHE groups listed,
+            # allow DHE, prohibit otherwise
+            if not ffGroupIntersect:
+                if clientGroups and \
+                        any(i for i in clientGroups if i in range(256, 512)):
+                    ffGroupIntersect = False
+                else:
+                    ffGroupIntersect = True
+
+        # Check and save clients heartbeat extension mode
+        heartbeat_ext = clientHello.getExtension(ExtensionType.heartbeat)
+        if heartbeat_ext:
+            if heartbeat_ext.mode == HeartbeatMode.PEER_ALLOWED_TO_SEND:
+                if settings.heartbeat_response_callback:
+                    self.heartbeat_can_send = True
+                    self.heartbeat_response_callback = settings.\
+                        heartbeat_response_callback
+            elif heartbeat_ext.mode == HeartbeatMode.PEER_NOT_ALLOWED_TO_SEND:
+                self.heartbeat_can_send = False
+            else:
+                for result in self._sendError(
+                        AlertDescription.illegal_parameter,
+                        "Received invalid value in Heartbeat extension"):
+                    yield result
+            self.heartbeat_supported = True
+            self.heartbeat_can_receive = True
+
+        size_limit_ext = clientHello.getExtension(
+            ExtensionType.record_size_limit)
+        if size_limit_ext:
+            if size_limit_ext.record_size_limit is None:
+                for result in self._sendError(
+                        AlertDescription.decode_error,
+                        "Malformed record_size_limit extension"):
+                    yield result
+            if not 64 <= size_limit_ext.record_size_limit:
+                for result in self._sendError(
+                        AlertDescription.illegal_parameter,
+                        "Invalid value in record_size_limit extension"):
+                    yield result
+            if settings.record_size_limit:
+                # in TLS 1.3 handshake is encrypted so we need to switch
+                # to sending smaller messages right away
+                if version >= (3, 4):
+                    # the client can send bigger values because it may
+                    # know protocol versions or extensions we don't know about
+                    # (but we need to still clamp it to protocol limit)
+                    self._send_record_limit = min(
+                        2**14, size_limit_ext.record_size_limit - 1)
+                    # the record layer excludes content type, extension doesn't
+                    # thus the "-1)
+                    self._recv_record_limit = min(2**14,
+                        settings.record_size_limit - 1)
+                else:
+                    # but in TLS 1.2 and earlier we need to postpone it till
+                    # handling of Finished
+                    self._peer_record_size_limit = min(
+                        2**14, size_limit_ext.record_size_limit)
+
+        #Now that the version is known, limit to only the ciphers available to
+        #that version and client capabilities.
+        cipherSuites = []
+        if verifierDB:
+            if cert_chain:
+                cipherSuites += \
+                    CipherSuite.getSrpCertSuites(settings, version)
+            cipherSuites += CipherSuite.getSrpSuites(settings, version)
+        elif cert_chain:
+            if ecGroupIntersect or ffGroupIntersect:
+                cipherSuites += CipherSuite.getTLS13Suites(settings,
+                                                           version)
+            if ecGroupIntersect:
+                cipherSuites += CipherSuite.getEcdsaSuites(settings, version)
+                cipherSuites += CipherSuite.getEcdheCertSuites(settings,
+                                                               version)
+            if ffGroupIntersect:
+                cipherSuites += CipherSuite.getDheCertSuites(settings,
+                                                             version)
+                cipherSuites += CipherSuite.getDheDsaSuites(settings,
+                                                            version)
+            cipherSuites += CipherSuite.getCertSuites(settings, version)
+        elif anon:
+            cipherSuites += CipherSuite.getAnonSuites(settings, version)
+            cipherSuites += CipherSuite.getEcdhAnonSuites(settings,
+                                                          version)
+        elif settings.pskConfigs:
+            cipherSuites += CipherSuite.getTLS13Suites(settings,
+                                                       version)
+        else:
+            assert False
+        cipherSuites = CipherSuite.filterForVersion(cipherSuites,
+                                                    minVersion=version,
+                                                    maxVersion=version)
+
+        ticket_ext = clientHello.getExtension(ExtensionType.session_ticket)
+
+        # If resumption was requested and we have a session cache...
+        if (clientHello.session_id and sessionCache) or (
+                ticket_ext and ticket_ext.ticket):
+            session = None
+
+            # Check if the session there is good enough and consistent with
+            # new Client Hello
+            try:
+                if ticket_ext:
+                    session = self._ticket_to_session(settings, ticket_ext)
+                    # client MAY send a random session_id to easily tell
+                    # if the session is resumed, for that server has to
+                    # echo the session_ID back
+                    if session and clientHello.session_id:
+                        session.sessionID = clientHello.session_id
+                if not session and \
+                        (not ticket_ext or ticket_ext and not ticket_ext.ticket)\
+                        and sessionCache and clientHello.session_id:
+                    # Session ID resumption is allowed only if the client
+                    # didn't send a ticket
+                    session = sessionCache[clientHello.session_id]
+                if not session:
+                    raise KeyError()
+                if not session.resumable:
+                    raise AssertionError()
+                # Check if we are willing to use that old cipher still
+                if session.cipherSuite not in cipherSuites:
+                    session = None
+                    raise KeyError()
+                # Check for consistency with ClientHello
+                # see RFC 5246 section 7.4.1.2, description of
+                # cipher_suites
+                if session.cipherSuite not in clientHello.cipher_suites:
+                    for result in self._sendError(
+                            AlertDescription.illegal_parameter):
+                        yield result
+                if clientHello.srp_username:
+                    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
+                if session.encryptThenMAC and \
+                        not clientHello.getExtension(
+                                ExtensionType.encrypt_then_mac):
+                    for result in self._sendError(
+                            AlertDescription.illegal_parameter):
+                        yield result
+                # if old session used EMS, new connection MUST use EMS
+                if session.extendedMasterSecret and \
+                        not clientHello.getExtension(
+                                ExtensionType.extended_master_secret):
+                    # RFC 7627, section 5.2 explicitly requires
+                    # handshake_failure
+                    for result in self._sendError(
+                            AlertDescription.handshake_failure):
+                        yield result
+                # if old session didn't use EMS but new connection
+                # advertises EMS, create a new session
+                elif not session.extendedMasterSecret and \
+                        clientHello.getExtension(
+                                ExtensionType.extended_master_secret):
+                    session = None
+            except KeyError:
+                pass
+
+            #If a session is found..
+            if session:
+                #Send ServerHello
+                extensions = []
+                if session.encryptThenMAC:
+                    self._recordLayer.encryptThenMAC = True
+                    etm = TLSExtension().create(ExtensionType.encrypt_then_mac,
+                                                bytearray(0))
+                    extensions.append(etm)
+                if session.extendedMasterSecret:
+                    ems = TLSExtension().create(ExtensionType.
+                                                extended_master_secret,
+                                                bytearray(0))
+                    extensions.append(ems)
+                secureRenego = False
+                renegoExt = clientHello.\
+                    getExtension(ExtensionType.renegotiation_info)
+                if renegoExt:
+                    if renegoExt.renegotiated_connection:
+                        for result in self._sendError(
+                                AlertDescription.handshake_failure):
+                            yield result
+                    secureRenego = True
+                elif CipherSuite.TLS_EMPTY_RENEGOTIATION_INFO_SCSV in \
+                        clientHello.cipher_suites:
+                    secureRenego = True
+                if secureRenego:
+                    extensions.append(RenegotiationInfoExtension()
+                                      .create(bytearray(0)))
+                selectedALPN = None
+                if alpn:
+                    alpnExt = clientHello.getExtension(ExtensionType.alpn)
+                    if alpnExt:
+                        for protocolName in alpnExt.protocol_names:
+                            if protocolName in alpn:
+                                ext = ALPNExtension().create([protocolName])
+                                extensions.append(ext)
+                                selectedALPN = protocolName
+                                break
+                        else:
+                            for result in self._sendError(
+                                    AlertDescription.no_application_protocol,
+                                    "No commonly supported application layer"
+                                    "protocol supported"):
+                                yield result
+
+                heartbeat_ext = clientHello.getExtension(
+                    ExtensionType.heartbeat)
+                if heartbeat_ext:
+                    if heartbeat_ext.mode == HeartbeatMode.PEER_ALLOWED_TO_SEND:
+                        self.heartbeat_can_send = True
+                    elif heartbeat_ext.mode == \
+                            HeartbeatMode.PEER_NOT_ALLOWED_TO_SEND:
+                        self.heartbeat_can_send = False
+                    else:
+                        for result in self._sendError(
+                                AlertDescription.illegal_parameter,
+                                "Client sent invalid Heartbeat extension"):
+                            yield result
+                    heartbeat = HeartbeatExtension().create(
+                        HeartbeatMode.PEER_ALLOWED_TO_SEND)
+                    self.heartbeat_can_receive = True
+                    self.heartbeat_supported = True
+                    extensions.append(heartbeat)
+                record_limit = clientHello.getExtension(
+                    ExtensionType.record_size_limit)
+                if record_limit and settings.record_size_limit:
+                    extensions.append(RecordSizeLimitExtension().create(
+                        min(2**14, settings.record_size_limit)))
+
+                # don't send empty extensions
+                if not extensions:
+                    extensions = None
+                serverHello = ServerHello()
+                serverHello.create(version, getRandomBytes(32),
+                                   session.sessionID, session.cipherSuite,
+                                   CertificateType.x509, None, None,
+                                   extensions=extensions)
+                for result in self._sendMsg(serverHello):
+                    yield result
+
+                #Calculate pending connection states
+                self._calcPendingStates(session.cipherSuite,
+                                        session.masterSecret,
+                                        clientHello.random,
+                                        serverHello.random,
+                                        settings.cipherImplementations)
+
+                #Exchange ChangeCipherSpec and Finished messages
+                for result in self._sendFinished(session.masterSecret,
+                                                 session.cipherSuite,
+                                                 settings=settings):
+                    yield result
+                for result in self._getFinished(session.masterSecret,
+                                                session.cipherSuite):
+                    yield result
+
+                #Set the session
+                self.session = session
+                self._clientRandom = clientHello.random
+                self._serverRandom = serverHello.random
+                self.session.appProto = selectedALPN
+                yield None # Handshake done!
+
+        #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.
+        #
+        #Given the current ciphersuite ordering, this means we prefer SRP
+        #over non-SRP.
+
+        try:
+            cipherSuite, sig_scheme, cert_chain, private_key = \
+                    self._server_select_certificate(settings, clientHello,
+                                                    cipherSuites, cert_chain,
+                                                    private_key, version)
+        except TLSHandshakeFailure as err:
+            for result in self._sendError(
+                    AlertDescription.handshake_failure,
+                    str(err)):
+                yield result
+        except TLSInsufficientSecurity as err:
+            for result in self._sendError(
+                    AlertDescription.insufficient_security,
+                    str(err)):
+                yield result
+        except TLSIllegalParameterException as err:
+            for result in self._sendError(
+                    AlertDescription.illegal_parameter,
+                    str(err)):
+                yield result
+
+        #If an RSA suite is chosen, check for certificate type intersection
+        if (cipherSuite in CipherSuite.certAllSuites or
+            cipherSuite in CipherSuite.ecdheEcdsaSuites) \
+                    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
+
+        # when we have selected TLS 1.3, check if we don't have to ask for
+        # a new client hello
+        if version > (3, 3):
+            self.version = version
+            hrr_ext = []
+
+            # check if we have good key share
+            share = clientHello.getExtension(ExtensionType.key_share)
+            if share:
+                share_ids = [i.group for i in share.client_shares]
+                acceptable_ids = [getattr(GroupName, i) for i in
+                                  chain(settings.keyShares, settings.eccCurves,
+                                        settings.dhGroups)]
+                for selected_group in acceptable_ids:
+                    if selected_group in share_ids:
+                        cl_key_share = next(i for i in share.client_shares
+                                            if i.group == selected_group)
+                        break
+                else:
+                    # if no key share is acceptable, pick one of the supported
+                    # groups that we support
+                    supported = clientHello.getExtension(ExtensionType
+                                                         .supported_groups)
+                    supported_ids = supported.groups
+                    selected_group = next((i for i in acceptable_ids
+                                           if i in supported_ids), None)
+                    if not selected_group:
+                        for result in self._sendError(AlertDescription
+                                                      .handshake_failure,
+                                                      "No acceptable group "
+                                                      "advertised by client"):
+                            yield result
+                    hrr_ks = HRRKeyShareExtension().create(selected_group)
+                    hrr_ext.append(hrr_ks)
+
+            if hrr_ext:
+                cookie = TLSExtension(extType=ExtensionType.cookie)
+                cookie = cookie.create(bytearray(b'\x00\x20') +
+                                       getRandomBytes(32))
+                hrr_ext.append(cookie)
+
+            if hrr_ext:
+                clientHello1 = clientHello
+
+                # create synthetic handshake hash of the first Client Hello
+                prf_name, prf_size = self._getPRFParams(cipherSuite)
+
+                client_hello_hash = self._handshake_hash.digest(prf_name)
+                self._handshake_hash = HandshakeHashes()
+                writer = Writer()
+                writer.add(HandshakeType.message_hash, 1)
+                writer.addVarSeq(client_hello_hash, 1, 3)
+                self._handshake_hash.update(writer.bytes)
+
+                # send the version that was really selected
+                vers = SrvSupportedVersionsExtension().create(version)
+                hrr_ext.append(vers)
+
+                # send the HRR
+                hrr = ServerHello()
+                # version is hardcoded in TLS 1.3, and real version
+                # is sent as extension
+                hrr.create((3, 3), TLS_1_3_HRR, clientHello.session_id,
+                           cipherSuite, extensions=hrr_ext)
+
+                msgs = [hrr]
+                if clientHello.session_id:
+                    ccs = ChangeCipherSpec().create()
+                    msgs.append(ccs)
+                for result in self._sendMsgs(msgs):
+                    yield result
+                self._ccs_sent = True
+
+                # copy for calculating PSK binders
+                self._pre_client_hello_handshake_hash = \
+                    self._handshake_hash.copy()
+
+                for result in self._getMsg(ContentType.handshake,
+                                           HandshakeType.client_hello):
+                    if result in (0, 1):
+                        yield result
+                    else:
+                        break
+                clientHello = result
+
+                # verify that the new key share is present
+                ext = clientHello.getExtension(ExtensionType.key_share)
+                if not ext:
+                    for result in self._sendError(AlertDescription
+                                                  .missing_extension,
+                                                  "Key share missing in "
+                                                  "Client Hello"):
+                        yield result
+
+                # here we're assuming that the HRR was sent because of
+                # missing key share, that may not always be the case
+                if len(ext.client_shares) != 1:
+                    for result in self._sendError(AlertDescription
+                                                  .illegal_parameter,
+                                                  "Multiple key shares in "
+                                                  "second Client Hello"):
+                        yield result
+                if ext.client_shares[0].group != selected_group:
+                    for result in self._sendError(AlertDescription
+                                                  .illegal_parameter,
+                                                  "Client key share does not "
+                                                  "match Hello Retry Request"):
+                        yield result
+
+                # here we're assuming no 0-RTT and possibly no session
+                # resumption
+                # verify that new client hello is like the old client hello
+                # with the exception of changes requested in HRR
+                old_ext = clientHello1.getExtension(ExtensionType.key_share)
+                new_ext = clientHello.getExtension(ExtensionType.key_share)
+                old_ext.client_shares = new_ext.client_shares
+
+                # TODO when 0-RTT supported, remove early_data from old hello
+
+                if cookie:
+                    # insert the extension at the same place in the old hello
+                    # as it is in the new hello so that later binary compare
+                    # works
+                    for i, ext in enumerate(clientHello.extensions):
+                        if ext.extType == ExtensionType.cookie:
+                            if ext.extData != cookie.extData:
+                                eType = AlertDescription.illegal_parameter
+                                eText = "Malformed cookie extension"
+                                for result in self._sendError(eType, eText):
+                                    yield result
+                            clientHello1.extensions.insert(i, ext)
+                            break
+                    else:
+                        for result in self._sendError(AlertDescription
+                                                      .missing_extension,
+                                                      "Second client hello "
+                                                      "does not contain "
+                                                      "cookie extension"):
+                            yield result
+
+                # also padding extension may change
+                old_ext = clientHello1.getExtension(
+                    ExtensionType.client_hello_padding)
+                new_ext = clientHello.getExtension(
+                    ExtensionType.client_hello_padding)
+                if old_ext != new_ext:
+                    if old_ext is None and new_ext:
+                        for i, ext in enumerate(clientHello.extensions):
+                            if ext.extType == \
+                                    ExtensionType.client_hello_padding:
+                                clientHello1.extensions.insert(i, ext)
+                                break
+                    elif old_ext and new_ext is None:
+                        # extension was removed, so remove it here too
+                        clientHello1.extensions[:] = \
+                            (i for i in clientHello1.extensions
+                             if i.extType !=
+                             ExtensionType.client_hello_padding)
+                    else:
+                        old_ext.paddingData = new_ext.paddingData
+
+                # PSKs not compatible with cipher suite MAY
+                # be removed, but must have updated obfuscated ticket age
+                # and binders
+                old_ext = clientHello1.getExtension(
+                    ExtensionType.pre_shared_key)
+                new_ext = clientHello.getExtension(
+                    ExtensionType.pre_shared_key)
+                if new_ext and old_ext:
+                    clientHello1.extensions[-1] = new_ext
+                    if clientHello.extensions[-1] is not new_ext:
+                        for result in self._sendError(
+                                AlertDescription.illegal_parameter,
+                                "PSK extension not last in client hello"):
+                            yield result
+                # early_data extension MUST be dropped
+                old_ext = clientHello1.getExtension(ExtensionType.early_data)
+                if old_ext:
+                    clientHello1.extensions.remove(old_ext)
+
+                if clientHello1 != clientHello:
+                    for result in self._sendError(AlertDescription
+                                                  .illegal_parameter,
+                                                  "Old Client Hello does not "
+                                                  "match the updated Client "
+                                                  "Hello"):
+                        yield result
+
+        # If resumption was not requested, or
+        # we have no session cache, or
+        # the client's session_id was not found in cache:
+#pylint: disable = undefined-loop-variable
+        yield (clientHello, version, cipherSuite, sig_scheme, private_key,
+               cert_chain)
+#pylint: enable = undefined-loop-variable

     def _serverSRPKeyExchange(self, clientHello, serverHello, verifierDB,
-        cipherSuite, privateKey, serverCertChain, settings):
+                              cipherSuite, privateKey, serverCertChain,
+                              settings):
         """Perform the server side of SRP key exchange"""
-        pass
+        try:
+            sigHash, serverCertChain, privateKey = \
+                self._pickServerKeyExchangeSig(settings, clientHello,
+                                               serverCertChain,
+                                               privateKey)
+        except TLSHandshakeFailure as alert:
+            for result in self._sendError(
+                    AlertDescription.handshake_failure,
+                    str(alert)):
+                yield result
+
+        keyExchange = SRPKeyExchange(cipherSuite,
+                                     clientHello,
+                                     serverHello,
+                                     privateKey,
+                                     verifierDB)
+
+        #Create ServerKeyExchange, signing it if necessary
+        try:
+            serverKeyExchange = keyExchange.makeServerKeyExchange(sigHash)
+        except TLSUnknownPSKIdentity:
+            for result in self._sendError(
+                    AlertDescription.unknown_psk_identity):
+                yield result
+        except TLSInsufficientSecurity:
+            for result in self._sendError(
+                    AlertDescription.insufficient_security):
+                yield result
+
+        #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 and check ClientKeyExchange
+        for result in self._getMsg(ContentType.handshake,
+                                  HandshakeType.client_key_exchange,
+                                  cipherSuite):
+            if result in (0,1): yield result
+            else: break
+        try:
+            premasterSecret = keyExchange.processClientKeyExchange(result)
+        except TLSIllegalParameterException:
+            for result in self._sendError(AlertDescription.illegal_parameter,
+                                          "Suspicious A value"):
+                yield result
+        except TLSDecodeError as alert:
+            for result in self._sendError(AlertDescription.decode_error,
+                                          str(alert)):
+                yield result
+
+        yield premasterSecret, privateKey, serverCertChain

     def _server_select_certificate(self, settings, client_hello,
-        cipher_suites, cert_chain, private_key, version):
+                                   cipher_suites, cert_chain,
+                                   private_key, version):
         """
         This method makes the decision on which certificate/key pair,
         signature algorithm and cipher to use based on the certificate.
         """
-        pass
+
+        last_cert = False
+        possible_certs = []
+
+        # Get client groups
+        client_groups = client_hello. \
+                getExtension(ExtensionType.supported_groups)
+        if client_groups is not None:
+            client_groups = client_groups.groups
+
+        # If client did send signature_algorithms_cert use it,
+        # otherwise fallback to signature_algorithms.
+        # Client can also decide not to send sigalg extension
+        client_sigalgs = \
+                client_hello. \
+                getExtension(ExtensionType.signature_algorithms_cert)
+        if client_sigalgs is not None:
+            client_sigalgs = \
+                    client_hello. \
+                    getExtension(ExtensionType.signature_algorithms_cert). \
+                    sigalgs
+        else:
+            client_sigalgs = \
+                    client_hello. \
+                    getExtension(ExtensionType.signature_algorithms)
+            if client_sigalgs is not None:
+                client_sigalgs = \
+                        client_hello. \
+                        getExtension(ExtensionType.signature_algorithms). \
+                        sigalgs
+            else:
+                client_sigalgs = []
+
+        client_psks = client_hello.getExtension(ExtensionType.pre_shared_key)
+
+        # Get all the certificates we can offer
+        alt_certs = ((X509CertChain(i.certificates), i.key) for vh in
+                     settings.virtual_hosts for i in vh.keys)
+        certs = [(cert, key)
+                 for cert, key in chain([(cert_chain, private_key)], alt_certs)]
+
+        for cert, key in certs:
+            # Check if this is the last (cert, key) pair we have to check
+            if (cert, key) == certs[-1]:
+                last_cert = True
+
+            # Mandatory checks. If any one of these checks fail, the certificate
+            # is not usuable.
+            try:
+                # Find a suitable ciphersuite based on the certificate
+                ciphers = CipherSuite.filter_for_certificate(cipher_suites, cert)
+                # but if we have matching PSKs, prefer those
+                if settings.pskConfigs and client_psks:
+                    client_identities = [
+                        i.identity for i in client_psks.identities]
+                    psks_prfs = [i[2] if len(i) == 3 else None for i in
+                                 settings.pskConfigs if
+                                 i[0] in client_identities]
+                    if psks_prfs:
+                        ciphers = CipherSuite.filter_for_prfs(ciphers,
+                                                              psks_prfs)
+                for cipher in ciphers:
+                    # select first mutually supported
+                    if cipher in client_hello.cipher_suites:
+                        break
+                else:
+                    # abort with context-specific alert if client indicated
+                    # support for FFDHE groups
+                    if client_groups and \
+                        any(i in range(256, 512) for i in client_groups) and \
+                        any(i in CipherSuite.dhAllSuites
+                            for i in client_hello.cipher_suites):
+                            raise TLSInsufficientSecurity(
+                                    "FFDHE groups not acceptable and no other common "
+                                    "ciphers")
+                    raise TLSHandshakeFailure("No mutual ciphersuite")
+
+                # Find a signature algorithm based on the certificate
+                try:
+                    sig_scheme, _, _ = \
+                        self._pickServerKeyExchangeSig(settings,
+                                                       client_hello,
+                                                       cert,
+                                                       key,
+                                                       version,
+                                                       False)
+                except TLSHandshakeFailure:
+                    raise TLSHandshakeFailure(
+                        "No common signature algorithms")
+
+                # If the certificate is ECDSA, we must check curve compatibility
+                if cert and cert.x509List[0].certAlg == 'ecdsa' and \
+                        client_groups and client_sigalgs:
+                    public_key = cert.getEndEntityPublicKey()
+                    curve = public_key.curve_name
+                    for name, aliases in CURVE_ALIASES.items():
+                        if curve in aliases:
+                            curve = getattr(GroupName, name)
+                            break
+
+                    if version <= (3, 3) and curve not in client_groups:
+                        raise TLSHandshakeFailure(
+                            "The curve in the public key is not "
+                            "supported by the client: {0}" \
+                                    .format(GroupName.toRepr(curve)))
+
+                    if version >= (3, 4):
+                        if GroupName.toRepr(curve) not in \
+                                ('secp256r1', 'secp384r1', 'secp521r1'):
+                            raise TLSIllegalParameterException(
+                                    "Curve in public key is not supported "
+                                    "in TLS1.3")
+
+                # If all mandatory checks passed add
+                # this as possible certificate we can use.
+                possible_certs.append((cipher, sig_scheme, cert, key))
+
+            except Exception:
+                if last_cert and not possible_certs:
+                    raise
+                continue
+
+            # Non-mandatory checks, if these fail the certificate is still usable
+            # but we should try to find one that passes all the checks
+
+            # Check if every certificate(except the self-signed root CA)
+            # in the certificate chain is signed with a signature algorithm
+            # supported by the client.
+            if cert:
+                cert_chain_ok = True
+                for i in range(len(cert.x509List)):
+                    if cert.x509List[i].issuer != cert.x509List[i].subject:
+                        if cert.x509List[i].sigalg not in client_sigalgs:
+                            cert_chain_ok = False
+                            break
+                if not cert_chain_ok:
+                    if not last_cert:
+                        continue
+                    break
+
+            # If all mandatory and non-mandatory checks passed
+            # return the (cert, key) pair, cipher and sig_scheme
+            return cipher, sig_scheme, cert, key
+
+        # If we can't find cert that passed all the checks, return the first usable one.
+        return possible_certs[0]
+
+
+    def _serverCertKeyExchange(self, clientHello, serverHello, sigHashAlg,
+                                serverCertChain, keyExchange,
+                                reqCert, reqCAs, cipherSuite,
+                                settings):
+        #Send ServerHello, Certificate[, ServerKeyExchange]
+        #[, CertificateRequest], ServerHelloDone
+        msgs = []
+
+        # If we verify a client cert chain, return it
+        clientCertChain = None
+
+        msgs.append(serverHello)
+        msgs.append(Certificate(CertificateType.x509).create(serverCertChain))
+        try:
+            serverKeyExchange = keyExchange.makeServerKeyExchange(sigHashAlg)
+        except TLSInternalError as alert:
+            for result in self._sendError(
+                    AlertDescription.internal_error,
+                    str(alert)):
+                yield result
+        except TLSInsufficientSecurity as alert:
+            for result in self._sendError(
+                    AlertDescription.insufficient_security,
+                    str(alert)):
+                yield result
+        if serverKeyExchange is not None:
+            msgs.append(serverKeyExchange)
+        if reqCert:
+            certificateRequest = CertificateRequest(self.version)
+            if not reqCAs:
+                reqCAs = []
+            cr_settings = settings.validate()
+            valid_sig_algs = self._sigHashesToList(cr_settings)
+
+            cert_types = []
+            if cr_settings.rsaSigHashes:
+                cert_types.append(ClientCertificateType.rsa_sign)
+            if cr_settings.ecdsaSigHashes or cr_settings.more_sig_schemes:
+                cert_types.append(ClientCertificateType.ecdsa_sign)
+            if cr_settings.dsaSigHashes:
+                cert_types.append(ClientCertificateType.dss_sign)
+
+            certificateRequest.create(cert_types,
+                                      reqCAs,
+                                      valid_sig_algs)
+            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
+
+                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.cert_chain and \
+                            clientCertificate.cert_chain.getNumCerts() != 0:
+                        clientCertChain = clientCertificate.cert_chain
+                else:
+                    raise AssertionError()
+            elif self.version in ((3,1), (3,2), (3,3)):
+                for result in self._getMsg(ContentType.handshake,
+                                          HandshakeType.certificate,
+                                          CertificateType.x509):
+                    if result in (0,1): yield result
+                    else: break
+                clientCertificate = result
+                if clientCertificate.cert_chain and \
+                        clientCertificate.cert_chain.getNumCerts() != 0:
+                    clientCertChain = clientCertificate.cert_chain
+            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
+
+        #Process ClientKeyExchange
+        try:
+            premasterSecret = \
+                keyExchange.processClientKeyExchange(clientKeyExchange)
+        except TLSIllegalParameterException as alert:
+            for result in self._sendError(AlertDescription.illegal_parameter,
+                                          str(alert)):
+                yield result
+        except TLSDecodeError as alert:
+            for result in self._sendError(AlertDescription.decode_error,
+                                          str(alert)):
+                yield result
+
+        #Get and check CertificateVerify, if relevant
+        self._certificate_verify_handshake_hash = self._handshake_hash.copy()
+        if clientCertChain:
+            for result in self._getMsg(ContentType.handshake,
+                                       HandshakeType.certificate_verify):
+                if result in (0, 1):
+                    yield result
+                else: break
+            certificateVerify = result
+            signatureAlgorithm = None
+            if self.version == (3, 3):
+                valid_sig_algs = \
+                    self._sigHashesToList(settings,
+                                          certList=clientCertChain)
+                if certificateVerify.signatureAlgorithm not in valid_sig_algs:
+                    for result in self._sendError(
+                            AlertDescription.illegal_parameter,
+                            "Invalid signature algorithm in Certificate "
+                            "Verify"):
+                        yield result
+                signatureAlgorithm = certificateVerify.signatureAlgorithm
+            if not signatureAlgorithm and \
+                    clientCertChain.x509List[0].certAlg == "ecdsa":
+                signatureAlgorithm = (HashAlgorithm.sha1,
+                                      SignatureAlgorithm.ecdsa)
+
+            cvhh = self._certificate_verify_handshake_hash
+            verify_bytes = KeyExchange.calcVerifyBytes(
+                self.version,
+                cvhh,
+                signatureAlgorithm,
+                premasterSecret,
+                clientHello.random,
+                serverHello.random,
+                key_type=clientCertChain.x509List[0].certAlg)
+
+            for result in self._check_certchain_with_settings(
+                    clientCertChain,
+                    settings):
+                if result in (0, 1):
+                    yield result
+                else: break
+            public_key = result
+
+            if signatureAlgorithm and signatureAlgorithm in (
+                    SignatureScheme.ed25519, SignatureScheme.ed448):
+                hash_name = "intrinsic"
+                salt_len = None
+                padding = None
+                ver_func = public_key.hashAndVerify
+            elif signatureAlgorithm and \
+                    signatureAlgorithm[1] == SignatureAlgorithm.dsa:
+                padding = None
+                hash_name = HashAlgorithm.toRepr(signatureAlgorithm[0])
+                salt_len = None
+                ver_func = public_key.verify
+            elif not signatureAlgorithm or \
+                    signatureAlgorithm[1] != SignatureAlgorithm.ecdsa:
+                scheme = SignatureScheme.toRepr(signatureAlgorithm)
+                # for pkcs1 signatures hash is used to add PKCS#1 prefix, but
+                # that was already done by calcVerifyBytes
+                hash_name = None
+                salt_len = 0
+                if scheme is None:
+                    padding = 'pkcs1'
+                else:
+                    padding = SignatureScheme.getPadding(scheme)
+                    if padding == 'pss':
+                        hash_name = SignatureScheme.getHash(scheme)
+                        salt_len = getattr(hashlib, hash_name)().digest_size
+                ver_func = public_key.verify
+            else:
+                hash_name = HashAlgorithm.toStr(signatureAlgorithm[0])
+                verify_bytes = verify_bytes[
+                    :public_key.public_key.curve.baselen]
+                padding = None
+                salt_len = None
+                ver_func = public_key.verify
+
+            if not ver_func(certificateVerify.signature,
+                            verify_bytes,
+                            padding,
+                            hash_name,
+                            salt_len):
+                for result in self._sendError(
+                        AlertDescription.decrypt_error,
+                        "Signature failed to verify"):
+                    yield result
+        yield (premasterSecret, clientCertChain)
+
+
+    def _serverAnonKeyExchange(self, serverHello, keyExchange, cipherSuite):
+
+        # Create ServerKeyExchange
+        serverKeyExchange = keyExchange.makeServerKeyExchange()
+
+        # Send ServerHello[, Certificate], ServerKeyExchange,
+        # ServerHelloDone
+        msgs = []
+        msgs.append(serverHello)
+        msgs.append(serverKeyExchange)
+        msgs.append(ServerHelloDone())
+        for result in self._sendMsgs(msgs):
+            yield result
+
+        # 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
+        cke = result
+        try:
+            premasterSecret = keyExchange.processClientKeyExchange(cke)
+        except TLSIllegalParameterException as alert:
+            for result in self._sendError(AlertDescription.illegal_parameter,
+                                          str(alert)):
+                yield result
+        except TLSDecodeError as alert:
+            for result in self._sendError(AlertDescription.decode_error,
+                                          str(alert)):
+                yield result
+
+        yield premasterSecret
+
+
+    def _serverFinished(self, premasterSecret, clientRandom, serverRandom,
+                        cipherSuite, cipherImplementations, nextProtos,
+                        settings, send_session_ticket=False,
+                        client_cert_chain=None):
+
+        masterSecret = self._calculate_master_secret(premasterSecret,
+                                                     cipherSuite,
+                                                     clientRandom,
+                                                     serverRandom)
+        self.session.masterSecret = masterSecret
+
+        #Calculate pending connection states
+        self._calcPendingStates(cipherSuite, masterSecret,
+                                clientRandom, serverRandom,
+                                cipherImplementations)
+
+        #Exchange ChangeCipherSpec and Finished messages
+        for result in self._getFinished(masterSecret,
+                                        cipherSuite,
+                                   expect_next_protocol=nextProtos is not None):
+            yield result
+
+        for result in self._sendFinished(masterSecret, cipherSuite,
+                                         settings=settings,
+                                         send_session_ticket=send_session_ticket,
+                                         client_cert_chain=client_cert_chain):
+            yield result
+
+    #*********************************************************
+    # Shared Handshake Functions
+    #*********************************************************
+
+    def _sendFinished(self, masterSecret, cipherSuite=None, nextProto=None,
+            settings=None, send_session_ticket=False, client_cert_chain=None):
+        if send_session_ticket:
+            for result in self._serverSendTickets(settings):
+                yield result
+
+        # send the CCS and Finished in single TCP packet
+        self.sock.buffer_writes = True
+        #Send ChangeCipherSpec
+        for result in self._sendMsg(ChangeCipherSpec()):
+            yield result
+
+        #Switch to pending write state
+        self._changeWriteState()
+
+        if self._peer_record_size_limit:
+            self._send_record_limit = self._peer_record_size_limit
+            # this is TLS 1.2 and earlier method, so the real limit may be
+            # lower that what's in the settings
+            self._recv_record_limit = min(2**14, settings.record_size_limit)
+
+        if nextProto is not None:
+            nextProtoMsg = NextProtocol().create(nextProto)
+            for result in self._sendMsg(nextProtoMsg):
+                yield result
+
+        #Figure out the correct label to use
+        if self._client:
+            label = b"client finished"
+        else:
+            label = b"server finished"
+        #Calculate verification data
+        verifyData = calc_key(self.version, masterSecret,
+                              cipherSuite, label,
+                              handshake_hashes=self._handshake_hash,
+                              output_length=12)
+        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
+        self.sock.flush()
+        self.sock.buffer_writes = False
+
+    def _getFinished(self, masterSecret, cipherSuite=None,
+                     expect_next_protocol=False, nextProto=None):
+
+        expect_ccs_message = True
+        # If we use SessionTicket resumption on client side, there are multiple
+        # situations where the server has the option to send new ticket
+        for result in self._getMsg(
+                (ContentType.handshake, ContentType.change_cipher_spec),
+                HandshakeType.new_session_ticket):
+            if result in (0,1):
+                yield result
+            else: break
+
+        if isinstance(result, NewSessionTicket1_0):
+            session_ticket = result
+            # If we receive new ticket we clear the old ones
+            del self.tls_1_0_tickets[:]
+            self.tls_1_0_tickets.append(Ticket(session_ticket.ticket,
+                                        session_ticket.ticket_lifetime,
+                                        masterSecret, cipherSuite))
+
+        else:
+            assert isinstance(result, ChangeCipherSpec)
+            expect_ccs_message = False
+
+            changeCipherSpec = result
+            if changeCipherSpec.type != 1:
+                for result in self._sendError(
+                        AlertDescription.illegal_parameter,
+                        "ChangeCipherSpec type incorrect"):
+                    yield result
+
+        if expect_ccs_message:
+            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()
+
+        #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
+
+        #Figure out which label to use.
+        if self._client:
+            label = b"server finished"
+        else:
+            label = b"client finished"
+
+        #Calculate verification data
+        verifyData = calc_key(self.version, masterSecret,
+                              cipherSuite, label,
+                              handshake_hashes=self._handshake_hash,
+                              output_length=12)
+
+        #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 _handshakeWrapperAsync(self, handshaker, checker):
+        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 GeneratorExit:
+            raise
+        except TLSAlert as alert:
+            if not self.fault:
+                raise
+            if alert.description not in Fault.faultAlerts[self.fault]:
+                raise TLSFaultError(str(alert))
+            else:
+                pass
+        except:
+            self._shutdown(False)
+            raise
+
+    def _calculate_master_secret(self, premaster_secret, cipher_suite,
+                                 client_random, server_random):
+        if self.extendedMasterSecret:
+            cvhh = self._certificate_verify_handshake_hash
+            # in case of resumption or lack of certificate authentication,
+            # the CVHH won't be initialised, but then it would also be equal
+            # to regular handshake hash
+            if not cvhh:
+                cvhh = self._handshake_hash
+            secret = calc_key(self.version, premaster_secret,
+                              cipher_suite, b"extended master secret",
+                              handshake_hashes=cvhh,
+                              output_length=48)
+        else:
+            secret = calc_key(self.version, premaster_secret,
+                              cipher_suite, b"master secret",
+                              client_random=client_random,
+                              server_random=server_random,
+                              output_length=48)
+        return secret

     @staticmethod
     def _pickServerKeyExchangeSig(settings, clientHello, certList=None,
-        private_key=None, version=(3, 3), check_alt=True):
+                                  private_key=None,
+                                  version=(3, 3), check_alt=True):
         """Pick a hash that matches most closely the supported ones"""
-        pass
+        hashAndAlgsExt = clientHello.getExtension(
+            ExtensionType.signature_algorithms)
+
+        if version > (3, 3):
+            if not hashAndAlgsExt:
+                # the error checking was done before hand, likely we're
+                # doing PSK key exchange
+                return None, certList, private_key
+
+        if hashAndAlgsExt is None or hashAndAlgsExt.sigalgs is None:
+            # RFC 5246 states that if there are no hashes advertised,
+            # sha1 should be picked
+            return "sha1", certList, private_key
+
+        if check_alt:
+            alt_certs = ((X509CertChain(i.certificates), i.key) for vh in
+                         settings.virtual_hosts for i in vh.keys)
+        else:
+            alt_certs = ()
+
+        for certs, key in chain([(certList, private_key)], alt_certs):
+            supported = TLSConnection._sigHashesToList(settings,
+                                                       certList=certs,
+                                                       version=version)
+
+            for schemeID in supported:
+                if schemeID in hashAndAlgsExt.sigalgs:
+                    name = SignatureScheme.toRepr(schemeID)
+                    if not name and schemeID[1] in (SignatureAlgorithm.rsa,
+                                                    SignatureAlgorithm.ecdsa,
+                                                    SignatureAlgorithm.dsa):
+                        name = HashAlgorithm.toRepr(schemeID[0])
+
+                    if name:
+                        return name, certs, key
+
+        # if no match, we must abort per RFC 5246
+        raise TLSHandshakeFailure("No common signature algorithms")

     @staticmethod
-    def _sigHashesToList(settings, privateKey=None, certList=None, version=
-        (3, 3)):
+    def _sigHashesToList(settings, privateKey=None, certList=None,
+                         version=(3, 3)):
         """Convert list of valid signature hashes to array of tuples"""
-        pass
+        certType = None
+        publicKey = None
+        if certList and certList.x509List:
+            certType = certList.x509List[0].certAlg
+            publicKey = certList.x509List[0].publicKey
+
+        sigAlgs = []
+
+        if not certType or certType == "Ed25519" or certType == "Ed448":
+            for sig_scheme in settings.more_sig_schemes:
+                if version < (3, 3):
+                    # EdDSA is supported only in TLS 1.2 and 1.3
+                    continue
+                if certType and sig_scheme != certType:
+                    continue
+                sigAlgs.append(getattr(SignatureScheme, sig_scheme.lower()))
+
+        if not certType or certType == "ecdsa":
+            for hashName in settings.ecdsaSigHashes:
+                # only SHA256, SHA384 and SHA512 are allowed in TLS 1.3
+                if version > (3, 3) and hashName in ("sha1", "sha224"):
+                    continue
+
+                # in TLS 1.3 ECDSA key curve is bound to hash
+                if publicKey and version > (3, 3):
+                    curve = publicKey.curve_name
+                    matching_hash = TLSConnection._curve_name_to_hash_name(
+                        curve)
+                    if hashName != matching_hash:
+                        continue
+
+                sigAlgs.append((getattr(HashAlgorithm, hashName),
+                                SignatureAlgorithm.ecdsa))
+
+        if not certType or certType == "dsa":
+            for hashName in settings.dsaSigHashes:
+                if version > (3, 3):
+                    continue
+
+                sigAlgs.append((getattr(HashAlgorithm, hashName),
+                                SignatureAlgorithm.dsa))
+
+        if not certType or certType in ("rsa", "rsa-pss"):
+            for schemeName in settings.rsaSchemes:
+                # pkcs#1 v1.5 signatures are not allowed in TLS 1.3
+                if version > (3, 3) and schemeName == "pkcs1":
+                    continue
+
+                for hashName in settings.rsaSigHashes:
+                    # rsa-pss certificates can't be used to make PKCS#1 v1.5
+                    # signatures
+                    if certType == "rsa-pss" and schemeName == "pkcs1":
+                        continue
+                    try:
+                        # 1024 bit keys are too small to create valid
+                        # rsa-pss-SHA512 signatures
+                        if schemeName == 'pss' and hashName == 'sha512'\
+                                and privateKey and privateKey.n < 2**2047:
+                            continue
+                        # advertise support for both rsaEncryption and RSA-PSS OID
+                        # key type
+                        if certType != 'rsa-pss':
+                            sigAlgs.append(getattr(SignatureScheme,
+                                                   "rsa_{0}_rsae_{1}"
+                                                   .format(schemeName, hashName)))
+                        if certType != 'rsa':
+                            sigAlgs.append(getattr(SignatureScheme,
+                                                   "rsa_{0}_pss_{1}"
+                                                   .format(schemeName, hashName)))
+                    except AttributeError:
+                        if schemeName == 'pkcs1':
+                            sigAlgs.append((getattr(HashAlgorithm, hashName),
+                                            SignatureAlgorithm.rsa))
+                        continue
+        return sigAlgs

     @staticmethod
     def _curveNamesToList(settings):
         """Convert list of acceptable curves to array identifiers"""
-        pass
+        return [getattr(GroupName, val) for val in settings.eccCurves]

     @staticmethod
     def _groupNamesToList(settings):
         """Convert list of acceptable ff groups to TLS identifiers."""
-        pass
+        return [getattr(GroupName, val) for val in settings.dhGroups]

     @staticmethod
     def _curve_name_to_hash_name(curve_name):
@@ -505,4 +4764,11 @@ class TLSConnection(TLSRecordLayer):

         expects the python-ecdsa curve names as parameter
         """
-        pass
+        if curve_name == "NIST256p":
+            return "sha256"
+        if curve_name == "NIST384p":
+            return "sha384"
+        if curve_name == "NIST521p":
+            return "sha512"
+        raise TLSIllegalParameterException(
+            "Curve {0} is not supported in TLS 1.3".format(curve_name))
diff --git a/tlslite/tlsrecordlayer.py b/tlslite/tlsrecordlayer.py
index 01f1c67..0cd31f2 100644
--- a/tlslite/tlsrecordlayer.py
+++ b/tlslite/tlsrecordlayer.py
@@ -1,8 +1,20 @@
+# Authors: 
+#   Trevor Perrin
+#   Google (adapted by Sam Rushing) - NPN support
+#   Google - minimal padding
+#   Martin von Loewis - python 3 port
+#   Yngve Pettersen (ported by Paul Sokolovsky) - TLS 1.2
+#   Hubert Kario
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """Helper class for TLSConnection."""
 from __future__ import generators
+
 import io
 import time
 import socket
+
 from .utils.compat import *
 from .utils.cryptomath import *
 from .utils.codec import Parser, BadCertificateError
@@ -18,7 +30,6 @@ from .bufferedsocket import BufferedSocket
 from .handshakesettings import HandshakeSettings
 from .keyexchange import KeyExchange

-
 class TLSRecordLayer(object):
     """
     This class handles data transmission for a TLS connection.
@@ -111,81 +122,137 @@ class TLSRecordLayer(object):
         sock = BufferedSocket(sock)
         self.sock = sock
         self._recordLayer = RecordLayer(sock)
+
+        #My session object (Session instance; read-only)
         self.session = None
+
+        #Buffers for processing messages
         self._defragmenter = Defragmenter()
         self._defragmenter.add_static_size(ContentType.change_cipher_spec, 1)
         self._defragmenter.add_static_size(ContentType.alert, 2)
         self._defragmenter.add_dynamic_size(ContentType.handshake, 1, 3)
         self.clearReadBuffer()
         self.clearWriteBuffer()
+
+        #Handshake digests
         self._handshake_hash = HandshakeHashes()
+        # Handshake digest used for Certificate Verify signature and
+        # also for EMS calculation, in practice, it excludes
+        # CertificateVerify and all following messages (Finished)
         self._certificate_verify_handshake_hash = None
+        # for PSK binders we need to be able to calculate the hash of all
+        # echanged messages and a _truncated_ ClientHello message so we
+        # need a copy of those hashes before CH was received
         self._pre_client_hello_handshake_hash = None
-        self.closed = True
-        self._refCount = 0
-        self.resumed = False
+
+        #Is the connection open?
+        self.closed = True #read-only
+        self._refCount = 0 #Used to trigger closure
+
+        #Is this a resumed session?
+        self.resumed = False #read-only
+
+        #What username did the client claim in his handshake?
         self.allegedSrpUsername = None
+
+        #On a call to close(), do we close the socket? (writeable)
         self.closeSocket = True
+
+        #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
-        self._user_record_limit = 16384
+
+        # Temporarily limit the size of outgoing records to following size
+        self._user_record_limit = 16384  # 2**14
+
+        # NewSessionTickets received from server
         self.tickets = []
+        # TLS 1.2 and earlier tickets received from server
         self.tls_1_0_tickets = []
+
+        # Indicator for heartbeat extension mode, if we can receive
+        # heartbeat requests
         self.heartbeat_can_receive = False
+
+        # Indicator for heartbeat extension mode, if we can send
+        # heartbeat requests
         self.heartbeat_can_send = False
+
+        # Indicator, that both sides want use heartbeat extension
         self.heartbeat_supported = False
+
+        # Callback function for handling responses to heartbeat requests
+        # we sent
         self.heartbeat_response_callback = None
+
         self._buffer_content_type = None
         self._buffer = bytearray()
+
+        # tuple with list of certificates and the private key that will be
+        # used for post handshake authentication in TLS 1.3
         self._client_keypair = None
+
+        # dictionary with CertificateRequest messages we (as a server) have
+        # sent, the keys are the "certificate request context" from the
+        # messages (which are the values)
         self._cert_requests = {}
+
+        # boolean to control if PHA needs to be aborted when the client
+        # doesn't provide a certificate
         self.client_cert_required = False
+
+        # boolean to control if ChangeCipherSpec in TLS1.3 is allowed or not
+        # we start with True, as peers MUST always processe the messages
+        # before the handshake is done, only then we disable it (for PHA)
         self._middlebox_compat_mode = True

     @property
     def _send_record_limit(self):
         """Maximum size of payload that can be sent."""
-        pass
+        return self._recordLayer.send_record_limit

     @_send_record_limit.setter
     def _send_record_limit(self, value):
         """Maximum size of payload that can be sent."""
-        pass
+        self._recordLayer.send_record_limit = value

     @property
     def _recv_record_limit(self):
         """Maximum size of payload that can be received."""
-        pass
+        return self._recordLayer.recv_record_limit

     @_recv_record_limit.setter
     def _recv_record_limit(self, value):
         """Maximum size of payload that can be received."""
-        pass
+        self._recordLayer.recv_record_limit = value

     @property
     def recordSize(self):
         """Maximum size of the records that will be sent out."""
-        pass
+        return min(self._user_record_limit, self._send_record_limit)

     @recordSize.setter
     def recordSize(self, value):
         """Size to automatically fragment records to."""
-        pass
+        self._user_record_limit = value

     @property
     def _client(self):
         """Boolean stating if the endpoint acts as a client"""
-        pass
+        return self._recordLayer.client

     @_client.setter
     def _client(self, value):
         """Set the endpoint to act as a client or not"""
-        pass
+        self._recordLayer.client = value

     @property
     def version(self):
         """Get the SSL protocol version of connection"""
-        pass
+        return self._recordLayer.version

     @version.setter
     def version(self, value):
@@ -196,12 +263,25 @@ class TLSRecordLayer(object):
         Don't use it! See at HandshakeSettings for options to set desired
         protocol version.
         """
-        pass
+        self._recordLayer.version = value
+        if value > (3, 3):
+            self._recordLayer.tls13record = True

     @property
     def encryptThenMAC(self):
         """Whether the connection uses Encrypt Then MAC (RFC 7366)"""
-        pass
+        return self._recordLayer.encryptThenMAC
+
+    def clearReadBuffer(self):
+        self._readBuffer = b''
+
+    def clearWriteBuffer(self):
+        self._send_writer = None
+
+
+    #*********************************************************
+    # Public Functions START
+    #*********************************************************

     def read(self, max=None, min=1):
         """Read some data from the TLS connection.
@@ -228,7 +308,9 @@ class TLSRecordLayer(object):
             without a preceding alert.
         :raises tlslite.errors.TLSAlert: If a TLS alert is signalled.
         """
-        pass
+        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.
@@ -242,7 +324,79 @@ class TLSRecordLayer(object):
         :rtype: iterable
         :returns: A generator; see above for details.
         """
-        pass
+        constructor_type = None
+        if self.version > (3, 3):
+            allowedTypes = (ContentType.application_data,
+                            ContentType.handshake)
+            if self._client_keypair:
+                allowedHsTypes = (HandshakeType.new_session_ticket,
+                                  HandshakeType.key_update,
+                                  HandshakeType.certificate_request)
+            elif self._cert_requests:
+                allowedHsTypes = (HandshakeType.new_session_ticket,
+                                  HandshakeType.key_update,
+                                  HandshakeType.certificate)
+                constructor_type = CertificateType.x509
+            else:
+                allowedHsTypes = (HandshakeType.new_session_ticket,
+                                  HandshakeType.key_update)
+        else:
+            allowedTypes = ContentType.application_data
+            allowedHsTypes = None
+        try:
+            try_once = True
+            # perform a read even if we were asked to read 0 bytes, but only
+            # if the buffer is empty; this is used to trigger
+            # processing of NST, KeyUpdate and PHA
+            while (len(self._readBuffer) < min or
+                    (not self._readBuffer and try_once)) \
+                    and not self.closed:
+                try_once = False
+                try:
+                    for result in self._getMsg(allowedTypes,
+                                               allowedHsTypes,
+                                               constructor_type):
+                        if result in (0, 1):
+                            yield result
+                    if isinstance(result, NewSessionTicket):
+                        result.time = time.time()
+                        self.tickets.append(result)
+                    elif isinstance(result, KeyUpdate):
+                        for result in self._handle_keyupdate_request(result):
+                            yield result
+                        # KeyUpdate messages are not solicited, while call with
+                        # min==0 are done to perform PHA
+                        try_once = True
+                    elif isinstance(result, Certificate):
+                        for result in self._handle_srv_pha(result):
+                            yield result
+                    elif isinstance(result, CertificateRequest):
+                        for result in self._handle_pha(result):
+                            yield result
+                    else:
+                        assert isinstance(result, ApplicationData)
+                        applicationData = result
+                        self._readBuffer += applicationData.write()
+                except TLSRemoteAlert as 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)
+
+            returnBytes = self._readBuffer[:max]
+            self._readBuffer = self._readBuffer[max:]
+            yield bytes(returnBytes)
+        except GeneratorExit:
+            raise
+        except:
+            self._shutdown(False)
+            raise

     def unread(self, b):
         """Add bytes to the front of the socket read buffer for future
@@ -250,7 +404,7 @@ class TLSRecordLayer(object):
         unread the last data from a socket, that won't wake up selected waiters,
         and those waiters may hang forever.
         """
-        pass
+        self._readBuffer = b + self._readBuffer

     def write(self, s):
         """Write some data to the TLS connection.
@@ -265,7 +419,8 @@ class TLSRecordLayer(object):

         :raises socket.error: If a socket error occurs.
         """
-        pass
+        for result in self.writeAsync(s):
+            pass

     def writeAsync(self, s):
         """Start a write operation on the TLS connection.
@@ -278,7 +433,21 @@ class TLSRecordLayer(object):
         :rtype: iterable
         :returns: A generator; see above for details.
         """
-        pass
+        try:
+            if self.closed:
+                raise TLSClosedConnectionError("attempt to write to closed connection")
+
+            applicationData = ApplicationData().create(bytearray(s))
+            for result in self._sendMsg(applicationData, \
+                                        randomizeFirstBlock=True):
+                yield result
+        except GeneratorExit:
+            raise
+        except Exception:
+            # Don't invalidate the session on write failure if abrupt closes are
+            # okay.
+            self._shutdown(self.ignoreAbruptClose)
+            raise

     def close(self):
         """Close the TLS connection.
@@ -301,7 +470,11 @@ class TLSRecordLayer(object):
             without a preceding alert.
         :raises tlslite.errors.TLSAlert: If a TLS alert is signalled.
         """
-        pass
+        if not self.closed:
+            for result in self._decrefAsync():
+                pass
+
+    # Python 3 callback
     _decref_socketios = close

     def closeAsync(self):
@@ -316,7 +489,44 @@ class TLSRecordLayer(object):
         :rtype: iterable
         :returns: A generator; see above for details.
         """
-        pass
+        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
+                # By default close the socket, since it's been observed
+                # that some other libraries will not respond to the 
+                # close_notify alert, thus leaving us hanging if we're
+                # expecting it
+                if self.closeSocket:
+                    self._shutdown(True)
+                else:
+                    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 GeneratorExit:
+                raise
+            except:
+                self._shutdown(False)
+                raise

     def getVersionName(self):
         """Get the name of this TLS version.
@@ -326,7 +536,12 @@ class TLSRecordLayer(object):
             Either None, 'SSL 3.0', 'TLS 1.0', 'TLS 1.1', 'TLS 1.2' or
             'TLS 1.3'.
         """
-        pass
+        ver = {(3, 0): "SSL 3.0",
+               (3, 1): "TLS 1.0",
+               (3, 2): "TLS 1.1",
+               (3, 3): "TLS 1.2",
+               (3, 4): "TLS 1.3"}
+        return ver.get(self.version)

     def getCipherName(self):
         """Get the name of the cipher used with this connection.
@@ -335,7 +550,7 @@ class TLSRecordLayer(object):
         :returns: The name of the cipher used with this connection.
             Either 'aes128', 'aes256', 'rc4', or '3des'.
         """
-        pass
+        return self._recordLayer.getCipherName()

     def getCipherImplementation(self):
         """Get the name of the cipher implementation used with
@@ -345,21 +560,23 @@ class TLSRecordLayer(object):
         :returns: The name of the cipher implementation used with
             this connection.  Either 'python', 'openssl', or 'pycrypto'.
         """
-        pass
+        return self._recordLayer.getCipherImplementation()

+    #Emulate a socket, somewhat -
     def send(self, s):
         """Send data to the TLS connection (socket emulation).

         :raises socket.error: If a socket error occurs.
         """
-        pass
+        self.write(s)
+        return len(s)

     def sendall(self, s):
         """Send data to the TLS connection (socket emulation).

         :raises socket.error: If a socket error occurs.
         """
-        pass
+        self.write(s)

     def recv(self, bufsize):
         """Get some data from the TLS connection (socket emulation).
@@ -369,72 +586,815 @@ class TLSRecordLayer(object):
             without a preceding alert.
         :raises tlslite.errors.TLSAlert: If a TLS alert is signalled.
         """
-        pass
+        return self.read(bufsize)

+    def recv_into(self, b):
+        # XXX doc string
+        data = self.read(len(b))
+        if not data:
+            return None
+        b[:len(data)] = data
+        return len(data)
+
+    # while the SocketIO and _fileobject in socket is private we really need
+    # to use it as it's what the real socket does internally
+
+    # pylint: disable=no-member,protected-access
     def makefile(self, mode='r', bufsize=-1):
         """Create a file object for the TLS connection (socket emulation).

         :rtype: socket._fileobject
         """
-        pass
+        self._refCount += 1
+        # So, it is pretty fragile to be using Python internal objects
+        # like this, but it is probably the best/easiest way to provide
+        # matching behavior for socket emulation purposes.  The 'close'
+        # argument is nice, its apparently a recent addition to this
+        # class, so that when fileobject.close() gets called, it will
+        # close() us, causing the refcount to be decremented (decrefAsync).
+        #
+        # If this is the last close() on the outstanding fileobjects / 
+        # TLSConnection, then the "actual" close alerts will be sent,
+        # socket closed, etc.
+
+        # for writes, we MUST buffer otherwise the lengths of headers leak
+        # through record layer boundaries
+        if 'w' in mode and bufsize <= 0:
+            bufsize = 2**14
+
+        if sys.version_info < (3,):
+            return socket._fileobject(self, mode, bufsize, close=True)
+        else:
+            if 'w' in mode:
+                return io.BufferedWriter(socket.SocketIO(self, mode), bufsize)
+            else:
+                return socket.SocketIO(self, mode)
+    # pylint: enable=no-member,protected-access

     def getsockname(self):
         """Return the socket's own address (socket emulation)."""
-        pass
+        return self.sock.getsockname()

     def getpeername(self):
         """Return the remote address to which the socket is connected
         (socket emulation)."""
-        pass
+        return self.sock.getpeername()

     def settimeout(self, value):
         """Set a timeout on blocking socket operations (socket emulation)."""
-        pass
+        return self.sock.settimeout(value)

     def gettimeout(self):
         """Return the timeout associated with socket operations (socket
         emulation)."""
-        pass
+        return self.sock.gettimeout()

     def setsockopt(self, level, optname, value):
         """Set the value of the given socket option (socket emulation)."""
-        pass
+        return self.sock.setsockopt(level, optname, value)

     def shutdown(self, how):
         """Shutdown the underlying socket."""
-        pass
-
+        return self.sock.shutdown(how)
+       
     def fileno(self):
         """Not implement in TLS Lite."""
-        pass
+        raise NotImplementedError()
+       
+
+     #*********************************************************
+     # Public Functions END
+     #*********************************************************
+
+    def _handle_pha(self, cert_request):
+        cert, p_key = self._client_keypair
+
+        handshake_context = self._first_handshake_hashes.copy()
+        handshake_context.update(cert_request.write())
+
+        prf_name = 'sha256'
+        prf_size = 32
+        if self.session.cipherSuite in CipherSuite.sha384PrfSuites:
+            prf_name = 'sha384'
+            prf_size = 48
+
+        msgs = []
+        msgs.append(Certificate(CertificateType.x509, self.version)
+                    .create(cert, cert_request.certificate_request_context))
+        handshake_context.update(msgs[0].write())
+        if cert.x509List and p_key:
+            # sign the CertificateVerify only when we have a private key to do
+            # that
+            valid_sig_algs = cert_request.supported_signature_algs
+            if not valid_sig_algs:
+                for result in self._sendError(
+                        AlertDescription.missing_extension,
+                        "No signature algorithms found in CertificateRequest"):
+                    yield result
+            avail_sig_algs = self._sigHashesToList(HandshakeSettings(), p_key,
+                                                   cert, version=(3, 4))
+            sig_scheme = getFirstMatching(avail_sig_algs, valid_sig_algs)
+            scheme = SignatureScheme.toRepr(sig_scheme)
+            sig_scheme = getattr(SignatureScheme, scheme)
+
+            signature_context = \
+                KeyExchange.calcVerifyBytes((3, 4),
+                                            handshake_context,
+                                            sig_scheme, None, None, None,
+                                            prf_name, b'client')
+
+            if sig_scheme in (SignatureScheme.ed25519, SignatureScheme.ed448):
+                pad_type = None
+                hash_name = "intrinsic"
+                salt_len = None
+                sig_func = p_key.hashAndSign
+                ver_func = p_key.hashAndVerify
+            elif sig_scheme[1] == SignatureAlgorithm.ecdsa:
+                pad_type = None
+                hash_name = HashAlgorithm.toRepr(sig_scheme[0])
+                salt_len = None
+                sig_func = p_key.sign
+                ver_func = p_key.verify
+            else:
+                pad_type = SignatureScheme.getPadding(scheme)
+                hash_name = SignatureScheme.getHash(scheme)
+                salt_len = getattr(hashlib, hash_name)().digest_size
+                sig_func = p_key.sign
+                ver_func = p_key.verify
+
+            signature = sig_func(signature_context,
+                                 pad_type,
+                                 hash_name,
+                                 salt_len)
+            if not ver_func(signature, signature_context,
+                            pad_type,
+                            hash_name,
+                            salt_len):
+                for result in self._sendError(
+                        AlertDescription.internal_error,
+                        "Certificate Verify signature failed"):
+                    yield result
+            certificate_verify = CertificateVerify(self.version)
+            certificate_verify.create(signature, sig_scheme)
+
+            msgs.append(certificate_verify)
+            handshake_context.update(certificate_verify.write())
+
+        finished_key = HKDF_expand_label(self.session.cl_app_secret,
+                                         b"finished", b"",
+                                         prf_size, prf_name)
+        verify_data = secureHMAC(finished_key,
+                                 handshake_context.digest(prf_name),
+                                 prf_name)
+
+        finished = Finished((3, 4), prf_size)
+        finished.create(verify_data)
+        msgs.append(finished)
+
+        for result in self._sendMsgs(msgs):
+            yield result

     def _handle_srv_pha(self, cert):
         """Process the post-handshake authentication from client."""
-        pass
+        prf_name = 'sha256'
+        prf_size = 32
+        if self.session.cipherSuite in CipherSuite.sha384PrfSuites:
+            prf_name = 'sha384'
+            prf_size = 48
+
+        cr_context = cert.certificate_request_context
+        if not cr_context:
+            for result in self._sendError(
+                    AlertDescription.illegal_parameter,
+                    "Certificate Request context missing in Certificate "
+                    "message from client"):
+                yield result
+
+        try:
+            cr = self._cert_requests.pop(bytes(cr_context))
+        except KeyError:
+            for result in self._sendError(
+                    AlertDescription.illegal_parameter,
+                    "Certificiate Request context is incorrect or was already "
+                    "handled previously"):
+                yield result
+
+        # TODO: verify that the extensions used by client were sent by us in
+        # CertificateReuest
+
+        handshake_context = self._first_handshake_hashes.copy()
+        handshake_context.update(cr.write())
+        handshake_context.update(cert.write())
+
+        if cert.cert_chain:
+            for result in self._getMsg(ContentType.handshake,
+                                       HandshakeType.certificate_verify):
+                if result in (0, 1):
+                    yield result
+                else:
+                    break
+            assert isinstance(result, CertificateVerify)
+            cert_verify = result
+
+            valid_sig_algs = cr.supported_signature_algs
+            if cert_verify.signatureAlgorithm not in valid_sig_algs:
+                for result in self._sendError(
+                        AlertDescription.illegal_parameter,
+                        "Client selected signature algorithm we didn't "
+                        "advertise"):
+                    yield result
+            avail_sig_algs = self._sigHashesToList(HandshakeSettings(), None,
+                                                   cert.cert_chain,
+                                                   version=(3, 4))
+            if cert_verify.signatureAlgorithm not in avail_sig_algs:
+                for result in self._sendError(
+                        AlertDescription.illegal_parameter,
+                        "Client selected signature algorithm not consistent "
+                        "with public key in its certificate"):
+                    yield result
+            scheme = SignatureScheme.toRepr(cert_verify.signatureAlgorithm)
+            sig_scheme = getattr(SignatureScheme, scheme)
+
+            signature_context = \
+                KeyExchange.calcVerifyBytes((3, 4),
+                                            handshake_context,
+                                            sig_scheme, None, None, None,
+                                            prf_name, b'client')
+
+            if sig_scheme in (SignatureScheme.ed25519, SignatureScheme.ed448):
+                pad_type = None
+                hash_name = "intrinsic"
+                salt_len = None
+                ver_func = cert.cert_chain.getEndEntityPublicKey().hashAndVerify
+            elif sig_scheme[1] == SignatureAlgorithm.ecdsa:
+                pad_type = None
+                hash_name = HashAlgorithm.toRepr(sig_scheme[0])
+                salt_len = None
+                ver_func = cert.cert_chain.getEndEntityPublicKey().verify
+            else:
+                pad_type = SignatureScheme.getPadding(scheme)
+                hash_name = SignatureScheme.getHash(scheme)
+                salt_len = getattr(hashlib, hash_name)().digest_size
+                ver_func = cert.cert_chain.getEndEntityPublicKey().verify
+
+            if not ver_func(
+                    cert_verify.signature, signature_context, pad_type,
+                    hash_name, salt_len):
+                for result in self._sendError(
+                        AlertDescription.decrypt_error,
+                        "Signature verification failed"):
+                    yield result
+            handshake_context.update(cert_verify.write())
+        elif self.client_cert_required:
+            for result in self._sendError(
+                    AlertDescription.certificate_required,
+                    "Client did not provide a certificate in post-handshake "
+                    "authentication"):
+                yield result
+
+        finished_key = HKDF_expand_label(self.session.cl_app_secret,
+                                         b'finished', b'',
+                                         prf_size, prf_name)
+        verify_data = secureHMAC(finished_key,
+                                 handshake_context.digest(prf_name),
+                                 prf_name)
+
+        for result in self._getMsg(ContentType.handshake,
+                                   HandshakeType.finished,
+                                   prf_size):
+            if result in (0, 1):
+                yield result
+            else:
+                break
+        assert isinstance(result, Finished)
+
+        finished = result
+
+        if finished.verify_data != verify_data:
+            for result in self._sendError(
+                    AlertDescription.decrypt_error,
+                    "Invalid Finished verify_data from client"):
+                yield result
+
+        self.session.clientCertChain = cert.cert_chain
+
+    def _shutdown(self, resumable):
+        self._recordLayer.shutdown()
+        self.version = (0,0)
+        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):
+        # make sure that the message goes out
+        self.sock.flush()
+        self.sock.buffer_writes = False
+        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):
+        # send messages together in a single TCP write
+        self.sock.buffer_writes = True
+        randomizeFirstBlock = True
+        for msg in msgs:
+            for result in self._sendMsg(msg, randomizeFirstBlock):
+                yield result
+            randomizeFirstBlock = True
+        self.sock.flush()
+        self.sock.buffer_writes = False

     def _sendMsg(self, msg, randomizeFirstBlock=True, update_hashes=True):
         """Fragment and send message through socket"""
-        pass
+        #Whenever we're connected and asked to send an app data message,
+        #we first send the first byte of the message.  This prevents
+        #an attacker from launching a chosen-plaintext attack based on
+        #knowing the next IV (a la BEAST).
+        if randomizeFirstBlock and self.version <= (3, 1) \
+                and self._recordLayer.isCBCMode() \
+                and msg.contentType == ContentType.application_data:
+            msgFirstByte = msg.splitFirstByte()
+            for result in self._sendMsgThroughSocket(msgFirstByte):
+                yield result
+            if len(msg.write()) == 0:
+                return
+
+        buf = msg.write()
+        contentType = msg.contentType
+        #Update handshake hashes
+        if update_hashes and contentType == ContentType.handshake:
+            self._handshake_hash.update(buf)
+
+        #Fragment big messages
+        while len(buf) > self.recordSize:
+            newB = buf[:self.recordSize]
+            buf = buf[self.recordSize:]
+
+            msgFragment = Message(contentType, newB)
+            for result in self._sendMsgThroughSocket(msgFragment):
+                yield result
+
+        msgFragment = Message(contentType, buf)
+        for result in self._sendMsgThroughSocket(msgFragment):
+            yield result

     def _queue_message(self, msg):
         """Just queue message for sending, for record layer coalescing."""
-        pass
+        if self._buffer_content_type is not None and \
+                self._buffer_content_type != msg.contentType:
+            raise ValueError("Queuing of wrong message types")
+        if self._buffer_content_type is None:
+            self._buffer_content_type = msg.contentType
+
+        serialised_msg = msg.write()
+        self._buffer += serialised_msg
+        if msg.contentType == ContentType.handshake:
+            self._handshake_hash.update(serialised_msg)

     def _queue_flush(self):
         """Send the queued messages."""
-        pass
+        msg = Message(self._buffer_content_type, self._buffer)
+        for result in self._sendMsg(msg, update_hashes=False):
+            yield result
+        self._buffer_content_type = None
+        self._buffer = bytearray()

     def _sendMsgThroughSocket(self, msg):
         """Send message, handle errors"""
-        pass

+        try:
+            for result in self._recordLayer.sendRecord(msg):
+                if result in (0, 1):
+                    yield result
+        except socket.error:
+            # The socket was unexpectedly closed.  The tricky part
+            # is that there may be an alert sent by the other party
+            # sitting in the read buffer.  So, if we get here after
+            # handshaking, we will just raise the error and let the
+            # caller read more data if it would like, thus stumbling
+            # upon the error.
+            #
+            # However, if we get here DURING handshaking, we take
+            # it upon ourselves to see if the next message is an
+            # Alert.
+            if msg.contentType == ContentType.handshake:
+
+                # See if there's an alert record
+                # Could raise socket.error or TLSAbruptCloseError
+                for result in self._getNextRecord():
+                    if result in (0, 1):
+                        yield result
+                    else:
+                        break
+
+                # Closes the socket
+                self._shutdown(False)
+
+                # If we got an alert, raise it
+                recordHeader, p = result
+                if recordHeader.type == ContentType.alert:
+                    alert = Alert().parse(p)
+                    raise TLSRemoteAlert(alert)
+            else:
+                # If we got some other message who know what
+                # the remote side is doing, just go ahead and
+                # raise the socket.error
+                raise
+
+    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
+                    else:
+                        break
+                recordHeader, p = result
+
+                # if this is a CCS message in TLS 1.3, sanity check and
+                # continue
+                if self.version > (3, 3) and \
+                        ContentType.handshake in expectedType and \
+                        self._middlebox_compat_mode and \
+                        recordHeader.type == ContentType.change_cipher_spec:
+                    ccs = ChangeCipherSpec().parse(p)
+                    if ccs.type != 1:
+                        for result in self._sendError(
+                                AlertDescription.unexpected_message,
+                                "Invalid CCS message received"):
+                            yield result
+                    # ignore the message
+                    continue
+
+                # TLS 1.3 Handshake messages MUST NOT be interleaved with
+                # other messages, Section 5.1 RFC 8446
+                if self.version > (3, 3) and \
+                        recordHeader.type != ContentType.handshake and \
+                        self._defragmenter.buffers[ContentType.handshake]:
+                    for result in self._sendError(
+                            AlertDescription.unexpected_message,
+                            "Interleaved Handshake and "
+                            "non-handshake messages"):
+                        yield result
+
+                #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 if we're not negotiating
+                        # a connection now, then try again
+                        if reneg and self.session:
+                            alertMsg = Alert()
+                            alertMsg.create(AlertDescription.no_renegotiation,
+                                            AlertLevel.warning)
+                            for result in self._sendMsg(alertMsg):
+                                yield result
+                            continue
+
+                    # If we received a heartbeat request and heartbeat
+                    # extension was negotiated
+                    if recordHeader.type == ContentType.heartbeat and \
+                            self.heartbeat_supported:
+                        try:
+                            heartbeat_message = Heartbeat().parse(p)
+                            # If we received heartbeat request, then we
+                            # response with response create from request
+                            if heartbeat_message.message_type == \
+                                    HeartbeatMessageType.heartbeat_request:
+                                if not self.heartbeat_can_receive:
+                                    for result in self._sendError(
+                                            AlertDescription.
+                                            unexpected_message,
+                                            "Received heartbeat_request to "
+                                            "peer_not_allowed_to_send mode"):
+                                        yield result
+                                if len(heartbeat_message.padding) < 16:
+                                    # per RFC, silently ignore if the message
+                                    # is malformed
+                                    continue
+                                heartbeat_response = heartbeat_message.\
+                                    create_response()
+                                for result in self._sendMsg(
+                                        heartbeat_response):
+                                    yield result
+                            # If we received heartbeat response, then we
+                            # check, if its payload is same as payload of
+                            # request we sent
+                            elif heartbeat_message.message_type == \
+                                    HeartbeatMessageType.heartbeat_response \
+                                    and self.heartbeat_response_callback:
+                                self.heartbeat_response_callback(
+                                    heartbeat_message)
+                        except (socket.error, SyntaxError):
+                            pass
+                        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
+
+                #If this is an empty application-data fragment, try again
+                if recordHeader.type == ContentType.application_data:
+                    if p.index == len(p.bytes):
+                        continue
+
+                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:
+                        exp = to_str_delimiter(HandshakeType.toStr(i) for i in
+                                               secondaryType)
+                        rec = HandshakeType.toStr(subType)
+                        for result in self._sendError(AlertDescription
+                                                      .unexpected_message,
+                                                      "Expecting {0}, got {1}"
+                                                      .format(exp, rec)):
+                            yield result
+
+                # in TLS 1.3 some Handshake messages MUST NOT span key changes
+                if self.version > (3, 3) and \
+                        subType in (HandshakeType.client_hello,
+                                    HandshakeType.end_of_early_data,
+                                    HandshakeType.server_hello,
+                                    HandshakeType.finished,
+                                    HandshakeType.key_update) and \
+                        not self._defragmenter.is_empty():
+                    for result in self._sendError(
+                            AlertDescription.unexpected_message,
+                            "CH, EOED, SH, Finished, or KU not aligned with "
+                            "record boundary"):
+                        yield result
+
+                #Update handshake hashes
+                self._handshake_hash.update(p.bytes)
+
+                #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, self.version).parse(p)
+                elif subType == HandshakeType.certificate_request:
+                    yield CertificateRequest(self.version).parse(p)
+                elif subType == HandshakeType.certificate_verify:
+                    yield CertificateVerify(self.version).parse(p)
+                elif subType == HandshakeType.server_key_exchange:
+                    yield ServerKeyExchange(constructorType,
+                                            self.version).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, constructorType).parse(p)
+                elif subType == HandshakeType.next_protocol:
+                    yield NextProtocol().parse(p)
+                elif subType == HandshakeType.encrypted_extensions:
+                    yield EncryptedExtensions().parse(p)
+                elif subType == HandshakeType.new_session_ticket:
+                    if self.version < (3, 4):
+                        yield NewSessionTicket1_0().parse(p)
+                    else:
+                        yield NewSessionTicket().parse(p)
+                elif subType == HandshakeType.key_update:
+                    yield KeyUpdate().parse(p)
+                else:
+                    raise AssertionError()
+
+        #If an exception was raised by a Parser or Message instance:
+        except BadCertificateError as e:
+            for result in self._sendError(AlertDescription.bad_certificate,
+                                          formatExceptionTrace(e)):
+                yield result
+        except SyntaxError as e:
+            for result in self._sendError(AlertDescription.decode_error,
+                                          formatExceptionTrace(e)):
+                yield result
+
+    #Returns next record or next handshake message
     def _getNextRecord(self):
         """read next message from socket, defragment message"""
-        pass
+
+        while True:
+            # support for fragmentation
+            # (RFC 5246 Section 6.2.1)
+            # Because the Record Layer is completely separate from the messages
+            # that traverse it, it should handle both application data and
+            # hadshake data in the same way. For that we buffer the handshake
+            # messages until they are completely read.
+            # This makes it possible to handle both handshake data not aligned
+            # to record boundary as well as handshakes longer than single
+            # record.
+            while True:
+                # empty message buffer
+                ret = self._defragmenter.get_message()
+                if ret is None:
+                    break
+                header = RecordHeader3().create(self.version, ret[0], 0)
+                yield header, Parser(ret[1])
+
+            # CCS can be sent before early_data but processing it will
+            # remove the flag from record layer, so reset it
+            early_data_ok = self._recordLayer.early_data_ok
+
+            # when the message buffer is empty, read next record from socket
+            for result in self._getNextRecordFromSocket():
+                if result in (0, 1):
+                    yield result
+                else:
+                    break
+
+            header, parser = result
+
+            # application data (and CCS in TLS1.3) isn't made out of messages,
+            # pass it through
+            if header.type == ContentType.application_data or \
+                    (self.version > (3, 3) and
+                     header.type == ContentType.change_cipher_spec):
+                # CCS doesn't change the status of undecryptable
+                # records
+                if header.type == ContentType.change_cipher_spec:
+                    self._recordLayer.early_data_ok = early_data_ok
+                yield (header, parser)
+            # heartbeat message isn't made out of messages, too
+            elif header.type == ContentType.heartbeat:
+                yield (header, parser)
+            # If it's an SSLv2 ClientHello, we can return it as well, since
+            # it's the only ssl2 type we support
+            elif header.ssl2:
+                yield (header, parser)
+            else:
+                # other types need to be put into buffers
+                self._defragmenter.add_data(header.type, parser.bytes)

     def _getNextRecordFromSocket(self):
         """Read a record, handle errors"""
-        pass
+
+        try:
+            # otherwise... read the next record
+            for result in self._recordLayer.recvRecord():
+                if result in (0, 1):
+                    yield result
+                else:
+                    break
+        except TLSUnexpectedMessage:
+            for result in self._sendError(AlertDescription.unexpected_message):
+                yield result
+        except TLSRecordOverflow:
+            for result in self._sendError(AlertDescription.record_overflow):
+                yield result
+        except TLSIllegalParameterException:
+            for result in self._sendError(AlertDescription.illegal_parameter):
+                yield result
+        except TLSDecryptionFailed:
+            for result in self._sendError(
+                    AlertDescription.decryption_failed,
+                    "Encrypted data not a multiple of blocksize"):
+                yield result
+        except TLSBadRecordMAC:
+            for result in self._sendError(
+                    AlertDescription.bad_record_mac,
+                    "MAC failure (or padding failure)"):
+                yield result
+
+        header, parser = result
+
+        # RFC5246 section 6.2.1: Implementations MUST NOT send
+        # zero-length fragments of content types other than Application
+        # Data.
+        if header.type != ContentType.application_data \
+                and parser.getRemainingLength() == 0:
+            for result in self._sendError(
+                    AlertDescription.unexpected_message,
+                    "Received empty non-application data record"):
+                yield result
+
+        if header.type not in ContentType.all:
+            for result in self._sendError(\
+                    AlertDescription.unexpected_message, \
+                    "Received record with unknown ContentType"):
+                yield result
+
+        yield (header, parser)
+
+    def _handshakeStart(self, client):
+        if not self.closed:
+            raise ValueError("Renegotiation disallowed for security reasons")
+        self._client = client
+        self._handshake_hash = HandshakeHashes()
+        self._certificate_verify_handshake_hash = None
+        self._pre_client_hello_handshake_hash = None
+        self._defragmenter.clear_buffers()
+        self.allegedSrpUsername = None
+        self._refCount = 1
+
+    def _handshakeDone(self, resumed):
+        self.resumed = resumed
+        self.closed = False
+
+    def _calcPendingStates(self, cipherSuite, masterSecret,
+                           clientRandom, serverRandom, implementations):
+        self._recordLayer.calcPendingStates(cipherSuite, masterSecret,
+                                            clientRandom, serverRandom,
+                                            implementations)
+
+    def _changeWriteState(self):
+        self._recordLayer.changeWriteState()
+
+    def _changeReadState(self):
+        self._recordLayer.changeReadState()

     def write_heartbeat(self, payload, padding_length):
         """Start a write operation of heartbeat_request.
@@ -448,7 +1408,18 @@ class TLSRecordLayer(object):

         :raise socket.error: If a socket error occurs.
         """
-        pass
+        if self.closed:
+            raise TLSClosedConnectionError(
+                "attempt to write to closed connection")
+        if not self.heartbeat_supported or not self.heartbeat_can_send:
+            raise TLSInternalError("attempt to send Heartbeat request when "
+                                   "we cant send it to other side")
+        heartbeat_request = Heartbeat().create(
+            HeartbeatMessageType.heartbeat_request, payload, padding_length)
+
+        for result in self._sendMsg(heartbeat_request,
+                                    randomizeFirstBlock=False):
+            yield result

     def send_heartbeat_request(self, payload, padding_length):
         """Synchronous version of write_heartbeat function.
@@ -462,7 +1433,8 @@ class TLSRecordLayer(object):

         :raise socket.error: If a socket error occurs.
         """
-        pass
+        for _ in self.write_heartbeat(payload, padding_length):
+            pass

     def _handle_keyupdate_request(self, request):
         """Process the KeyUpdate request.
@@ -470,7 +1442,22 @@ class TLSRecordLayer(object):
         :type request: KeyUpdate
         :param request: Recieved KeyUpdate message.
         """
-        pass
+        if request.message_type == KeyUpdateMessageType.update_not_requested or\
+                request.message_type == KeyUpdateMessageType.update_requested:
+            self.session.cl_app_secret, self.session.sr_app_secret = self._recordLayer.\
+                calcTLS1_3KeyUpdate_sender(
+                    self.session.cipherSuite,
+                    self.session.cl_app_secret,
+                    self.session.sr_app_secret)
+            if request.message_type == KeyUpdateMessageType.update_requested:
+                for result in self.send_keyupdate_request(
+                        KeyUpdateMessageType.update_not_requested):
+                    yield result
+        else:
+            for result in self._sendError(
+                    AlertDescription.illegal_parameter,
+                    "Received KeyUpdate request with unknown message_type"):
+                yield result

     def send_keyupdate_request(self, message_type):
         """Send a KeyUpdate message.
@@ -480,4 +1467,18 @@ class TLSRecordLayer(object):

         :raise socket.error: If a socket error occurs.
         """
-        pass
+        if self.closed:
+            raise TLSClosedConnectionError(
+                "attempt to write to closed connection")
+        if self.version != (3, 4):
+            raise TLSIllegalParameterException("KeyUpdate is a TLS 1.3 specific"
+                                               " feature")
+
+        keyupdate_request = KeyUpdate().create(message_type)
+        for result in self._sendMsg(keyupdate_request):
+            yield result
+        self.session.cl_app_secret, self.session.sr_app_secret = \
+            self._recordLayer.calcTLS1_3KeyUpdate_reciever(
+                    self.session.cipherSuite,
+                    self.session.cl_app_secret,
+                    self.session.sr_app_secret)
diff --git a/tlslite/utils/aes.py b/tlslite/utils/aes.py
index b15fe4c..830ff37 100644
--- a/tlslite/utils/aes.py
+++ b/tlslite/utils/aes.py
@@ -1,8 +1,9 @@
-"""Abstract class for AES."""
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.

+"""Abstract class for AES."""

 class AES(object):
-
     def __init__(self, key, mode, IV, implementation):
         if len(key) not in (16, 24, 32):
             raise AssertionError()
@@ -18,11 +19,21 @@ class AES(object):
         self.isAEAD = False
         self.block_size = 16
         self.implementation = implementation
-        if len(key) == 16:
-            self.name = 'aes128'
-        elif len(key) == 24:
-            self.name = 'aes192'
-        elif len(key) == 32:
-            self.name = 'aes256'
+        if len(key)==16:
+            self.name = "aes128"
+        elif len(key)==24:
+            self.name = "aes192"
+        elif len(key)==32:
+            self.name = "aes256"
         else:
             raise AssertionError()
+
+    #CBC-Mode encryption, returns ciphertext
+    #WARNING: *MAY* modify the input as well
+    def encrypt(self, plaintext):
+        assert(len(plaintext) % 16 == 0)
+
+    #CBC-Mode decryption, returns plaintext
+    #WARNING: *MAY* modify the input as well
+    def decrypt(self, ciphertext):
+        assert(len(ciphertext) % 16 == 0)
diff --git a/tlslite/utils/aesccm.py b/tlslite/utils/aesccm.py
index fccfb40..9c76962 100644
--- a/tlslite/utils/aesccm.py
+++ b/tlslite/utils/aesccm.py
@@ -1,9 +1,15 @@
+# Copyright (c) 2019 Ivan Nikolchev
+#
+# See the LICENSE file for legal information regarding use of this file.
+#
+
 from __future__ import division
 from tlslite.utils.cryptomath import numberToByteArray
 from tlslite.utils import python_aes


 class AESCCM(object):
+    # AES-CCM implementation per RFC3610

     def __init__(self, key, implementation, rawAesEncrypt, tag_length=16):
         self.isBlockCipher = False
@@ -12,14 +18,138 @@ class AESCCM(object):
         self.tagLength = tag_length
         self.nonceLength = 12
         self.implementation = implementation
+
         if len(self.key) == 16 and self.tagLength == 8:
-            self.name = 'aes128ccm_8'
+            self.name = "aes128ccm_8"
         elif len(self.key) == 16 and self.tagLength == 16:
-            self.name = 'aes128ccm'
+            self.name = "aes128ccm"
         elif len(self.key) == 32 and self.tagLength == 8:
-            self.name = 'aes256ccm_8'
+            self.name = "aes256ccm_8"
         else:
             assert len(self.key) == 32 and self.tagLength == 16
-            self.name = 'aes256ccm'
+            self.name = "aes256ccm"
+
         self._ctr = python_aes.new(self.key, 6, bytearray(b'\x00' * 16))
         self._cbc = python_aes.new(self.key, 2, bytearray(b'\x00' * 16))
+
+
+    def _cbcmac_calc(self, nonce, aad, msg):
+        L = 15 - len(nonce)
+        mac_data = bytearray()
+
+        # Flags constructed as in section 2.2 in the rfc
+        flags = 64 * (len(aad) > 0)
+        flags += 8 * ((self.tagLength - 2) // 2)
+        flags += 1 * (L - 1)
+
+        # Construct B_0
+        b_0 = bytearray([flags]) + nonce + numberToByteArray(len(msg), L)
+
+        aad_len_encoded = bytearray()
+        if len(aad) > 0:
+            if len(aad) < (2 ** 16 - 2 ** 8):
+                oct_size = 2
+            elif len(aad) < (2 ** 32):
+                oct_size = 4
+                aad_len_encoded = b'\xFF\xFE'
+            else:
+                oct_size = 8
+                aad_len_encoded = b'\xFF\xFF'
+
+            aad_len_encoded += numberToByteArray(len(aad), oct_size)
+
+        # Construct the bytearray that goes into the MAC
+        mac_data += b_0
+        mac_data += aad_len_encoded
+        mac_data += aad
+
+        # We need to pad with zeroes before and after msg blocks are added
+        self._pad_with_zeroes(mac_data, 16)
+        if msg != b'':
+            mac_data += msg
+            self._pad_with_zeroes(mac_data, 16)
+
+        # The mac data is now constructed and
+        # we need to run in through AES-CBC with 0 IV
+
+        self._cbc.IV = bytearray(b'\x00' * 16)
+        cbcmac = self._cbc.encrypt(mac_data)
+
+        # If the tagLength has default value 16, we return
+        # the whole last block. Otherwise we return only
+        # the first tagLength bytes from the last block
+        if self.tagLength == 16:
+            t = cbcmac[-16:]
+        else:
+            t = cbcmac[-16:-(16-self.tagLength)]
+        return t
+
+    def seal(self, nonce, msg, aad):
+
+        if len(nonce) != 12:
+            raise ValueError("Bad nonce length")
+
+        L = 15 - len(nonce)
+
+        # We construct the key stream blocks.
+        # S_0 is not used for encrypting the message, it is only used
+        # to compute the authentication value.
+        # S_1..S_n are used to encrypt the message.
+
+        flags = L - 1
+        s_0 = bytearray([flags]) + nonce + numberToByteArray(0, L)
+
+        mac = self._cbcmac_calc(nonce, aad, msg)
+        self._ctr.counter = s_0
+        if self.tagLength == 16:
+            auth_value = self._ctr.encrypt(mac)
+        else:
+            assert self.tagLength == 8
+            self._pad_with_zeroes(mac, 16)
+            auth_value = self._ctr.encrypt(mac)[:8]
+        enc_msg = self._ctr.encrypt(msg)
+
+        ciphertext = enc_msg + auth_value
+        return ciphertext
+
+    def open(self, nonce, ciphertext, aad):
+
+        if len(nonce) != 12:
+            raise ValueError("Bad nonce length")
+        if self.tagLength == 16 and len(ciphertext) < 16:
+            return None
+        if self.tagLength == 8 and len(ciphertext) < 8:
+            return None
+
+        L = 15 - len(nonce)
+        flags = L - 1
+
+        # Same construction as in seal function
+
+        s_0 = bytearray([flags]) + nonce + numberToByteArray(0, L)
+
+        auth_value = ciphertext[-self.tagLength:]
+
+        # We decrypt the auth value
+        self._ctr.counter = s_0
+        if self.tagLength == 16:
+            received_mac = self._ctr.decrypt(auth_value)
+        else:
+            assert self.tagLength == 8
+            self._pad_with_zeroes(auth_value, 16)
+            received_mac = self._ctr.decrypt(auth_value)[:8]
+        msg = self._ctr.decrypt(ciphertext)
+        msg = msg[:-self.tagLength]
+        computed_mac = self._cbcmac_calc(nonce, aad, msg)
+
+
+        # Compare the mac vlaue is the same as the one we computed
+        if received_mac != computed_mac:
+            return None
+        return msg
+
+    @staticmethod
+    def _pad_with_zeroes(data, size):
+        if len(data) % size != 0:
+            zeroes_to_add = size - (len(data) % size)
+            data += b'\x00' * zeroes_to_add
diff --git a/tlslite/utils/aesgcm.py b/tlslite/utils/aesgcm.py
index 37d5518..34e7738 100644
--- a/tlslite/utils/aesgcm.py
+++ b/tlslite/utils/aesgcm.py
@@ -1,9 +1,23 @@
+# Author: Google
+# See the LICENSE file for legal information regarding use of this file.
+
+# GCM derived from Go's implementation in crypto/cipher.
+#
+# https://golang.org/src/crypto/cipher/gcm.go
+
+# GCM works over elements of the field GF(2^128), each of which is a 128-bit
+# polynomial. Throughout this implementation, polynomials are represented as
+# Python integers with the low-order terms at the most significant bits. So a
+# 128-bit polynomial is an integer from 0 to 2^128-1 with the most significant
+# bit representing the x^0 term and the least significant bit representing the
+# x^127 term. This bit reversal also applies to polynomials used as indices in a
+# look-up table.
+
 from __future__ import division
 from tlslite.utils import python_aes
 from .constanttime import ct_compare_digest
 from .cryptomath import bytesToNumber, numberToByteArray

-
 class AESGCM(object):
     """
     AES-GCM implementation. Note: this implementation does not attempt
@@ -17,33 +31,97 @@ class AESGCM(object):
         self.tagLength = 16
         self.implementation = implementation
         if len(key) == 16:
-            self.name = 'aes128gcm'
+            self.name = "aes128gcm"
         elif len(key) == 32:
-            self.name = 'aes256gcm'
+            self.name = "aes256gcm"
         else:
             raise AssertionError()
         self.key = key
+
         self._rawAesEncrypt = rawAesEncrypt
         self._ctr = python_aes.new(self.key, 6, bytearray(b'\x00' * 16))
+
+        # The GCM key is AES(0).
         h = bytesToNumber(self._rawAesEncrypt(bytearray(16)))
+
+        # Pre-compute all 4-bit multiples of h. Note that bits are reversed
+        # because our polynomial representation places low-order terms at the
+        # most significant bit. Thus x^0 * h = h is at index 0b1000 = 8 and
+        # x^1 * h is at index 0b0100 = 4.
         self._productTable = [0] * 16
         self._productTable[self._reverseBits(1)] = h
         for i in range(2, 16, 2):
-            self._productTable[self._reverseBits(i)] = self._gcmShift(self.
-                _productTable[self._reverseBits(i // 2)])
-            self._productTable[self._reverseBits(i + 1)] = self._gcmAdd(self
-                ._productTable[self._reverseBits(i)], h)
+            self._productTable[self._reverseBits(i)] = \
+                self._gcmShift(self._productTable[self._reverseBits(i//2)])
+            self._productTable[self._reverseBits(i+1)] = \
+                self._gcmAdd(self._productTable[self._reverseBits(i)], h)
+
+
+    def _auth(self, ciphertext, ad, tagMask):
+        y = 0
+        y = self._update(y, ad)
+        y = self._update(y, ciphertext)
+        y ^= (len(ad) << (3 + 64)) | (len(ciphertext) << 3)
+        y = self._mul(y)
+        y ^= bytesToNumber(tagMask)
+        return numberToByteArray(y, 16)
+
+    def _update(self, y, data):
+        for i in range(0, len(data) // 16):
+            y ^= bytesToNumber(data[16*i:16*i+16])
+            y = self._mul(y)
+        extra = len(data) % 16
+        if extra != 0:
+            block = bytearray(16)
+            block[:extra] = data[-extra:]
+            y ^= bytesToNumber(block)
+            y = self._mul(y)
+        return y

     def _mul(self, y):
         """ Returns y*H, where H is the GCM key. """
-        pass
+        ret = 0
+        # Multiply H by y 4 bits at a time, starting with the highest power
+        # terms.
+        for i in range(0, 128, 4):
+            # Multiply by x^4. The reduction for the top four terms is
+            # precomputed.
+            retHigh = ret & 0xf
+            ret >>= 4
+            ret ^= (AESGCM._gcmReductionTable[retHigh] << (128-16))
+
+            # Add in y' * H where y' are the next four terms of y, shifted down
+            # to the x^0..x^4. This is one of the pre-computed multiples of
+            # H. The multiplication by x^4 shifts them back into place.
+            ret ^= self._productTable[y & 0xf]
+            y >>= 4
+        assert y == 0
+        return ret

     def seal(self, nonce, plaintext, data):
         """
         Encrypts and authenticates plaintext using nonce and data. Returns the
         ciphertext, consisting of the encrypted plaintext and tag concatenated.
         """
-        pass
+
+        if len(nonce) != 12:
+            raise ValueError("Bad nonce length")
+
+        # The initial counter value is the nonce, followed by a 32-bit counter
+        # that starts at 1. It's used to compute the tag mask.
+        counter = bytearray(16)
+        counter[:12] = nonce
+        counter[-1] = 1
+        tagMask = self._rawAesEncrypt(counter)
+
+        # The counter starts at 2 for the actual encryption.
+        counter[-1] = 2
+        self._ctr.counter = counter
+        ciphertext = self._ctr.encrypt(plaintext)
+
+        tag = self._auth(ciphertext, data, tagMask)
+
+        return ciphertext + tag

     def open(self, nonce, ciphertext, data):
         """
@@ -51,6 +129,65 @@ class AESGCM(object):
         tag is valid, the plaintext is returned. If the tag is invalid,
         returns None.
         """
-        pass
-    _gcmReductionTable = [0, 7200, 14400, 9312, 28800, 27808, 18624, 21728,
-        57600, 64800, 55616, 50528, 37248, 36256, 43456, 46560]
+
+        if len(nonce) != 12:
+            raise ValueError("Bad nonce length")
+        if len(ciphertext) < 16:
+            return None
+
+        tag = ciphertext[-16:]
+        ciphertext = ciphertext[:-16]
+
+        # The initial counter value is the nonce, followed by a 32-bit counter
+        # that starts at 1. It's used to compute the tag mask.
+        counter = bytearray(16)
+        counter[:12] = nonce
+        counter[-1] = 1
+        tagMask = self._rawAesEncrypt(counter)
+
+        if not ct_compare_digest(tag, self._auth(ciphertext, data, tagMask)):
+            return None
+
+        # The counter starts at 2 for the actual decryption.
+        counter[-1] = 2
+        self._ctr.counter = counter
+        return self._ctr.decrypt(ciphertext)
+
+    @staticmethod
+    def _reverseBits(i):
+        assert i < 16
+        i = ((i << 2) & 0xc) | ((i >> 2) & 0x3)
+        i = ((i << 1) & 0xa) | ((i >> 1) & 0x5)
+        return i
+
+    @staticmethod
+    def _gcmAdd(x, y):
+        return x ^ y
+
+    @staticmethod
+    def _gcmShift(x):
+        # Multiplying by x is a right shift, due to bit order.
+        highTermSet = x & 1
+        x >>= 1
+        if highTermSet:
+            # The x^127 term was shifted up to x^128, so subtract a 1+x+x^2+x^7
+            # term. This is 0b11100001 or 0xe1 when represented as an 8-bit
+            # polynomial.
+            x ^= 0xe1 << (128-8)
+        return x
+
+    @staticmethod
+    def _inc32(counter):
+        for i in range(len(counter)-1, len(counter)-5, -1):
+            counter[i] = (counter[i] + 1) % 256
+            if counter[i] != 0:
+                break
+        return counter
+
+    # _gcmReductionTable[i] is i * (1+x+x^2+x^7) for all 4-bit polynomials i. The
+    # result is stored as a 16-bit polynomial. This is used in the reduction step to
+    # multiply elements of GF(2^128) by x^4.
+    _gcmReductionTable = [
+        0x0000, 0x1c20, 0x3840, 0x2460, 0x7080, 0x6ca0, 0x48c0, 0x54e0,
+        0xe100, 0xfd20, 0xd940, 0xc560, 0x9180, 0x8da0, 0xa9c0, 0xb5e0,
+    ]
diff --git a/tlslite/utils/asn1parser.py b/tlslite/utils/asn1parser.py
index 126a3ff..6edfa7e 100644
--- a/tlslite/utils/asn1parser.py
+++ b/tlslite/utils/asn1parser.py
@@ -1,4 +1,10 @@
+# Author: Trevor Perrin
+# Patch from Google adding getChildBytes()
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """Abstract Syntax Notation One (ASN.1) parsing"""
+
 from .codec import Parser


@@ -45,8 +51,14 @@ class ASN1Parser(object):
         :param bytes: DER encoded ASN.1 object
         """
         p = Parser(bytes)
+
+        # Get Type
         self.type = self._parse_type(p)
+
+        #Get Length
         self.length = self._getASN1Length(p)
+
+        #Get Value
         self.value = p.getFixBytes(self.length)

     def getChild(self, which):
@@ -59,7 +71,7 @@ class ASN1Parser(object):
         :rtype: ASN1Parser
         :returns: decoded child object
         """
-        pass
+        return ASN1Parser(self.getChildBytes(which))

     def getChildCount(self):
         """
@@ -68,7 +80,16 @@ class ASN1Parser(object):
         :rtype: int
         :returns: number of children in the object
         """
-        pass
+        p = Parser(self.value)
+        count = 0
+        while True:
+            if p.getRemainingLength() == 0:
+                break
+            p.skip_bytes(1)  # skip Type
+            length = self._getASN1Length(p)
+            p.skip_bytes(length)  # skip value
+            count += 1
+        return count

     def getChildBytes(self, which):
         """
@@ -80,14 +101,40 @@ class ASN1Parser(object):
         :rtype: bytearray
         :returns: raw child object
         """
-        pass
+        p = Parser(self.value)
+        for _ in range(which+1):
+            markIndex = p.index
+            p.skip_bytes(1)  # skip Type
+            length = self._getASN1Length(p)
+            p.skip_bytes(length)
+        return p.bytes[markIndex : p.index]

     @staticmethod
     def _getASN1Length(p):
         """Decode the ASN.1 DER length field"""
-        pass
+        firstLength = p.get(1)
+        if firstLength <= 127:
+            return firstLength
+        else:
+            lengthLength = firstLength & 0x7F
+            return p.get(lengthLength)

     @staticmethod
     def _parse_type(parser):
         """Decode the ASN.1 DER type field"""
-        pass
+        header = parser.get(1)
+        tag_class = (header & 0xc0) >> 6
+        tag_is_primitive = (header & 0x20) >> 5
+        tag_id = header & 0x1f
+
+        if tag_id == 0x1f:
+            tag_id = 0
+            while True:
+                value = parser.get(1)
+                tag_id += value & 0x7f
+                if not value & 0x80:
+                    break
+                tag_id <<= 7
+
+        asn1type = ASN1Type(tag_class, tag_is_primitive, tag_id)
+        return asn1type
diff --git a/tlslite/utils/chacha.py b/tlslite/utils/chacha.py
index e370972..ac6e94c 100644
--- a/tlslite/utils/chacha.py
+++ b/tlslite/utils/chacha.py
@@ -1,70 +1,157 @@
+# Copyright (c) 2015, Hubert Kario
+#
+# See the LICENSE file for legal information regarding use of this file.
 """Pure Python implementation of ChaCha cipher

 Implementation that follows RFC 7539 closely.
 """
+
 from __future__ import division
 from .compat import compat26Str
 import copy
 import struct
 try:
+    # in Python 3 the native zip returns iterator
     from itertools import izip
 except ImportError:
     izip = zip

-
 class ChaCha(object):
+
     """Pure python implementation of ChaCha cipher"""
-    constants = [1634760805, 857760878, 2036477234, 1797285236]
+
+    constants = [0x61707865, 0x3320646e, 0x79622d32, 0x6b206574]

     @staticmethod
     def rotl32(v, c):
         """Rotate left a 32 bit integer v by c bits"""
-        pass
+        return ((v << c) & 0xffffffff) | (v >> (32 - c))

     @staticmethod
     def quarter_round(x, a, b, c, d):
         """Perform a ChaCha quarter round"""
-        pass
-    _round_mixup_box = [(0, 4, 8, 12), (1, 5, 9, 13), (2, 6, 10, 14), (3, 7,
-        11, 15), (0, 5, 10, 15), (1, 6, 11, 12), (2, 7, 8, 13), (3, 4, 9, 14)]
+        xa = x[a]
+        xb = x[b]
+        xc = x[c]
+        xd = x[d]
+
+        xa = (xa + xb) & 0xffffffff
+        xd = xd ^ xa
+        xd = ((xd << 16) & 0xffffffff | (xd >> 16))
+
+        xc = (xc + xd) & 0xffffffff
+        xb = xb ^ xc
+        xb = ((xb << 12) & 0xffffffff | (xb >> 20))
+
+        xa = (xa + xb) & 0xffffffff
+        xd = xd ^ xa
+        xd = ((xd << 8) & 0xffffffff | (xd >> 24))
+
+        xc = (xc + xd) & 0xffffffff
+        xb = xb ^ xc
+        xb = ((xb << 7) & 0xffffffff | (xb >> 25))
+
+        x[a] = xa
+        x[b] = xb
+        x[c] = xc
+        x[d] = xd
+
+    _round_mixup_box = [(0, 4, 8, 12),
+                        (1, 5, 9, 13),
+                        (2, 6, 10, 14),
+                        (3, 7, 11, 15),
+                        (0, 5, 10, 15),
+                        (1, 6, 11, 12),
+                        (2, 7, 8, 13),
+                        (3, 4, 9, 14)]

     @classmethod
     def double_round(cls, x):
         """Perform two rounds of ChaCha cipher"""
-        pass
+        for a, b, c, d in cls._round_mixup_box:
+            xa = x[a]
+            xb = x[b]
+            xc = x[c]
+            xd = x[d]
+
+            xa = (xa + xb) & 0xffffffff
+            xd = xd ^ xa
+            xd = ((xd << 16) & 0xffffffff | (xd >> 16))
+
+            xc = (xc + xd) & 0xffffffff
+            xb = xb ^ xc
+            xb = ((xb << 12) & 0xffffffff | (xb >> 20))
+
+            xa = (xa + xb) & 0xffffffff
+            xd = xd ^ xa
+            xd = ((xd << 8) & 0xffffffff | (xd >> 24))
+
+            xc = (xc + xd) & 0xffffffff
+            xb = xb ^ xc
+            xb = ((xb << 7) & 0xffffffff | (xb >> 25))
+
+            x[a] = xa
+            x[b] = xb
+            x[c] = xc
+            x[d] = xd

     @staticmethod
     def chacha_block(key, counter, nonce, rounds):
         """Generate a state of a single block"""
-        pass
+        state = ChaCha.constants + key + [counter] + nonce
+
+        working_state = state[:]
+        dbl_round = ChaCha.double_round
+        for _ in range(0, rounds // 2):
+            dbl_round(working_state)
+
+        return [(st + wrkSt) & 0xffffffff for st, wrkSt
+                in izip(state, working_state)]

     @staticmethod
     def word_to_bytearray(state):
         """Convert state to little endian bytestream"""
-        pass
+        return bytearray(struct.pack('<LLLLLLLLLLLLLLLL', *state))

     @staticmethod
     def _bytearray_to_words(data):
         """Convert a bytearray to array of word sized ints"""
-        pass
+        ret = []
+        for i in range(0, len(data)//4):
+            ret.extend(struct.unpack('<L',
+                                     compat26Str(data[i*4:(i+1)*4])))
+        return ret

     def __init__(self, key, nonce, counter=0, rounds=20):
         """Set the initial state for the ChaCha cipher"""
         if len(key) != 32:
-            raise ValueError('Key must be 256 bit long')
+            raise ValueError("Key must be 256 bit long")
         if len(nonce) != 12:
-            raise ValueError('Nonce must be 96 bit long')
+            raise ValueError("Nonce must be 96 bit long")
         self.key = []
         self.nonce = []
         self.counter = counter
         self.rounds = rounds
+
+        # convert bytearray key and nonce to little endian 32 bit unsigned ints
         self.key = ChaCha._bytearray_to_words(key)
         self.nonce = ChaCha._bytearray_to_words(nonce)

     def encrypt(self, plaintext):
         """Encrypt the data"""
-        pass
+        encrypted_message = bytearray()
+        for i, block in enumerate(plaintext[i:i+64] for i
+                                  in range(0, len(plaintext), 64)):
+            key_stream = ChaCha.chacha_block(self.key,
+                                             self.counter + i,
+                                             self.nonce,
+                                             self.rounds)
+            key_stream = ChaCha.word_to_bytearray(key_stream)
+            encrypted_message += bytearray(x ^ y for x, y
+                                           in izip(key_stream, block))
+
+        return encrypted_message

     def decrypt(self, ciphertext):
         """Decrypt the data"""
-        pass
+        return self.encrypt(ciphertext)
diff --git a/tlslite/utils/chacha20_poly1305.py b/tlslite/utils/chacha20_poly1305.py
index 0268f68..bed7178 100644
--- a/tlslite/utils/chacha20_poly1305.py
+++ b/tlslite/utils/chacha20_poly1305.py
@@ -1,47 +1,69 @@
+# Copyright (c) 2015, Hubert Kario
+#
+# See the LICENSE file for legal information regarding use of this file.
 """Pure Python implementation of ChaCha20/Poly1305 AEAD cipher

 Implementation that follows RFC 7539 and draft-ietf-tls-chacha20-poly1305-00
 """
+
 from __future__ import division
 from .constanttime import ct_compare_digest
 from .chacha import ChaCha
 from .poly1305 import Poly1305
 import struct

-
 class CHACHA20_POLY1305(object):
+
     """Pure python implementation of ChaCha20/Poly1305 AEAD cipher"""

     def __init__(self, key, implementation):
         """Set the initial state for the ChaCha20 AEAD"""
         if len(key) != 32:
-            raise ValueError('Key must be 256 bit long')
-        if implementation != 'python':
-            raise ValueError('Implementations other then python unsupported')
+            raise ValueError("Key must be 256 bit long")
+        if implementation != "python":
+            raise ValueError("Implementations other then python unsupported")
+
         self.isBlockCipher = False
         self.isAEAD = True
         self.nonceLength = 12
         self.tagLength = 16
         self.implementation = implementation
-        self.name = 'chacha20-poly1305'
+        self.name = "chacha20-poly1305"
         self.key = key

     @staticmethod
     def poly1305_key_gen(key, nonce):
         """Generate the key for the Poly1305 authenticator"""
-        pass
+        poly = ChaCha(key, nonce)
+        return poly.encrypt(bytearray(32))

     @staticmethod
     def pad16(data):
         """Return padding for the Associated Authenticated Data"""
-        pass
+        if len(data) % 16 == 0:
+            return bytearray(0)
+        else:
+            return bytearray(16-(len(data)%16))

     def seal(self, nonce, plaintext, data):
         """
         Encrypts and authenticates plaintext using nonce and data. Returns the
         ciphertext, consisting of the encrypted plaintext and tag concatenated.
         """
-        pass
+        if len(nonce) != 12:
+            raise ValueError("Nonce must be 96 bit large")
+
+        otk = self.poly1305_key_gen(self.key, nonce)
+
+        ciphertext = ChaCha(self.key, nonce, counter=1).encrypt(plaintext)
+
+        mac_data = data + self.pad16(data)
+        mac_data += ciphertext + self.pad16(ciphertext)
+        mac_data += struct.pack('<Q', len(data))
+        mac_data += struct.pack('<Q', len(ciphertext))
+        tag = Poly1305(otk).create_tag(mac_data)
+
+        return ciphertext + tag

     def open(self, nonce, ciphertext, data):
         """
@@ -49,4 +71,24 @@ class CHACHA20_POLY1305(object):
         tag is valid, the plaintext is returned. If the tag is invalid,
         returns None.
         """
-        pass
+        if len(nonce) != 12:
+            raise ValueError("Nonce must be 96 bit long")
+
+        if len(ciphertext) < 16:
+            return None
+
+        expected_tag = ciphertext[-16:]
+        ciphertext = ciphertext[:-16]
+
+        otk = self.poly1305_key_gen(self.key, nonce)
+
+        mac_data = data + self.pad16(data)
+        mac_data += ciphertext + self.pad16(ciphertext)
+        mac_data += struct.pack('<Q', len(data))
+        mac_data += struct.pack('<Q', len(ciphertext))
+        tag = Poly1305(otk).create_tag(mac_data)
+
+        if not ct_compare_digest(tag, expected_tag):
+            return None
+
+        return ChaCha(self.key, nonce, counter=1).decrypt(ciphertext)
diff --git a/tlslite/utils/cipherfactory.py b/tlslite/utils/cipherfactory.py
index 02b959f..076ad56 100644
--- a/tlslite/utils/cipherfactory.py
+++ b/tlslite/utils/cipherfactory.py
@@ -1,5 +1,10 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 """Factory functions for symmetric cryptography."""
+
 import os
+
 from tlslite.utils import python_aes
 from tlslite.utils import python_aesgcm
 from tlslite.utils import python_aesccm
@@ -8,19 +13,26 @@ from tlslite.utils import python_rc4
 from tlslite.utils import python_tripledes
 from tlslite.utils import openssl_aesccm
 from tlslite.utils import openssl_aesgcm
+
 from tlslite.utils import cryptomath
+
 tripleDESPresent = True
 """Inform if the 3DES algorithm is supported."""
+
 if cryptomath.m2cryptoLoaded:
     from tlslite.utils import openssl_aes
     from tlslite.utils import openssl_rc4
     from tlslite.utils import openssl_tripledes
+
 if cryptomath.pycryptoLoaded:
     from tlslite.utils import pycrypto_aes
     from tlslite.utils import pycrypto_aesgcm
     from tlslite.utils import pycrypto_rc4
     from tlslite.utils import pycrypto_tripledes

+# **************************************************************************
+# Factory Functions for AES
+# **************************************************************************

 def createAES(key, IV, implList=None):
     """Create a new AES object.
@@ -34,8 +46,17 @@ def createAES(key, IV, implList=None):
     :rtype: tlslite.utils.AES
     :returns: An AES object.
     """
-    pass
-
+    if implList is None:
+        implList = ["openssl", "pycrypto", "python"]
+
+    for impl in implList:
+        if impl == "openssl" and cryptomath.m2cryptoLoaded:
+            return openssl_aes.new(key, 2, IV)
+        elif impl == "pycrypto" and cryptomath.pycryptoLoaded:
+            return pycrypto_aes.new(key, 2, IV)
+        elif impl == "python":
+                return python_aes.new(key, 2, IV)
+    raise NotImplementedError()

 def createAESCTR(key, IV, implList=None):
     """Create a new AESCTR object.
@@ -49,8 +70,13 @@ def createAESCTR(key, IV, implList=None):
     :rtype: tlslite.utils.AES
     :returns: An AES object.
     """
-    pass
+    if implList is None:
+        implList = ["python"]

+    for impl in implList:
+        if impl == "python":
+            return python_aes.new(key, 6, IV)
+    raise NotImplementedError()

 def createAESGCM(key, implList=None):
     """Create a new AESGCM object.
@@ -61,8 +87,17 @@ def createAESGCM(key, implList=None):
     :rtype: tlslite.utils.AESGCM
     :returns: An AESGCM object.
     """
-    pass
-
+    if implList is None:
+        implList = ["openssl", "pycrypto", "python"]
+
+    for impl in implList:
+        if impl == "openssl" and cryptomath.m2cryptoLoaded:
+            return openssl_aesgcm.new(key)
+        if impl == "pycrypto" and cryptomath.pycryptoLoaded:
+            return pycrypto_aesgcm.new(key)
+        if impl == "python":
+            return python_aesgcm.new(key)
+    raise NotImplementedError()

 def createAESCCM(key, implList=None):
     """ Create a new AESCCM object.
@@ -73,8 +108,17 @@ def createAESCCM(key, implList=None):
     :rtype: tlslite.utils.AESCCM
     :returns: An AESCCM object.
     """
-    pass

+    if implList is None:
+        implList = ["openssl", "python"]
+
+    for impl in implList:
+        if impl == "openssl" and cryptomath.m2cryptoLoaded:
+            return openssl_aesccm.new(key)
+        if impl == "python":
+            return python_aesccm.new(key)
+
+    raise NotImplementedError()

 def createAESCCM_8(key, implList=None):
     """ Create a new AESCCM object with truncated tag.
@@ -85,8 +129,17 @@ def createAESCCM_8(key, implList=None):
     :rtype: tlslite.utils.AESCCM
     :returns: An AESCCM object.
     """
-    pass

+    if implList is None:
+        implList = ["openssl", "python"]
+
+    for impl in implList:
+        if impl == "openssl" and cryptomath.m2cryptoLoaded:
+            return openssl_aesccm.new(key, 8)
+        if impl == "python":
+            return python_aesccm.new(key, 8)
+
+    raise NotImplementedError()

 def createCHACHA20(key, implList=None):
     """Create a new CHACHA20_POLY1305 object.
@@ -97,8 +150,13 @@ def createCHACHA20(key, implList=None):
     :rtype: tlslite.utils.CHACHA20_POLY1305
     :returns: A ChaCha20/Poly1305 object
     """
-    pass
+    if implList is None:
+        implList = ["python"]

+    for impl in implList:
+        if impl == "python":
+            return python_chacha20_poly1305.new(key)
+    raise NotImplementedError()

 def createRC4(key, IV, implList=None):
     """Create a new RC4 object.
@@ -112,9 +170,21 @@ def createRC4(key, IV, implList=None):
     :rtype: tlslite.utils.RC4
     :returns: An RC4 object.
     """
-    pass
-
-
+    if implList is None:
+        implList = ["openssl", "pycrypto", "python"]
+
+    if len(IV) != 0:
+        raise AssertionError()
+    for impl in implList:
+        if impl == "openssl" and cryptomath.m2cryptoLoaded:
+            return openssl_rc4.new(key)
+        elif impl == "pycrypto" and cryptomath.pycryptoLoaded:
+            return pycrypto_rc4.new(key)
+        elif impl == "python":
+            return python_rc4.new(key)
+    raise NotImplementedError()
+
+#Create a new TripleDES instance
 def createTripleDES(key, IV, implList=None):
     """Create a new 3DES object.

@@ -127,4 +197,14 @@ def createTripleDES(key, IV, implList=None):
     :rtype: tlslite.utils.TripleDES
     :returns: A 3DES object.
     """
-    pass
+    if implList is None:
+        implList = ["openssl", "pycrypto", "python"]
+
+    for impl in implList:
+        if impl == "openssl" and cryptomath.m2cryptoLoaded:
+            return openssl_tripledes.new(key, 2, IV)
+        elif impl == "pycrypto" and cryptomath.pycryptoLoaded:
+            return pycrypto_tripledes.new(key, 2, IV)
+        elif impl == "python":
+            return python_tripledes.new(key, IV)
+    raise NotImplementedError()
diff --git a/tlslite/utils/codec.py b/tlslite/utils/codec.py
index bd40984..71fb306 100644
--- a/tlslite/utils/codec.py
+++ b/tlslite/utils/codec.py
@@ -1,5 +1,10 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 """Classes for reading/writing binary data (such as TLS records)."""
+
 from __future__ import division
+
 import sys
 import struct
 from struct import pack
@@ -25,35 +30,53 @@ class Writer(object):

     def addOne(self, val):
         """Add a single-byte wide element to buffer, see add()."""
-        pass
-    if sys.version_info < (2, 7):
+        self.bytes.append(val)

+    if sys.version_info < (2, 7):
+        # struct.pack on Python2.6 does not raise exception if the value
+        # is larger than can fit inside the specified size
         def addTwo(self, val):
             """Add a double-byte wide element to buffer, see add()."""
-            pass
+            if not 0 <= val <= 0xffff:
+                raise ValueError("Can't represent value in specified length")
+            self.bytes += pack('>H', val)

         def addThree(self, val):
             """Add a three-byte wide element to buffer, see add()."""
-            pass
+            if not 0 <= val <= 0xffffff:
+                raise ValueError("Can't represent value in specified length")
+            self.bytes += pack('>BH', val >> 16, val & 0xffff)

         def addFour(self, val):
             """Add a four-byte wide element to buffer, see add()."""
-            pass
+            if not 0 <= val <= 0xffffffff:
+                raise ValueError("Can't represent value in specified length")
+            self.bytes += pack('>I', val)
     else:
-
         def addTwo(self, val):
             """Add a double-byte wide element to buffer, see add()."""
-            pass
+            try:
+                self.bytes += pack('>H', val)
+            except struct.error:
+                raise ValueError("Can't represent value in specified length")

         def addThree(self, val):
             """Add a three-byte wide element to buffer, see add()."""
-            pass
+            try:
+                self.bytes += pack('>BH', val >> 16, val & 0xffff)
+            except struct.error:
+                raise ValueError("Can't represent value in specified length")

         def addFour(self, val):
             """Add a four-byte wide element to buffer, see add()."""
-            pass
-    if sys.version_info >= (3, 0):
+            try:
+                self.bytes += pack('>I', val)
+            except struct.error:
+                raise ValueError("Can't represent value in specified length")

+    if sys.version_info >= (3, 0):
+        # the method is called thousands of times, so it's better to extern
+        # the version info check
         def add(self, x, length):
             """
             Add a single positive integer value x, encode it in length bytes
@@ -67,9 +90,12 @@ class Writer(object):
             :type length: int
             :param length: number of bytes to use for encoding the value
             """
-            pass
+            try:
+                self.bytes += x.to_bytes(length, 'big')
+            except OverflowError:
+                raise ValueError("Can't represent value in specified length")
     else:
-        _addMethods = {(1): addOne, (2): addTwo, (3): addThree, (4): addFour}
+        _addMethods = {1: addOne, 2: addTwo, 3: addThree, 4: addFour}

         def add(self, x, length):
             """
@@ -84,7 +110,17 @@ class Writer(object):
             :type length: int
             :param length: number of bytes to use for encoding the value
             """
-            pass
+            try:
+                self._addMethods[length](self, x)
+            except KeyError:
+                self.bytes += bytearray(length)
+                newIndex = len(self.bytes) - 1
+                for i in range(newIndex, newIndex - length, -1):
+                    self.bytes[i] = x & 0xFF
+                    x >>= 8
+                if x != 0:
+                    raise ValueError("Can't represent value in specified "
+                                     "length")

     def addFixSeq(self, seq, length):
         """
@@ -99,12 +135,18 @@ class Writer(object):
         :type length: int
         :param length: number of bytes to which encode every element
         """
-        pass
-    if sys.version_info < (2, 7):
+        for e in seq:
+            self.add(e, length)

+    if sys.version_info < (2, 7):
+        # struct.pack on Python2.6 does not raise exception if the value
+        # is larger than can fit inside the specified size
         def _addVarSeqTwo(self, seq):
             """Helper method for addVarSeq"""
-            pass
+            if not all(0 <= i <= 0xffff for i in seq):
+                raise ValueError("Can't represent value in specified "
+                                 "length")
+            self.bytes += pack('>' + 'H' * len(seq), *seq)

         def addVarSeq(self, seq, length, lengthLength):
             """
@@ -123,9 +165,15 @@ class Writer(object):
             :param lengthLength: amount of bytes in which to encode the overall
                 length of the array
             """
-            pass
+            self.add(len(seq)*length, lengthLength)
+            if length == 1:
+                self.bytes.extend(seq)
+            elif length == 2:
+                self._addVarSeqTwo(seq)
+            else:
+                for i in seq:
+                    self.add(i, length)
     else:
-
         def addVarSeq(self, seq, length, lengthLength):
             """
             Add a bounded list of same-sized values
@@ -143,7 +191,19 @@ class Writer(object):
             :param lengthLength: amount of bytes in which to encode the overall
                 length of the array
             """
-            pass
+            seqLen = len(seq)
+            self.add(seqLen*length, lengthLength)
+            if length == 1:
+                self.bytes.extend(seq)
+            elif length == 2:
+                try:
+                    self.bytes += pack('>' + 'H' * seqLen, *seq)
+                except struct.error:
+                    raise ValueError("Can't represent value in specified "
+                                     "length")
+            else:
+                for i in seq:
+                    self.add(i, length)

     def addVarTupleSeq(self, seq, length, lengthLength):
         """
@@ -162,7 +222,23 @@ class Writer(object):
         :type lengthLength: int
         :param lengthLength: length in bytes of overall length field
         """
-        pass
+        if not seq:
+            self.add(0, lengthLength)
+        else:
+            startPos = len(self.bytes)
+            dataLength = len(seq) * len(seq[0]) * length
+            self.add(dataLength, lengthLength)
+            # since at the time of writing, all the calls encode single byte
+            # elements, and it's very easy to speed up that case, give it
+            # special case
+            if length == 1:
+                for elemTuple in seq:
+                    self.bytes.extend(elemTuple)
+            else:
+                for elemTuple in seq:
+                    self.addFixSeq(elemTuple, length)
+            if startPos + dataLength + lengthLength != len(self.bytes):
+                raise ValueError("Tuples of different lengths")

     def add_var_bytes(self, data, length_length):
         """
@@ -176,7 +252,9 @@ class Writer(object):
         :param int length_length: size of the field to represent the length
             of the data string
         """
-        pass
+        length = len(data)
+        self.add(length, length_length)
+        self.bytes += data


 class Parser(object):
@@ -228,7 +306,8 @@ class Parser(object):

         :rtype: int
         """
-        pass
+        ret = self.getFixBytes(length)
+        return bytes_to_int(ret, 'big')

     def getFixBytes(self, lengthBytes):
         """
@@ -239,11 +318,18 @@ class Parser(object):

         :rtype: bytearray
         """
-        pass
+        end = self.index + lengthBytes
+        if end > len(self.bytes):
+            raise DecodeError("Read past end of buffer")
+        ret = self.bytes[self.index : end]
+        self.index += lengthBytes
+        return ret

     def skip_bytes(self, length):
         """Move the internal pointer ahead length bytes."""
-        pass
+        if self.index + length > len(self.bytes):
+            raise DecodeError("Read past end of buffer")
+        self.index += length

     def getVarBytes(self, lengthLength):
         """
@@ -257,7 +343,8 @@ class Parser(object):

         :rtype: bytearray
         """
-        pass
+        lengthBytes = self.get(lengthLength)
+        return self.getFixBytes(lengthBytes)

     def getFixList(self, length, lengthList):
         """
@@ -271,7 +358,10 @@ class Parser(object):

         :rtype: list of int
         """
-        pass
+        l = [0] * lengthList
+        for x in range(lengthList):
+            l[x] = self.get(length)
+        return l

     def getVarList(self, length, lengthLength):
         """
@@ -285,7 +375,15 @@ class Parser(object):

         :rtype: list of int
         """
-        pass
+        lengthList = self.get(lengthLength)
+        if lengthList % length != 0:
+            raise DecodeError("Encoded length not a multiple of element "
+                              "length")
+        lengthList = lengthList // length
+        l = [0] * lengthList
+        for x in range(lengthList):
+            l[x] = self.get(length)
+        return l

     def getVarTupleList(self, elemLength, elemNum, lengthLength):
         """
@@ -302,7 +400,18 @@ class Parser(object):

         :rtype: list of tuple of int
         """
-        pass
+        lengthList = self.get(lengthLength)
+        if lengthList % (elemLength * elemNum) != 0:
+            raise DecodeError("Encoded length not a multiple of element "
+                              "length")
+        tupleCount = lengthList // (elemLength * elemNum)
+        tupleList = []
+        for _ in range(tupleCount):
+            currentTuple = []
+            for _ in range(elemNum):
+                currentTuple.append(self.get(elemLength))
+            tupleList.append(tuple(currentTuple))
+        return tupleList

     def startLengthCheck(self, lengthLength):
         """
@@ -311,7 +420,8 @@ class Parser(object):
         :type lengthLength: int
         :param lengthLength: number of bytes in which the length is encoded
         """
-        pass
+        self.lengthCheck = self.get(lengthLength)
+        self.indexCheck = self.index

     def setLengthCheck(self, length):
         """
@@ -320,7 +430,8 @@ class Parser(object):
         :type length: int
         :param length: expected size of parsed struct in bytes
         """
-        pass
+        self.lengthCheck = length
+        self.indexCheck = self.index

     def stopLengthCheck(self):
         """
@@ -329,7 +440,8 @@ class Parser(object):
         In case the expected length was mismatched with actual length of
         processed data, raises an exception.
         """
-        pass
+        if (self.index - self.indexCheck) != self.lengthCheck:
+            raise DecodeError("Under- or over-flow while reading buffer")

     def atLengthCheck(self):
         """
@@ -341,8 +453,13 @@ class Parser(object):
         Will raise an exception if overflow occured (amount of data read was
         greater than expected size)
         """
-        pass
+        if (self.index - self.indexCheck) < self.lengthCheck:
+            return False
+        elif (self.index - self.indexCheck) == self.lengthCheck:
+            return True
+        else:
+            raise DecodeError("Read past end of buffer")

     def getRemainingLength(self):
         """Return amount of data remaining in struct being parsed."""
-        pass
+        return len(self.bytes) - self.index
diff --git a/tlslite/utils/compat.py b/tlslite/utils/compat.py
index 103b51c..542c3b0 100644
--- a/tlslite/utils/compat.py
+++ b/tlslite/utils/compat.py
@@ -1,4 +1,8 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 """Miscellaneous functions to mask Python version differences."""
+
 import sys
 import re
 import os
@@ -8,100 +12,218 @@ import binascii
 import traceback
 import time
 import ecdsa
-if sys.version_info >= (3, 0):
-    if sys.version_info < (3, 4):

+if sys.version_info >= (3,0):
+
+    def compat26Str(x): return x
+
+    # Python 3.3 requires bytes instead of bytearrays for HMAC
+    # So, python 2.6 requires strings, python 3 requires 'bytes',
+    # and python 2.7 and 3.5 can handle bytearrays...
+    # pylint: disable=invalid-name
+    # we need to keep compatHMAC and `x` for API compatibility
+    if sys.version_info < (3, 4):
         def compatHMAC(x):
             """Convert bytes-like input to format acceptable for HMAC."""
-            pass
+            return bytes(x)
     else:
-
         def compatHMAC(x):
             """Convert bytes-like input to format acceptable for HMAC."""
-            pass
+            return x
+    # pylint: enable=invalid-name

     def compatAscii2Bytes(val):
         """Convert ASCII string to bytes."""
-        pass
+        if isinstance(val, str):
+            return bytes(val, 'ascii')
+        return val

     def compat_b2a(val):
         """Convert an ASCII bytes string to string."""
-        pass
+        return str(val, 'ascii')
+
+    def raw_input(s):
+        return input(s)
+    
+    # So, the python3 binascii module deals with bytearrays, and python2
+    # deals with strings...  I would rather deal with the "a" part as
+    # strings, and the "b" part as bytearrays, regardless of python version,
+    # so...
+    def a2b_hex(s):
+        try:
+            b = bytearray(binascii.a2b_hex(bytearray(s, "ascii")))
+        except Exception as e:
+            raise SyntaxError("base16 error: %s" % e) 
+        return b  
+
+    def a2b_base64(s):
+        try:
+            if isinstance(s, str):
+                s = bytearray(s, "ascii")
+            b = bytearray(binascii.a2b_base64(s))
+        except Exception as e:
+            raise SyntaxError("base64 error: %s" % e)
+        return b
+
+    def b2a_hex(b):
+        return binascii.b2a_hex(b).decode("ascii")    
+            
+    def b2a_base64(b):
+        return binascii.b2a_base64(b).decode("ascii") 
+
+    def readStdinBinary():
+        return sys.stdin.buffer.read()        
+
+    def compatLong(num):
+        return int(num)
+
     int_types = tuple([int])

     def formatExceptionTrace(e):
         """Return exception information formatted as string"""
-        pass
+        return str(e)

     def time_stamp():
         """Returns system time as a float"""
-        pass
+        if sys.version_info >= (3, 3):
+            return time.perf_counter()
+        return time.clock()

     def remove_whitespace(text):
         """Removes all whitespace from passed in string"""
-        pass
+        return re.sub(r"\s+", "", text, flags=re.UNICODE)
+
+    # pylint: disable=invalid-name
+    # pylint is stupid here and deson't notice it's a function, not
+    # constant
     bytes_to_int = int.from_bytes
+    # pylint: enable=invalid-name

     def bit_length(val):
         """Return number of bits necessary to represent an integer."""
-        pass
+        return val.bit_length()

-    def int_to_bytes(val, length=None, byteorder='big'):
+    def int_to_bytes(val, length=None, byteorder="big"):
         """Return number converted to bytes"""
-        pass
+        if length is None:
+            length = byte_length(val)
+        # for gmpy we need to convert back to native int
+        if type(val) != int:
+            val = int(val)
+        return bytearray(val.to_bytes(length=length, byteorder=byteorder))
+
 else:
-    if sys.version_info < (2, 7) or sys.version_info < (2, 7, 4
-        ) or platform.system() == 'Java':
+    # Python 2.6 requires strings instead of bytearrays in a couple places,
+    # so we define this function so it does the conversion if needed.
+    # same thing with very old 2.7 versions
+    # or on Jython
+    if sys.version_info < (2, 7) or sys.version_info < (2, 7, 4) \
+            or platform.system() == 'Java':
+        def compat26Str(x): return str(x)

         def remove_whitespace(text):
             """Removes all whitespace from passed in string"""
-            pass
+            return re.sub(r"\s+", "", text)

         def bit_length(val):
             """Return number of bits necessary to represent an integer."""
-            pass
+            if val == 0:
+                return 0
+            return len(bin(val))-2
     else:
+        def compat26Str(x): return x

         def remove_whitespace(text):
             """Removes all whitespace from passed in string"""
-            pass
+            return re.sub(r"\s+", "", text, flags=re.UNICODE)

         def bit_length(val):
             """Return number of bits necessary to represent an integer."""
-            pass
+            return val.bit_length()

     def compatAscii2Bytes(val):
         """Convert ASCII string to bytes."""
-        pass
+        return val

     def compat_b2a(val):
         """Convert an ASCII bytes string to string."""
-        pass
-    int_types = int, long
-
+        return str(val)
+
+    # So, python 2.6 requires strings, python 3 requires 'bytes',
+    # and python 2.7 can handle bytearrays...     
+    def compatHMAC(x): return compat26Str(x)
+
+    def a2b_hex(s):
+        try:
+            b = bytearray(binascii.a2b_hex(s))
+        except Exception as e:
+            raise SyntaxError("base16 error: %s" % e)
+        return b
+
+    def a2b_base64(s):
+        try:
+            b = bytearray(binascii.a2b_base64(s))
+        except Exception as e:
+            raise SyntaxError("base64 error: %s" % e)
+        return b
+        
+    def b2a_hex(b):
+        return binascii.b2a_hex(compat26Str(b))
+        
+    def b2a_base64(b):
+        return binascii.b2a_base64(compat26Str(b))
+
+    def compatLong(num):
+        return long(num)
+
+    int_types = (int, long)
+
+    # pylint on Python3 goes nuts for the sys dereferences...
+
+    #pylint: disable=no-member
     def formatExceptionTrace(e):
         """Return exception information formatted as string"""
-        pass
+        newStr = "".join(traceback.format_exception(sys.exc_type,
+                                                    sys.exc_value,
+                                                    sys.exc_traceback))
+        return newStr
+    #pylint: enable=no-member

     def time_stamp():
         """Returns system time as a float"""
-        pass
+        return time.clock()

     def bytes_to_int(val, byteorder):
         """Convert bytes to an int."""
-        pass
-
-    def int_to_bytes(val, length=None, byteorder='big'):
+        if not val:
+            return 0
+        if byteorder == "big":
+            return int(b2a_hex(val), 16)
+        if byteorder == "little":
+            return int(b2a_hex(val[::-1]), 16)
+        raise ValueError("Only 'big' and 'little' endian supported")
+
+    def int_to_bytes(val, length=None, byteorder="big"):
         """Return number converted to bytes"""
-        pass
+        if length is None:
+            length = byte_length(val)
+        if byteorder == "big":
+            return bytearray((val >> i) & 0xff
+                             for i in reversed(range(0, length*8, 8)))
+        if byteorder == "little":
+            return bytearray((val >> i) & 0xff
+                             for i in range(0, length*8, 8))
+        raise ValueError("Only 'big' or 'little' endian supported")


 def byte_length(val):
     """Return number of bytes necessary to represent an integer."""
-    pass
+    length = bit_length(val)
+    return (length + 7) // 8


 try:
+    # Fedora and Red Hat Enterprise Linux versions have small curves removed
     getattr(ecdsa, 'NIST192p')
 except AttributeError:
     ecdsaAllCurves = False
diff --git a/tlslite/utils/constanttime.py b/tlslite/utils/constanttime.py
index e520a94..8d4541e 100644
--- a/tlslite/utils/constanttime.py
+++ b/tlslite/utils/constanttime.py
@@ -1,9 +1,13 @@
+# Copyright (c) 2015, Hubert Kario
+#
+# See the LICENSE file for legal information regarding use of this file.
 """Various constant time functions for processing sensitive data"""
+
 from __future__ import division
+
 from .compat import compatHMAC
 import hmac

-
 def ct_lt_u32(val_a, val_b):
     """
     Returns 1 if val_a < val_b, 0 otherwise. Constant time.
@@ -14,7 +18,10 @@ def ct_lt_u32(val_a, val_b):
     :param val_b: an unsigned integer representable as a 32 bit value
     :rtype: int
     """
-    pass
+    val_a &= 0xffffffff
+    val_b &= 0xffffffff
+
+    return (val_a^((val_a^val_b)|(((val_a-val_b)&0xffffffff)^val_b)))>>31


 def ct_gt_u32(val_a, val_b):
@@ -27,7 +34,7 @@ def ct_gt_u32(val_a, val_b):
     :param val_b: an unsigned integer representable as a 32 bit value
     :rtype: int
     """
-    pass
+    return ct_lt_u32(val_b, val_a)


 def ct_le_u32(val_a, val_b):
@@ -40,17 +47,26 @@ def ct_le_u32(val_a, val_b):
     :param val_b: an unsigned integer representable as a 32 bit value
     :rtype: int
     """
-    pass
+    return 1 ^ ct_gt_u32(val_a, val_b)


 def ct_lsb_prop_u8(val):
     """Propagate LSB to all 8 bits of the returned int. Constant time."""
-    pass
+    val &= 0x01
+    val |= val << 1
+    val |= val << 2
+    val |= val << 4
+    return val


 def ct_lsb_prop_u16(val):
     """Propagate LSB to all 16 bits of the returned int. Constant time."""
-    pass
+    val &= 0x01
+    val |= val << 1
+    val |= val << 2
+    val |= val << 4
+    val |= val << 8
+    return val


 def ct_isnonzero_u32(val):
@@ -61,7 +77,8 @@ def ct_isnonzero_u32(val):
     :param val: an unsigned integer representable as a 32 bit value
     :rtype: int
     """
-    pass
+    val &= 0xffffffff
+    return (val|(-val&0xffffffff)) >> 31


 def ct_neq_u32(val_a, val_b):
@@ -74,8 +91,10 @@ def ct_neq_u32(val_a, val_b):
     :param val_b: an unsigned integer representable as a 32 bit value
     :rtype: int
     """
-    pass
+    val_a &= 0xffffffff
+    val_b &= 0xffffffff

+    return (((val_a-val_b)&0xffffffff) | ((val_b-val_a)&0xffffffff)) >> 31

 def ct_eq_u32(val_a, val_b):
     """
@@ -87,11 +106,10 @@ def ct_eq_u32(val_a, val_b):
     :param val_b: an unsigned integer representable as a 32 bit value
     :rtype: int
     """
-    pass
-
+    return 1 ^ ct_neq_u32(val_a, val_b)

 def ct_check_cbc_mac_and_pad(data, mac, seqnumBytes, contentType, version,
-    block_size=16):
+                             block_size=16):
     """
     Check CBC cipher HMAC and padding. Close to constant time.

@@ -114,13 +132,87 @@ def ct_check_cbc_mac_and_pad(data, mac, seqnumBytes, contentType, version,
     :rtype: boolean
     :returns: True if MAC and pad is ok, False otherwise
     """
-    pass
-
+    assert version in ((3, 0), (3, 1), (3, 2), (3, 3))
+
+    data_len = len(data)
+    if mac.digest_size + 1 > data_len: # data_len is public
+        return False
+
+    # 0 - OK
+    result = 0x00
+
+    #
+    # check padding
+    #
+    pad_length = data[data_len-1]
+    pad_start = data_len - pad_length - 1
+    pad_start = max(0, pad_start)
+
+    if version == (3, 0): # version is public
+        # in SSLv3 we can only check if pad is not longer than the cipher
+        # block size
+
+        # subtract 1 for the pad length byte
+        mask = ct_lsb_prop_u8(ct_lt_u32(block_size, pad_length))
+        result |= mask
+    else:
+        start_pos = max(0, data_len - 256)
+        for i in range(start_pos, data_len):
+            # if pad_start < i: mask = 0xff; else: mask = 0x00
+            mask = ct_lsb_prop_u8(ct_le_u32(pad_start, i))
+            # if data[i] != pad_length and "inside_pad": result = False
+            result |= (data[i] ^ pad_length) & mask
+
+    #
+    # check MAC
+    #
+
+    # real place where mac starts and data ends
+    mac_start = pad_start - mac.digest_size
+    mac_start = max(0, mac_start)
+
+    # place to start processing
+    start_pos = max(0, data_len - (256 + mac.digest_size)) // mac.block_size
+    start_pos *= mac.block_size
+
+    # add start data
+    data_mac = mac.copy()
+    data_mac.update(compatHMAC(seqnumBytes))
+    data_mac.update(compatHMAC(bytearray([contentType])))
+    if version != (3, 0): # version is public
+        data_mac.update(compatHMAC(bytearray([version[0]])))
+        data_mac.update(compatHMAC(bytearray([version[1]])))
+    data_mac.update(compatHMAC(bytearray([mac_start >> 8])))
+    data_mac.update(compatHMAC(bytearray([mac_start & 0xff])))
+    data_mac.update(compatHMAC(data[:start_pos]))
+
+    # don't check past the array end (already checked to be >= zero)
+    end_pos = data_len - mac.digest_size
+
+    # calculate all possible
+    for i in range(start_pos, end_pos): # constant for given overall length
+        cur_mac = data_mac.copy()
+        cur_mac.update(compatHMAC(data[start_pos:i]))
+        mac_compare = bytearray(cur_mac.digest())
+        # compare the hash for real only if it's the place where mac is
+        # supposed to be
+        mask = ct_lsb_prop_u8(ct_eq_u32(i, mac_start))
+        for j in range(0, mac.digest_size): # digest_size is public
+            result |= (data[i+j] ^ mac_compare[j]) & mask
+
+    # return python boolean
+    return result == 0

 if hasattr(hmac, 'compare_digest'):
     ct_compare_digest = hmac.compare_digest
 else:
-
     def ct_compare_digest(val_a, val_b):
         """Compares if string like objects are equal. Constant time."""
-        pass
+        if len(val_a) != len(val_b):
+            return False
+
+        result = 0
+        for x, y in zip(val_a, val_b):
+            result |= x ^ y
+
+        return result == 0
diff --git a/tlslite/utils/cryptomath.py b/tlslite/utils/cryptomath.py
index 24564a0..0ab0362 100644
--- a/tlslite/utils/cryptomath.py
+++ b/tlslite/utils/cryptomath.py
@@ -1,3 +1,10 @@
+# Authors: 
+#   Trevor Perrin
+#   Martin von Loewis - python 3 port
+#   Yngve Pettersen (ported by Paul Sokolovsky) - TLS 1.2
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """cryptomath module

 This module has basic math/crypto code."""
@@ -6,74 +13,144 @@ import os
 import math
 import base64
 import binascii
-from .compat import compat26Str, compatHMAC, compatLong, bytes_to_int, int_to_bytes, bit_length, byte_length
+
+from .compat import compat26Str, compatHMAC, compatLong, \
+        bytes_to_int, int_to_bytes, bit_length, byte_length
 from .codec import Writer
+
 from . import tlshashlib as hashlib
 from . import tlshmac as hmac
+
+
+# **************************************************************************
+# Load Optional Modules
+# **************************************************************************
+
+# Try to load M2Crypto/OpenSSL
+# pylint: disable=invalid-name
 try:
     from M2Crypto import m2
     m2cryptoLoaded = True
     M2CRYPTO_AES_CTR = False
     if hasattr(m2, 'aes_192_ctr'):
         M2CRYPTO_AES_CTR = True
+
     try:
         with open('/proc/sys/crypto/fips_enabled', 'r') as fipsFile:
             if '1' in fipsFile.read():
                 m2cryptoLoaded = False
     except (IOError, OSError):
+        # looks like we're running in container, likely not FIPS mode
         m2cryptoLoaded = True
+
+    # If AES-CBC is not available, don't use m2crypto
     if not hasattr(m2, 'aes_192_cbc'):
         m2cryptoLoaded = False
+
 except ImportError:
     m2cryptoLoaded = False
+# pylint: enable=invalid-name
+
+#Try to load GMPY
 try:
     import gmpy
     gmpy.mpz
     gmpyLoaded = True
 except ImportError:
     gmpyLoaded = False
+
+
+# Try to load GMPY2
 try:
     from gmpy2 import powmod
     GMPY2_LOADED = True
 except ImportError:
     GMPY2_LOADED = False
+
+
+# Use the faster mpz
 if GMPY2_LOADED:
     from gmpy2 import mpz
 elif gmpyLoaded:
     from gmpy import mpz
+
+
+#Try to load pycrypto
+# pylint: disable=invalid-name
 try:
     import Crypto.Cipher.AES
+    # check if we're not using pycryptodome
     try:
-        Crypto.Cipher.AES.AESCipher(b'2' * (128 // 8))
+        # pycrypto defaults to ECB when just key is provided
+        # pycryptodome requires specifying the mode of operation
+        Crypto.Cipher.AES.AESCipher(b'2' * (128//8))
         pycryptoLoaded = True
     except AttributeError:
         pycryptoLoaded = False
 except ImportError:
     pycryptoLoaded = False
+# pylint: enable=invalid-name
+
+
+# **************************************************************************
+# PRNG Functions
+# **************************************************************************
+
+# Check that os.urandom works
 import zlib
 assert len(zlib.compress(os.urandom(1000))) > 900
-prngName = 'os.urandom'

+def getRandomBytes(howMany):
+    b = bytearray(os.urandom(howMany))
+    assert(len(b) == howMany)
+    return b
+
+prngName = "os.urandom"
+
+# **************************************************************************
+# Simple hash functions
+# **************************************************************************

 def MD5(b):
     """Return a MD5 digest of data"""
-    pass
-
+    return secureHash(b, 'md5')

 def SHA1(b):
     """Return a SHA1 digest of data"""
-    pass
-
+    return secureHash(b, 'sha1')

 def secureHash(data, algorithm):
     """Return a digest of `data` using `algorithm`"""
-    pass
-
+    hashInstance = hashlib.new(algorithm)
+    hashInstance.update(compat26Str(data))
+    return bytearray(hashInstance.digest())

 def secureHMAC(k, b, algorithm):
     """Return a HMAC using `b` and `k` using `algorithm`"""
-    pass
+    k = compatHMAC(k)
+    b = compatHMAC(b)
+    return bytearray(hmac.new(k, b, getattr(hashlib, algorithm)).digest())

+def HMAC_MD5(k, b):
+    return secureHMAC(k, b, 'md5')
+
+def HMAC_SHA1(k, b):
+    return secureHMAC(k, b, 'sha1')
+
+def HMAC_SHA256(k, b):
+    return secureHMAC(k, b, 'sha256')
+
+def HMAC_SHA384(k, b):
+    return secureHMAC(k, b, 'sha384')
+
+def HKDF_expand(PRK, info, L, algorithm):
+    N = divceil(L, getattr(hashlib, algorithm)().digest_size)
+    T = bytearray()
+    Titer = bytearray()
+    for x in range(1, N+2):
+        T += Titer
+        Titer = secureHMAC(PRK, Titer + info + bytearray([x]), algorithm)
+    return T[:L]

 def HKDF_expand_label(secret, label, hashValue, length, algorithm):
     """
@@ -88,8 +165,12 @@ def HKDF_expand_label(secret, label, hashValue, length, algorithm):
         basis of the HKDF
     :rtype: bytearray
     """
-    pass
+    hkdfLabel = Writer()
+    hkdfLabel.addTwo(length)
+    hkdfLabel.addVarSeq(bytearray(b"tls13 ") + label, 1, 1)
+    hkdfLabel.addVarSeq(hashValue, 1, 1)

+    return HKDF_expand(secret, hkdfLabel.bytes, length, algorithm)

 def derive_secret(secret, label, handshake_hashes, algorithm):
     """
@@ -105,19 +186,28 @@ def derive_secret(secret, label, handshake_hashes, algorithm):
         be generated
     :rtype: bytearray
     """
-    pass
-
-
-def bytesToNumber(b, endian='big'):
+    if handshake_hashes is None:
+        hs_hash = secureHash(bytearray(b''), algorithm)
+    else:
+        hs_hash = handshake_hashes.digest(algorithm)
+    return HKDF_expand_label(secret, label, hs_hash,
+                             getattr(hashlib, algorithm)().digest_size,
+                             algorithm)
+
+# **************************************************************************
+# Converter Functions
+# **************************************************************************
+
+def bytesToNumber(b, endian="big"):
     """
     Convert a number stored in bytearray to an integer.

     By default assumes big-endian encoding of the number.
     """
-    pass
+    return bytes_to_int(b, endian)


-def numberToByteArray(n, howManyBytes=None, endian='big'):
+def numberToByteArray(n, howManyBytes=None, endian="big"):
     """
     Convert an integer into a bytearray, zero-pad to howManyBytes.

@@ -125,34 +215,161 @@ def numberToByteArray(n, howManyBytes=None, endian='big'):
     not be larger.  The returned bytearray will contain a big- or little-endian
     encoding of the input integer (n). Big endian encoding is used by default.
     """
-    pass
+    if howManyBytes is not None:
+        length = byte_length(n)
+        if howManyBytes < length:
+            ret = int_to_bytes(n, length, endian)
+            if endian == "big":
+                return ret[length-howManyBytes:length]
+            return ret[:howManyBytes]
+    return int_to_bytes(n, howManyBytes, endian)


 def mpiToNumber(mpi):
     """Convert a MPI (OpenSSL bignum string) to an integer."""
-    pass
+    byte = bytearray(mpi)
+    if byte[4] & 0x80:
+        raise ValueError("Input must be a positive integer")
+    return bytesToNumber(byte[4:])
+
+
+def numberToMPI(n):
+    b = numberToByteArray(n)
+    ext = 0
+    #If the high-order bit is going to be set,
+    #add an extra byte of zeros
+    if (numBits(n) & 0x7)==0:
+        ext = 1
+    length = numBytes(n) + ext
+    b = bytearray(4+ext) + b
+    b[0] = (length >> 24) & 0xFF
+    b[1] = (length >> 16) & 0xFF
+    b[2] = (length >> 8) & 0xFF
+    b[3] = length & 0xFF
+    return bytes(b)
+
+
+# **************************************************************************
+# Misc. Utility Functions
+# **************************************************************************
+
+
+# pylint: disable=invalid-name
+# pylint recognises them as constants, not function names, also
+# we can't change their names without API change
+numBits = bit_length


-numBits = bit_length
 numBytes = byte_length
+# pylint: enable=invalid-name
+
+
+# **************************************************************************
+# Big Number Math
+# **************************************************************************
+
+def getRandomNumber(low, high):
+    assert low < high
+    howManyBits = numBits(high)
+    howManyBytes = numBytes(high)
+    lastBits = howManyBits % 8
+    while 1:
+        bytes = getRandomBytes(howManyBytes)
+        if lastBits:
+            bytes[0] = bytes[0] % (1 << lastBits)
+        n = bytesToNumber(bytes)
+        if n >= low and n < high:
+            return n
+
+def gcd(a,b):
+    a, b = max(a,b), min(a,b)
+    while b:
+        a, b = b, a % b
+    return a
+
+def lcm(a, b):
+    return (a * b) // gcd(a, b)
+
+# pylint: disable=invalid-name
+# disable pylint check as the (a, b) are part of the API
 if GMPY2_LOADED:
-
     def invMod(a, b):
         """Return inverse of a mod b, zero if none."""
-        pass
+        if a == 0:
+            return 0
+        return powmod(a, -1, b)
 else:
-
+    # Use Extended Euclidean Algorithm
     def invMod(a, b):
         """Return inverse of a mod b, zero if none."""
-        pass
+        c, d = a, b
+        uc, ud = 1, 0
+        while c != 0:
+            q = d // c
+            c, d = d-(q*c), c
+            uc, ud = ud - (q * uc), uc
+        if d == 1:
+            return ud % b
+        return 0
+# pylint: enable=invalid-name
+
+
 if gmpyLoaded or GMPY2_LOADED:
+    def powMod(base, power, modulus):
+        base = mpz(base)
+        power = mpz(power)
+        modulus = mpz(modulus)
+        result = pow(base, power, modulus)
+        return compatLong(result)
 else:
     powMod = pow


 def divceil(divident, divisor):
     """Integer division with rounding up"""
-    pass
+    quot, r = divmod(divident, divisor)
+    return quot + int(bool(r))
+
+
+#Pre-calculate a sieve of the ~100 primes < 1000:
+def makeSieve(n):
+    sieve = list(range(n))
+    for count in range(2, int(math.sqrt(n))+1):
+        if sieve[count] == 0:
+            continue
+        x = sieve[count] * 2
+        while x < len(sieve):
+            sieve[x] = 0
+            x += sieve[count]
+    sieve = [x for x in sieve[2:] if x]
+    return sieve
+
+def isPrime(n, iterations=5, display=False, sieve=makeSieve(1000)):
+    #Trial division with sieve
+    for x in sieve:
+        if x >= n: return True
+        if n % x == 0: return False
+    #Passed trial division, proceed to Rabin-Miller
+    #Rabin-Miller implemented per Ferguson & Schneier
+    #Compute s, t for Rabin-Miller
+    if display: print("*", end=' ')
+    s, t = n-1, 0
+    while s % 2 == 0:
+        s, t = s//2, t+1
+    #Repeat Rabin-Miller x times
+    a = 2 #Use 2 as a base for first iteration speedup, per HAC
+    for count in range(iterations):
+        v = powMod(a, s, n)
+        if v==1:
+            continue
+        i = 0
+        while v != n-1:
+            if i == t-1:
+                return False
+            else:
+                v, i = powMod(v, 2, n), i+1
+        a = getRandomNumber(2, n)
+    return True


 def getRandomPrime(bits, display=False):
@@ -162,13 +379,52 @@ def getRandomPrime(bits, display=False):
     the number will be 'bits' bits long (i.e. generated number will be
     larger than `(2^(bits-1) * 3 ) / 2` but smaller than 2^bits.
     """
-    pass
-
-
+    assert bits >= 10
+    #The 1.5 ensures the 2 MSBs are set
+    #Thus, when used for p,q in RSA, n will have its MSB set
+    #
+    #Since 30 is lcm(2,3,5), we'll set our test numbers to
+    #29 % 30 and keep them there
+    low = ((2 ** (bits-1)) * 3) // 2
+    high = 2 ** bits - 30
+    while True:
+        if display:
+            print(".", end=' ')
+        cand_p = getRandomNumber(low, high)
+        # make odd
+        if cand_p % 2 == 0:
+            cand_p += 1
+        if isPrime(cand_p, display=display):
+            return cand_p
+
+
+#Unused at the moment...
 def getRandomSafePrime(bits, display=False):
     """Generate a random safe prime.

     Will generate a prime `bits` bits long (see getRandomPrime) such that
     the (p-1)/2 will also be prime.
     """
-    pass
+    assert bits >= 10
+    #The 1.5 ensures the 2 MSBs are set
+    #Thus, when used for p,q in RSA, n will have its MSB set
+    #
+    #Since 30 is lcm(2,3,5), we'll set our test numbers to
+    #29 % 30 and keep them there
+    low = (2 ** (bits-2)) * 3//2
+    high = (2 ** (bits-1)) - 30
+    q = getRandomNumber(low, high)
+    q += 29 - (q % 30)
+    while 1:
+        if display: print(".", end=' ')
+        q += 30
+        if (q >= high):
+            q = getRandomNumber(low, high)
+            q += 29 - (q % 30)
+        #Ideas from Tom Wu's SRP code
+        #Do trial division on p and q before Rabin-Miller
+        if isPrime(q, 0, display=display):
+            p = (2 * q) + 1
+            if isPrime(p, display=display):
+                if isPrime(q, display=display):
+                    return p
diff --git a/tlslite/utils/datefuncs.py b/tlslite/utils/datefuncs.py
index 07fc72b..d8f0d24 100644
--- a/tlslite/utils/datefuncs.py
+++ b/tlslite/utils/datefuncs.py
@@ -1,6 +1,77 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 import os
-if os.name != 'java':
+
+#Functions for manipulating datetime objects
+#CCYY-MM-DDThh:mm:ssZ
+def parseDateClass(s):
+    year, month, day = s.split("-")
+    day, tail = day[:2], day[2:]
+    hour, minute, second = tail[1:].split(":")
+    second = second[:2]
+    year, month, day = int(year), int(month), int(day)
+    hour, minute, second = int(hour), int(minute), int(second)
+    return createDateClass(year, month, day, hour, minute, second)
+
+
+if os.name != "java":
     from datetime import datetime, timedelta
+
+    #Helper functions for working with a date/time class
+    def createDateClass(year, month, day, hour, minute, second):
+        return datetime(year, month, day, hour, minute, second)
+
+    def printDateClass(d):
+        #Split off fractional seconds, append 'Z'
+        return d.isoformat().split(".")[0]+"Z"
+
+    def getNow():
+        return datetime.utcnow()
+
+    def getHoursFromNow(hours):
+        return datetime.utcnow() + timedelta(hours=hours)
+
+    def getMinutesFromNow(minutes):
+        return datetime.utcnow() + timedelta(minutes=minutes)
+
+    def isDateClassExpired(d):
+        return d < datetime.utcnow()
+
+    def isDateClassBefore(d1, d2):
+        return d1 < d2
+
 else:
+    #Jython 2.1 is missing lots of python 2.3 stuff,
+    #which we have to emulate here:
     import java
     import jarray
+
+    def createDateClass(year, month, day, hour, minute, second):
+        c = java.util.Calendar.getInstance()
+        c.setTimeZone(java.util.TimeZone.getTimeZone("UTC"))
+        c.set(year, month-1, day, hour, minute, second)
+        return c
+
+    def printDateClass(d):
+        return "%04d-%02d-%02dT%02d:%02d:%02dZ" % \
+        (d.get(d.YEAR), d.get(d.MONTH)+1, d.get(d.DATE), \
+        d.get(d.HOUR_OF_DAY), d.get(d.MINUTE), d.get(d.SECOND))
+
+    def getNow():
+        c = java.util.Calendar.getInstance()
+        c.setTimeZone(java.util.TimeZone.getTimeZone("UTC"))
+        c.get(c.HOUR) #force refresh?
+        return c
+
+    def getHoursFromNow(hours):
+        d = getNow()
+        d.add(d.HOUR, hours)
+        return d
+
+    def isDateClassExpired(d):
+        n = getNow()
+        return d.before(n)
+
+    def isDateClassBefore(d1, d2):
+        return d1.before(d2)
diff --git a/tlslite/utils/deprecations.py b/tlslite/utils/deprecations.py
index e4f676c..b5a9175 100644
--- a/tlslite/utils/deprecations.py
+++ b/tlslite/utils/deprecations.py
@@ -1,11 +1,15 @@
+# Copyright (c) 2018 Hubert Kario
+#
+# See the LICENSE file for legal information regarding use of this file.
 """Methods for deprecating old names for arguments or attributes."""
 import warnings
 import inspect
 from functools import wraps


-def deprecated_class_name(old_name, warn=
-    "Class name '{old_name}' is deprecated, please use '{new_name}'"):
+def deprecated_class_name(old_name,
+                          warn="Class name '{old_name}' is deprecated, "
+                          "please use '{new_name}'"):
     """
     Class decorator to deprecate a use of class.

@@ -17,11 +21,53 @@ def deprecated_class_name(old_name, warn=
        keyword name and the 'new_name' for the current one.
        Example: "Old name: {old_nam}, use '{new_name}' instead".
     """
-    pass
-
-
-def deprecated_params(names, warn=
-    "Param name '{old_name}' is deprecated, please use '{new_name}'"):
+    def _wrap(obj):
+        assert callable(obj)
+
+        def _warn():
+            warnings.warn(warn.format(old_name=old_name,
+                                      new_name=obj.__name__),
+                          DeprecationWarning,
+                          stacklevel=3)
+
+        def _wrap_with_warn(func, is_inspect):
+            @wraps(func)
+            def _func(*args, **kwargs):
+                if is_inspect:
+                    # XXX: If use another name to call,
+                    # you will not get the warning.
+                    # we do this instead of subclassing or metaclass as
+                    # we want to isinstance(new_name(), old_name) and
+                    # isinstance(old_name(), new_name) to work
+                    frame = inspect.currentframe().f_back
+                    code = inspect.getframeinfo(frame).code_context
+                    if [line for line in code
+                            if '{0}('.format(old_name) in line]:
+                        _warn()
+                else:
+                    _warn()
+                return func(*args, **kwargs)
+            return _func
+
+        # Make old name available.
+        frame = inspect.currentframe().f_back
+        if old_name in frame.f_globals:
+            raise NameError("Name '{0}' already in use.".format(old_name))
+
+        if inspect.isclass(obj):
+            obj.__init__ = _wrap_with_warn(obj.__init__, True)
+            placeholder = obj
+        else:
+            placeholder = _wrap_with_warn(obj, False)
+
+        frame.f_globals[old_name] = placeholder
+
+        return obj
+    return _wrap
+
+
+def deprecated_params(names, warn="Param name '{old_name}' is deprecated, "
+                                  "please use '{new_name}'"):
     """Decorator to translate obsolete names and warn about their use.

     :param dict names: dictionary with pairs of new_name: old_name
@@ -32,11 +78,27 @@ def deprecated_params(names, warn=
         deprecated keyword name and 'new_name' for the current one.
         Example: "Old name: {old_name}, use {new_name} instead".
     """
-    pass
-
-
-def deprecated_instance_attrs(names, warn=
-    "Attribute '{old_name}' is deprecated, please use '{new_name}'"):
+    def decorator(func):
+        @wraps(func)
+        def wrapper(*args, **kwargs):
+            for new_name, old_name in names.items():
+                if old_name in kwargs:
+                    if new_name in kwargs:
+                        raise TypeError("got multiple values for keyword "
+                                        "argument '{0}'".format(new_name))
+                    warnings.warn(warn.format(old_name=old_name,
+                                              new_name=new_name),
+                                  DeprecationWarning,
+                                  stacklevel=2)
+                    kwargs[new_name] = kwargs.pop(old_name)
+            return func(*args, **kwargs)
+        return wrapper
+    return decorator
+
+
+def deprecated_instance_attrs(names,
+                              warn="Attribute '{old_name}' is deprecated, "
+                                   "please use '{new_name}'"):
     """Decorator to deprecate class instance attributes.

     Translates all names in `names` to use new names and emits warnings
@@ -51,11 +113,59 @@ def deprecated_instance_attrs(names, warn=
         deprecated keyword name and 'new_name' for the current one.
         Example: "Old name: {old_name}, use {new_name} instead".
     """
-    pass
-
-
-def deprecated_attrs(names, warn=
-    "Attribute '{old_name}' is deprecated, please use '{new_name}'"):
+    # reverse the dict as we're looking for old attributes, not new ones
+    names = dict((j, i) for i, j in names.items())
+
+    def decorator(clazz):
+        def getx(self, name, __old_getx=getattr(clazz, "__getattr__", None)):
+            if name in names:
+                warnings.warn(warn.format(old_name=name,
+                                          new_name=names[name]),
+                              DeprecationWarning,
+                              stacklevel=2)
+                return getattr(self, names[name])
+            if __old_getx:
+                if hasattr(__old_getx, "__func__"):
+                    return __old_getx.__func__(self, name)
+                return __old_getx(self, name)
+            raise AttributeError("'{0}' object has no attribute '{1}'"
+                                 .format(clazz.__name__, name))
+
+        getx.__name__ = "__getattr__"
+        clazz.__getattr__ = getx
+
+        def setx(self, name, value, __old_setx=getattr(clazz, "__setattr__")):
+            if name in names:
+                warnings.warn(warn.format(old_name=name,
+                                          new_name=names[name]),
+                              DeprecationWarning,
+                              stacklevel=2)
+                setattr(self, names[name], value)
+            else:
+                __old_setx(self, name, value)
+
+        setx.__name__ = "__setattr__"
+        clazz.__setattr__ = setx
+
+        def delx(self, name, __old_delx=getattr(clazz, "__delattr__")):
+            if name in names:
+                warnings.warn(warn.format(old_name=name,
+                                          new_name=names[name]),
+                              DeprecationWarning,
+                              stacklevel=2)
+                delattr(self, names[name])
+            else:
+                __old_delx(self, name)
+
+        delx.__name__ = "__delattr__"
+        clazz.__delattr__ = delx
+
+        return clazz
+    return decorator
+
+
+def deprecated_attrs(names, warn="Attribute '{old_name}' is deprecated, "
+                                 "please use '{new_name}'"):
     """Decorator to deprecate all specified attributes in class.

     Translates all names in `names` to use new names and emits warnings
@@ -70,12 +180,39 @@ def deprecated_attrs(names, warn=
         deprecated keyword name and 'new_name' for the current one.
         Example: "Old name: {old_name}, use {new_name} instead".
     """
-    pass
-
+    # prepare metaclass for handling all the class methods, class variables
+    # and static methods (as they don't go through instance's __getattr__)
+    class DeprecatedProps(type):
+        pass
+
+    metaclass = deprecated_instance_attrs(names, warn)(DeprecatedProps)
+
+    def wrapper(cls):
+        cls = deprecated_instance_attrs(names, warn)(cls)
+
+        # apply metaclass
+        orig_vars = cls.__dict__.copy()
+        slots = orig_vars.get('__slots__')
+        if slots is not None:
+            if isinstance(slots, str):
+                slots = [slots]
+            for slots_var in slots:
+                orig_vars.pop(slots_var)
+        orig_vars.pop('__dict__', None)
+        orig_vars.pop('__weakref__', None)
+        return metaclass(cls.__name__, cls.__bases__, orig_vars)
+    return wrapper

 def deprecated_method(message):
     """Decorator for deprecating methods.

     :param ste message: The message you want to display.
     """
-    pass
+    def decorator(func):
+        @wraps(func)
+        def wrapper(*args, **kwargs):
+            warnings.warn("{0} is a deprecated method. {1}".format(func.__name__, message),
+                          DeprecationWarning, stacklevel=2)
+            return func(*args, **kwargs)
+        return wrapper
+    return decorator
diff --git a/tlslite/utils/dns_utils.py b/tlslite/utils/dns_utils.py
index 40e08e5..1c4fa29 100644
--- a/tlslite/utils/dns_utils.py
+++ b/tlslite/utils/dns_utils.py
@@ -1,4 +1,9 @@
+# Copyright (c) 2017 Hubert Kario
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """Utilities for handling DNS hostnames"""
+
 import re


@@ -10,4 +15,28 @@ def is_valid_hostname(hostname):
     :param hostname: string to check
     :rtype: boolean
     """
-    pass
+    try:
+        if not isinstance(hostname, str):
+            hostname = hostname.decode('ascii', 'strict')
+    except UnicodeDecodeError:
+        return False
+    if hostname[-1] == ".":
+        # strip exactly one dot from the right, if present
+        hostname = hostname[:-1]
+    # the maximum length of the domain name is 255 bytes, but because they
+    # are encoded as labels (which is a length byte and an up to 63 character
+    # ascii string), you change the dots to the length bytes, but the
+    # host element of the FQDN doesn't start with a dot and the name doesn't
+    # end with a dot (specification of a root label), we need to subtract 2
+    # bytes from the 255 byte maximum when looking at dot-deliminated FQDN
+    # with the trailing dot removed
+    # see RFC 1035
+    if len(hostname) > 253:
+        return False
+
+    # must not be all-numeric, so that it can't be confused with an ip-address
+    if re.match(r"[\d.]+$", hostname):
+        return False
+
+    allowed = re.compile(r"(?!-)[A-Z\d-]{1,63}(?<!-)$", re.IGNORECASE)
+    return all(allowed.match(x) for x in hostname.split("."))
diff --git a/tlslite/utils/dsakey.py b/tlslite/utils/dsakey.py
index a9aea75..2a12b10 100644
--- a/tlslite/utils/dsakey.py
+++ b/tlslite/utils/dsakey.py
@@ -1,6 +1,5 @@
 """Abstract class for DSA."""

-
 class DSAKey(object):
     """This is an abstract base class for DSA keys.

@@ -41,7 +40,7 @@ class DSAKey(object):

         :rtype: bool
         """
-        pass
+        raise NotImplementedError()

     def hashAndSign(self, data, hAlg):
         """Hash and sign the passed-in bytes.
@@ -59,9 +58,12 @@ class DSAKey(object):
         :rtype: bytearray
         :returns: An DSA signature on the passed-in data.
         """
-        pass
+        raise NotImplementedError()
+
+    def sign(self, data):
+        raise NotImplementedError()

-    def hashAndVerify(self, signature, data, hAlg='sha1'):
+    def hashAndVerify(self, signature, data, hAlg="sha1"):
         """Hash and verify the passed-in bytes with signature.

         :type signature: ASN1 bytearray
@@ -76,7 +78,7 @@ class DSAKey(object):
         :rtype: bool
         :returns: return True if verification is OK.
         """
-        pass
+        raise NotImplementedError()

     @staticmethod
     def generate(L, N):
@@ -91,7 +93,7 @@ class DSAKey(object):
         :rtype: DSAkey
         :returns: DSAkey(domain parameters, private key, public key)
         """
-        pass
+        raise NotImplementedError()

     @staticmethod
     def generate_qp(L, N):
@@ -106,4 +108,4 @@ class DSAKey(object):
         :rtype: (int, int)
         :returns: new p and q key parameters
         """
-        pass
+        raise NotImplementedError()
diff --git a/tlslite/utils/ecc.py b/tlslite/utils/ecc.py
index 85dd043..1b02685 100644
--- a/tlslite/utils/ecc.py
+++ b/tlslite/utils/ecc.py
@@ -1,13 +1,46 @@
+# Copyright (c) 2015, Hubert Kario
+#
+# See the LICENSE file for legal information regarding use of this file.
 """Methods for dealing with ECC points"""
+
 import ecdsa
 from .compat import ecdsaAllCurves


 def getCurveByName(curveName):
     """Return curve identified by curveName"""
-    pass
+    curveMap = {'secp256r1':ecdsa.NIST256p,
+                'secp384r1':ecdsa.NIST384p,
+                'secp521r1':ecdsa.NIST521p,
+                'secp256k1':ecdsa.SECP256k1,
+                'brainpoolP256r1': ecdsa.BRAINPOOLP256r1,
+                'brainpoolP384r1': ecdsa.BRAINPOOLP384r1,
+                'brainpoolP512r1': ecdsa.BRAINPOOLP512r1}
+    if ecdsaAllCurves:
+        curveMap['secp224r1'] = ecdsa.NIST224p
+        curveMap['secp192r1'] = ecdsa.NIST192p

+    if curveName in curveMap:
+        return curveMap[curveName]
+    else:
+        raise ValueError("Curve of name '{0}' unknown".format(curveName))

 def getPointByteSize(point):
     """Convert the point or curve bit size to bytes"""
-    pass
+    curveMap = {ecdsa.NIST256p.curve: 256//8,
+                ecdsa.NIST384p.curve: 384//8,
+                ecdsa.NIST521p.curve: (521+7)//8,
+                ecdsa.SECP256k1.curve: 256//8,
+                ecdsa.BRAINPOOLP256r1.curve: 256//8,
+                ecdsa.BRAINPOOLP384r1.curve: 384//8,
+                ecdsa.BRAINPOOLP512r1.curve: 512//8}
+    if ecdsaAllCurves:
+        curveMap[ecdsa.NIST224p.curve] = 224//8
+        curveMap[ecdsa.NIST192p.curve] = 192//8
+
+    if hasattr(point, 'curve'):
+        if callable(point.curve):
+            return curveMap[point.curve()]
+        else:
+            return curveMap[point.curve]
+    raise ValueError("Parameter must be a curve or point on curve")
diff --git a/tlslite/utils/ecdsakey.py b/tlslite/utils/ecdsakey.py
index 794305d..5fd8989 100644
--- a/tlslite/utils/ecdsakey.py
+++ b/tlslite/utils/ecdsakey.py
@@ -1,4 +1,8 @@
+# Author: Stanislav Zidek
+# See the LICENSE file for legal information regarding use of this file.
+
 """Abstract class for ECDSA."""
+
 from .cryptomath import secureHash


@@ -39,7 +43,16 @@ class ECDSAKey(object):

         :rtype: bool
         """
-        pass
+        raise NotImplementedError()
+
+    def _sign(self, data, hash_alg):
+        raise NotImplementedError()
+
+    def _hashAndSign(self, data, hAlg):
+        raise NotImplementedError()
+
+    def _verify(self, signature, hash_bytes):
+        raise NotImplementedError()

     def hashAndSign(self, bytes, rsaScheme=None, hAlg='sha1', sLen=None):
         """Hash and sign the passed-in bytes.
@@ -62,10 +75,13 @@ class ECDSAKey(object):
         :rtype: bytearray
         :returns: An ECDSA signature on the passed-in data.
         """
-        pass
+        hAlg = hAlg.lower()
+        hashBytes = secureHash(bytearray(bytes), hAlg)
+        return self.sign(hashBytes, padding=rsaScheme, hashAlg=hAlg,
+                         saltLen=sLen)

     def hashAndVerify(self, sigBytes, bytes, rsaScheme=None, hAlg='sha1',
-        sLen=None):
+                      sLen=None):
         """Hash and verify the passed-in bytes with the signature.

         This verifies an ECDSA signature on the passed-in data
@@ -89,9 +105,12 @@ class ECDSAKey(object):
         :rtype: bool
         :returns: Whether the signature matches the passed-in data.
         """
-        pass
+        hAlg = hAlg.lower()
+
+        hashBytes = secureHash(bytearray(bytes), hAlg)
+        return self.verify(sigBytes, hashBytes, rsaScheme, hAlg, sLen)

-    def sign(self, bytes, padding=None, hashAlg='sha1', saltLen=None):
+    def sign(self, bytes, padding=None, hashAlg="sha1", saltLen=None):
         """Sign the passed-in bytes.

         This requires the key to have a private component.  It performs
@@ -113,10 +132,11 @@ class ECDSAKey(object):
         :rtype: bytearray
         :returns: An ECDSA signature on the passed-in data.
         """
-        pass
+        sigBytes = self._sign(bytes, hashAlg)
+        return sigBytes

-    def verify(self, sigBytes, bytes, padding=None, hashAlg=None, saltLen=None
-        ):
+    def verify(self, sigBytes, bytes, padding=None, hashAlg=None,
+               saltLen=None):
         """Verify the passed-in bytes with the signature.

         This verifies a PKCS1 signature on the passed-in data.
@@ -133,7 +153,7 @@ class ECDSAKey(object):
         :rtype: bool
         :returns: Whether the signature matches the passed-in data.
         """
-        pass
+        return self._verify(sigBytes, bytes)

     def acceptsPassword(self):
         """Return True if the write() method accepts a password for use
@@ -141,7 +161,7 @@ class ECDSAKey(object):

         :rtype: bool
         """
-        pass
+        raise NotImplementedError()

     def write(self, password=None):
         """Return a string containing the key.
@@ -150,7 +170,7 @@ class ECDSAKey(object):
         :returns: A string describing the key, in whichever format (PEM)
             is native to the implementation.
         """
-        pass
+        raise NotImplementedError()

     @staticmethod
     def generate(bits):
@@ -158,4 +178,4 @@ class ECDSAKey(object):

         :rtype: ~tlslite.utils.ECDSAKey.ECDSAKey
         """
-        pass
+        raise NotImplementedError()
diff --git a/tlslite/utils/eddsakey.py b/tlslite/utils/eddsakey.py
index f9ddd18..7754319 100644
--- a/tlslite/utils/eddsakey.py
+++ b/tlslite/utils/eddsakey.py
@@ -1,3 +1,6 @@
+# Author: Hubert Kario
+# See the LICENSE file for legal information regarding use of this file.
+
 """Abstract class for EdDSA."""


@@ -13,7 +16,6 @@ class EdDSAKey(object):
     directly.  Instead, use the factory functions in
     :py:class:`~tlslite.utils.keyfactory`.
     """
-
     def __len__(self):
         """Return the size of the order of the curve of this key, in bits.

@@ -26,7 +28,13 @@ class EdDSAKey(object):

         :rtype: bool
         """
-        pass
+        raise NotImplementedError()
+
+    def _hashAndSign(self, data):
+        raise NotImplementedError()
+
+    def _hashAndVerify(self, signature, data):
+        raise NotImplementedError()

     def hashAndSign(self, data, rsaScheme=None, hAlg=None, sLen=None):
         """Hash and sign the passed-in bytes.
@@ -49,10 +57,10 @@ class EdDSAKey(object):
         :rtype: bytearray
         :returns: An EdDSA signature on the passed-in data.
         """
-        pass
+        return self._hashAndSign(data)

     def hashAndVerify(self, sig_bytes, data, rsaScheme=None, hAlg=None,
-        sLen=None):
+                      sLen=None):
         """Hash and verify the passed-in bytes with the signature.

         This verifies an EdDSA signature on the passed-in data
@@ -76,10 +84,10 @@ class EdDSAKey(object):
         :rtype: bool
         :returns: Whether the signature matches the passed-in data.
         """
-        pass
+        return self._hashAndVerify(sig_bytes, data)

     @staticmethod
-    def sign(self, bytes, padding=None, hashAlg='sha1', saltLen=None):
+    def sign(self, bytes, padding=None, hashAlg="sha1", saltLen=None):
         """Sign the passed-in bytes.

         Note: this method is unsupported for EdDSA keys, as pre-hash
@@ -98,11 +106,12 @@ class EdDSAKey(object):
         :type saltLen: int
         :param saltLen: Ignored
         """
-        pass
+        raise TypeError("Only Pure EdDSA signatures are supported, use "
+                        "hashAndSign() instead.")

     @staticmethod
-    def verify(self, sigBytes, bytes, padding=None, hashAlg=None, saltLen=None
-        ):
+    def verify(self, sigBytes, bytes, padding=None, hashAlg=None,
+               saltLen=None):
         """Verify the passed-in bytes with the signature.

         Note: this method is unsupported for EdDSA keys, as pre-hash
@@ -118,7 +127,8 @@ class EdDSAKey(object):
         :type padding: str
         :param padding: Ignored
         """
-        pass
+        raise TypeError("Only Pure EdDSA signatures are supported, use "
+                        "hashAndVerify() instead.")

     def acceptsPassword(self):
         """Return True if the write() method accepts a password for use
@@ -126,7 +136,7 @@ class EdDSAKey(object):

         :rtype: bool
         """
-        pass
+        raise NotImplementedError()

     def write(self, password=None):
         """Return a string containing the key.
@@ -135,7 +145,7 @@ class EdDSAKey(object):
         :returns: A string describing the key, in whichever format (PEM)
             is native to the implementation.
         """
-        pass
+        raise NotImplementedError()

     @staticmethod
     def generate(bits):
@@ -143,4 +153,4 @@ class EdDSAKey(object):

         :rtype: ~tlslite.utils.EdDSAKey.EdDSAKey
         """
-        pass
+        raise NotImplementedError()
diff --git a/tlslite/utils/format_output.py b/tlslite/utils/format_output.py
index 76c5157..161633e 100644
--- a/tlslite/utils/format_output.py
+++ b/tlslite/utils/format_output.py
@@ -1,3 +1,7 @@
+# Author: Milan Lysonek
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """Helper functions for output formatting"""


@@ -10,4 +14,6 @@ def none_as_unknown(text, number):
     :type number: int
     :param number: number used in text
     """
-    pass
+    if not text:
+        text = "unknown({0})".format(number)
+    return text
diff --git a/tlslite/utils/keyfactory.py b/tlslite/utils/keyfactory.py
index 10fe885..eca8f91 100644
--- a/tlslite/utils/keyfactory.py
+++ b/tlslite/utils/keyfactory.py
@@ -1,18 +1,28 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 """Factory functions for asymmetric cryptography."""
+
 from .compat import *
+
 from .rsakey import RSAKey
 from .python_rsakey import Python_RSAKey
 from .python_ecdsakey import Python_ECDSAKey
 from .python_dsakey import Python_DSAKey
 from .python_eddsakey import Python_EdDSAKey
 from tlslite.utils import cryptomath
+
 if cryptomath.m2cryptoLoaded:
     from .openssl_rsakey import OpenSSL_RSAKey
+
 if cryptomath.pycryptoLoaded:
     from .pycrypto_rsakey import PyCrypto_RSAKey

+# **************************************************************************
+# Factory Functions for RSA Keys
+# **************************************************************************

-def generateRSAKey(bits, implementations=['openssl', 'python']):
+def generateRSAKey(bits, implementations=["openssl", "python"]):
     """Generate an RSA key with the specified bit length.

     :type bits: int
@@ -21,11 +31,16 @@ def generateRSAKey(bits, implementations=['openssl', 'python']):
     :rtype: ~tlslite.utils.rsakey.RSAKey
     :returns: A new RSA private key.
     """
-    pass
-
-
+    for implementation in implementations:
+        if implementation == "openssl" and cryptomath.m2cryptoLoaded:
+            return OpenSSL_RSAKey.generate(bits)
+        elif implementation == "python":
+            return Python_RSAKey.generate(bits)
+    raise ValueError("No acceptable implementations")
+
+#Parse as an OpenSSL or Python key
 def parsePEMKey(s, private=False, public=False, passwordCallback=None,
-    implementations=['openssl', 'python']):
+                implementations=["openssl", "python"]):
     """Parse a PEM-format key.

     The PEM format is used by OpenSSL and other tools.  The
@@ -81,7 +96,48 @@ def parsePEMKey(s, private=False, public=False, passwordCallback=None,

     :raises SyntaxError: If the key is not properly formatted.
     """
-    pass
+    # as old versions of openssl can't handle RSA-PSS or ECDSA keys, first
+    # try to detect what kind of key it is (we ignore errors as the python
+    # code can't handle encrypted key files while m2crypto/openssl can)
+    key_type = "rsa"
+    try:
+        key = Python_RSAKey.parsePEM(s)
+        key_type = key.key_type
+        del key
+    except Exception:
+        pass
+
+    for implementation in implementations:
+        if implementation == "openssl" and cryptomath.m2cryptoLoaded \
+                and key_type == "rsa":
+            key = OpenSSL_RSAKey.parse(s, passwordCallback)
+            break
+        elif implementation == "python":
+            key = Python_RSAKey.parsePEM(s)
+            break
+    else:
+        raise ValueError("No acceptable implementations")
+
+    return _parseKeyHelper(key, private, public)
+
+
+def _parseKeyHelper(key, private, public):
+    if private and not key.hasPrivateKey():
+        raise SyntaxError("Not a private key!")
+
+    if public:
+        return _createPublicKey(key)
+
+    if private:
+        if cryptomath.m2cryptoLoaded:
+            if type(key) == Python_RSAKey:
+                return _createPrivateKey(key)
+            assert type(key) in (OpenSSL_RSAKey, Python_ECDSAKey,
+                Python_DSAKey, Python_EdDSAKey), type(key)
+            return key
+        elif hasattr(key, "d"):
+            return _createPrivateKey(key)
+    return key


 def parseAsPublicKey(s):
@@ -95,8 +151,7 @@ def parseAsPublicKey(s):

     :raises SyntaxError: If the key is not properly formatted.
     """
-    pass
-
+    return parsePEMKey(s, public=True)

 def parsePrivateKey(s):
     """Parse a PEM-formatted private key.
@@ -109,26 +164,57 @@ def parsePrivateKey(s):

     :raises SyntaxError: If the key is not properly formatted.
     """
-    pass
-
+    return parsePEMKey(s, private=True)

 def _createPublicKey(key):
     """
     Create a new public key.  Discard any private component,
     and return the most efficient key possible.
     """
-    pass
-
+    if not isinstance(key, RSAKey):
+        raise AssertionError()
+    return _createPublicRSAKey(key.n, key.e, key.key_type)

 def _createPrivateKey(key):
     """
     Create a new private key.  Return the most efficient key possible.
     """
-    pass
-
-
-def _create_public_ecdsa_key(point_x, point_y, curve_name, implementations=
-    ('python',)):
+    if not isinstance(key, RSAKey):
+        raise AssertionError()
+    if not key.hasPrivateKey():
+        raise AssertionError()
+    return _createPrivateRSAKey(key.n, key.e, key.d, key.p, key.q, key.dP,
+                                key.dQ, key.qInv, key.key_type)
+
+# n, e, d, etc. are the names used in mathematical proofs for the variables
+# so using so short names makes it actually more readable
+# pylint: disable=invalid-name
+def _createPublicRSAKey(n, e, key_type,
+                        implementations=("openssl", "pycrypto", "python")):
+    for implementation in implementations:
+        if implementation == "openssl" and cryptomath.m2cryptoLoaded:
+            return OpenSSL_RSAKey(n, e, key_type=key_type)
+        elif implementation == "pycrypto" and cryptomath.pycryptoLoaded:
+            return PyCrypto_RSAKey(n, e, key_type=key_type)
+        elif implementation == "python":
+            return Python_RSAKey(n, e, key_type=key_type)
+    raise ValueError("No acceptable implementations")
+
+def _createPrivateRSAKey(n, e, d, p, q, dP, dQ, qInv, key_type,
+                         implementations=("pycrypto", "python")):
+    for implementation in implementations:
+        if implementation == "pycrypto" and cryptomath.pycryptoLoaded:
+            return PyCrypto_RSAKey(n, e, d, p, q, dP, dQ, qInv,
+                                   key_type=key_type)
+        elif implementation == "python":
+            return Python_RSAKey(n, e, d, p, q, dP, dQ, qInv,
+                                 key_type=key_type)
+    raise ValueError("No acceptable implementations")
+# pylint: enable=invalid-name
+
+
+def _create_public_ecdsa_key(point_x, point_y, curve_name,
+                             implementations=("python",)):
     """
     Convert public key parameters into concrete implementation of verifier.

@@ -148,18 +234,26 @@ def _create_public_ecdsa_key(point_x, point_y, curve_name, implementations=
         concrete implementation of the verifying key (only 'python' is
         supported currently)
     """
-    pass
+    for impl in implementations:
+        if impl == "python":
+            return Python_ECDSAKey(point_x, point_y, curve_name)
+    raise ValueError("No acceptable implementation")


-def _create_public_eddsa_key(public_key, implementations=('python',)):
+def _create_public_eddsa_key(public_key,
+                             implementations=("python",)):
     """
     Convert the python-ecdsa public key into concrete implementation of
     verifier.
     """
-    pass
+    for impl in implementations:
+        if impl == "python":
+            return Python_EdDSAKey(public_key)
+    raise ValueError("No acceptable implementation")


-def _create_public_dsa_key(p, q, g, y, implementations=('python',)):
+def _create_public_dsa_key(p, q, g, y,
+                           implementations=("python",)):
     """
     Convert public key parameters into concrete implementation of verifier.

@@ -178,4 +272,7 @@ def _create_public_dsa_key(p, q, g, y, implementations=('python',)):
         concrete implementation of the verifying key (only 'python' is
         supported currently)
     """
-    pass
+    for impl in implementations:
+        if impl == "python":
+            return Python_DSAKey(p=p, q=q, g=g, y=y)
+    raise ValueError("No acceptable implementation")
diff --git a/tlslite/utils/lists.py b/tlslite/utils/lists.py
index 021c4ba..d8f2bab 100644
--- a/tlslite/utils/lists.py
+++ b/tlslite/utils/lists.py
@@ -1,4 +1,10 @@
+# Authors:
+#   Hubert Kario (2016)
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """Helper functions for handling lists"""
+
 from itertools import chain


@@ -15,10 +21,13 @@ def getFirstMatching(values, matches):
     :type matches: collections.abc.Container
     :param matches: list of items to check against
     """
-    pass
+    assert matches is not None
+    if not values:
+        return None
+    return next((i for i in values if i in matches), None)


-def to_str_delimiter(values, delim=', ', last_delim=' or '):
+def to_str_delimiter(values, delim=", ", last_delim=" or "):
     """
     Format the list as a human readable string.

@@ -34,4 +43,7 @@ def to_str_delimiter(values, delim=', ', last_delim=' or '):
     :param last_delim: delimiter for last object in list
     :rtype: str
     """
-    pass
+    # we need to slice the iterator, so we need a copy
+    values = list(values)
+    return delim.join(chain((str(i) for i in values[:-2]),
+                            [last_delim.join(str(i) for i in values[-2:])]))
diff --git a/tlslite/utils/openssl_aes.py b/tlslite/utils/openssl_aes.py
index 1d3b56d..21b16c3 100644
--- a/tlslite/utils/openssl_aes.py
+++ b/tlslite/utils/openssl_aes.py
@@ -1,26 +1,85 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 """OpenSSL/M2Crypto AES implementation."""
+
 from .cryptomath import *
 from .aes import *
 from .python_aes import Python_AES_CTR
+
 if m2cryptoLoaded:

     def new(key, mode, IV):
+        # IV argument name is a part of the interface
+        # pylint: disable=invalid-name
         """
         Try using AES CTR from m2crpyto,
         if it is not available fall back to the
         python implementation.
         """
-        pass
+        if mode == 2:
+            return OpenSSL_AES(key, mode, IV)
+        elif mode == 6:
+            if M2CRYPTO_AES_CTR:
+                return OpenSSL_CTR(key, mode, IV)
+            return Python_AES_CTR(key, mode, IV)
+        else:
+            raise NotImplementedError()


     class OpenSSL_AES(AES):

         def __init__(self, key, mode, IV):
-            AES.__init__(self, key, mode, IV, 'openssl')
+            # IV argument/field names are a part of the interface
+            # pylint: disable=invalid-name
+            AES.__init__(self, key, mode, IV, "openssl")
             self._IV, self._key = IV, key
             self._context = None
             self._encrypt = None

+        @property
+        def IV(self):
+            return self._IV
+
+        @IV.setter
+        def IV(self, iv):
+            if self._context is not None:
+                m2.cipher_ctx_free(self._context)
+            self._IV = iv
+            self._init_context()
+
+        def _init_context(self, encrypt=True):
+            if len(self._key) == 16:
+                cipherType = m2.aes_128_cbc()
+            if len(self._key) == 24:
+                cipherType = m2.aes_192_cbc()
+            if len(self._key) == 32:
+                cipherType = m2.aes_256_cbc()
+            self._context = m2.cipher_ctx_new()
+            m2.cipher_init(self._context, cipherType, self._key, self._IV,
+                           int(encrypt))
+            m2.cipher_set_padding(self._context, 0)
+            self._encrypt = encrypt
+
+        def encrypt(self, plaintext):
+            if self._context is None:
+                self._init_context(encrypt=True)
+            else:
+                assert self._encrypt, '.encrypt() not allowed after .decrypt()'
+            AES.encrypt(self, plaintext)
+            ciphertext = m2.cipher_update(self._context, plaintext)
+            return bytearray(ciphertext)
+
+        def decrypt(self, ciphertext):
+            if self._context is None:
+                self._init_context(encrypt=False)
+            else:
+                assert not self._encrypt, \
+                       '.decrypt() not allowed after .encrypt()'
+            AES.decrypt(self, ciphertext)
+            plaintext = m2.cipher_update(self._context, ciphertext)
+            return bytearray(plaintext)
+
         def __del__(self):
             if self._context is not None:
                 m2.cipher_ctx_free(self._context)
@@ -29,7 +88,9 @@ if m2cryptoLoaded:
     class OpenSSL_CTR(AES):

         def __init__(self, key, mode, IV):
-            AES.__init__(self, key, mode, IV, 'openssl')
+            # IV argument/field names are a part of the interface
+            # pylint: disable=invalid-name
+            AES.__init__(self, key, mode, IV, "openssl")
             self._IV = IV
             self.key = key
             self._context = None
@@ -37,6 +98,38 @@ if m2cryptoLoaded:
             if len(key) not in (16, 24, 32):
                 raise AssertionError()

+        @property
+        def counter(self):
+            return self._IV
+
+        @counter.setter
+        def counter(self, ctr):
+            if self._context is not None:
+                m2.cipher_ctx_free(self._context)
+            self._IV = ctr
+            self._init_context()
+
+        def _init_context(self, encrypt=True):
+            if len(self.key) == 16:
+                cipherType = m2.aes_128_ctr()
+            if len(self.key) == 24:
+                cipherType = m2.aes_192_ctr()
+            if len(self.key) == 32:
+                cipherType = m2.aes_256_ctr()
+            self._context = m2.cipher_ctx_new()
+            m2.cipher_init(self._context, cipherType, self.key, self._IV,
+                           int(encrypt))
+            m2.cipher_set_padding(self._context, 0)
+            self._encrypt = encrypt
+
+        def encrypt(self, plaintext):
+            ciphertext = m2.cipher_update(self._context, plaintext)
+            return bytearray(ciphertext)
+
+        def decrypt(self, ciphertext):
+            plaintext = m2.cipher_update(self._context, ciphertext)
+            return bytearray(plaintext)
+
         def __del__(self):
             if self._context is not None:
                 m2.cipher_ctx_free(self._context)
diff --git a/tlslite/utils/openssl_aesccm.py b/tlslite/utils/openssl_aesccm.py
index 0c807d4..9ee8d7a 100644
--- a/tlslite/utils/openssl_aesccm.py
+++ b/tlslite/utils/openssl_aesccm.py
@@ -1,14 +1,21 @@
+# Author: Ivan Nikolchev
+# See the LICENSE file for legal information regarding use of this file.
+
 """AESCCM with CTR and CBC from m2crypto"""
+
 from tlslite.utils.cryptomath import m2cryptoLoaded
 from tlslite.utils.aesccm import AESCCM
 from tlslite.utils import openssl_aes
+
+
 if m2cryptoLoaded:
+    def new(key, tagLength=16):
+        return OPENSSL_AESCCM(key, "openssl", bytearray(16), tagLength)


 class OPENSSL_AESCCM(AESCCM):
-
     def __init__(self, key, implementation, rawAesEncrypt, tagLength):
-        super(OPENSSL_AESCCM, self).__init__(key, implementation,
-            rawAesEncrypt, tagLength)
+        super(OPENSSL_AESCCM, self).__init__(key, implementation, rawAesEncrypt, tagLength)
+
         self._ctr = openssl_aes.new(key, 6, bytearray(b'\x00' * 16))
         self._cbc = openssl_aes.new(key, 2, bytearray(b'\x00' * 16))
diff --git a/tlslite/utils/openssl_aesgcm.py b/tlslite/utils/openssl_aesgcm.py
index 0b6af7d..e9f342f 100644
--- a/tlslite/utils/openssl_aesgcm.py
+++ b/tlslite/utils/openssl_aesgcm.py
@@ -1,14 +1,20 @@
+# Author: Ivan Nikolchev
+# See the LICENSE file for legal information regarding use of this file.
+
 """AESGCM with CTR from m2crypto"""
+
 from tlslite.utils.cryptomath import m2cryptoLoaded
 from tlslite.utils.aesgcm import AESGCM
 from tlslite.utils import openssl_aes
 from tlslite.utils.rijndael import Rijndael
+
 if m2cryptoLoaded:
+    def new(key):
+        return OPENSSL_AESGCM(key, "openssl", Rijndael(key, 16).encrypt)


 class OPENSSL_AESGCM(AESGCM):
-
     def __init__(self, key, implementation, rawAesEncrypt):
-        super(OPENSSL_AESGCM, self).__init__(key, implementation, rawAesEncrypt
-            )
+        super(OPENSSL_AESGCM, self).__init__(key, implementation, rawAesEncrypt)
+
         self._ctr = openssl_aes.new(key, 6, bytearray(b'\x00' * 16))
diff --git a/tlslite/utils/openssl_rc4.py b/tlslite/utils/openssl_rc4.py
index fcd3fd7..2fbfa07 100644
--- a/tlslite/utils/openssl_rc4.py
+++ b/tlslite/utils/openssl_rc4.py
@@ -1,15 +1,28 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 """OpenSSL/M2Crypto RC4 implementation."""
+
 from .cryptomath import *
 from .rc4 import RC4
+
 if m2cryptoLoaded:

+    def new(key):
+        return OpenSSL_RC4(key)

     class OpenSSL_RC4(RC4):

         def __init__(self, key):
-            RC4.__init__(self, key, 'openssl')
+            RC4.__init__(self, key, "openssl")
             self.rc4 = m2.rc4_new()
             m2.rc4_set_key(self.rc4, key)

         def __del__(self):
             m2.rc4_free(self.rc4)
+
+        def encrypt(self, plaintext):
+            return bytearray(m2.rc4_update(self.rc4, plaintext))
+
+        def decrypt(self, ciphertext):
+            return bytearray(self.encrypt(ciphertext))
diff --git a/tlslite/utils/openssl_rsakey.py b/tlslite/utils/openssl_rsakey.py
index 587131f..240bf78 100644
--- a/tlslite/utils/openssl_rsakey.py
+++ b/tlslite/utils/openssl_rsakey.py
@@ -1,18 +1,42 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 """OpenSSL/M2Crypto RSA implementation."""
+
 from .cryptomath import *
+
 from .rsakey import *
 from .python_rsakey import Python_RSAKey
 from .compat import compatAscii2Bytes, compat_b2a
+
+#copied from M2Crypto.util.py, so when we load the local copy of m2
+#we can still use it
+def password_callback(v, prompt1='Enter private key passphrase:',
+                           prompt2='Verify passphrase:'):
+    from getpass import getpass
+    while 1:
+        try:
+            p1=getpass(prompt1)
+            if v:
+                p2=getpass(prompt2)
+                if p1==p2:
+                    break
+            else:
+                break
+        except KeyboardInterrupt:
+            return None
+    return p1
+
+
 if m2cryptoLoaded:
+    # pylint: disable=import-error
     from M2Crypto.RSA import RSAError

-
     class OpenSSL_RSAKey(RSAKey):
-
-        def __init__(self, n=0, e=0, key_type='rsa'):
+        def __init__(self, n=0, e=0, key_type="rsa"):
             self.rsa = None
             self._hasPrivateKey = False
-            if n and not e or e and not n:
+            if (n and not e) or (e and not n):
                 raise AssertionError()
             if n and e:
                 self.rsa = m2.rsa_new()
@@ -35,3 +59,128 @@ if m2cryptoLoaded:
                 return mpiToNumber(m2.rsa_get_n(self.rsa))
             else:
                 raise AttributeError
+
+        def hasPrivateKey(self):
+            return self._hasPrivateKey
+
+        def _rawPrivateKeyOp(self, message):
+            data = numberToByteArray(message, numBytes(self.n))
+            string = m2.rsa_private_encrypt(self.rsa, bytes(data),
+                                            m2.no_padding)
+            ciphertext = bytesToNumber(bytearray(string))
+            return ciphertext
+
+        def _raw_private_key_op_bytes(self, message):
+            return self._call_m2crypto(
+                m2.rsa_private_encrypt, message,
+                "Bad parameters to private key operation")
+
+        def _rawPublicKeyOp(self, ciphertext):
+            data = numberToByteArray(ciphertext, numBytes(self.n))
+            string = m2.rsa_public_decrypt(self.rsa, bytes(data),
+                                           m2.no_padding)
+            message = bytesToNumber(bytearray(string))
+            return message
+
+        def _call_m2crypto(self, method, param, err_msg):
+            try:
+                return bytearray(method(self.rsa, bytes(param), m2.no_padding))
+            except RSAError:
+                raise ValueError(err_msg)
+
+        def _raw_public_key_op_bytes(self, ciphertext):
+            return self._call_m2crypto(
+                m2.rsa_public_decrypt, ciphertext,
+                "Bad parameters to public key operation")
+
+        def acceptsPassword(self): return True
+
+        def write(self, password=None):
+            bio = m2.bio_new(m2.bio_s_mem())
+            if self._hasPrivateKey:
+                if password:
+                    def f(v): return password
+                    m2.rsa_write_key(self.rsa, bio, m2.des_ede_cbc(), f)
+                else:
+                    def f(): pass
+                    m2.rsa_write_key_no_cipher(self.rsa, bio, f)
+            else:
+                if password:
+                    raise AssertionError()
+                m2.rsa_write_pub_key(self.rsa, bio)
+            s = m2.bio_read(bio, m2.bio_ctrl_pending(bio))
+            m2.bio_free(bio)
+            return s
+
+        @staticmethod
+        def generate(bits, key_type="rsa"):
+            key = OpenSSL_RSAKey()
+            def f():pass
+            # pylint: disable=no-member
+            key.rsa = m2.rsa_generate_key(bits, 65537, f)
+            # pylint: enable=no-member
+            key._hasPrivateKey = True
+            key.key_type = key_type
+            b64_key = compat_b2a(key.write())
+            py_key = Python_RSAKey.parsePEM(b64_key)
+            key.d = py_key.d
+            return key
+
+        @staticmethod
+        def parse(s, passwordCallback=None):
+            # Skip forward to the first PEM header
+            start = s.find("-----BEGIN ")
+            if start == -1:
+                raise SyntaxError()
+            s = s[start:]            
+            if s.startswith("-----BEGIN "):
+                if passwordCallback==None:
+                    callback = password_callback
+                else:
+                    def f(v, prompt1=None, prompt2=None):
+                        return passwordCallback()
+                    callback = f
+                bio = m2.bio_new(m2.bio_s_mem())
+                try:
+                    m2.bio_write(bio, compatAscii2Bytes(s))
+                    key = OpenSSL_RSAKey()
+                    # parse SSLay format PEM file
+                    if s.startswith("-----BEGIN RSA PRIVATE KEY-----"):
+                        def f():pass
+                        key.rsa = m2.rsa_read_key(bio, callback)
+                        if key.rsa == None:
+                            raise SyntaxError()
+                        key._hasPrivateKey = True
+                    # parse a standard PKCS#8 PEM file
+                    elif s.startswith("-----BEGIN PRIVATE KEY-----"):
+                        def f():pass
+                        key.rsa = m2.pkey_read_pem(bio, callback)
+                        # the below code assumes RSA key while PKCS#8 files
+                        # (and by extension the EVP_PKEY structure) can be
+                        # also DSA or EC, thus the double check against None
+                        # (first if the file was properly loaded and second
+                        # if the file actually has a RSA key in it)
+                        # tlslite doesn't support DSA or EC so it's useless
+                        # to handle them in a different way
+                        if key.rsa == None:
+                            raise SyntaxError()
+                        key.rsa = m2.pkey_get1_rsa(key.rsa)
+                        if key.rsa == None:
+                            raise SyntaxError()
+                        key._hasPrivateKey = True
+                    elif s.startswith("-----BEGIN PUBLIC KEY-----"):
+                        key.rsa = m2.rsa_read_pub_key(bio)
+                        if key.rsa == None:
+                            raise SyntaxError()
+                        key._hasPrivateKey = False
+                    else:
+                        raise SyntaxError()
+                    if key._hasPrivateKey:
+                        b64_key = compat_b2a(key.write())
+                        py_key = Python_RSAKey.parsePEM(b64_key)
+                        key.d = py_key.d
+                    return key
+                finally:
+                    m2.bio_free(bio)
+            else:
+                raise SyntaxError()
diff --git a/tlslite/utils/openssl_tripledes.py b/tlslite/utils/openssl_tripledes.py
index 19810f4..89daf8c 100644
--- a/tlslite/utils/openssl_tripledes.py
+++ b/tlslite/utils/openssl_tripledes.py
@@ -1,17 +1,51 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 """OpenSSL/M2Crypto 3DES implementation."""
+
 from .cryptomath import *
 from .tripledes import *
+
 if m2cryptoLoaded:

+    def new(key, mode, IV):
+        return OpenSSL_TripleDES(key, mode, IV)

     class OpenSSL_TripleDES(TripleDES):

         def __init__(self, key, mode, IV):
-            TripleDES.__init__(self, key, mode, IV, 'openssl')
+            TripleDES.__init__(self, key, mode, IV, "openssl")
             self._IV, self._key = IV, key
             self._context = None
             self._encrypt = None

+        def _init_context(self, encrypt=True):
+            cipherType = m2.des_ede3_cbc()
+            self._context = m2.cipher_ctx_new()
+            m2.cipher_init(self._context, cipherType, self._key, self._IV,
+                           int(encrypt))
+            m2.cipher_set_padding(self._context, 0)
+            self._encrypt = encrypt
+
+        def encrypt(self, plaintext):
+            if self._context is None:
+                self._init_context(encrypt=True)
+            else:
+                assert self._encrypt, '.encrypt() not allowed after .decrypt()'
+            TripleDES.encrypt(self, plaintext)
+            ciphertext = m2.cipher_update(self._context, plaintext)
+            return bytearray(ciphertext)
+
+        def decrypt(self, ciphertext):
+            if self._context is None:
+                self._init_context(encrypt=False)
+            else:
+                assert not self._encrypt, \
+                       '.decrypt() not allowed after .encrypt()'
+            TripleDES.decrypt(self, ciphertext)
+            plaintext = m2.cipher_update(self._context, ciphertext)
+            return bytearray(plaintext)
+
         def __del__(self):
             if self._context is not None:
                 m2.cipher_ctx_free(self._context)
diff --git a/tlslite/utils/pem.py b/tlslite/utils/pem.py
index b7cfe98..548bc34 100644
--- a/tlslite/utils/pem.py
+++ b/tlslite/utils/pem.py
@@ -1,6 +1,11 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 from .compat import *
 import binascii

+#This code is shared with tackpy (somewhat), so I'd rather make minimal
+#changes, and preserve the use of a2b_base64 throughout.

 def dePem(s, name):
     """Decode a PEM string into a bytearray of its payload.
@@ -17,9 +22,18 @@ def dePem(s, name):
     The first such PEM block in the input will be found, and its
     payload will be base64 decoded and returned.
     """
-    pass
-
-
+    prefix  = "-----BEGIN %s-----" % name
+    postfix = "-----END %s-----" % name    
+    start = s.find(prefix)
+    if start == -1:
+        raise SyntaxError("Missing PEM prefix")
+    end = s.find(postfix, start+len(prefix))
+    if end == -1:
+        raise SyntaxError("Missing PEM postfix")
+    s = s[start+len("-----BEGIN %s-----" % name) : end]
+    retBytes = a2b_base64(s) # May raise SyntaxError
+    return retBytes
+    
 def dePemList(s, name):
     """Decode a sequence of PEM blocks into a list of bytearrays.

@@ -44,8 +58,20 @@ def dePemList(s, name):
     All such PEM blocks will be found, decoded, and return in an ordered list
     of bytearrays, which may have zero elements if not PEM blocks are found.
     """
-    pass
-
+    bList = []
+    prefix  = "-----BEGIN %s-----" % name
+    postfix = "-----END %s-----" % name
+    while 1:
+        start = s.find(prefix)
+        if start == -1:
+            return bList
+        end = s.find(postfix, start+len(prefix))
+        if end == -1:
+            raise SyntaxError("Missing PEM postfix")
+        s2 = s[start+len(prefix) : end]
+        retBytes = a2b_base64(s2) # May raise SyntaxError
+        bList.append(retBytes)
+        s = s[end+len(postfix) : ]

 def pem(b, name):
     """Encode a payload bytearray into a PEM string.
@@ -59,4 +85,15 @@ def pem(b, name):
         KoZIhvcNAQEFBQADAwA5kw==
         -----END CERTIFICATE-----
     """
-    pass
+    s1 = b2a_base64(b)[:-1] # remove terminating \n
+    s2 = ""
+    while s1:
+        s2 += s1[:64] + "\n"
+        s1 = s1[64:]
+    s = ("-----BEGIN %s-----\n" % name) + s2 + \
+        ("-----END %s-----\n" % name)     
+    return s
+
+def pemSniff(inStr, name):
+    searchStr = "-----BEGIN %s-----" % name
+    return searchStr in inStr
diff --git a/tlslite/utils/poly1305.py b/tlslite/utils/poly1305.py
index 23b34cb..935178c 100644
--- a/tlslite/utils/poly1305.py
+++ b/tlslite/utils/poly1305.py
@@ -1,30 +1,49 @@
+# Copyright (c) 2015, Hubert Kario
+#
+# See the LICENSE file for legal information regarding use of this file.
 """Implementation of Poly1305 authenticator for RFC 7539"""
-from .cryptomath import divceil

+from .cryptomath import divceil

 class Poly1305(object):
+
     """Poly1305 authenticator"""
-    P = 1361129467683753853853498429727072845819
+
+    P = 0x3fffffffffffffffffffffffffffffffb # 2^130-5

     @staticmethod
     def le_bytes_to_num(data):
         """Convert a number from little endian byte format"""
-        pass
+        ret = 0
+        for i in range(len(data) - 1, -1, -1):
+            ret <<= 8
+            ret += data[i]
+        return ret

     @staticmethod
     def num_to_16_le_bytes(num):
         """Convert number to 16 bytes in little endian format"""
-        pass
+        ret = [0]*16
+        for i, _ in enumerate(ret):
+            ret[i] = num & 0xff
+            num >>= 8
+        return bytearray(ret)

     def __init__(self, key):
         """Set the authenticator key"""
         if len(key) != 32:
-            raise ValueError('Key must be 256 bit long')
+            raise ValueError("Key must be 256 bit long")
         self.acc = 0
         self.r = self.le_bytes_to_num(key[0:16])
-        self.r &= 21267647620597763993911028882763415551
+        self.r &= 0x0ffffffc0ffffffc0ffffffc0fffffff
         self.s = self.le_bytes_to_num(key[16:32])

     def create_tag(self, data):
         """Calculate authentication tag for data"""
-        pass
+        for i in range(0, divceil(len(data), 16)):
+            n = self.le_bytes_to_num(data[i*16:(i+1)*16] + b'\x01')
+            self.acc += n
+            self.acc = (self.r * self.acc) % self.P
+        self.acc += self.s
+        return self.num_to_16_le_bytes(self.acc)
+
diff --git a/tlslite/utils/pycrypto_aes.py b/tlslite/utils/pycrypto_aes.py
index 87b2e5e..b3425c0 100644
--- a/tlslite/utils/pycrypto_aes.py
+++ b/tlslite/utils/pycrypto_aes.py
@@ -1,14 +1,29 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 """PyCrypto AES implementation."""
+
 from .cryptomath import *
 from .aes import *
+
 if pycryptoLoaded:
     import Crypto.Cipher.AES

+    def new(key, mode, IV):
+        return PyCrypto_AES(key, mode, IV)

     class PyCrypto_AES(AES):

         def __init__(self, key, mode, IV):
-            AES.__init__(self, key, mode, IV, 'pycrypto')
+            AES.__init__(self, key, mode, IV, "pycrypto")
             key = bytes(key)
             IV = bytes(IV)
             self.context = Crypto.Cipher.AES.new(key, mode, IV)
+
+        def encrypt(self, plaintext):
+            plaintext = bytes(plaintext)
+            return bytearray(self.context.encrypt(plaintext))
+
+        def decrypt(self, ciphertext):
+            ciphertext = bytes(ciphertext)
+            return bytearray(self.context.decrypt(ciphertext))
diff --git a/tlslite/utils/pycrypto_aesgcm.py b/tlslite/utils/pycrypto_aesgcm.py
index e22fd29..ee187ee 100644
--- a/tlslite/utils/pycrypto_aesgcm.py
+++ b/tlslite/utils/pycrypto_aesgcm.py
@@ -1,5 +1,16 @@
+# Author: Google
+# See the LICENSE file for legal information regarding use of this file.
+
 """PyCrypto AES-GCM implementation."""
+
 from .cryptomath import *
 from .aesgcm import AESGCM
+
 if pycryptoLoaded:
     import Crypto.Cipher.AES
+
+    def new(key):
+        cipher = Crypto.Cipher.AES.new(bytes(key))
+        def encrypt(plaintext):
+            return bytearray(cipher.encrypt(bytes(plaintext)))
+        return AESGCM(key, "pycrypto", encrypt)
diff --git a/tlslite/utils/pycrypto_rc4.py b/tlslite/utils/pycrypto_rc4.py
index d3fad7d..fc98d7c 100644
--- a/tlslite/utils/pycrypto_rc4.py
+++ b/tlslite/utils/pycrypto_rc4.py
@@ -1,13 +1,28 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 """PyCrypto RC4 implementation."""
+
 from .cryptomath import *
 from .rc4 import *
+
 if pycryptoLoaded:
     import Crypto.Cipher.ARC4

+    def new(key):
+        return PyCrypto_RC4(key)

     class PyCrypto_RC4(RC4):

         def __init__(self, key):
-            RC4.__init__(self, key, 'pycrypto')
+            RC4.__init__(self, key, "pycrypto")
             key = bytes(key)
             self.context = Crypto.Cipher.ARC4.new(key)
+
+        def encrypt(self, plaintext):
+            plaintext = bytes(plaintext)
+            return bytearray(self.context.encrypt(plaintext))
+
+        def decrypt(self, ciphertext):
+            ciphertext = bytes(ciphertext)
+            return bytearray(self.context.decrypt(ciphertext))
\ No newline at end of file
diff --git a/tlslite/utils/pycrypto_rsakey.py b/tlslite/utils/pycrypto_rsakey.py
index bca321b..5a4e730 100644
--- a/tlslite/utils/pycrypto_rsakey.py
+++ b/tlslite/utils/pycrypto_rsakey.py
@@ -1,25 +1,71 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 """PyCrypto RSA implementation."""
+
 from __future__ import print_function
 import sys
+
 from .cryptomath import *
+
 from .rsakey import *
 from .python_rsakey import Python_RSAKey
 from .compat import compatLong
+
 if pycryptoLoaded:
-    from Crypto.PublicKey import RSA

+    from Crypto.PublicKey import RSA

     class PyCrypto_RSAKey(RSAKey):
-
         def __init__(self, n=0, e=0, d=0, p=0, q=0, dP=0, dQ=0, qInv=0,
-            key_type='rsa'):
-            del dP, dQ, qInv
+                     key_type="rsa"):
+            del dP, dQ, qInv  # pycrypto calculates them by its own
             if not d:
                 self.rsa = RSA.construct((compatLong(n), compatLong(e)))
             else:
                 self.rsa = RSA.construct((compatLong(n), compatLong(e),
-                    compatLong(d), compatLong(p), compatLong(q)))
+                                          compatLong(d), compatLong(p),
+                                          compatLong(q)))
             self.key_type = key_type

         def __getattr__(self, name):
             return getattr(self.rsa, name)
+
+        def hasPrivateKey(self):
+            return self.rsa.has_private()
+
+        def _rawPrivateKeyOp(self, message):
+            try:
+                return self.rsa.decrypt((compatLong(message),))
+            except ValueError as e:
+                print("rsa: {0!r}".format(self.rsa), file=sys.stderr)
+                values = []
+                for name in ["n", "e", "d", "p", "q", "dP", "dQ", "qInv"]:
+                    values.append("{0}: {1}".format(name,
+                                                    getattr(self, name, None)))
+                print(", ".join(values), file=sys.stderr)
+                print("message: {0}".format(message), file=sys.stderr)
+                raise
+
+
+        def _rawPublicKeyOp(self, ciphertext):
+            try:
+                return self.rsa.encrypt(compatLong(ciphertext), None)[0]
+            except ValueError as e:
+                print("rsa: {0!r}".format(self.rsa), file=sys.stderr)
+                values = []
+                for name in ["n", "e", "d", "p", "q", "dP", "dQ", "qInv"]:
+                    values.append("{0}: {1}".format(name,
+                                                    getattr(self, name, None)))
+                print(", ".join(values), file=sys.stderr)
+                print("ciphertext: {0}".format(ciphertext), file=sys.stderr)
+                raise
+
+        @staticmethod
+        def generate(bits, key_type="rsa"):
+            key = PyCrypto_RSAKey()
+            def f(numBytes):
+                return bytes(getRandomBytes(numBytes))
+            key.rsa = RSA.generate(bits, f)
+            key.key_type = key_type
+            return key
diff --git a/tlslite/utils/pycrypto_tripledes.py b/tlslite/utils/pycrypto_tripledes.py
index 7f9b72f..8117f34 100644
--- a/tlslite/utils/pycrypto_tripledes.py
+++ b/tlslite/utils/pycrypto_tripledes.py
@@ -1,14 +1,29 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 """PyCrypto 3DES implementation."""
+
 from .cryptomath import *
 from .tripledes import *
+
 if pycryptoLoaded:
     import Crypto.Cipher.DES3

+    def new(key, mode, IV):
+        return PyCrypto_TripleDES(key, mode, IV)

     class PyCrypto_TripleDES(TripleDES):

         def __init__(self, key, mode, IV):
-            TripleDES.__init__(self, key, mode, IV, 'pycrypto')
+            TripleDES.__init__(self, key, mode, IV, "pycrypto")
             key = bytes(key)
             IV = bytes(IV)
             self.context = Crypto.Cipher.DES3.new(key, mode, IV)
+
+        def encrypt(self, plaintext):
+            plaintext = bytes(plaintext)
+            return bytearray(self.context.encrypt(plaintext))
+
+        def decrypt(self, ciphertext):
+            ciphertext = bytes(ciphertext)
+            return bytearray(self.context.decrypt(ciphertext))
\ No newline at end of file
diff --git a/tlslite/utils/python_aes.py b/tlslite/utils/python_aes.py
index 70897d2..df24922 100644
--- a/tlslite/utils/python_aes.py
+++ b/tlslite/utils/python_aes.py
@@ -1,24 +1,119 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 """Pure-Python AES implementation."""
+
 from .aes import AES
 from .rijndael import Rijndael
 from .cryptomath import bytesToNumber, numberToByteArray
+
 __all__ = ['new', 'Python_AES']


-class Python_AES(AES):
+def new(key, mode, IV):
+    # IV argument name is a part of the interface
+    # pylint: disable=invalid-name
+    if mode == 2:
+        return Python_AES(key, mode, IV)
+    elif mode == 6:
+        return Python_AES_CTR(key, mode, IV)
+    else:
+        raise NotImplementedError()

+
+class Python_AES(AES):
     def __init__(self, key, mode, IV):
+        # IV argument/field names are a part of the interface
+        # pylint: disable=invalid-name
         key, IV = bytearray(key), bytearray(IV)
-        super(Python_AES, self).__init__(key, mode, IV, 'python')
+        super(Python_AES, self).__init__(key, mode, IV, "python")
         self.rijndael = Rijndael(key, 16)
         self.IV = IV

+    def encrypt(self, plaintext):
+        super(Python_AES, self).encrypt(plaintext)

-class Python_AES_CTR(AES):
+        plaintextBytes = bytearray(plaintext)
+        chainBytes = self.IV[:]
+
+        #CBC Mode: For each block...
+        for x in range(len(plaintextBytes)//16):
+
+            #XOR with the chaining block
+            blockBytes = plaintextBytes[x*16 : (x*16)+16]
+            for y in range(16):
+                blockBytes[y] ^= chainBytes[y]
+
+            #Encrypt it
+            encryptedBytes = self.rijndael.encrypt(blockBytes)
+
+            #Overwrite the input with the output
+            for y in range(16):
+                plaintextBytes[(x*16)+y] = encryptedBytes[y]
+
+            #Set the next chaining block
+            chainBytes = encryptedBytes
+
+        self.IV = chainBytes[:]
+        return plaintextBytes
+
+    def decrypt(self, ciphertext):
+        super(Python_AES, self).decrypt(ciphertext)

+        ciphertextBytes = ciphertext[:]
+        chainBytes = self.IV[:]
+
+        #CBC Mode: For each block...
+        for x in range(len(ciphertextBytes)//16):
+
+            #Decrypt it
+            blockBytes = ciphertextBytes[x*16 : (x*16)+16]
+            decryptedBytes = self.rijndael.decrypt(blockBytes)
+
+            #XOR with the chaining block and overwrite the input with output
+            for y in range(16):
+                decryptedBytes[y] ^= chainBytes[y]
+                ciphertextBytes[(x*16)+y] = decryptedBytes[y]
+
+            #Set the next chaining block
+            chainBytes = blockBytes
+
+        self.IV = chainBytes[:]
+        return ciphertextBytes
+
+
+class Python_AES_CTR(AES):
     def __init__(self, key, mode, IV):
-        super(Python_AES_CTR, self).__init__(key, mode, IV, 'python')
+        super(Python_AES_CTR, self).__init__(key, mode, IV, "python")
         self.rijndael = Rijndael(key, 16)
         self.IV = IV
         self._counter_bytes = 16 - len(self.IV)
         self._counter = self.IV + bytearray(b'\x00' * self._counter_bytes)
+
+    @property
+    def counter(self):
+        return self._counter
+
+    @counter.setter
+    def counter(self, ctr):
+        self._counter = ctr
+
+    def _counter_update(self):
+        counter_int = bytesToNumber(self._counter) + 1
+        self._counter = numberToByteArray(counter_int, 16)
+        if self._counter_bytes > 0 and \
+                self._counter[-self._counter_bytes:] == \
+                bytearray(b'\xff' * self._counter_bytes):
+            raise OverflowError("CTR counter overflowed")
+
+    def encrypt(self, plaintext):
+
+        mask = bytearray()
+        while len(mask) < len(plaintext):
+            mask += self.rijndael.encrypt(self._counter)
+            self._counter_update()
+        inp_bytes = bytearray(i ^ j for i, j in zip(plaintext, mask))
+        return inp_bytes
+
+    def decrypt(self, ciphertext):
+        return self.encrypt(ciphertext)
diff --git a/tlslite/utils/python_aesccm.py b/tlslite/utils/python_aesccm.py
index c2335a1..ad3db99 100644
--- a/tlslite/utils/python_aesccm.py
+++ b/tlslite/utils/python_aesccm.py
@@ -1,2 +1,9 @@
+# Author: Ivan Nikolchev
+# See the LICENSE file for legal information regarding use of this file.
+
 """ Pure Python AES-CCM implementation."""
+
 from tlslite.utils.aesccm import AESCCM
+
+def new(key, tagLength=16):
+    return AESCCM(key, "python", bytearray(16), tagLength)
diff --git a/tlslite/utils/python_aesgcm.py b/tlslite/utils/python_aesgcm.py
index f90729a..8e7e343 100644
--- a/tlslite/utils/python_aesgcm.py
+++ b/tlslite/utils/python_aesgcm.py
@@ -1,3 +1,11 @@
+# Author: Google
+# See the LICENSE file for legal information regarding use of this file.
+
 """Pure-Python AES-GCM implementation."""
+
 from .aesgcm import AESGCM
 from .rijndael import Rijndael
+
+
+def new(key):
+    return AESGCM(key, "python", Rijndael(key, 16).encrypt)
diff --git a/tlslite/utils/python_chacha20_poly1305.py b/tlslite/utils/python_chacha20_poly1305.py
index d18ffd6..30a74e6 100644
--- a/tlslite/utils/python_chacha20_poly1305.py
+++ b/tlslite/utils/python_chacha20_poly1305.py
@@ -1,7 +1,11 @@
+# Author: Hubert Kario (c) 2015
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """Pure-Python ChaCha20/Poly1305 implementation."""
-from .chacha20_poly1305 import CHACHA20_POLY1305

+from .chacha20_poly1305 import CHACHA20_POLY1305

 def new(key):
     """Return an AEAD cipher implementation"""
-    pass
+    return CHACHA20_POLY1305(key, "python")
diff --git a/tlslite/utils/python_dsakey.py b/tlslite/utils/python_dsakey.py
index e515031..b590881 100644
--- a/tlslite/utils/python_dsakey.py
+++ b/tlslite/utils/python_dsakey.py
@@ -1,20 +1,26 @@
+# Author: Frantisek Krenzelok
 """Pure-Python RSA implementation."""
-from ecdsa.der import encode_sequence, encode_integer, remove_sequence, remove_integer
-from .cryptomath import getRandomNumber, getRandomPrime, powMod, numBits, bytesToNumber, invMod, secureHash, GMPY2_LOADED, gmpyLoaded
+from ecdsa.der import encode_sequence, encode_integer,  \
+    remove_sequence, remove_integer
+
+from .cryptomath import getRandomNumber, getRandomPrime,    \
+    powMod, numBits, bytesToNumber, invMod,   \
+    secureHash, GMPY2_LOADED, gmpyLoaded
+
 from .compat import compatHMAC
+
 if GMPY2_LOADED:
     from gmpy2 import mpz
 elif gmpyLoaded:
     from gmpy import mpz
-from .dsakey import DSAKey

+from .dsakey import DSAKey

 class Python_DSAKey(DSAKey):
     """
     Concrete implementaion of DSA object.
     for func docstring see tlslite/dsakey.py
     """
-
     def __init__(self, p=0, q=0, g=0, x=0, y=0):
         if gmpyLoaded or GMPY2_LOADED:
             p = mpz(p)
@@ -29,11 +35,52 @@ class Python_DSAKey(DSAKey):
         self.public_key = y
         if self.private_key and not self.public_key:
             self.public_key = powMod(g, self.private_key, p)
-        self.key_type = 'dsa'
+        self.key_type = "dsa"

     def __len__(self):
         return numBits(self.p)

+    def hasPrivateKey(self):
+        return bool(self.private_key)
+
+    @staticmethod
+    def generate(L, N):
+        assert (L, N) in [(1024, 160), (2048, 224), (2048, 256), (3072, 256)]
+        key = Python_DSAKey()
+        (q, p) = Python_DSAKey.generate_qp(L, N)
+
+        index = getRandomNumber(1, (p-1))
+        g = powMod(index, int((p-1)/q), p)
+        x = getRandomNumber(1, q-1)
+        y = powMod(g, x, p)
+        if gmpyLoaded or GMPY2_LOADED:
+            p = mpz(p)
+            q = mpz(q)
+            g = mpz(g)
+            x = mpz(x)
+            y = mpz(y)
+        key.q = q
+        key.p = p
+        key.g = g
+        key.private_key = x
+        key.public_key = y
+        return key
+
+    @staticmethod
+    def generate_qp(L, N):
+        assert (L, N) in [(1024, 160), (2048, 224), (2048, 256), (3072, 256)]
+
+        q = int(getRandomPrime(N))
+        while True:
+            p = int(getRandomPrime(L))
+            if (p-1) % q:
+                break
+        return (q, p)
+
+    def hashAndSign(self, data, hAlg="sha1"):
+        hashData = (secureHash(bytearray(data), hAlg))
+        return self.sign(hashData)
+
     def sign(self, data, padding=None, hashAlg=None, saltLen=None):
         """
         :type data: bytearray
@@ -49,10 +96,23 @@ class Python_DSAKey(DSAKey):
         :type saltLen: int
         :param saltLen: Ignored, present for API compatibility with RSA
         """
-        pass
+        N = numBits(self.q)
+        digest_len = len(data) * 8
+        digest = bytesToNumber(data)
+        if N < digest_len:
+            digest >>= digest_len - N
+
+        k = getRandomNumber(1, (self.q-1))
+        if gmpyLoaded or GMPY2_LOADED:
+            k = mpz(k)
+            digest = mpz(digest)
+        r = powMod(self.g, k, self.p) % self.q
+        s = invMod(k, self.q) * (digest + self.private_key * r) % self.q
+
+        return encode_sequence(encode_integer(r), encode_integer(s))

     def verify(self, signature, hashData, padding=None, hashAlg=None,
-        saltLen=None):
+               saltLen=None):
         """Verify the passed-in bytes with the signature.

         This verifies a DSA signature on the passed-in data.
@@ -75,4 +135,40 @@ class Python_DSAKey(DSAKey):
         :rtype: bool
         :returns: Whether the signature matches the passed-in data.
         """
-        pass
+        N = numBits(self.q)
+        digest_len = len(hashData) * 8
+        digest = bytesToNumber(hashData)
+
+        if N < digest_len:
+            digest >>= digest_len - N
+
+        signature = compatHMAC(signature)
+
+        # get r, s keys
+        if not signature:
+            return False
+        body, rest = remove_sequence(signature)
+        if rest:
+            return False
+        r, rest = remove_integer(body)
+        s, rest = remove_integer(rest)
+        if rest:
+            return False
+
+        if gmpyLoaded or GMPY2_LOADED:
+            r = mpz(r)
+            s = mpz(s)
+
+        # check the signature
+        if 0 < r < self.q and 0 < s < self.q:
+            w = invMod(s, self.q)
+            u1 = (digest * w) % self.q
+            u2 = (r * w) % self.q
+            v = ((powMod(self.g, u1, self.p) * \
+                  powMod(self.public_key, u2, self.p)) % self.p) % self.q
+            return r == v
+        return False
+
+    def hashAndVerify(self, signature, data, hAlg="sha1"):
+        digest = secureHash(bytearray(data), hAlg)
+        return self.verify(signature, digest)
diff --git a/tlslite/utils/python_ecdsakey.py b/tlslite/utils/python_ecdsakey.py
index 8523fc6..b354793 100644
--- a/tlslite/utils/python_ecdsakey.py
+++ b/tlslite/utils/python_ecdsakey.py
@@ -1,3 +1,5 @@
+# Author Hubert Kario, copyright 2019
+
 from .ecdsakey import ECDSAKey
 from ecdsa.curves import curves
 from ecdsa.util import sigencode_der, sigdecode_der
@@ -8,7 +10,6 @@ from . import tlshashlib
 from .cryptomath import numBits
 from .compat import compatHMAC

-
 class Python_ECDSAKey(ECDSAKey):
     """
     Concrete implementation of ECDSA object backed by python-ecdsa.
@@ -31,26 +32,64 @@ class Python_ECDSAKey(ECDSAKey):

     def __init__(self, x, y, curve_name, secret_multiplier=None):
         if not curve_name:
-            raise ValueError('curve_name must be specified')
+            raise ValueError("curve_name must be specified")
         self.curve_name = curve_name
+
         for c in curves:
             if c.name == curve_name or c.openssl_name == curve_name:
                 curve = c
                 break
         else:
-            raise ValueError("Curve '{0}' not supported by python-ecdsa".
-                format(curve_name))
+            raise ValueError("Curve '{0}' not supported by python-ecdsa"
+                             .format(curve_name))
+
         self.private_key = None
         self.public_key = None
-        self.key_type = 'ecdsa'
+        self.key_type = "ecdsa"
+
         if secret_multiplier:
             self.private_key = SigningKey.from_secret_exponent(
                 secret_multiplier, curve)
+
         if x and y:
             point = Point(curve.curve, x, y)
             self.public_key = VerifyingKey.from_public_point(point, curve)
+
         if not self.public_key:
             self.public_key = self.private_key.get_verifying_key()

     def __len__(self):
         return numBits(self.public_key.curve.order)
+
+    def hasPrivateKey(self):
+        return bool(self.private_key)
+
+    def acceptsPassword(self):
+        return False
+
+    @staticmethod
+    def generate(bits):
+        raise NotImplementedError()
+
+    def _sign(self, data, hAlg):
+        func = getattr(tlshashlib, hAlg)
+
+        return self.private_key.\
+            sign_digest_deterministic(compatHMAC(data),
+                                      hashfunc=func,
+                                      sigencode=sigencode_der)
+
+    def _hashAndSign(self, data, hAlg):
+        return self.private_key.sign_deterministic(compatHMAC(data),
+                                                   hash=getattr(tlshashlib,
+                                                                hAlg),
+                                                   sigencode=sigencode_der)
+
+    def _verify(self, signature, hash_bytes):
+        try:
+            return self.public_key.verify_digest(compatHMAC(signature),
+                                                 compatHMAC(hash_bytes),
+                                                 sigdecode_der)
+        # https://github.com/warner/python-ecdsa/issues/114
+        except (BadSignatureError, UnexpectedDER, IndexError, AssertionError):
+            return False
diff --git a/tlslite/utils/python_eddsakey.py b/tlslite/utils/python_eddsakey.py
index 6129165..0829944 100644
--- a/tlslite/utils/python_eddsakey.py
+++ b/tlslite/utils/python_eddsakey.py
@@ -1,3 +1,5 @@
+# Author Hubert Kario, copyright 2021
+
 from .eddsakey import EdDSAKey
 from ecdsa.keys import BadSignatureError
 from ecdsa.der import UnexpectedDER
@@ -27,13 +29,36 @@ class Python_EdDSAKey(EdDSAKey):

     def __init__(self, public_key, private_key=None):
         if not public_key and not private_key:
-            raise ValueError('at least one key must be provided')
+            raise ValueError("at least one key must be provided")
         if not public_key:
             public_key = private_key.verifying_key
+
         self.curve_name = public_key.curve.name
+
         self.private_key = private_key
         self.public_key = public_key
         self.key_type = self.curve_name

     def __len__(self):
         return numBits(self.public_key.curve.order)
+
+    def hasPrivateKey(self):
+        return bool(self.private_key)
+
+    def acceptsPassword(self):
+        return False
+
+    @staticmethod
+    def generate(bits):
+        raise NotImplementedError()
+
+    def _hashAndSign(self, data):
+        return self.private_key.sign_deterministic(compatHMAC(data))
+
+    def _hashAndVerify(self, signature, data):
+        try:
+            return self.public_key.verify(compatHMAC(signature),
+                                          compatHMAC(data))
+        # https://github.com/warner/python-ecdsa/issues/114
+        except (BadSignatureError, UnexpectedDER, IndexError, AssertionError):
+            return False
diff --git a/tlslite/utils/python_key.py b/tlslite/utils/python_key.py
index 5c423c7..29e4701 100644
--- a/tlslite/utils/python_key.py
+++ b/tlslite/utils/python_key.py
@@ -1,3 +1,5 @@
+
+
 from .python_rsakey import Python_RSAKey
 from .python_ecdsakey import Python_ECDSAKey
 from .python_dsakey import Python_DSAKey
@@ -9,7 +11,6 @@ from .compat import compatHMAC
 from ecdsa.curves import NIST256p, NIST384p, NIST521p
 from ecdsa.keys import SigningKey, VerifyingKey

-
 class Python_Key(object):
     """
     Generic methods for parsing private keys from files.
@@ -20,16 +21,162 @@ class Python_Key(object):
     @staticmethod
     def parsePEM(s, passwordCallback=None):
         """Parse a string containing a PEM-encoded <privateKey>."""
-        pass
+
+        if pemSniff(s, "PRIVATE KEY"):
+            bytes = dePem(s, "PRIVATE KEY")
+            return Python_Key._parse_pkcs8(bytes)
+        elif pemSniff(s, "RSA PRIVATE KEY"):
+            bytes = dePem(s, "RSA PRIVATE KEY")
+            return Python_Key._parse_ssleay(bytes, "rsa")
+        elif pemSniff(s, "DSA PRIVATE KEY"):
+            bytes = dePem(s, "DSA PRIVATE KEY")
+            return Python_Key._parse_dsa_ssleay(bytes)
+        elif pemSniff(s, "EC PRIVATE KEY"):
+            bytes = dePem(s, "EC PRIVATE KEY")
+            return Python_Key._parse_ecc_ssleay(bytes)
+        elif pemSniff(s, "PUBLIC KEY"):
+            bytes = dePem(s, "PUBLIC KEY")
+            return Python_Key._parse_public_key(bytes)
+        else:
+            raise SyntaxError("Not a PEM private key file")
+
+    @staticmethod
+    def _parse_public_key(bytes):
+        # public keys are encoded as the subject_public_key_info objects
+        spk_info = ASN1Parser(bytes)
+
+        # first element of the SEQUENCE is the AlgorithmIdentifier
+        alg_id = spk_info.getChild(0)
+
+        # AlgId has two elements, the OID of the algorithm and parameters
+        # parameters generally have to be NULL, with exception of RSA-PSS
+
+        alg_oid = alg_id.getChild(0)
+
+        if list(alg_oid.value) == [42, 134, 72, 134, 247, 13, 1, 1, 1]:
+            key_type = "rsa"
+        elif list(alg_oid.value) == [42, 134, 72, 206, 56, 4, 1]:
+            key_type = "dsa"
+        else:
+            raise SyntaxError("Only RSA or DSA Public keys supported")
+
+        if key_type == "rsa":
+            subject_public_key = ASN1Parser(
+                ASN1Parser(spk_info.getChildBytes(1)).value[1:])
+
+            modulus = subject_public_key.getChild(0)
+            exponent = subject_public_key.getChild(1)
+
+            n = bytesToNumber(modulus.value)
+            e = bytesToNumber(exponent.value)
+
+            return Python_RSAKey(n, e, key_type="rsa")
+
+        elif key_type == "dsa":
+            # public key
+            subject_public_key = ASN1Parser(
+                ASN1Parser(spk_info.getChildBytes(1)).value[1:])
+
+            public_key = bytesToNumber(subject_public_key.value)
+
+            # domain parameters
+            domain = alg_id.getChild(1)
+
+            p = bytesToNumber(domain.getChild(0).value)
+            q = bytesToNumber(domain.getChild(1).value)
+            g = bytesToNumber(domain.getChild(2).value)
+
+            return Python_DSAKey(p, q, g, y=public_key)
+
+    @staticmethod
+    def _parse_pkcs8(bytes):
+        parser = ASN1Parser(bytes)
+
+        # first element in PrivateKeyInfo is an INTEGER
+        version = parser.getChild(0).value
+        if bytesToNumber(version) != 0:
+            raise SyntaxError("Unrecognized PKCS8 version")
+
+        # second element in PrivateKeyInfo is a SEQUENCE of type
+        # AlgorithmIdentifier
+        alg_ident = parser.getChild(1)
+        seq_len = alg_ident.getChildCount()
+        # first item of AlgorithmIdentifier is an OBJECT (OID)
+        oid = alg_ident.getChild(0)
+        if list(oid.value) == [42, 134, 72, 134, 247, 13, 1, 1, 1]:
+            key_type = "rsa"
+        elif list(oid.value) == [42, 134, 72, 134, 247, 13, 1, 1, 10]:
+            key_type = "rsa-pss"
+        elif list(oid.value) == [42, 134, 72, 206, 56, 4, 1]:
+            key_type = "dsa"
+        elif list(oid.value) == [42, 134, 72, 206, 61, 2, 1]:
+            key_type = "ecdsa"
+        elif list(oid.value) == [43, 101, 112]:
+            key_type = "Ed25519"
+        elif list(oid.value) == [43, 101, 113]:
+            key_type = "Ed448"
+        else:
+            raise SyntaxError("Unrecognized AlgorithmIdentifier: {0}"
+                              .format(list(oid.value)))
+        # second item of AlgorithmIdentifier are parameters (defined by
+        # above algorithm)
+        if key_type == "rsa":
+            if seq_len != 2:
+                raise SyntaxError("Missing parameters for RSA algorithm ID")
+            parameters = alg_ident.getChild(1)
+            if parameters.value != bytearray(0):
+                raise SyntaxError("RSA parameters are not NULL")
+        if key_type == "dsa":
+            if seq_len != 2:
+                raise SyntaxError("Invalid encoding of algorithm identifier")
+            parameters = alg_ident.getChild(1)
+            if parameters.value == bytearray(0):
+                parameters = None
+        elif key_type == "ecdsa":
+            if seq_len != 2:
+                raise SyntaxError("Invalid encoding of algorithm identifier")
+            curveID = alg_ident.getChild(1)
+            if list(curveID.value) == [42, 134, 72, 206, 61, 3, 1, 7]:
+                curve = NIST256p
+            elif list(curveID.value) == [43, 129, 4, 0, 34]:
+                curve = NIST384p
+            elif list(curveID.value) == [43, 129, 4, 0, 35]:
+                curve = NIST521p
+            else:
+                raise SyntaxError("Unknown curve")
+        else:  # rsa-pss
+            pass  # ignore parameters - don't apply restrictions
+
+        if seq_len > 2:
+            raise SyntaxError("Invalid encoding of AlgorithmIdentifier")
+
+        #Get the privateKey
+        private_key_parser = parser.getChild(2)
+
+        #Adjust for OCTET STRING encapsulation
+        private_key_parser = ASN1Parser(private_key_parser.value)
+
+        if key_type in ("Ed25519", "Ed448"):
+            return Python_Key._parse_eddsa_private_key(bytes)
+        if key_type == "ecdsa":
+            return Python_Key._parse_ecdsa_private_key(private_key_parser,
+                                                       curve)
+        elif key_type == "dsa":
+            return Python_Key._parse_dsa_private_key(private_key_parser, parameters)
+        else:
+            return Python_Key._parse_asn1_private_key(private_key_parser,
+                                                      key_type)

     @staticmethod
-    def _parse_ssleay(data, key_type='rsa'):
+    def _parse_ssleay(data, key_type="rsa"):
         """
         Parse binary structure of the old SSLeay file format used by OpenSSL.

         For RSA keys.
         """
-        pass
+        private_key_parser = ASN1Parser(data)
+        # "rsa" type as old format doesn't support rsa-pss parameters
+        return Python_Key._parse_asn1_private_key(private_key_parser, key_type)

     @staticmethod
     def _parse_dsa_ssleay(data):
@@ -38,7 +185,8 @@ class Python_Key(object):

         For DSA keys.
         """
-        pass
+        private_key_parser = ASN1Parser(data)
+        return Python_Key._parse_dsa_private_key(private_key_parser)

     @staticmethod
     def _parse_ecc_ssleay(data):
@@ -47,9 +195,78 @@ class Python_Key(object):

         For ECDSA keys.
         """
-        pass
+        private_key = SigningKey.from_der(compatHMAC(data))
+        secret_mult = private_key.privkey.secret_multiplier
+        return Python_ECDSAKey(None, None, private_key.curve.name,
+                               secret_mult)
+
+    @staticmethod
+    def _parse_ecdsa_private_key(private, curve):
+        ver = private.getChild(0)
+        if ver.value != b'\x01':
+            raise SyntaxError("Unexpected EC key version")
+        private_key = private.getChild(1)
+        public_key = private.getChild(2)
+        # first two bytes are the ASN.1 custom type and the length of payload
+        # while the latter two bytes are just specification of the public
+        # key encoding (uncompressed)
+        # TODO: update ecdsa lib to be able to parse PKCS#8 files
+        if curve is not NIST521p:
+            if list(public_key.value[:1]) != [3] or \
+                    list(public_key.value[2:4]) != [0, 4]:
+                raise SyntaxError("Invalid or unsupported encoding of public key")
+
+            pub_key = VerifyingKey.from_string(
+                    compatHMAC(public_key.value[4:]),
+                    curve)
+        else:
+            if list(public_key.value[:3]) != [3, 129, 134] or \
+                    list(public_key.value[3:5]) != [0, 4]:
+                raise SyntaxError("Invalid or unsupported encoding of public key")
+
+            pub_key = VerifyingKey.from_string(
+                    compatHMAC(public_key.value[5:]),
+                    curve)
+        pub_x = pub_key.pubkey.point.x()
+        pub_y = pub_key.pubkey.point.y()
+        priv_key = SigningKey.from_string(compatHMAC(private_key.value),
+                                          curve)
+        mult = priv_key.privkey.secret_multiplier
+        return Python_ECDSAKey(pub_x, pub_y, curve.name, mult)

     @staticmethod
     def _parse_eddsa_private_key(data):
         """Parse a DER encoded EdDSA key."""
-        pass
+        priv_key = SigningKey.from_der(data)
+        return Python_EdDSAKey(priv_key.verifying_key, private_key=priv_key)
+
+    @staticmethod
+    def _parse_asn1_private_key(private_key_parser, key_type):
+        version = private_key_parser.getChild(0).value[0]
+        if version != 0:
+            raise SyntaxError("Unrecognized RSAPrivateKey version")
+        n = bytesToNumber(private_key_parser.getChild(1).value)
+        e = bytesToNumber(private_key_parser.getChild(2).value)
+        d = bytesToNumber(private_key_parser.getChild(3).value)
+        p = bytesToNumber(private_key_parser.getChild(4).value)
+        q = bytesToNumber(private_key_parser.getChild(5).value)
+        dP = bytesToNumber(private_key_parser.getChild(6).value)
+        dQ = bytesToNumber(private_key_parser.getChild(7).value)
+        qInv = bytesToNumber(private_key_parser.getChild(8).value)
+        return Python_RSAKey(n, e, d, p, q, dP, dQ, qInv, key_type)
+
+
+    @staticmethod
+    def _parse_dsa_private_key(private_key_parser, domain_parameters=None):
+        if domain_parameters:
+            p = bytesToNumber(domain_parameters.getChild(0).value)
+            q = bytesToNumber(domain_parameters.getChild(1).value)
+            g = bytesToNumber(domain_parameters.getChild(2).value)
+            x = bytesToNumber(private_key_parser.value)
+            return Python_DSAKey(p, q, g, x)
+        p = bytesToNumber(private_key_parser.getChild(1).value)
+        q = bytesToNumber(private_key_parser.getChild(2).value)
+        g = bytesToNumber(private_key_parser.getChild(3).value)
+        y = bytesToNumber(private_key_parser.getChild(4).value)
+        x = bytesToNumber(private_key_parser.getChild(5).value)
+        return Python_DSAKey(p, q, g, x, y)
diff --git a/tlslite/utils/python_rc4.py b/tlslite/utils/python_rc4.py
index 84242a5..15d9fd4 100644
--- a/tlslite/utils/python_rc4.py
+++ b/tlslite/utils/python_rc4.py
@@ -1,17 +1,41 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 """Pure-Python RC4 implementation."""
+
 from .rc4 import RC4
 from .cryptomath import *

+def new(key):
+    return Python_RC4(key)

 class Python_RC4(RC4):
-
     def __init__(self, keyBytes):
-        RC4.__init__(self, keyBytes, 'python')
+        RC4.__init__(self, keyBytes, "python")
         S = [i for i in range(256)]
         j = 0
         for i in range(256):
             j = (j + S[i] + keyBytes[i % len(keyBytes)]) % 256
             S[i], S[j] = S[j], S[i]
+
         self.S = S
         self.i = 0
         self.j = 0
+
+    def encrypt(self, plaintextBytes):
+        ciphertextBytes = plaintextBytes[:]
+        S = self.S
+        i = self.i
+        j = self.j
+        for x in range(len(ciphertextBytes)):
+            i = (i + 1) % 256
+            j = (j + S[i]) % 256
+            S[i], S[j] = S[j], S[i]
+            t = (S[i] + S[j]) % 256
+            ciphertextBytes[x] ^= S[t]
+        self.i = i
+        self.j = j
+        return ciphertextBytes
+
+    def decrypt(self, ciphertext):
+        return self.encrypt(ciphertext)
diff --git a/tlslite/utils/python_rsakey.py b/tlslite/utils/python_rsakey.py
index 8e9c317..ab4bf41 100644
--- a/tlslite/utils/python_rsakey.py
+++ b/tlslite/utils/python_rsakey.py
@@ -1,3 +1,6 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 """Pure-Python RSA implementation."""
 import threading
 from .cryptomath import *
@@ -9,15 +12,13 @@ if GMPY2_LOADED:
 elif gmpyLoaded:
     from gmpy import mpz

-
 class Python_RSAKey(RSAKey):
-
     def __init__(self, n=0, e=0, d=0, p=0, q=0, dP=0, dQ=0, qInv=0,
-        key_type='rsa'):
+                 key_type="rsa"):
         """Initialise key directly from integers.

         see also generate() and parsePEM()."""
-        if n and not e or e and not n:
+        if (n and not e) or (e and not n):
             raise AssertionError()
         if gmpyLoaded or GMPY2_LOADED:
             n = mpz(n)
@@ -31,7 +32,7 @@ class Python_RSAKey(RSAKey):
         self.n = n
         self.e = e
         if p and not q or not p and q:
-            raise ValueError('p and q must be set or left unset together')
+            raise ValueError("p and q must be set or left unset together")
         if not d and p and q:
             t = lcm(p - 1, q - 1)
             d = invMod(e, t)
@@ -57,22 +58,94 @@ class Python_RSAKey(RSAKey):
         Does the key has the associated private key (True) or is it only
         the public part (False).
         """
-        pass
+        return self.d != 0
+
+    def _rawPrivateKeyOp(self, message):
+        n = self.n
+        with self._lock:
+            # Create blinding values, on the first pass:
+            if not self.blinder:
+                self.unblinder = getRandomNumber(2, n)
+                self.blinder = powMod(invMod(self.unblinder, n), self.e,
+                                      n)
+            unblinder = self.unblinder
+            blinder = self.blinder
+
+            # Update blinding values
+            self.blinder = (blinder * blinder) % n
+            self.unblinder = (unblinder * unblinder) % n
+
+        # Blind the input
+        message = (message * blinder) % n
+
+        # Perform the RSA operation
+        cipher = self._rawPrivateKeyOpHelper(message)
+
+        # Unblind the output
+        cipher = (cipher * unblinder) % n
+
+        # Return the output
+        return cipher
+
+    def _rawPrivateKeyOpHelper(self, m):
+        #Non-CRT version
+        #c = pow(m, self.d, self.n)
+
+        #CRT version  (~3x faster).
+        p, q = self.p, self.q
+        s1 = pow(m, self.dP, p)
+        s2 = pow(m, self.dQ, q)
+        h = ((s1 - s2) * self.qInv) % p
+        c = s2 + q * h
+        return c
+
+    def _rawPublicKeyOp(self, ciphertext):
+        msg = pow(ciphertext, self.e, self.n)
+        return msg

     def acceptsPassword(self):
         """Does it support encrypted key files."""
-        pass
+        return False

     @staticmethod
-    def generate(bits, key_type='rsa'):
+    def generate(bits, key_type="rsa"):
         """Generate a private key with modulus 'bits' bit big.

         key_type can be "rsa" for a universal rsaEncryption key or
         "rsa-pss" for a key that can be used only for RSASSA-PSS."""
-        pass
+        # p, q, and t are standard names for the variables in RSA, so
+        # ignore the fact those are one character long variable names
+        # pylint: disable=invalid-name
+        key = Python_RSAKey()
+        while True:
+            p = getRandomPrime(bits//2, False)
+            q = getRandomPrime(bits//2, False)
+            if gmpyLoaded or GMPY2_LOADED:
+                p = mpz(p)
+                q = mpz(q)
+            t = lcm(p-1, q-1)
+            # since we need to calculate inverse of 65537 mod t, they
+            # must be relatively prime (coprime)
+            if gcd(t, 65537) == 1:
+                break
+        key.n = p * q
+        if gmpyLoaded or GMPY2_LOADED:
+            key.e = mpz(65537)
+        else:
+            key.e = 65537
+        key.d = invMod(key.e, t)
+        key.p = p
+        key.q = q
+        key.dP = key.d % (p-1)
+        key.dQ = key.d % (q-1)
+        key.qInv = invMod(q, p)
+        key.key_type = key_type
+        # pylint: enable=invalid-name
+        return key

     @staticmethod
-    @deprecated_params({'data': 's', 'password_callback': 'passwordCallback'})
+    @deprecated_params({"data": "s", "password_callback": "passwordCallback"})
     def parsePEM(data, password_callback=None):
         """Parse a string containing a PEM-encoded <privateKey>."""
-        pass
+        from .python_key import Python_Key
+        return Python_Key.parsePEM(data, password_callback)
diff --git a/tlslite/utils/python_tripledes.py b/tlslite/utils/python_tripledes.py
index c926c9c..431be5d 100644
--- a/tlslite/utils/python_tripledes.py
+++ b/tlslite/utils/python_tripledes.py
@@ -1,3 +1,20 @@
+#################################################
+#               Documentation                   #
+#################################################
+
+# Author:   Todd Whiteman
+# Date:     16th March, 2009
+# Verion:   2.0.0
+# License:  Public Domain - free to do as you wish
+# Homepage: http://twhiteman.netfirms.com/Des.html
+#
+# Modified by: Adam Varga, 2018
+#
+# A pure python implementation of the DES and Triple DES
+# encryption algorithms using CBC mode. Triple DES class is
+# implemented by utilising the DES base. Triple DES is
+# DES-EDE3 with a 24 byte key, or DES-EDE2 with a 16 byte key.
+
 """
 Class initialization
 --------------------
@@ -8,14 +25,18 @@ key -> Bytes containing the encryption key. 8 bytes for DES, 16 or 24 bytes
        for Triple DES
 iv  -> Initialization Vector in bytes. Length must be 8 bytes.
 """
+
 import sys
 import warnings
+
+# PY_VER is used to handle Python2 and Python3 differences.
 PY_VER = sys.version_info


 def new(key, iv):
     """Operate this 3DES cipher."""
-    pass
+
+    return Python_TripleDES(key, iv)


 class _baseDes(object):
@@ -30,7 +51,28 @@ class _baseDes(object):
         Only accept byte strings or ascii unicode values.
         Otherwise there is no way to correctly decode the data into bytes.
         """
-        pass
+
+        if PY_VER < (3, ):
+            if isinstance(data, unicode):
+                raise ValueError("Only bytes, bytearray or memoryview "
+                                 "objects of them should be passed, "
+                                 "not Unicode strings")
+        else:
+            if isinstance(data, str):
+                warnings.warn("Only bytes, bytearray or memoryview "
+                              "objects of them should be passed",
+                              DeprecationWarning,
+                              stacklevel=3)
+                # Only accept ascii unicode values.
+                try:
+                    return data.encode('ascii')
+                except UnicodeEncodeError:
+                    raise ValueError("The Unicode string shouldn't be passed")
+        return data
+
+#############################################
+#                   DES                     #
+#############################################


 class Des(_baseDes):
@@ -38,98 +80,303 @@ class Des(_baseDes):

     Supports CBC (Cypher Block Chaining) mode.
     """
-    __pc1 = [56, 48, 40, 32, 24, 16, 8, 0, 57, 49, 41, 33, 25, 17, 9, 1, 58,
-        50, 42, 34, 26, 18, 10, 2, 59, 51, 43, 35, 62, 54, 46, 38, 30, 22, 
-        14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 60, 52, 44, 36, 28, 20, 12, 4,
-        27, 19, 11, 3]
-    __left_rotations = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1]
-    __pc2 = [13, 16, 10, 23, 0, 4, 2, 27, 14, 5, 20, 9, 22, 18, 11, 3, 25, 
-        7, 15, 6, 26, 19, 12, 1, 40, 51, 30, 36, 46, 54, 29, 39, 50, 44, 32,
-        47, 43, 48, 38, 55, 33, 52, 45, 41, 49, 35, 28, 31]
-    __ip = [57, 49, 41, 33, 25, 17, 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, 61,
-        53, 45, 37, 29, 21, 13, 5, 63, 55, 47, 39, 31, 23, 15, 7, 56, 48, 
-        40, 32, 24, 16, 8, 0, 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36,
-        28, 20, 12, 4, 62, 54, 46, 38, 30, 22, 14, 6]
-    __expansion_table = [31, 0, 1, 2, 3, 4, 3, 4, 5, 6, 7, 8, 7, 8, 9, 10, 
-        11, 12, 11, 12, 13, 14, 15, 16, 15, 16, 17, 18, 19, 20, 19, 20, 21,
-        22, 23, 24, 23, 24, 25, 26, 27, 28, 27, 28, 29, 30, 31, 0]
-    __sbox = [[14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, 0, 15,
-        7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, 4, 1, 14, 8, 13, 6, 
-        2, 11, 15, 12, 9, 7, 3, 10, 5, 0, 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 
-        3, 14, 10, 0, 6, 13], [15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 
-        0, 5, 10, 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, 0, 
-        14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15, 13, 8, 10, 1, 3,
-        15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9], [10, 0, 9, 14, 6, 3, 15, 5, 1,
-        13, 12, 7, 11, 4, 2, 8, 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 
-        11, 15, 1, 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7, 1,
-        10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12], [7, 13, 14, 3, 
-        0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, 13, 8, 11, 5, 6, 15, 0, 3, 
-        4, 7, 2, 12, 1, 10, 14, 9, 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14,
-        5, 2, 8, 4, 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14],
-        [2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, 14, 11, 2, 
-        12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, 4, 2, 1, 11, 10, 13, 7, 
-        8, 15, 9, 12, 5, 6, 3, 0, 14, 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0,
-        9, 10, 4, 5, 3], [12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5,
-        11, 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, 9, 14, 15,
-        5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6, 4, 3, 2, 12, 9, 5, 15, 
-        10, 11, 14, 1, 7, 6, 0, 8, 13], [4, 11, 2, 14, 15, 0, 8, 13, 3, 12,
-        9, 7, 5, 10, 6, 1, 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 
-        8, 6, 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2, 6, 11, 
-        13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12], [13, 2, 8, 4, 6, 15,
-        11, 1, 10, 9, 3, 14, 5, 0, 12, 7, 1, 15, 13, 8, 10, 3, 7, 4, 12, 5,
-        6, 11, 0, 14, 9, 2, 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3,
-        5, 8, 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11]]
-    __p = [15, 6, 19, 20, 28, 11, 27, 16, 0, 14, 22, 25, 4, 17, 30, 9, 1, 7,
-        23, 13, 31, 26, 2, 8, 18, 12, 29, 5, 21, 10, 3, 24]
-    __fp = [39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, 14, 54, 22, 62, 30, 
-        37, 5, 45, 13, 53, 21, 61, 29, 36, 4, 44, 12, 52, 20, 60, 28, 35, 3,
-        43, 11, 51, 19, 59, 27, 34, 2, 42, 10, 50, 18, 58, 26, 33, 1, 41, 9,
-        49, 17, 57, 25, 32, 0, 40, 8, 48, 16, 56, 24]
-    ENCRYPT = 0
-    DECRYPT = 1

+    # Permutation and translation tables for DES
+    __pc1 = [56, 48, 40, 32, 24, 16, 8,
+             0, 57, 49, 41, 33, 25, 17,
+             9, 1, 58, 50, 42, 34, 26,
+             18, 10, 2, 59, 51, 43, 35,
+             62, 54, 46, 38, 30, 22, 14,
+             6, 61, 53, 45, 37, 29, 21,
+             13, 5, 60, 52, 44, 36, 28,
+             20, 12, 4, 27, 19, 11, 3]
+
+    # Number left rotations of pc1
+    __left_rotations = [1, 1, 2, 2, 2, 2, 2, 2,
+                        1, 2, 2, 2, 2, 2, 2, 1]
+
+    # Permuted choice key (table 2)
+    __pc2 = [13, 16, 10, 23, 0, 4,
+             2, 27, 14, 5, 20, 9,
+             22, 18, 11, 3, 25, 7,
+             15, 6, 26, 19, 12, 1,
+             40, 51, 30, 36, 46, 54,
+             29, 39, 50, 44, 32, 47,
+             43, 48, 38, 55, 33, 52,
+             45, 41, 49, 35, 28, 31]
+
+    # Initial permutation IP
+    __ip = [57, 49, 41, 33, 25, 17, 9, 1,
+            59, 51, 43, 35, 27, 19, 11, 3,
+            61, 53, 45, 37, 29, 21, 13, 5,
+            63, 55, 47, 39, 31, 23, 15, 7,
+            56, 48, 40, 32, 24, 16, 8, 0,
+            58, 50, 42, 34, 26, 18, 10, 2,
+            60, 52, 44, 36, 28, 20, 12, 4,
+            62, 54, 46, 38, 30, 22, 14, 6]
+
+    # Expansion table for turning 32 bit blocks into 48 bits
+    __expansion_table = [31, 0, 1, 2, 3, 4,
+                         3, 4, 5, 6, 7, 8,
+                         7, 8, 9, 10, 11, 12,
+                         11, 12, 13, 14, 15, 16,
+                         15, 16, 17, 18, 19, 20,
+                         19, 20, 21, 22, 23, 24,
+                         23, 24, 25, 26, 27, 28,
+                         27, 28, 29, 30, 31, 0]
+
+    # The (in)famous S-boxes
+    __sbox = [  # S1
+              [14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
+               0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
+               4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
+               15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13],
+
+              # S2
+              [15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
+               3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
+               0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
+               13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9],
+
+              # S3
+              [10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
+               13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
+               13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
+               1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12],
+
+              # S4
+              [7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
+               13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
+               10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
+               3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14],
+
+              # S5
+              [2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
+               14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
+               4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
+               11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3],
+
+              # S6
+              [12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
+               10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
+               9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
+               4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13],
+
+              # S7
+              [4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
+               13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
+               1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
+               6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12],
+
+              # S8
+              [13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
+               1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
+               7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
+               2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11], ]
+
+    # 32-bit permutation function P used on the output of the S-boxes
+    __p = [15, 6, 19, 20, 28, 11,
+           27, 16, 0, 14, 22, 25,
+           4, 17, 30, 9, 1, 7,
+           23, 13, 31, 26, 2, 8,
+           18, 12, 29, 5, 21, 10,
+           3, 24]
+
+    # Final permutation IP^-1
+    __fp = [39, 7, 47, 15, 55, 23, 63, 31,
+            38, 6, 46, 14, 54, 22, 62, 30,
+            37, 5, 45, 13, 53, 21, 61, 29,
+            36, 4, 44, 12, 52, 20, 60, 28,
+            35, 3, 43, 11, 51, 19, 59, 27,
+            34, 2, 42, 10, 50, 18, 58, 26,
+            33, 1, 41, 9, 49, 17, 57, 25,
+            32, 0, 40, 8, 48, 16, 56, 24]
+
+    # Type of crypting being done
+    ENCRYPT = 0x00
+    DECRYPT = 0x01
+
+    # Initialisation
     def __init__(self, key, iv=None):
+        # Sanity checking of arguments
         if len(key) != 8:
-            raise ValueError(
-                'Invalid DES key size. Key must be exactly 8 bytes long')
+            raise ValueError("Invalid DES key size. Key must be exactly "
+                             "8 bytes long")
         super(Des, self).__init__(iv)
+
         self.key_size = 8
         self._l = []
         self._r = []
-        self._kn = [[0] * 48] * 16
+        self._kn = [[0] * 48] * 16  # 16 48-bit keys (K1 - K16)
         self._final = []
+
         self.set_key(key)

     def set_key(self, key):
         """Set the crypting key for this object. Must be 8 bytes."""
-        pass
+
+        self.key = key
+        self.__create_sub_keys()

     def __string_to_bitlist(self, data):
         """Turn the string data into a list of bits (1, 0)'s."""
-        pass
+
+        if PY_VER < (3, ):
+            # Turn the strings into integers. Python 3 uses a bytes
+            # class, which already has this behaviour
+            if not isinstance(data, bytearray):
+                data = [ord(c) for c in data]
+        len_data = len(data) * 8
+        result = [0] * len_data
+        pos = 0
+        for ch in data:
+            i = 7
+            while i >= 0:
+                if ch & (1 << i) != 0:
+                    result[pos] = 1
+                else:
+                    result[pos] = 0
+                pos += 1
+                i -= 1
+        return result

     def __bitlist_to_string(self, data):
         """Turn the data as list of bits into a string."""
-        pass
+
+        result = []
+        pos = 0
+        c = 0
+        while pos < len(data):
+            c += data[pos] << (7 - (pos % 8))
+            if (pos % 8) == 7:
+                result.append(c)
+                c = 0
+            pos += 1
+
+        if PY_VER < (3, ):
+            return ''.join([chr(c) for c in result])
+        else:
+            return bytes(result)

     def __permutate(self, table, block):
         """Permutate this block with the specified table."""
-        pass
+        return [block[x] for x in table]

     def __create_sub_keys(self):
         """Transform the secret key for data processing.

         Create the 16 subkeys k[1] to k[16] from the given key.
         """
-        pass
+        key = self.__permutate(Des.__pc1,
+                               self.__string_to_bitlist(self.key))
+        # Split into Left and Right sections
+        self._l = key[:28]
+        self._r = key[28:]
+        for i in range(16):
+            # Perform circular left shifts
+            for _ in range(Des.__left_rotations[i]):
+                self._l.append(self._l[0])
+                del self._l[0]
+                self._r.append(self._r[0])
+                del self._r[0]
+
+            # Create one of the 16 subkeys through pc2 permutation
+            self._kn[i] = self.__permutate(Des.__pc2, self._l + self._r)

     def __des_crypt(self, block, crypt_type):
         """Crypt the block of data through DES bit-manipulation."""
-        pass
+
+        block = self.__permutate(Des.__ip, block)
+        self._l = block[:32]
+        self._r = block[32:]
+
+        # Encryption starts from _kn[1] through to _kn[16]
+        if crypt_type == Des.ENCRYPT:
+            iteration = 0
+            iteration_adjustment = 1
+        # Decryption starts from _kn[16] down to _kn[1]
+        else:
+            iteration = 15
+            iteration_adjustment = -1
+
+        for _ in range(16):
+            # Make a copy of _r[i-1], this will later become _l[i]
+            temp_r = self._r[:]
+
+            # Permutate _r[i - 1] to start creating _r[i]
+            self._r = self.__permutate(Des.__expansion_table, self._r)
+
+            # Exclusive or _r[i - 1] with k[i], create b[1] to b[8] whilst here
+            self._r = [x ^ y for x, y in zip(self._r, self._kn[iteration])]
+            b = [self._r[:6], self._r[6:12], self._r[12:18], self._r[18:24],
+                 self._r[24:30], self._r[30:36], self._r[36:42], self._r[42:]]
+
+            # Permutate b[1] to b[8] using the S-Boxes
+            bn = [0] * 32
+            pos = 0
+            for j in range(8):
+                # Work out the offsets
+                m = (b[j][0] << 1) + b[j][5]
+                n = (b[j][1] << 3) + (b[j][2] << 2) + (b[j][3] << 1) + b[j][4]
+
+                # Find the permutation value
+                v = Des.__sbox[j][(m << 4) + n]
+
+                # Turn value into bits, add it to result: bn
+                bn[pos] = (v & 8) >> 3
+                bn[pos + 1] = (v & 4) >> 2
+                bn[pos + 2] = (v & 2) >> 1
+                bn[pos + 3] = v & 1
+
+                pos += 4
+
+            # Permutate the concatination of b[1] to b[8] (bn)
+            self._r = self.__permutate(Des.__p, bn)
+
+            # Xor with _l[i - 1]
+            self._r = [x ^ y for x, y in zip(self._r, self._l)]
+            self._l = temp_r
+
+            iteration += iteration_adjustment
+
+        # Final permutation of _r[16]_l[16]
+        self._final = self.__permutate(Des.__fp, self._r + self._l)
+        return self._final

     def crypt(self, data, crypt_type):
         """Crypt the data in blocks, running it through des_crypt()."""
-        pass
+
+        iv = self.__string_to_bitlist(self.iv)
+
+        # Split the data into blocks, crypting each one seperately
+        i = 0
+        result = []
+        while i < len(data):
+            # Test code for caching encryption results
+            block = self.__string_to_bitlist(data[i:i+8])
+
+            # Xor with iv if using CBC mode
+            if crypt_type == Des.ENCRYPT:
+                block = [x ^ y for x, y in zip(block, iv)]
+
+            processed_block = self.__des_crypt(block, crypt_type)
+
+            if crypt_type == Des.DECRYPT:
+                processed_block = [x ^ y for x, y in zip(processed_block, iv)]
+                iv = block
+            else:
+                iv = processed_block
+
+            # Add the resulting crypted block to our list
+            result.append(self.__bitlist_to_string(processed_block))
+            i += 8
+
+        # Return the full crypted string
+        return b''.join(result)
+
+#############################################
+#               Triple DES                  #
+#############################################


 class Python_TripleDES(_baseDes):
@@ -139,34 +386,37 @@ class Python_TripleDES(_baseDes):
     the DES-EDE2 (when a 16 byte key is supplied) encryption methods.
     Supports CBC (Cypher Block Chaining) mode.
     """
-
     def __init__(self, key, iv=None):
         self.block_size = 8
         if iv:
             if len(iv) != self.block_size:
-                raise ValueError(
-                    'Invalid Initialization Vector (iv) must be {0} bytes long'
-                    .format(self.block_size))
+                raise ValueError("Invalid Initialization Vector (iv) must be"
+                                 " {0} bytes long".format(self.block_size))
             iv = self._guard_against_unicode(iv)
         else:
-            raise ValueError('Initialization Vector (iv) must be supplied')
+            raise ValueError("Initialization Vector (iv) must be supplied")
+
         super(Python_TripleDES, self).__init__(iv)
+
+        # Will set crypting key for this object. Either 16/24 bytes long.
         self.key_size = len(key)
         if self.key_size not in (16, 24):
-            raise ValueError(
-                'Invalid triple DES key size. Key must be either 16 or 24 bytes long'
-                )
+            raise ValueError("Invalid triple DES key size. "
+                             "Key must be either 16 or 24 bytes long")
         key = self._guard_against_unicode(key)
+
         self.__key1 = Des(key[:8], self.iv)
         self.__key2 = Des(key[8:16], self.iv)
         if self.key_size == 16:
             self.__key3 = Des(key[:8], self.iv)
         else:
             self.__key3 = Des(key[16:], self.iv)
+
         self.isAEAD = False
         self.isBlockCipher = True
-        self.name = '3des'
-        self.implementation = 'python'
+        self.name = "3des"
+        self.implementation = "python"
+
         self.__key1.iv = self.iv
         self.__key2.iv = self.iv
         self.__key3.iv = self.iv
@@ -179,7 +429,31 @@ class Python_TripleDES(_baseDes):
         The data must be a multiple of 8 bytes and will be encrypted
         with the already specified key.
         """
-        pass
+
+        ENCRYPT = Des.ENCRYPT
+        DECRYPT = Des.DECRYPT
+
+        if not data:
+            return bytearray(b'')
+
+        data = self._guard_against_unicode(data)
+        if len(data) % self.block_size:
+            raise ValueError("Invalid data length, must be a multiple "
+                             "of {0} bytes".format(self.block_size))
+
+        i = 0
+        result = []
+        while i < len(data):
+            block = self.__key1.crypt(data[i:i+8], ENCRYPT)
+            block = self.__key2.crypt(block, DECRYPT)
+            block = self.__key3.crypt(block, ENCRYPT)
+            self.__key1.iv = block
+            self.__key2.iv = block
+            self.__key3.iv = block
+            result.append(block)
+            i += 8
+
+        return bytearray(b''.join(result))

     def decrypt(self, data):
         """Decrypt data and return bytes.
@@ -189,4 +463,29 @@ class Python_TripleDES(_baseDes):
         The data must be a multiple of 8 bytes and will be decrypted
         with the already specified key.
         """
-        pass
+        ENCRYPT = Des.ENCRYPT
+        DECRYPT = Des.DECRYPT
+
+        if not data:
+            return bytearray(b'')
+
+        data = self._guard_against_unicode(data)
+        if len(data) % self.block_size:
+            raise ValueError("Invalid data length, must be a multiple "
+                             "of {0} bytes".format(self.block_size))
+
+        i = 0
+        result = []
+        while i < len(data):
+            iv = data[i:i+8]
+            block = self.__key3.crypt(iv, DECRYPT)
+            block = self.__key2.crypt(block, ENCRYPT)
+            block = self.__key1.crypt(block, DECRYPT)
+            self.__key1.iv = iv
+            self.__key2.iv = iv
+            self.__key3.iv = iv
+            result.append(block)
+            i += 8
+        data = b''.join(result)
+
+        return bytearray(data)
diff --git a/tlslite/utils/rc4.py b/tlslite/utils/rc4.py
index 2692715..3853f5b 100644
--- a/tlslite/utils/rc4.py
+++ b/tlslite/utils/rc4.py
@@ -1,12 +1,20 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 """Abstract class for RC4."""


 class RC4(object):
-
     def __init__(self, keyBytes, implementation):
         if len(keyBytes) < 16 or len(keyBytes) > 256:
             raise ValueError()
         self.isBlockCipher = False
         self.isAEAD = False
-        self.name = 'rc4'
+        self.name = "rc4"
         self.implementation = implementation
+
+    def encrypt(self, plaintext):
+        raise NotImplementedError()
+
+    def decrypt(self, ciphertext):
+        raise NotImplementedError()
diff --git a/tlslite/utils/rijndael.py b/tlslite/utils/rijndael.py
index 7a1e878..a1bf6a9 100644
--- a/tlslite/utils/rijndael.py
+++ b/tlslite/utils/rijndael.py
@@ -1,3 +1,10 @@
+# Authors:
+#   Bram Cohen
+#   Trevor Perrin - various changes
+#
+# See the LICENSE file for legal information regarding use of this file.
+# Also see Bram Cohen's statement below
+
 """
 A pure python (slow) implementation of rijndael with a decent interface

@@ -19,564 +26,879 @@ plaintext = r.decrypt(ciphertext)

 If any strings are of the wrong length a ValueError is thrown
 """
+
 from .deprecations import deprecated_class_name
-shifts = [[[0, 0], [1, 3], [2, 2], [3, 1]], [[0, 0], [1, 5], [2, 4], [3, 3]
-    ], [[0, 0], [1, 7], [3, 5], [4, 4]]]
-num_rounds = {(16): {(16): 10, (24): 12, (32): 14}, (24): {(16): 12, (24): 
-    12, (32): 14}, (32): {(16): 14, (24): 14, (32): 14}}
-S = (99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 
-    118, 202, 130, 201, 125, 250, 89, 71, 240, 173, 212, 162, 175, 156, 164,
-    114, 192, 183, 253, 147, 38, 54, 63, 247, 204, 52, 165, 229, 241, 113, 
-    216, 49, 21, 4, 199, 35, 195, 24, 150, 5, 154, 7, 18, 128, 226, 235, 39,
-    178, 117, 9, 131, 44, 26, 27, 110, 90, 160, 82, 59, 214, 179, 41, 227, 
-    47, 132, 83, 209, 0, 237, 32, 252, 177, 91, 106, 203, 190, 57, 74, 76, 
-    88, 207, 208, 239, 170, 251, 67, 77, 51, 133, 69, 249, 2, 127, 80, 60, 
-    159, 168, 81, 163, 64, 143, 146, 157, 56, 245, 188, 182, 218, 33, 16, 
-    255, 243, 210, 205, 12, 19, 236, 95, 151, 68, 23, 196, 167, 126, 61, 
-    100, 93, 25, 115, 96, 129, 79, 220, 34, 42, 144, 136, 70, 238, 184, 20,
-    222, 94, 11, 219, 224, 50, 58, 10, 73, 6, 36, 92, 194, 211, 172, 98, 
-    145, 149, 228, 121, 231, 200, 55, 109, 141, 213, 78, 169, 108, 86, 244,
-    234, 101, 122, 174, 8, 186, 120, 37, 46, 28, 166, 180, 198, 232, 221, 
-    116, 31, 75, 189, 139, 138, 112, 62, 181, 102, 72, 3, 246, 14, 97, 53, 
-    87, 185, 134, 193, 29, 158, 225, 248, 152, 17, 105, 217, 142, 148, 155,
-    30, 135, 233, 206, 85, 40, 223, 140, 161, 137, 13, 191, 230, 66, 104, 
-    65, 153, 45, 15, 176, 84, 187, 22)
-Si = (82, 9, 106, 213, 48, 54, 165, 56, 191, 64, 163, 158, 129, 243, 215, 
-    251, 124, 227, 57, 130, 155, 47, 255, 135, 52, 142, 67, 68, 196, 222, 
-    233, 203, 84, 123, 148, 50, 166, 194, 35, 61, 238, 76, 149, 11, 66, 250,
-    195, 78, 8, 46, 161, 102, 40, 217, 36, 178, 118, 91, 162, 73, 109, 139,
-    209, 37, 114, 248, 246, 100, 134, 104, 152, 22, 212, 164, 92, 204, 93, 
-    101, 182, 146, 108, 112, 72, 80, 253, 237, 185, 218, 94, 21, 70, 87, 
-    167, 141, 157, 132, 144, 216, 171, 0, 140, 188, 211, 10, 247, 228, 88, 
-    5, 184, 179, 69, 6, 208, 44, 30, 143, 202, 63, 15, 2, 193, 175, 189, 3,
-    1, 19, 138, 107, 58, 145, 17, 65, 79, 103, 220, 234, 151, 242, 207, 206,
-    240, 180, 230, 115, 150, 172, 116, 34, 231, 173, 53, 133, 226, 249, 55,
-    232, 28, 117, 223, 110, 71, 241, 26, 113, 29, 41, 197, 137, 111, 183, 
-    98, 14, 170, 24, 190, 27, 252, 86, 62, 75, 198, 210, 121, 32, 154, 219,
-    192, 254, 120, 205, 90, 244, 31, 221, 168, 51, 136, 7, 199, 49, 177, 18,
-    16, 89, 39, 128, 236, 95, 96, 81, 127, 169, 25, 181, 74, 13, 45, 229, 
-    122, 159, 147, 201, 156, 239, 160, 224, 59, 77, 174, 42, 245, 176, 200,
-    235, 187, 60, 131, 83, 153, 97, 23, 43, 4, 126, 186, 119, 214, 38, 225,
-    105, 20, 99, 85, 33, 12, 125)
-T1 = (3328402341, 4168907908, 4000806809, 4135287693, 4294111757, 
-    3597364157, 3731845041, 2445657428, 1613770832, 33620227, 3462883241, 
-    1445669757, 3892248089, 3050821474, 1303096294, 3967186586, 2412431941,
-    528646813, 2311702848, 4202528135, 4026202645, 2992200171, 2387036105, 
-    4226871307, 1101901292, 3017069671, 1604494077, 1169141738, 597466303, 
-    1403299063, 3832705686, 2613100635, 1974974402, 3791519004, 1033081774,
-    1277568618, 1815492186, 2118074177, 4126668546, 2211236943, 1748251740,
-    1369810420, 3521504564, 4193382664, 3799085459, 2883115123, 1647391059,
-    706024767, 134480908, 2512897874, 1176707941, 2646852446, 806885416, 
-    932615841, 168101135, 798661301, 235341577, 605164086, 461406363, 
-    3756188221, 3454790438, 1311188841, 2142417613, 3933566367, 302582043, 
-    495158174, 1479289972, 874125870, 907746093, 3698224818, 3025820398, 
-    1537253627, 2756858614, 1983593293, 3084310113, 2108928974, 1378429307,
-    3722699582, 1580150641, 327451799, 2790478837, 3117535592, 0, 
-    3253595436, 1075847264, 3825007647, 2041688520, 3059440621, 3563743934,
-    2378943302, 1740553945, 1916352843, 2487896798, 2555137236, 2958579944,
-    2244988746, 3151024235, 3320835882, 1336584933, 3992714006, 2252555205,
-    2588757463, 1714631509, 293963156, 2319795663, 3925473552, 67240454, 
-    4269768577, 2689618160, 2017213508, 631218106, 1269344483, 2723238387, 
-    1571005438, 2151694528, 93294474, 1066570413, 563977660, 1882732616, 
-    4059428100, 1673313503, 2008463041, 2950355573, 1109467491, 537923632, 
-    3858759450, 4260623118, 3218264685, 2177748300, 403442708, 638784309, 
-    3287084079, 3193921505, 899127202, 2286175436, 773265209, 2479146071, 
-    1437050866, 4236148354, 2050833735, 3362022572, 3126681063, 840505643, 
-    3866325909, 3227541664, 427917720, 2655997905, 2749160575, 1143087718, 
-    1412049534, 999329963, 193497219, 2353415882, 3354324521, 1807268051, 
-    672404540, 2816401017, 3160301282, 369822493, 2916866934, 3688947771, 
-    1681011286, 1949973070, 336202270, 2454276571, 201721354, 1210328172, 
-    3093060836, 2680341085, 3184776046, 1135389935, 3294782118, 965841320, 
-    831886756, 3554993207, 4068047243, 3588745010, 2345191491, 1849112409, 
-    3664604599, 26054028, 2983581028, 2622377682, 1235855840, 3630984372, 
-    2891339514, 4092916743, 3488279077, 3395642799, 4101667470, 1202630377,
-    268961816, 1874508501, 4034427016, 1243948399, 1546530418, 941366308, 
-    1470539505, 1941222599, 2546386513, 3421038627, 2715671932, 3899946140,
-    1042226977, 2521517021, 1639824860, 227249030, 260737669, 3765465232, 
-    2084453954, 1907733956, 3429263018, 2420656344, 100860677, 4160157185, 
-    470683154, 3261161891, 1781871967, 2924959737, 1773779408, 394692241, 
-    2579611992, 974986535, 664706745, 3655459128, 3958962195, 731420851, 
-    571543859, 3530123707, 2849626480, 126783113, 865375399, 765172662, 
-    1008606754, 361203602, 3387549984, 2278477385, 2857719295, 1344809080, 
-    2782912378, 59542671, 1503764984, 160008576, 437062935, 1707065306, 
-    3622233649, 2218934982, 3496503480, 2185314755, 697932208, 1512910199, 
-    504303377, 2075177163, 2824099068, 1841019862, 739644986)
-T2 = (2781242211, 2230877308, 2582542199, 2381740923, 234877682, 3184946027,
-    2984144751, 1418839493, 1348481072, 50462977, 2848876391, 2102799147, 
-    434634494, 1656084439, 3863849899, 2599188086, 1167051466, 2636087938, 
-    1082771913, 2281340285, 368048890, 3954334041, 3381544775, 201060592, 
-    3963727277, 1739838676, 4250903202, 3930435503, 3206782108, 4149453988,
-    2531553906, 1536934080, 3262494647, 484572669, 2923271059, 1783375398, 
-    1517041206, 1098792767, 49674231, 1334037708, 1550332980, 4098991525, 
-    886171109, 150598129, 2481090929, 1940642008, 1398944049, 1059722517, 
-    201851908, 1385547719, 1699095331, 1587397571, 674240536, 2704774806, 
-    252314885, 3039795866, 151914247, 908333586, 2602270848, 1038082786, 
-    651029483, 1766729511, 3447698098, 2682942837, 454166793, 2652734339, 
-    1951935532, 775166490, 758520603, 3000790638, 4004797018, 4217086112, 
-    4137964114, 1299594043, 1639438038, 3464344499, 2068982057, 1054729187,
-    1901997871, 2534638724, 4121318227, 1757008337, 0, 750906861, 
-    1614815264, 535035132, 3363418545, 3988151131, 3201591914, 1183697867, 
-    3647454910, 1265776953, 3734260298, 3566750796, 3903871064, 1250283471,
-    1807470800, 717615087, 3847203498, 384695291, 3313910595, 3617213773, 
-    1432761139, 2484176261, 3481945413, 283769337, 100925954, 2180939647, 
-    4037038160, 1148730428, 3123027871, 3813386408, 4087501137, 4267549603,
-    3229630528, 2315620239, 2906624658, 3156319645, 1215313976, 82966005, 
-    3747855548, 3245848246, 1974459098, 1665278241, 807407632, 451280895, 
-    251524083, 1841287890, 1283575245, 337120268, 891687699, 801369324, 
-    3787349855, 2721421207, 3431482436, 959321879, 1469301956, 4065699751, 
-    2197585534, 1199193405, 2898814052, 3887750493, 724703513, 2514908019, 
-    2696962144, 2551808385, 3516813135, 2141445340, 1715741218, 2119445034,
-    2872807568, 2198571144, 3398190662, 700968686, 3547052216, 1009259540, 
-    2041044702, 3803995742, 487983883, 1991105499, 1004265696, 1449407026, 
-    1316239930, 504629770, 3683797321, 168560134, 1816667172, 3837287516, 
-    1570751170, 1857934291, 4014189740, 2797888098, 2822345105, 2754712981,
-    936633572, 2347923833, 852879335, 1133234376, 1500395319, 3084545389, 
-    2348912013, 1689376213, 3533459022, 3762923945, 3034082412, 4205598294,
-    133428468, 634383082, 2949277029, 2398386810, 3913789102, 403703816, 
-    3580869306, 2297460856, 1867130149, 1918643758, 607656988, 4049053350, 
-    3346248884, 1368901318, 600565992, 2090982877, 2632479860, 557719327, 
-    3717614411, 3697393085, 2249034635, 2232388234, 2430627952, 1115438654,
-    3295786421, 2865522278, 3633334344, 84280067, 33027830, 303828494, 
-    2747425121, 1600795957, 4188952407, 3496589753, 2434238086, 1486471617,
-    658119965, 3106381470, 953803233, 334231800, 3005978776, 857870609, 
-    3151128937, 1890179545, 2298973838, 2805175444, 3056442267, 574365214, 
-    2450884487, 550103529, 1233637070, 4289353045, 2018519080, 2057691103, 
-    2399374476, 4166623649, 2148108681, 387583245, 3664101311, 836232934, 
-    3330556482, 3100665960, 3280093505, 2955516313, 2002398509, 287182607, 
-    3413881008, 4238890068, 3597515707, 975967766)
-T3 = (1671808611, 2089089148, 2006576759, 2072901243, 4061003762, 
-    1807603307, 1873927791, 3310653893, 810573872, 16974337, 1739181671, 
-    729634347, 4263110654, 3613570519, 2883997099, 1989864566, 3393556426, 
-    2191335298, 3376449993, 2106063485, 4195741690, 1508618841, 1204391495,
-    4027317232, 2917941677, 3563566036, 2734514082, 2951366063, 2629772188,
-    2767672228, 1922491506, 3227229120, 3082974647, 4246528509, 2477669779,
-    644500518, 911895606, 1061256767, 4144166391, 3427763148, 878471220, 
-    2784252325, 3845444069, 4043897329, 1905517169, 3631459288, 827548209, 
-    356461077, 67897348, 3344078279, 593839651, 3277757891, 405286936, 
-    2527147926, 84871685, 2595565466, 118033927, 305538066, 2157648768, 
-    3795705826, 3945188843, 661212711, 2999812018, 1973414517, 152769033, 
-    2208177539, 745822252, 439235610, 455947803, 1857215598, 1525593178, 
-    2700827552, 1391895634, 994932283, 3596728278, 3016654259, 695947817, 
-    3812548067, 795958831, 2224493444, 1408607827, 3513301457, 0, 
-    3979133421, 543178784, 4229948412, 2982705585, 1542305371, 1790891114, 
-    3410398667, 3201918910, 961245753, 1256100938, 1289001036, 1491644504, 
-    3477767631, 3496721360, 4012557807, 2867154858, 4212583931, 1137018435,
-    1305975373, 861234739, 2241073541, 1171229253, 4178635257, 33948674, 
-    2139225727, 1357946960, 1011120188, 2679776671, 2833468328, 1374921297,
-    2751356323, 1086357568, 2408187279, 2460827538, 2646352285, 944271416, 
-    4110742005, 3168756668, 3066132406, 3665145818, 560153121, 271589392, 
-    4279952895, 4077846003, 3530407890, 3444343245, 202643468, 322250259, 
-    3962553324, 1608629855, 2543990167, 1154254916, 389623319, 3294073796, 
-    2817676711, 2122513534, 1028094525, 1689045092, 1575467613, 422261273, 
-    1939203699, 1621147744, 2174228865, 1339137615, 3699352540, 577127458, 
-    712922154, 2427141008, 2290289544, 1187679302, 3995715566, 3100863416, 
-    339486740, 3732514782, 1591917662, 186455563, 3681988059, 3762019296, 
-    844522546, 978220090, 169743370, 1239126601, 101321734, 611076132, 
-    1558493276, 3260915650, 3547250131, 2901361580, 1655096418, 2443721105,
-    2510565781, 3828863972, 2039214713, 3878868455, 3359869896, 928607799, 
-    1840765549, 2374762893, 3580146133, 1322425422, 2850048425, 1823791212,
-    1459268694, 4094161908, 3928346602, 1706019429, 2056189050, 2934523822,
-    135794696, 3134549946, 2022240376, 628050469, 779246638, 472135708, 
-    2800834470, 3032970164, 3327236038, 3894660072, 3715932637, 1956440180,
-    522272287, 1272813131, 3185336765, 2340818315, 2323976074, 1888542832, 
-    1044544574, 3049550261, 1722469478, 1222152264, 50660867, 4127324150, 
-    236067854, 1638122081, 895445557, 1475980887, 3117443513, 2257655686, 
-    3243809217, 489110045, 2662934430, 3778599393, 4162055160, 2561878936, 
-    288563729, 1773916777, 3648039385, 2391345038, 2493985684, 2612407707, 
-    505560094, 2274497927, 3911240169, 3460925390, 1442818645, 678973480, 
-    3749357023, 2358182796, 2717407649, 2306869641, 219617805, 3218761151, 
-    3862026214, 1120306242, 1756942440, 1103331905, 2578459033, 762796589, 
-    252780047, 2966125488, 1425844308, 3151392187, 372911126)
-T4 = (1667474886, 2088535288, 2004326894, 2071694838, 4075949567, 
-    1802223062, 1869591006, 3318043793, 808472672, 16843522, 1734846926, 
-    724270422, 4278065639, 3621216949, 2880169549, 1987484396, 3402253711, 
-    2189597983, 3385409673, 2105378810, 4210693615, 1499065266, 1195886990,
-    4042263547, 2913856577, 3570689971, 2728590687, 2947541573, 2627518243,
-    2762274643, 1920112356, 3233831835, 3082273397, 4261223649, 2475929149,
-    640051788, 909531756, 1061110142, 4160160501, 3435941763, 875846760, 
-    2779116625, 3857003729, 4059105529, 1903268834, 3638064043, 825316194, 
-    353713962, 67374088, 3351728789, 589522246, 3284360861, 404236336, 
-    2526454071, 84217610, 2593830191, 117901582, 303183396, 2155911963, 
-    3806477791, 3958056653, 656894286, 2998062463, 1970642922, 151591698, 
-    2206440989, 741110872, 437923380, 454765878, 1852748508, 1515908788, 
-    2694904667, 1381168804, 993742198, 3604373943, 3014905469, 690584402, 
-    3823320797, 791638366, 2223281939, 1398011302, 3520161977, 0, 
-    3991743681, 538992704, 4244381667, 2981218425, 1532751286, 1785380564, 
-    3419096717, 3200178535, 960056178, 1246420628, 1280103576, 1482221744, 
-    3486468741, 3503319995, 4025428677, 2863326543, 4227536621, 1128514950,
-    1296947098, 859002214, 2240123921, 1162203018, 4193849577, 33687044, 
-    2139062782, 1347481760, 1010582648, 2678045221, 2829640523, 1364325282,
-    2745433693, 1077985408, 2408548869, 2459086143, 2644360225, 943212656, 
-    4126475505, 3166494563, 3065430391, 3671750063, 555836226, 269496352, 
-    4294908645, 4092792573, 3537006015, 3452783745, 202118168, 320025894, 
-    3974901699, 1600119230, 2543297077, 1145359496, 387397934, 3301201811, 
-    2812801621, 2122220284, 1027426170, 1684319432, 1566435258, 421079858, 
-    1936954854, 1616945344, 2172753945, 1330631070, 3705438115, 572679748, 
-    707427924, 2425400123, 2290647819, 1179044492, 4008585671, 3099120491, 
-    336870440, 3739122087, 1583276732, 185277718, 3688593069, 3772791771, 
-    842159716, 976899700, 168435220, 1229577106, 101059084, 606366792, 
-    1549591736, 3267517855, 3553849021, 2897014595, 1650632388, 2442242105,
-    2509612081, 3840161747, 2038008818, 3890688725, 3368567691, 926374254, 
-    1835907034, 2374863873, 3587531953, 1313788572, 2846482505, 1819063512,
-    1448540844, 4109633523, 3941213647, 1701162954, 2054852340, 2930698567,
-    134748176, 3132806511, 2021165296, 623210314, 774795868, 471606328, 
-    2795958615, 3031746419, 3334885783, 3907527627, 3722280097, 1953799400,
-    522133822, 1263263126, 3183336545, 2341176845, 2324333839, 1886425312, 
-    1044267644, 3048588401, 1718004428, 1212733584, 50529542, 4143317495, 
-    235803164, 1633788866, 892690282, 1465383342, 3115962473, 2256965911, 
-    3250673817, 488449850, 2661202215, 3789633753, 4177007595, 2560144171, 
-    286339874, 1768537042, 3654906025, 2391705863, 2492770099, 2610673197, 
-    505291324, 2273808917, 3924369609, 3469625735, 1431699370, 673740880, 
-    3755965093, 2358021891, 2711746649, 2307489801, 218961690, 3217021541, 
-    3873845719, 1111672452, 1751693520, 1094828930, 2576986153, 757954394, 
-    252645662, 2964376443, 1414855848, 3149649517, 370555436)
-T5 = (1374988112, 2118214995, 437757123, 975658646, 1001089995, 530400753, 
-    2902087851, 1273168787, 540080725, 2910219766, 2295101073, 4110568485, 
-    1340463100, 3307916247, 641025152, 3043140495, 3736164937, 632953703, 
-    1172967064, 1576976609, 3274667266, 2169303058, 2370213795, 1809054150,
-    59727847, 361929877, 3211623147, 2505202138, 3569255213, 1484005843, 
-    1239443753, 2395588676, 1975683434, 4102977912, 2572697195, 666464733, 
-    3202437046, 4035489047, 3374361702, 2110667444, 1675577880, 3843699074,
-    2538681184, 1649639237, 2976151520, 3144396420, 4269907996, 4178062228,
-    1883793496, 2403728665, 2497604743, 1383856311, 2876494627, 1917518562,
-    3810496343, 1716890410, 3001755655, 800440835, 2261089178, 3543599269, 
-    807962610, 599762354, 33778362, 3977675356, 2328828971, 2809771154, 
-    4077384432, 1315562145, 1708848333, 101039829, 3509871135, 3299278474, 
-    875451293, 2733856160, 92987698, 2767645557, 193195065, 1080094634, 
-    1584504582, 3178106961, 1042385657, 2531067453, 3711829422, 1306967366,
-    2438237621, 1908694277, 67556463, 1615861247, 429456164, 3602770327, 
-    2302690252, 1742315127, 2968011453, 126454664, 3877198648, 2043211483, 
-    2709260871, 2084704233, 4169408201, 0, 159417987, 841739592, 504459436,
-    1817866830, 4245618683, 260388950, 1034867998, 908933415, 168810852, 
-    1750902305, 2606453969, 607530554, 202008497, 2472011535, 3035535058, 
-    463180190, 2160117071, 1641816226, 1517767529, 470948374, 3801332234, 
-    3231722213, 1008918595, 303765277, 235474187, 4069246893, 766945465, 
-    337553864, 1475418501, 2943682380, 4003061179, 2743034109, 4144047775, 
-    1551037884, 1147550661, 1543208500, 2336434550, 3408119516, 3069049960,
-    3102011747, 3610369226, 1113818384, 328671808, 2227573024, 2236228733, 
-    3535486456, 2935566865, 3341394285, 496906059, 3702665459, 226906860, 
-    2009195472, 733156972, 2842737049, 294930682, 1206477858, 2835123396, 
-    2700099354, 1451044056, 573804783, 2269728455, 3644379585, 2362090238, 
-    2564033334, 2801107407, 2776292904, 3669462566, 1068351396, 742039012, 
-    1350078989, 1784663195, 1417561698, 4136440770, 2430122216, 775550814, 
-    2193862645, 2673705150, 1775276924, 1876241833, 3475313331, 3366754619,
-    270040487, 3902563182, 3678124923, 3441850377, 1851332852, 3969562369, 
-    2203032232, 3868552805, 2868897406, 566021896, 4011190502, 3135740889, 
-    1248802510, 3936291284, 699432150, 832877231, 708780849, 3332740144, 
-    899835584, 1951317047, 4236429990, 3767586992, 866637845, 4043610186, 
-    1106041591, 2144161806, 395441711, 1984812685, 1139781709, 3433712980, 
-    3835036895, 2664543715, 1282050075, 3240894392, 1181045119, 2640243204,
-    25965917, 4203181171, 4211818798, 3009879386, 2463879762, 3910161971, 
-    1842759443, 2597806476, 933301370, 1509430414, 3943906441, 3467192302, 
-    3076639029, 3776767469, 2051518780, 2631065433, 1441952575, 404016761, 
-    1942435775, 1408749034, 1610459739, 3745345300, 2017778566, 3400528769,
-    3110650942, 941896748, 3265478751, 371049330, 3168937228, 675039627, 
-    4279080257, 967311729, 135050206, 3635733660, 1683407248, 2076935265, 
-    3576870512, 1215061108, 3501741890)
-T6 = (1347548327, 1400783205, 3273267108, 2520393566, 3409685355, 
-    4045380933, 2880240216, 2471224067, 1428173050, 4138563181, 2441661558,
-    636813900, 4233094615, 3620022987, 2149987652, 2411029155, 1239331162, 
-    1730525723, 2554718734, 3781033664, 46346101, 310463728, 2743944855, 
-    3328955385, 3875770207, 2501218972, 3955191162, 3667219033, 768917123, 
-    3545789473, 692707433, 1150208456, 1786102409, 2029293177, 1805211710, 
-    3710368113, 3065962831, 401639597, 1724457132, 3028143674, 409198410, 
-    2196052529, 1620529459, 1164071807, 3769721975, 2226875310, 486441376, 
-    2499348523, 1483753576, 428819965, 2274680428, 3075636216, 598438867, 
-    3799141122, 1474502543, 711349675, 129166120, 53458370, 2592523643, 
-    2782082824, 4063242375, 2988687269, 3120694122, 1559041666, 730517276, 
-    2460449204, 4042459122, 2706270690, 3446004468, 3573941694, 533804130, 
-    2328143614, 2637442643, 2695033685, 839224033, 1973745387, 957055980, 
-    2856345839, 106852767, 1371368976, 4181598602, 1033297158, 2933734917, 
-    1179510461, 3046200461, 91341917, 1862534868, 4284502037, 605657339, 
-    2547432937, 3431546947, 2003294622, 3182487618, 2282195339, 954669403, 
-    3682191598, 1201765386, 3917234703, 3388507166, 0, 2198438022, 
-    1211247597, 2887651696, 1315723890, 4227665663, 1443857720, 507358933, 
-    657861945, 1678381017, 560487590, 3516619604, 975451694, 2970356327, 
-    261314535, 3535072918, 2652609425, 1333838021, 2724322336, 1767536459, 
-    370938394, 182621114, 3854606378, 1128014560, 487725847, 185469197, 
-    2918353863, 3106780840, 3356761769, 2237133081, 1286567175, 3152976349,
-    4255350624, 2683765030, 3160175349, 3309594171, 878443390, 1988838185, 
-    3704300486, 1756818940, 1673061617, 3403100636, 272786309, 1075025698, 
-    545572369, 2105887268, 4174560061, 296679730, 1841768865, 1260232239, 
-    4091327024, 3960309330, 3497509347, 1814803222, 2578018489, 4195456072,
-    575138148, 3299409036, 446754879, 3629546796, 4011996048, 3347532110, 
-    3252238545, 4270639778, 915985419, 3483825537, 681933534, 651868046, 
-    2755636671, 3828103837, 223377554, 2607439820, 1649704518, 3270937875, 
-    3901806776, 1580087799, 4118987695, 3198115200, 2087309459, 2842678573,
-    3016697106, 1003007129, 2802849917, 1860738147, 2077965243, 164439672, 
-    4100872472, 32283319, 2827177882, 1709610350, 2125135846, 136428751, 
-    3874428392, 3652904859, 3460984630, 3572145929, 3593056380, 2939266226,
-    824852259, 818324884, 3224740454, 930369212, 2801566410, 2967507152, 
-    355706840, 1257309336, 4148292826, 243256656, 790073846, 2373340630, 
-    1296297904, 1422699085, 3756299780, 3818836405, 457992840, 3099667487, 
-    2135319889, 77422314, 1560382517, 1945798516, 788204353, 1521706781, 
-    1385356242, 870912086, 325965383, 2358957921, 2050466060, 2388260884, 
-    2313884476, 4006521127, 901210569, 3990953189, 1014646705, 1503449823, 
-    1062597235, 2031621326, 3212035895, 3931371469, 1533017514, 350174575, 
-    2256028891, 2177544179, 1052338372, 741876788, 1606591296, 1914052035, 
-    213705253, 2334669897, 1107234197, 1899603969, 3725069491, 2631447780, 
-    2422494913, 1635502980, 1893020342, 1950903388, 1120974935)
-T7 = (2807058932, 1699970625, 2764249623, 1586903591, 1808481195, 
-    1173430173, 1487645946, 59984867, 4199882800, 1844882806, 1989249228, 
-    1277555970, 3623636965, 3419915562, 1149249077, 2744104290, 1514790577,
-    459744698, 244860394, 3235995134, 1963115311, 4027744588, 2544078150, 
-    4190530515, 1608975247, 2627016082, 2062270317, 1507497298, 2200818878,
-    567498868, 1764313568, 3359936201, 2305455554, 2037970062, 1047239000, 
-    1910319033, 1337376481, 2904027272, 2892417312, 984907214, 1243112415, 
-    830661914, 861968209, 2135253587, 2011214180, 2927934315, 2686254721, 
-    731183368, 1750626376, 4246310725, 1820824798, 4172763771, 3542330227, 
-    48394827, 2404901663, 2871682645, 671593195, 3254988725, 2073724613, 
-    145085239, 2280796200, 2779915199, 1790575107, 2187128086, 472615631, 
-    3029510009, 4075877127, 3802222185, 4107101658, 3201631749, 1646252340,
-    4270507174, 1402811438, 1436590835, 3778151818, 3950355702, 3963161475,
-    4020912224, 2667994737, 273792366, 2331590177, 104699613, 95345982, 
-    3175501286, 2377486676, 1560637892, 3564045318, 369057872, 4213447064, 
-    3919042237, 1137477952, 2658625497, 1119727848, 2340947849, 1530455833,
-    4007360968, 172466556, 266959938, 516552836, 0, 2256734592, 3980931627,
-    1890328081, 1917742170, 4294704398, 945164165, 3575528878, 958871085, 
-    3647212047, 2787207260, 1423022939, 775562294, 1739656202, 3876557655, 
-    2530391278, 2443058075, 3310321856, 547512796, 1265195639, 437656594, 
-    3121275539, 719700128, 3762502690, 387781147, 218828297, 3350065803, 
-    2830708150, 2848461854, 428169201, 122466165, 3720081049, 1627235199, 
-    648017665, 4122762354, 1002783846, 2117360635, 695634755, 3336358691, 
-    4234721005, 4049844452, 3704280881, 2232435299, 574624663, 287343814, 
-    612205898, 1039717051, 840019705, 2708326185, 793451934, 821288114, 
-    1391201670, 3822090177, 376187827, 3113855344, 1224348052, 1679968233, 
-    2361698556, 1058709744, 752375421, 2431590963, 1321699145, 3519142200, 
-    2734591178, 188127444, 2177869557, 3727205754, 2384911031, 3215212461, 
-    2648976442, 2450346104, 3432737375, 1180849278, 331544205, 3102249176, 
-    4150144569, 2952102595, 2159976285, 2474404304, 766078933, 313773861, 
-    2570832044, 2108100632, 1668212892, 3145456443, 2013908262, 418672217, 
-    3070356634, 2594734927, 1852171925, 3867060991, 3473416636, 3907448597,
-    2614737639, 919489135, 164948639, 2094410160, 2997825956, 590424639, 
-    2486224549, 1723872674, 3157750862, 3399941250, 3501252752, 3625268135,
-    2555048196, 3673637356, 1343127501, 4130281361, 3599595085, 2957853679,
-    1297403050, 81781910, 3051593425, 2283490410, 532201772, 1367295589, 
-    3926170974, 895287692, 1953757831, 1093597963, 492483431, 3528626907, 
-    1446242576, 1192455638, 1636604631, 209336225, 344873464, 1015671571, 
-    669961897, 3375740769, 3857572124, 2973530695, 3747192018, 1933530610, 
-    3464042516, 935293895, 3454686199, 2858115069, 1863638845, 3683022916, 
-    4085369519, 3292445032, 875313188, 1080017571, 3279033885, 621591778, 
-    1233856572, 2504130317, 24197544, 3017672716, 3835484340, 3247465558, 
-    2220981195, 3060847922, 1551124588, 1463996600)
-T8 = (4104605777, 1097159550, 396673818, 660510266, 2875968315, 2638606623,
-    4200115116, 3808662347, 821712160, 1986918061, 3430322568, 38544885, 
-    3856137295, 718002117, 893681702, 1654886325, 2975484382, 3122358053, 
-    3926825029, 4274053469, 796197571, 1290801793, 1184342925, 3556361835, 
-    2405426947, 2459735317, 1836772287, 1381620373, 3196267988, 1948373848,
-    3764988233, 3385345166, 3263785589, 2390325492, 1480485785, 3111247143,
-    3780097726, 2293045232, 548169417, 3459953789, 3746175075, 439452389, 
-    1362321559, 1400849762, 1685577905, 1806599355, 2174754046, 137073913, 
-    1214797936, 1174215055, 3731654548, 2079897426, 1943217067, 1258480242,
-    529487843, 1437280870, 3945269170, 3049390895, 3313212038, 923313619, 
-    679998000, 3215307299, 57326082, 377642221, 3474729866, 2041877159, 
-    133361907, 1776460110, 3673476453, 96392454, 878845905, 2801699524, 
-    777231668, 4082475170, 2330014213, 4142626212, 2213296395, 1626319424, 
-    1906247262, 1846563261, 562755902, 3708173718, 1040559837, 3871163981, 
-    1418573201, 3294430577, 114585348, 1343618912, 2566595609, 3186202582, 
-    1078185097, 3651041127, 3896688048, 2307622919, 425408743, 3371096953, 
-    2081048481, 1108339068, 2216610296, 0, 2156299017, 736970802, 292596766,
-    1517440620, 251657213, 2235061775, 2933202493, 758720310, 265905162, 
-    1554391400, 1532285339, 908999204, 174567692, 1474760595, 4002861748, 
-    2610011675, 3234156416, 3693126241, 2001430874, 303699484, 2478443234, 
-    2687165888, 585122620, 454499602, 151849742, 2345119218, 3064510765, 
-    514443284, 4044981591, 1963412655, 2581445614, 2137062819, 19308535, 
-    1928707164, 1715193156, 4219352155, 1126790795, 600235211, 3992742070, 
-    3841024952, 836553431, 1669664834, 2535604243, 3323011204, 1243905413, 
-    3141400786, 4180808110, 698445255, 2653899549, 2989552604, 2253581325, 
-    3252932727, 3004591147, 1891211689, 2487810577, 3915653703, 4237083816,
-    4030667424, 2100090966, 865136418, 1229899655, 953270745, 3399679628, 
-    3557504664, 4118925222, 2061379749, 3079546586, 2915017791, 983426092, 
-    2022837584, 1607244650, 2118541908, 2366882550, 3635996816, 972512814, 
-    3283088770, 1568718495, 3499326569, 3576539503, 621982671, 2895723464, 
-    410887952, 2623762152, 1002142683, 645401037, 1494807662, 2595684844, 
-    1335535747, 2507040230, 4293295786, 3167684641, 367585007, 3885750714, 
-    1865862730, 2668221674, 2960971305, 2763173681, 1059270954, 2777952454,
-    2724642869, 1320957812, 2194319100, 2429595872, 2815956275, 77089521, 
-    3973773121, 3444575871, 2448830231, 1305906550, 4021308739, 2857194700,
-    2516901860, 3518358430, 1787304780, 740276417, 1699839814, 1592394909, 
-    2352307457, 2272556026, 188821243, 1729977011, 3687994002, 274084841, 
-    3594982253, 3613494426, 2701949495, 4162096729, 322734571, 2837966542, 
-    1640576439, 484830689, 1202797690, 3537852828, 4067639125, 349075736, 
-    3342319475, 4157467219, 4255800159, 1030690015, 1155237496, 2951971274,
-    1757691577, 607398968, 2738905026, 499347990, 3794078908, 1011452712, 
-    227885567, 2818666809, 213114376, 3034881240, 1455525988, 3414450555, 
-    850817237, 1817998408, 3092726480)
-U1 = (0, 235474187, 470948374, 303765277, 941896748, 908933415, 607530554, 
-    708780849, 1883793496, 2118214995, 1817866830, 1649639237, 1215061108, 
-    1181045119, 1417561698, 1517767529, 3767586992, 4003061179, 4236429990,
-    4069246893, 3635733660, 3602770327, 3299278474, 3400528769, 2430122216,
-    2664543715, 2362090238, 2193862645, 2835123396, 2801107407, 3035535058,
-    3135740889, 3678124923, 3576870512, 3341394285, 3374361702, 3810496343,
-    3977675356, 4279080257, 4043610186, 2876494627, 2776292904, 3076639029,
-    3110650942, 2472011535, 2640243204, 2403728665, 2169303058, 1001089995,
-    899835584, 666464733, 699432150, 59727847, 226906860, 530400753, 
-    294930682, 1273168787, 1172967064, 1475418501, 1509430414, 1942435775, 
-    2110667444, 1876241833, 1641816226, 2910219766, 2743034109, 2976151520,
-    3211623147, 2505202138, 2606453969, 2302690252, 2269728455, 3711829422,
-    3543599269, 3240894392, 3475313331, 3843699074, 3943906441, 4178062228,
-    4144047775, 1306967366, 1139781709, 1374988112, 1610459739, 1975683434,
-    2076935265, 1775276924, 1742315127, 1034867998, 866637845, 566021896, 
-    800440835, 92987698, 193195065, 429456164, 395441711, 1984812685, 
-    2017778566, 1784663195, 1683407248, 1315562145, 1080094634, 1383856311,
-    1551037884, 101039829, 135050206, 437757123, 337553864, 1042385657, 
-    807962610, 573804783, 742039012, 2531067453, 2564033334, 2328828971, 
-    2227573024, 2935566865, 2700099354, 3001755655, 3168937228, 3868552805,
-    3902563182, 4203181171, 4102977912, 3736164937, 3501741890, 3265478751,
-    3433712980, 1106041591, 1340463100, 1576976609, 1408749034, 2043211483,
-    2009195472, 1708848333, 1809054150, 832877231, 1068351396, 766945465, 
-    599762354, 159417987, 126454664, 361929877, 463180190, 2709260871, 
-    2943682380, 3178106961, 3009879386, 2572697195, 2538681184, 2236228733,
-    2336434550, 3509871135, 3745345300, 3441850377, 3274667266, 3910161971,
-    3877198648, 4110568485, 4211818798, 2597806476, 2497604743, 2261089178,
-    2295101073, 2733856160, 2902087851, 3202437046, 2968011453, 3936291284,
-    3835036895, 4136440770, 4169408201, 3535486456, 3702665459, 3467192302,
-    3231722213, 2051518780, 1951317047, 1716890410, 1750902305, 1113818384,
-    1282050075, 1584504582, 1350078989, 168810852, 67556463, 371049330, 
-    404016761, 841739592, 1008918595, 775550814, 540080725, 3969562369, 
-    3801332234, 4035489047, 4269907996, 3569255213, 3669462566, 3366754619,
-    3332740144, 2631065433, 2463879762, 2160117071, 2395588676, 2767645557,
-    2868897406, 3102011747, 3069049960, 202008497, 33778362, 270040487, 
-    504459436, 875451293, 975658646, 675039627, 641025152, 2084704233, 
-    1917518562, 1615861247, 1851332852, 1147550661, 1248802510, 1484005843,
-    1451044056, 933301370, 967311729, 733156972, 632953703, 260388950, 
-    25965917, 328671808, 496906059, 1206477858, 1239443753, 1543208500, 
-    1441952575, 2144161806, 1908694277, 1675577880, 1842759443, 3610369226,
-    3644379585, 3408119516, 3307916247, 4011190502, 3776767469, 4077384432,
-    4245618683, 2809771154, 2842737049, 3144396420, 3043140495, 2673705150,
-    2438237621, 2203032232, 2370213795)
-U2 = (0, 185469197, 370938394, 487725847, 741876788, 657861945, 975451694, 
-    824852259, 1483753576, 1400783205, 1315723890, 1164071807, 1950903388, 
-    2135319889, 1649704518, 1767536459, 2967507152, 3152976349, 2801566410,
-    2918353863, 2631447780, 2547432937, 2328143614, 2177544179, 3901806776,
-    3818836405, 4270639778, 4118987695, 3299409036, 3483825537, 3535072918,
-    3652904859, 2077965243, 1893020342, 1841768865, 1724457132, 1474502543,
-    1559041666, 1107234197, 1257309336, 598438867, 681933534, 901210569, 
-    1052338372, 261314535, 77422314, 428819965, 310463728, 3409685355, 
-    3224740454, 3710368113, 3593056380, 3875770207, 3960309330, 4045380933,
-    4195456072, 2471224067, 2554718734, 2237133081, 2388260884, 3212035895,
-    3028143674, 2842678573, 2724322336, 4138563181, 4255350624, 3769721975,
-    3955191162, 3667219033, 3516619604, 3431546947, 3347532110, 2933734917,
-    2782082824, 3099667487, 3016697106, 2196052529, 2313884476, 2499348523,
-    2683765030, 1179510461, 1296297904, 1347548327, 1533017514, 1786102409,
-    1635502980, 2087309459, 2003294622, 507358933, 355706840, 136428751, 
-    53458370, 839224033, 957055980, 605657339, 790073846, 2373340630, 
-    2256028891, 2607439820, 2422494913, 2706270690, 2856345839, 3075636216,
-    3160175349, 3573941694, 3725069491, 3273267108, 3356761769, 4181598602,
-    4063242375, 4011996048, 3828103837, 1033297158, 915985419, 730517276, 
-    545572369, 296679730, 446754879, 129166120, 213705253, 1709610350, 
-    1860738147, 1945798516, 2029293177, 1239331162, 1120974935, 1606591296,
-    1422699085, 4148292826, 4233094615, 3781033664, 3931371469, 3682191598,
-    3497509347, 3446004468, 3328955385, 2939266226, 2755636671, 3106780840,
-    2988687269, 2198438022, 2282195339, 2501218972, 2652609425, 1201765386,
-    1286567175, 1371368976, 1521706781, 1805211710, 1620529459, 2105887268,
-    1988838185, 533804130, 350174575, 164439672, 46346101, 870912086, 
-    954669403, 636813900, 788204353, 2358957921, 2274680428, 2592523643, 
-    2441661558, 2695033685, 2880240216, 3065962831, 3182487618, 3572145929,
-    3756299780, 3270937875, 3388507166, 4174560061, 4091327024, 4006521127,
-    3854606378, 1014646705, 930369212, 711349675, 560487590, 272786309, 
-    457992840, 106852767, 223377554, 1678381017, 1862534868, 1914052035, 
-    2031621326, 1211247597, 1128014560, 1580087799, 1428173050, 32283319, 
-    182621114, 401639597, 486441376, 768917123, 651868046, 1003007129, 
-    818324884, 1503449823, 1385356242, 1333838021, 1150208456, 1973745387, 
-    2125135846, 1673061617, 1756818940, 2970356327, 3120694122, 2802849917,
-    2887651696, 2637442643, 2520393566, 2334669897, 2149987652, 3917234703,
-    3799141122, 4284502037, 4100872472, 3309594171, 3460984630, 3545789473,
-    3629546796, 2050466060, 1899603969, 1814803222, 1730525723, 1443857720,
-    1560382517, 1075025698, 1260232239, 575138148, 692707433, 878443390, 
-    1062597235, 243256656, 91341917, 409198410, 325965383, 3403100636, 
-    3252238545, 3704300486, 3620022987, 3874428392, 3990953189, 4042459122,
-    4227665663, 2460449204, 2578018489, 2226875310, 2411029155, 3198115200,
-    3046200461, 2827177882, 2743944855)
-U3 = (0, 218828297, 437656594, 387781147, 875313188, 958871085, 775562294, 
-    590424639, 1750626376, 1699970625, 1917742170, 2135253587, 1551124588, 
-    1367295589, 1180849278, 1265195639, 3501252752, 3720081049, 3399941250,
-    3350065803, 3835484340, 3919042237, 4270507174, 4085369519, 3102249176,
-    3051593425, 2734591178, 2952102595, 2361698556, 2177869557, 2530391278,
-    2614737639, 3145456443, 3060847922, 2708326185, 2892417312, 2404901663,
-    2187128086, 2504130317, 2555048196, 3542330227, 3727205754, 3375740769,
-    3292445032, 3876557655, 3926170974, 4246310725, 4027744588, 1808481195,
-    1723872674, 1910319033, 2094410160, 1608975247, 1391201670, 1173430173,
-    1224348052, 59984867, 244860394, 428169201, 344873464, 935293895, 
-    984907214, 766078933, 547512796, 1844882806, 1627235199, 2011214180, 
-    2062270317, 1507497298, 1423022939, 1137477952, 1321699145, 95345982, 
-    145085239, 532201772, 313773861, 830661914, 1015671571, 731183368, 
-    648017665, 3175501286, 2957853679, 2807058932, 2858115069, 2305455554, 
-    2220981195, 2474404304, 2658625497, 3575528878, 3625268135, 3473416636,
-    3254988725, 3778151818, 3963161475, 4213447064, 4130281361, 3599595085,
-    3683022916, 3432737375, 3247465558, 3802222185, 4020912224, 4172763771,
-    4122762354, 3201631749, 3017672716, 2764249623, 2848461854, 2331590177,
-    2280796200, 2431590963, 2648976442, 104699613, 188127444, 472615631, 
-    287343814, 840019705, 1058709744, 671593195, 621591778, 1852171925, 
-    1668212892, 1953757831, 2037970062, 1514790577, 1463996600, 1080017571,
-    1297403050, 3673637356, 3623636965, 3235995134, 3454686199, 4007360968,
-    3822090177, 4107101658, 4190530515, 2997825956, 3215212461, 2830708150,
-    2779915199, 2256734592, 2340947849, 2627016082, 2443058075, 172466556, 
-    122466165, 273792366, 492483431, 1047239000, 861968209, 612205898, 
-    695634755, 1646252340, 1863638845, 2013908262, 1963115311, 1446242576, 
-    1530455833, 1277555970, 1093597963, 1636604631, 1820824798, 2073724613,
-    1989249228, 1436590835, 1487645946, 1337376481, 1119727848, 164948639, 
-    81781910, 331544205, 516552836, 1039717051, 821288114, 669961897, 
-    719700128, 2973530695, 3157750862, 2871682645, 2787207260, 2232435299, 
-    2283490410, 2667994737, 2450346104, 3647212047, 3564045318, 3279033885,
-    3464042516, 3980931627, 3762502690, 4150144569, 4199882800, 3070356634,
-    3121275539, 2904027272, 2686254721, 2200818878, 2384911031, 2570832044,
-    2486224549, 3747192018, 3528626907, 3310321856, 3359936201, 3950355702,
-    3867060991, 4049844452, 4234721005, 1739656202, 1790575107, 2108100632,
-    1890328081, 1402811438, 1586903591, 1233856572, 1149249077, 266959938, 
-    48394827, 369057872, 418672217, 1002783846, 919489135, 567498868, 
-    752375421, 209336225, 24197544, 376187827, 459744698, 945164165, 
-    895287692, 574624663, 793451934, 1679968233, 1764313568, 2117360635, 
-    1933530610, 1343127501, 1560637892, 1243112415, 1192455638, 3704280881,
-    3519142200, 3336358691, 3419915562, 3907448597, 3857572124, 4075877127,
-    4294704398, 3029510009, 3113855344, 2927934315, 2744104290, 2159976285,
-    2377486676, 2594734927, 2544078150)
-U4 = (0, 151849742, 303699484, 454499602, 607398968, 758720310, 908999204, 
-    1059270954, 1214797936, 1097159550, 1517440620, 1400849762, 1817998408,
-    1699839814, 2118541908, 2001430874, 2429595872, 2581445614, 2194319100,
-    2345119218, 3034881240, 3186202582, 2801699524, 2951971274, 3635996816,
-    3518358430, 3399679628, 3283088770, 4237083816, 4118925222, 4002861748,
-    3885750714, 1002142683, 850817237, 698445255, 548169417, 529487843, 
-    377642221, 227885567, 77089521, 1943217067, 2061379749, 1640576439, 
-    1757691577, 1474760595, 1592394909, 1174215055, 1290801793, 2875968315,
-    2724642869, 3111247143, 2960971305, 2405426947, 2253581325, 2638606623,
-    2487810577, 3808662347, 3926825029, 4044981591, 4162096729, 3342319475,
-    3459953789, 3576539503, 3693126241, 1986918061, 2137062819, 1685577905,
-    1836772287, 1381620373, 1532285339, 1078185097, 1229899655, 1040559837,
-    923313619, 740276417, 621982671, 439452389, 322734571, 137073913, 
-    19308535, 3871163981, 4021308739, 4104605777, 4255800159, 3263785589, 
-    3414450555, 3499326569, 3651041127, 2933202493, 2815956275, 3167684641,
-    3049390895, 2330014213, 2213296395, 2566595609, 2448830231, 1305906550,
-    1155237496, 1607244650, 1455525988, 1776460110, 1626319424, 2079897426,
-    1928707164, 96392454, 213114376, 396673818, 514443284, 562755902, 
-    679998000, 865136418, 983426092, 3708173718, 3557504664, 3474729866, 
-    3323011204, 4180808110, 4030667424, 3945269170, 3794078908, 2507040230,
-    2623762152, 2272556026, 2390325492, 2975484382, 3092726480, 2738905026,
-    2857194700, 3973773121, 3856137295, 4274053469, 4157467219, 3371096953,
-    3252932727, 3673476453, 3556361835, 2763173681, 2915017791, 3064510765,
-    3215307299, 2156299017, 2307622919, 2459735317, 2610011675, 2081048481,
-    1963412655, 1846563261, 1729977011, 1480485785, 1362321559, 1243905413,
-    1126790795, 878845905, 1030690015, 645401037, 796197571, 274084841, 
-    425408743, 38544885, 188821243, 3613494426, 3731654548, 3313212038, 
-    3430322568, 4082475170, 4200115116, 3780097726, 3896688048, 2668221674,
-    2516901860, 2366882550, 2216610296, 3141400786, 2989552604, 2837966542,
-    2687165888, 1202797690, 1320957812, 1437280870, 1554391400, 1669664834,
-    1787304780, 1906247262, 2022837584, 265905162, 114585348, 499347990, 
-    349075736, 736970802, 585122620, 972512814, 821712160, 2595684844, 
-    2478443234, 2293045232, 2174754046, 3196267988, 3079546586, 2895723464,
-    2777952454, 3537852828, 3687994002, 3234156416, 3385345166, 4142626212,
-    4293295786, 3841024952, 3992742070, 174567692, 57326082, 410887952, 
-    292596766, 777231668, 660510266, 1011452712, 893681702, 1108339068, 
-    1258480242, 1343618912, 1494807662, 1715193156, 1865862730, 1948373848,
-    2100090966, 2701949495, 2818666809, 3004591147, 3122358053, 2235061775,
-    2352307457, 2535604243, 2653899549, 3915653703, 3764988233, 4219352155,
-    4067639125, 3444575871, 3294430577, 3746175075, 3594982253, 836553431, 
-    953270745, 600235211, 718002117, 367585007, 484830689, 133361907, 
-    251657213, 2041877159, 1891211689, 1806599355, 1654886325, 1568718495, 
-    1418573201, 1335535747, 1184342925)
-rcon = (1, 2, 4, 8, 16, 32, 64, 128, 27, 54, 108, 216, 171, 77, 154, 47, 94,
-    188, 99, 198, 151, 53, 106, 212, 179, 125, 250, 239, 197, 145)

+# ported from the Java reference code by Bram Cohen, bram@gawth.com, April 2001
+# this code is public domain, unless someone makes
+# an intellectual property claim against the reference
+# code, in which case it can be made public domain by
+# deleting all the comments and renaming all the variables
+
+shifts = [[[0, 0], [1, 3], [2, 2], [3, 1]],
+          [[0, 0], [1, 5], [2, 4], [3, 3]],
+          [[0, 0], [1, 7], [3, 5], [4, 4]]]
+
+# [keysize][block_size]
+num_rounds = {16: {16: 10, 24: 12, 32: 14},
+              24: {16: 12, 24: 12, 32: 14},
+              32: {16: 14, 24: 14, 32: 14}}
+
+# see unit_tests/test_tlslite_utils_rijndael.py for algorithm used to
+# calculate S, Si, T, U and rcon arrays
+
+# S box
+S = (99, 124, 119, 123, 242, 107, 111, 197,
+     48, 1, 103, 43, 254, 215, 171, 118,
+     202, 130, 201, 125, 250, 89, 71, 240,
+     173, 212, 162, 175, 156, 164, 114, 192,
+     183, 253, 147, 38, 54, 63, 247, 204,
+     52, 165, 229, 241, 113, 216, 49, 21,
+     4, 199, 35, 195, 24, 150, 5, 154,
+     7, 18, 128, 226, 235, 39, 178, 117,
+     9, 131, 44, 26, 27, 110, 90, 160,
+     82, 59, 214, 179, 41, 227, 47, 132,
+     83, 209, 0, 237, 32, 252, 177, 91,
+     106, 203, 190, 57, 74, 76, 88, 207,
+     208, 239, 170, 251, 67, 77, 51, 133,
+     69, 249, 2, 127, 80, 60, 159, 168,
+     81, 163, 64, 143, 146, 157, 56, 245,
+     188, 182, 218, 33, 16, 255, 243, 210,
+     205, 12, 19, 236, 95, 151, 68, 23,
+     196, 167, 126, 61, 100, 93, 25, 115,
+     96, 129, 79, 220, 34, 42, 144, 136,
+     70, 238, 184, 20, 222, 94, 11, 219,
+     224, 50, 58, 10, 73, 6, 36, 92,
+     194, 211, 172, 98, 145, 149, 228, 121,
+     231, 200, 55, 109, 141, 213, 78, 169,
+     108, 86, 244, 234, 101, 122, 174, 8,
+     186, 120, 37, 46, 28, 166, 180, 198,
+     232, 221, 116, 31, 75, 189, 139, 138,
+     112, 62, 181, 102, 72, 3, 246, 14,
+     97, 53, 87, 185, 134, 193, 29, 158,
+     225, 248, 152, 17, 105, 217, 142, 148,
+     155, 30, 135, 233, 206, 85, 40, 223,
+     140, 161, 137, 13, 191, 230, 66, 104,
+     65, 153, 45, 15, 176, 84, 187, 22)
+
+# inverse of S box
+Si = (82, 9, 106, 213, 48, 54, 165, 56,
+      191, 64, 163, 158, 129, 243, 215, 251,
+      124, 227, 57, 130, 155, 47, 255, 135,
+      52, 142, 67, 68, 196, 222, 233, 203,
+      84, 123, 148, 50, 166, 194, 35, 61,
+      238, 76, 149, 11, 66, 250, 195, 78,
+      8, 46, 161, 102, 40, 217, 36, 178,
+      118, 91, 162, 73, 109, 139, 209, 37,
+      114, 248, 246, 100, 134, 104, 152, 22,
+      212, 164, 92, 204, 93, 101, 182, 146,
+      108, 112, 72, 80, 253, 237, 185, 218,
+      94, 21, 70, 87, 167, 141, 157, 132,
+      144, 216, 171, 0, 140, 188, 211, 10,
+      247, 228, 88, 5, 184, 179, 69, 6,
+      208, 44, 30, 143, 202, 63, 15, 2,
+      193, 175, 189, 3, 1, 19, 138, 107,
+      58, 145, 17, 65, 79, 103, 220, 234,
+      151, 242, 207, 206, 240, 180, 230, 115,
+      150, 172, 116, 34, 231, 173, 53, 133,
+      226, 249, 55, 232, 28, 117, 223, 110,
+      71, 241, 26, 113, 29, 41, 197, 137,
+      111, 183, 98, 14, 170, 24, 190, 27,
+      252, 86, 62, 75, 198, 210, 121, 32,
+      154, 219, 192, 254, 120, 205, 90, 244,
+      31, 221, 168, 51, 136, 7, 199, 49,
+      177, 18, 16, 89, 39, 128, 236, 95,
+      96, 81, 127, 169, 25, 181, 74, 13,
+      45, 229, 122, 159, 147, 201, 156, 239,
+      160, 224, 59, 77, 174, 42, 245, 176,
+      200, 235, 187, 60, 131, 83, 153, 97,
+      23, 43, 4, 126, 186, 119, 214, 38,
+      225, 105, 20, 99, 85, 33, 12, 125)
+
+T1 = (0xc66363a5, 0xf87c7c84, 0xee777799, 0xf67b7b8d,
+      0xfff2f20d, 0xd66b6bbd, 0xde6f6fb1, 0x91c5c554,
+      0x60303050, 0x2010103, 0xce6767a9, 0x562b2b7d,
+      0xe7fefe19, 0xb5d7d762, 0x4dababe6, 0xec76769a,
+      0x8fcaca45, 0x1f82829d, 0x89c9c940, 0xfa7d7d87,
+      0xeffafa15, 0xb25959eb, 0x8e4747c9, 0xfbf0f00b,
+      0x41adadec, 0xb3d4d467, 0x5fa2a2fd, 0x45afafea,
+      0x239c9cbf, 0x53a4a4f7, 0xe4727296, 0x9bc0c05b,
+      0x75b7b7c2, 0xe1fdfd1c, 0x3d9393ae, 0x4c26266a,
+      0x6c36365a, 0x7e3f3f41, 0xf5f7f702, 0x83cccc4f,
+      0x6834345c, 0x51a5a5f4, 0xd1e5e534, 0xf9f1f108,
+      0xe2717193, 0xabd8d873, 0x62313153, 0x2a15153f,
+      0x804040c, 0x95c7c752, 0x46232365, 0x9dc3c35e,
+      0x30181828, 0x379696a1, 0xa05050f, 0x2f9a9ab5,
+      0xe070709, 0x24121236, 0x1b80809b, 0xdfe2e23d,
+      0xcdebeb26, 0x4e272769, 0x7fb2b2cd, 0xea75759f,
+      0x1209091b, 0x1d83839e, 0x582c2c74, 0x341a1a2e,
+      0x361b1b2d, 0xdc6e6eb2, 0xb45a5aee, 0x5ba0a0fb,
+      0xa45252f6, 0x763b3b4d, 0xb7d6d661, 0x7db3b3ce,
+      0x5229297b, 0xdde3e33e, 0x5e2f2f71, 0x13848497,
+      0xa65353f5, 0xb9d1d168, 0x0, 0xc1eded2c,
+      0x40202060, 0xe3fcfc1f, 0x79b1b1c8, 0xb65b5bed,
+      0xd46a6abe, 0x8dcbcb46, 0x67bebed9, 0x7239394b,
+      0x944a4ade, 0x984c4cd4, 0xb05858e8, 0x85cfcf4a,
+      0xbbd0d06b, 0xc5efef2a, 0x4faaaae5, 0xedfbfb16,
+      0x864343c5, 0x9a4d4dd7, 0x66333355, 0x11858594,
+      0x8a4545cf, 0xe9f9f910, 0x4020206, 0xfe7f7f81,
+      0xa05050f0, 0x783c3c44, 0x259f9fba, 0x4ba8a8e3,
+      0xa25151f3, 0x5da3a3fe, 0x804040c0, 0x58f8f8a,
+      0x3f9292ad, 0x219d9dbc, 0x70383848, 0xf1f5f504,
+      0x63bcbcdf, 0x77b6b6c1, 0xafdada75, 0x42212163,
+      0x20101030, 0xe5ffff1a, 0xfdf3f30e, 0xbfd2d26d,
+      0x81cdcd4c, 0x180c0c14, 0x26131335, 0xc3ecec2f,
+      0xbe5f5fe1, 0x359797a2, 0x884444cc, 0x2e171739,
+      0x93c4c457, 0x55a7a7f2, 0xfc7e7e82, 0x7a3d3d47,
+      0xc86464ac, 0xba5d5de7, 0x3219192b, 0xe6737395,
+      0xc06060a0, 0x19818198, 0x9e4f4fd1, 0xa3dcdc7f,
+      0x44222266, 0x542a2a7e, 0x3b9090ab, 0xb888883,
+      0x8c4646ca, 0xc7eeee29, 0x6bb8b8d3, 0x2814143c,
+      0xa7dede79, 0xbc5e5ee2, 0x160b0b1d, 0xaddbdb76,
+      0xdbe0e03b, 0x64323256, 0x743a3a4e, 0x140a0a1e,
+      0x924949db, 0xc06060a, 0x4824246c, 0xb85c5ce4,
+      0x9fc2c25d, 0xbdd3d36e, 0x43acacef, 0xc46262a6,
+      0x399191a8, 0x319595a4, 0xd3e4e437, 0xf279798b,
+      0xd5e7e732, 0x8bc8c843, 0x6e373759, 0xda6d6db7,
+      0x18d8d8c, 0xb1d5d564, 0x9c4e4ed2, 0x49a9a9e0,
+      0xd86c6cb4, 0xac5656fa, 0xf3f4f407, 0xcfeaea25,
+      0xca6565af, 0xf47a7a8e, 0x47aeaee9, 0x10080818,
+      0x6fbabad5, 0xf0787888, 0x4a25256f, 0x5c2e2e72,
+      0x381c1c24, 0x57a6a6f1, 0x73b4b4c7, 0x97c6c651,
+      0xcbe8e823, 0xa1dddd7c, 0xe874749c, 0x3e1f1f21,
+      0x964b4bdd, 0x61bdbddc, 0xd8b8b86, 0xf8a8a85,
+      0xe0707090, 0x7c3e3e42, 0x71b5b5c4, 0xcc6666aa,
+      0x904848d8, 0x6030305, 0xf7f6f601, 0x1c0e0e12,
+      0xc26161a3, 0x6a35355f, 0xae5757f9, 0x69b9b9d0,
+      0x17868691, 0x99c1c158, 0x3a1d1d27, 0x279e9eb9,
+      0xd9e1e138, 0xebf8f813, 0x2b9898b3, 0x22111133,
+      0xd26969bb, 0xa9d9d970, 0x78e8e89, 0x339494a7,
+      0x2d9b9bb6, 0x3c1e1e22, 0x15878792, 0xc9e9e920,
+      0x87cece49, 0xaa5555ff, 0x50282878, 0xa5dfdf7a,
+      0x38c8c8f, 0x59a1a1f8, 0x9898980, 0x1a0d0d17,
+      0x65bfbfda, 0xd7e6e631, 0x844242c6, 0xd06868b8,
+      0x824141c3, 0x299999b0, 0x5a2d2d77, 0x1e0f0f11,
+      0x7bb0b0cb, 0xa85454fc, 0x6dbbbbd6, 0x2c16163a)
+
+T2 = (0xa5c66363, 0x84f87c7c, 0x99ee7777, 0x8df67b7b,
+      0xdfff2f2, 0xbdd66b6b, 0xb1de6f6f, 0x5491c5c5,
+      0x50603030, 0x3020101, 0xa9ce6767, 0x7d562b2b,
+      0x19e7fefe, 0x62b5d7d7, 0xe64dabab, 0x9aec7676,
+      0x458fcaca, 0x9d1f8282, 0x4089c9c9, 0x87fa7d7d,
+      0x15effafa, 0xebb25959, 0xc98e4747, 0xbfbf0f0,
+      0xec41adad, 0x67b3d4d4, 0xfd5fa2a2, 0xea45afaf,
+      0xbf239c9c, 0xf753a4a4, 0x96e47272, 0x5b9bc0c0,
+      0xc275b7b7, 0x1ce1fdfd, 0xae3d9393, 0x6a4c2626,
+      0x5a6c3636, 0x417e3f3f, 0x2f5f7f7, 0x4f83cccc,
+      0x5c683434, 0xf451a5a5, 0x34d1e5e5, 0x8f9f1f1,
+      0x93e27171, 0x73abd8d8, 0x53623131, 0x3f2a1515,
+      0xc080404, 0x5295c7c7, 0x65462323, 0x5e9dc3c3,
+      0x28301818, 0xa1379696, 0xf0a0505, 0xb52f9a9a,
+      0x90e0707, 0x36241212, 0x9b1b8080, 0x3ddfe2e2,
+      0x26cdebeb, 0x694e2727, 0xcd7fb2b2, 0x9fea7575,
+      0x1b120909, 0x9e1d8383, 0x74582c2c, 0x2e341a1a,
+      0x2d361b1b, 0xb2dc6e6e, 0xeeb45a5a, 0xfb5ba0a0,
+      0xf6a45252, 0x4d763b3b, 0x61b7d6d6, 0xce7db3b3,
+      0x7b522929, 0x3edde3e3, 0x715e2f2f, 0x97138484,
+      0xf5a65353, 0x68b9d1d1, 0x0, 0x2cc1eded,
+      0x60402020, 0x1fe3fcfc, 0xc879b1b1, 0xedb65b5b,
+      0xbed46a6a, 0x468dcbcb, 0xd967bebe, 0x4b723939,
+      0xde944a4a, 0xd4984c4c, 0xe8b05858, 0x4a85cfcf,
+      0x6bbbd0d0, 0x2ac5efef, 0xe54faaaa, 0x16edfbfb,
+      0xc5864343, 0xd79a4d4d, 0x55663333, 0x94118585,
+      0xcf8a4545, 0x10e9f9f9, 0x6040202, 0x81fe7f7f,
+      0xf0a05050, 0x44783c3c, 0xba259f9f, 0xe34ba8a8,
+      0xf3a25151, 0xfe5da3a3, 0xc0804040, 0x8a058f8f,
+      0xad3f9292, 0xbc219d9d, 0x48703838, 0x4f1f5f5,
+      0xdf63bcbc, 0xc177b6b6, 0x75afdada, 0x63422121,
+      0x30201010, 0x1ae5ffff, 0xefdf3f3, 0x6dbfd2d2,
+      0x4c81cdcd, 0x14180c0c, 0x35261313, 0x2fc3ecec,
+      0xe1be5f5f, 0xa2359797, 0xcc884444, 0x392e1717,
+      0x5793c4c4, 0xf255a7a7, 0x82fc7e7e, 0x477a3d3d,
+      0xacc86464, 0xe7ba5d5d, 0x2b321919, 0x95e67373,
+      0xa0c06060, 0x98198181, 0xd19e4f4f, 0x7fa3dcdc,
+      0x66442222, 0x7e542a2a, 0xab3b9090, 0x830b8888,
+      0xca8c4646, 0x29c7eeee, 0xd36bb8b8, 0x3c281414,
+      0x79a7dede, 0xe2bc5e5e, 0x1d160b0b, 0x76addbdb,
+      0x3bdbe0e0, 0x56643232, 0x4e743a3a, 0x1e140a0a,
+      0xdb924949, 0xa0c0606, 0x6c482424, 0xe4b85c5c,
+      0x5d9fc2c2, 0x6ebdd3d3, 0xef43acac, 0xa6c46262,
+      0xa8399191, 0xa4319595, 0x37d3e4e4, 0x8bf27979,
+      0x32d5e7e7, 0x438bc8c8, 0x596e3737, 0xb7da6d6d,
+      0x8c018d8d, 0x64b1d5d5, 0xd29c4e4e, 0xe049a9a9,
+      0xb4d86c6c, 0xfaac5656, 0x7f3f4f4, 0x25cfeaea,
+      0xafca6565, 0x8ef47a7a, 0xe947aeae, 0x18100808,
+      0xd56fbaba, 0x88f07878, 0x6f4a2525, 0x725c2e2e,
+      0x24381c1c, 0xf157a6a6, 0xc773b4b4, 0x5197c6c6,
+      0x23cbe8e8, 0x7ca1dddd, 0x9ce87474, 0x213e1f1f,
+      0xdd964b4b, 0xdc61bdbd, 0x860d8b8b, 0x850f8a8a,
+      0x90e07070, 0x427c3e3e, 0xc471b5b5, 0xaacc6666,
+      0xd8904848, 0x5060303, 0x1f7f6f6, 0x121c0e0e,
+      0xa3c26161, 0x5f6a3535, 0xf9ae5757, 0xd069b9b9,
+      0x91178686, 0x5899c1c1, 0x273a1d1d, 0xb9279e9e,
+      0x38d9e1e1, 0x13ebf8f8, 0xb32b9898, 0x33221111,
+      0xbbd26969, 0x70a9d9d9, 0x89078e8e, 0xa7339494,
+      0xb62d9b9b, 0x223c1e1e, 0x92158787, 0x20c9e9e9,
+      0x4987cece, 0xffaa5555, 0x78502828, 0x7aa5dfdf,
+      0x8f038c8c, 0xf859a1a1, 0x80098989, 0x171a0d0d,
+      0xda65bfbf, 0x31d7e6e6, 0xc6844242, 0xb8d06868,
+      0xc3824141, 0xb0299999, 0x775a2d2d, 0x111e0f0f,
+      0xcb7bb0b0, 0xfca85454, 0xd66dbbbb, 0x3a2c1616)
+
+T3 = (0x63a5c663, 0x7c84f87c, 0x7799ee77, 0x7b8df67b,
+      0xf20dfff2, 0x6bbdd66b, 0x6fb1de6f, 0xc55491c5,
+      0x30506030, 0x1030201, 0x67a9ce67, 0x2b7d562b,
+      0xfe19e7fe, 0xd762b5d7, 0xabe64dab, 0x769aec76,
+      0xca458fca, 0x829d1f82, 0xc94089c9, 0x7d87fa7d,
+      0xfa15effa, 0x59ebb259, 0x47c98e47, 0xf00bfbf0,
+      0xadec41ad, 0xd467b3d4, 0xa2fd5fa2, 0xafea45af,
+      0x9cbf239c, 0xa4f753a4, 0x7296e472, 0xc05b9bc0,
+      0xb7c275b7, 0xfd1ce1fd, 0x93ae3d93, 0x266a4c26,
+      0x365a6c36, 0x3f417e3f, 0xf702f5f7, 0xcc4f83cc,
+      0x345c6834, 0xa5f451a5, 0xe534d1e5, 0xf108f9f1,
+      0x7193e271, 0xd873abd8, 0x31536231, 0x153f2a15,
+      0x40c0804, 0xc75295c7, 0x23654623, 0xc35e9dc3,
+      0x18283018, 0x96a13796, 0x50f0a05, 0x9ab52f9a,
+      0x7090e07, 0x12362412, 0x809b1b80, 0xe23ddfe2,
+      0xeb26cdeb, 0x27694e27, 0xb2cd7fb2, 0x759fea75,
+      0x91b1209, 0x839e1d83, 0x2c74582c, 0x1a2e341a,
+      0x1b2d361b, 0x6eb2dc6e, 0x5aeeb45a, 0xa0fb5ba0,
+      0x52f6a452, 0x3b4d763b, 0xd661b7d6, 0xb3ce7db3,
+      0x297b5229, 0xe33edde3, 0x2f715e2f, 0x84971384,
+      0x53f5a653, 0xd168b9d1, 0x0, 0xed2cc1ed,
+      0x20604020, 0xfc1fe3fc, 0xb1c879b1, 0x5bedb65b,
+      0x6abed46a, 0xcb468dcb, 0xbed967be, 0x394b7239,
+      0x4ade944a, 0x4cd4984c, 0x58e8b058, 0xcf4a85cf,
+      0xd06bbbd0, 0xef2ac5ef, 0xaae54faa, 0xfb16edfb,
+      0x43c58643, 0x4dd79a4d, 0x33556633, 0x85941185,
+      0x45cf8a45, 0xf910e9f9, 0x2060402, 0x7f81fe7f,
+      0x50f0a050, 0x3c44783c, 0x9fba259f, 0xa8e34ba8,
+      0x51f3a251, 0xa3fe5da3, 0x40c08040, 0x8f8a058f,
+      0x92ad3f92, 0x9dbc219d, 0x38487038, 0xf504f1f5,
+      0xbcdf63bc, 0xb6c177b6, 0xda75afda, 0x21634221,
+      0x10302010, 0xff1ae5ff, 0xf30efdf3, 0xd26dbfd2,
+      0xcd4c81cd, 0xc14180c, 0x13352613, 0xec2fc3ec,
+      0x5fe1be5f, 0x97a23597, 0x44cc8844, 0x17392e17,
+      0xc45793c4, 0xa7f255a7, 0x7e82fc7e, 0x3d477a3d,
+      0x64acc864, 0x5de7ba5d, 0x192b3219, 0x7395e673,
+      0x60a0c060, 0x81981981, 0x4fd19e4f, 0xdc7fa3dc,
+      0x22664422, 0x2a7e542a, 0x90ab3b90, 0x88830b88,
+      0x46ca8c46, 0xee29c7ee, 0xb8d36bb8, 0x143c2814,
+      0xde79a7de, 0x5ee2bc5e, 0xb1d160b, 0xdb76addb,
+      0xe03bdbe0, 0x32566432, 0x3a4e743a, 0xa1e140a,
+      0x49db9249, 0x60a0c06, 0x246c4824, 0x5ce4b85c,
+      0xc25d9fc2, 0xd36ebdd3, 0xacef43ac, 0x62a6c462,
+      0x91a83991, 0x95a43195, 0xe437d3e4, 0x798bf279,
+      0xe732d5e7, 0xc8438bc8, 0x37596e37, 0x6db7da6d,
+      0x8d8c018d, 0xd564b1d5, 0x4ed29c4e, 0xa9e049a9,
+      0x6cb4d86c, 0x56faac56, 0xf407f3f4, 0xea25cfea,
+      0x65afca65, 0x7a8ef47a, 0xaee947ae, 0x8181008,
+      0xbad56fba, 0x7888f078, 0x256f4a25, 0x2e725c2e,
+      0x1c24381c, 0xa6f157a6, 0xb4c773b4, 0xc65197c6,
+      0xe823cbe8, 0xdd7ca1dd, 0x749ce874, 0x1f213e1f,
+      0x4bdd964b, 0xbddc61bd, 0x8b860d8b, 0x8a850f8a,
+      0x7090e070, 0x3e427c3e, 0xb5c471b5, 0x66aacc66,
+      0x48d89048, 0x3050603, 0xf601f7f6, 0xe121c0e,
+      0x61a3c261, 0x355f6a35, 0x57f9ae57, 0xb9d069b9,
+      0x86911786, 0xc15899c1, 0x1d273a1d, 0x9eb9279e,
+      0xe138d9e1, 0xf813ebf8, 0x98b32b98, 0x11332211,
+      0x69bbd269, 0xd970a9d9, 0x8e89078e, 0x94a73394,
+      0x9bb62d9b, 0x1e223c1e, 0x87921587, 0xe920c9e9,
+      0xce4987ce, 0x55ffaa55, 0x28785028, 0xdf7aa5df,
+      0x8c8f038c, 0xa1f859a1, 0x89800989, 0xd171a0d,
+      0xbfda65bf, 0xe631d7e6, 0x42c68442, 0x68b8d068,
+      0x41c38241, 0x99b02999, 0x2d775a2d, 0xf111e0f,
+      0xb0cb7bb0, 0x54fca854, 0xbbd66dbb, 0x163a2c16)
+
+T4 = (0x6363a5c6, 0x7c7c84f8, 0x777799ee, 0x7b7b8df6,
+      0xf2f20dff, 0x6b6bbdd6, 0x6f6fb1de, 0xc5c55491,
+      0x30305060, 0x1010302, 0x6767a9ce, 0x2b2b7d56,
+      0xfefe19e7, 0xd7d762b5, 0xababe64d, 0x76769aec,
+      0xcaca458f, 0x82829d1f, 0xc9c94089, 0x7d7d87fa,
+      0xfafa15ef, 0x5959ebb2, 0x4747c98e, 0xf0f00bfb,
+      0xadadec41, 0xd4d467b3, 0xa2a2fd5f, 0xafafea45,
+      0x9c9cbf23, 0xa4a4f753, 0x727296e4, 0xc0c05b9b,
+      0xb7b7c275, 0xfdfd1ce1, 0x9393ae3d, 0x26266a4c,
+      0x36365a6c, 0x3f3f417e, 0xf7f702f5, 0xcccc4f83,
+      0x34345c68, 0xa5a5f451, 0xe5e534d1, 0xf1f108f9,
+      0x717193e2, 0xd8d873ab, 0x31315362, 0x15153f2a,
+      0x4040c08, 0xc7c75295, 0x23236546, 0xc3c35e9d,
+      0x18182830, 0x9696a137, 0x5050f0a, 0x9a9ab52f,
+      0x707090e, 0x12123624, 0x80809b1b, 0xe2e23ddf,
+      0xebeb26cd, 0x2727694e, 0xb2b2cd7f, 0x75759fea,
+      0x9091b12, 0x83839e1d, 0x2c2c7458, 0x1a1a2e34,
+      0x1b1b2d36, 0x6e6eb2dc, 0x5a5aeeb4, 0xa0a0fb5b,
+      0x5252f6a4, 0x3b3b4d76, 0xd6d661b7, 0xb3b3ce7d,
+      0x29297b52, 0xe3e33edd, 0x2f2f715e, 0x84849713,
+      0x5353f5a6, 0xd1d168b9, 0x0, 0xeded2cc1,
+      0x20206040, 0xfcfc1fe3, 0xb1b1c879, 0x5b5bedb6,
+      0x6a6abed4, 0xcbcb468d, 0xbebed967, 0x39394b72,
+      0x4a4ade94, 0x4c4cd498, 0x5858e8b0, 0xcfcf4a85,
+      0xd0d06bbb, 0xefef2ac5, 0xaaaae54f, 0xfbfb16ed,
+      0x4343c586, 0x4d4dd79a, 0x33335566, 0x85859411,
+      0x4545cf8a, 0xf9f910e9, 0x2020604, 0x7f7f81fe,
+      0x5050f0a0, 0x3c3c4478, 0x9f9fba25, 0xa8a8e34b,
+      0x5151f3a2, 0xa3a3fe5d, 0x4040c080, 0x8f8f8a05,
+      0x9292ad3f, 0x9d9dbc21, 0x38384870, 0xf5f504f1,
+      0xbcbcdf63, 0xb6b6c177, 0xdada75af, 0x21216342,
+      0x10103020, 0xffff1ae5, 0xf3f30efd, 0xd2d26dbf,
+      0xcdcd4c81, 0xc0c1418, 0x13133526, 0xecec2fc3,
+      0x5f5fe1be, 0x9797a235, 0x4444cc88, 0x1717392e,
+      0xc4c45793, 0xa7a7f255, 0x7e7e82fc, 0x3d3d477a,
+      0x6464acc8, 0x5d5de7ba, 0x19192b32, 0x737395e6,
+      0x6060a0c0, 0x81819819, 0x4f4fd19e, 0xdcdc7fa3,
+      0x22226644, 0x2a2a7e54, 0x9090ab3b, 0x8888830b,
+      0x4646ca8c, 0xeeee29c7, 0xb8b8d36b, 0x14143c28,
+      0xdede79a7, 0x5e5ee2bc, 0xb0b1d16, 0xdbdb76ad,
+      0xe0e03bdb, 0x32325664, 0x3a3a4e74, 0xa0a1e14,
+      0x4949db92, 0x6060a0c, 0x24246c48, 0x5c5ce4b8,
+      0xc2c25d9f, 0xd3d36ebd, 0xacacef43, 0x6262a6c4,
+      0x9191a839, 0x9595a431, 0xe4e437d3, 0x79798bf2,
+      0xe7e732d5, 0xc8c8438b, 0x3737596e, 0x6d6db7da,
+      0x8d8d8c01, 0xd5d564b1, 0x4e4ed29c, 0xa9a9e049,
+      0x6c6cb4d8, 0x5656faac, 0xf4f407f3, 0xeaea25cf,
+      0x6565afca, 0x7a7a8ef4, 0xaeaee947, 0x8081810,
+      0xbabad56f, 0x787888f0, 0x25256f4a, 0x2e2e725c,
+      0x1c1c2438, 0xa6a6f157, 0xb4b4c773, 0xc6c65197,
+      0xe8e823cb, 0xdddd7ca1, 0x74749ce8, 0x1f1f213e,
+      0x4b4bdd96, 0xbdbddc61, 0x8b8b860d, 0x8a8a850f,
+      0x707090e0, 0x3e3e427c, 0xb5b5c471, 0x6666aacc,
+      0x4848d890, 0x3030506, 0xf6f601f7, 0xe0e121c,
+      0x6161a3c2, 0x35355f6a, 0x5757f9ae, 0xb9b9d069,
+      0x86869117, 0xc1c15899, 0x1d1d273a, 0x9e9eb927,
+      0xe1e138d9, 0xf8f813eb, 0x9898b32b, 0x11113322,
+      0x6969bbd2, 0xd9d970a9, 0x8e8e8907, 0x9494a733,
+      0x9b9bb62d, 0x1e1e223c, 0x87879215, 0xe9e920c9,
+      0xcece4987, 0x5555ffaa, 0x28287850, 0xdfdf7aa5,
+      0x8c8c8f03, 0xa1a1f859, 0x89898009, 0xd0d171a,
+      0xbfbfda65, 0xe6e631d7, 0x4242c684, 0x6868b8d0,
+      0x4141c382, 0x9999b029, 0x2d2d775a, 0xf0f111e,
+      0xb0b0cb7b, 0x5454fca8, 0xbbbbd66d, 0x16163a2c)
+
+T5 = (0x51f4a750, 0x7e416553, 0x1a17a4c3, 0x3a275e96,
+      0x3bab6bcb, 0x1f9d45f1, 0xacfa58ab, 0x4be30393,
+      0x2030fa55, 0xad766df6, 0x88cc7691, 0xf5024c25,
+      0x4fe5d7fc, 0xc52acbd7, 0x26354480, 0xb562a38f,
+      0xdeb15a49, 0x25ba1b67, 0x45ea0e98, 0x5dfec0e1,
+      0xc32f7502, 0x814cf012, 0x8d4697a3, 0x6bd3f9c6,
+      0x38f5fe7, 0x15929c95, 0xbf6d7aeb, 0x955259da,
+      0xd4be832d, 0x587421d3, 0x49e06929, 0x8ec9c844,
+      0x75c2896a, 0xf48e7978, 0x99583e6b, 0x27b971dd,
+      0xbee14fb6, 0xf088ad17, 0xc920ac66, 0x7dce3ab4,
+      0x63df4a18, 0xe51a3182, 0x97513360, 0x62537f45,
+      0xb16477e0, 0xbb6bae84, 0xfe81a01c, 0xf9082b94,
+      0x70486858, 0x8f45fd19, 0x94de6c87, 0x527bf8b7,
+      0xab73d323, 0x724b02e2, 0xe31f8f57, 0x6655ab2a,
+      0xb2eb2807, 0x2fb5c203, 0x86c57b9a, 0xd33708a5,
+      0x302887f2, 0x23bfa5b2, 0x2036aba, 0xed16825c,
+      0x8acf1c2b, 0xa779b492, 0xf307f2f0, 0x4e69e2a1,
+      0x65daf4cd, 0x605bed5, 0xd134621f, 0xc4a6fe8a,
+      0x342e539d, 0xa2f355a0, 0x58ae132, 0xa4f6eb75,
+      0xb83ec39, 0x4060efaa, 0x5e719f06, 0xbd6e1051,
+      0x3e218af9, 0x96dd063d, 0xdd3e05ae, 0x4de6bd46,
+      0x91548db5, 0x71c45d05, 0x406d46f, 0x605015ff,
+      0x1998fb24, 0xd6bde997, 0x894043cc, 0x67d99e77,
+      0xb0e842bd, 0x7898b88, 0xe7195b38, 0x79c8eedb,
+      0xa17c0a47, 0x7c420fe9, 0xf8841ec9, 0x0,
+      0x9808683, 0x322bed48, 0x1e1170ac, 0x6c5a724e,
+      0xfd0efffb, 0xf853856, 0x3daed51e, 0x362d3927,
+      0xa0fd964, 0x685ca621, 0x9b5b54d1, 0x24362e3a,
+      0xc0a67b1, 0x9357e70f, 0xb4ee96d2, 0x1b9b919e,
+      0x80c0c54f, 0x61dc20a2, 0x5a774b69, 0x1c121a16,
+      0xe293ba0a, 0xc0a02ae5, 0x3c22e043, 0x121b171d,
+      0xe090d0b, 0xf28bc7ad, 0x2db6a8b9, 0x141ea9c8,
+      0x57f11985, 0xaf75074c, 0xee99ddbb, 0xa37f60fd,
+      0xf701269f, 0x5c72f5bc, 0x44663bc5, 0x5bfb7e34,
+      0x8b432976, 0xcb23c6dc, 0xb6edfc68, 0xb8e4f163,
+      0xd731dcca, 0x42638510, 0x13972240, 0x84c61120,
+      0x854a247d, 0xd2bb3df8, 0xaef93211, 0xc729a16d,
+      0x1d9e2f4b, 0xdcb230f3, 0xd8652ec, 0x77c1e3d0,
+      0x2bb3166c, 0xa970b999, 0x119448fa, 0x47e96422,
+      0xa8fc8cc4, 0xa0f03f1a, 0x567d2cd8, 0x223390ef,
+      0x87494ec7, 0xd938d1c1, 0x8ccaa2fe, 0x98d40b36,
+      0xa6f581cf, 0xa57ade28, 0xdab78e26, 0x3fadbfa4,
+      0x2c3a9de4, 0x5078920d, 0x6a5fcc9b, 0x547e4662,
+      0xf68d13c2, 0x90d8b8e8, 0x2e39f75e, 0x82c3aff5,
+      0x9f5d80be, 0x69d0937c, 0x6fd52da9, 0xcf2512b3,
+      0xc8ac993b, 0x10187da7, 0xe89c636e, 0xdb3bbb7b,
+      0xcd267809, 0x6e5918f4, 0xec9ab701, 0x834f9aa8,
+      0xe6956e65, 0xaaffe67e, 0x21bccf08, 0xef15e8e6,
+      0xbae79bd9, 0x4a6f36ce, 0xea9f09d4, 0x29b07cd6,
+      0x31a4b2af, 0x2a3f2331, 0xc6a59430, 0x35a266c0,
+      0x744ebc37, 0xfc82caa6, 0xe090d0b0, 0x33a7d815,
+      0xf104984a, 0x41ecdaf7, 0x7fcd500e, 0x1791f62f,
+      0x764dd68d, 0x43efb04d, 0xccaa4d54, 0xe49604df,
+      0x9ed1b5e3, 0x4c6a881b, 0xc12c1fb8, 0x4665517f,
+      0x9d5eea04, 0x18c355d, 0xfa877473, 0xfb0b412e,
+      0xb3671d5a, 0x92dbd252, 0xe9105633, 0x6dd64713,
+      0x9ad7618c, 0x37a10c7a, 0x59f8148e, 0xeb133c89,
+      0xcea927ee, 0xb761c935, 0xe11ce5ed, 0x7a47b13c,
+      0x9cd2df59, 0x55f2733f, 0x1814ce79, 0x73c737bf,
+      0x53f7cdea, 0x5ffdaa5b, 0xdf3d6f14, 0x7844db86,
+      0xcaaff381, 0xb968c43e, 0x3824342c, 0xc2a3405f,
+      0x161dc372, 0xbce2250c, 0x283c498b, 0xff0d9541,
+      0x39a80171, 0x80cb3de, 0xd8b4e49c, 0x6456c190,
+      0x7bcb8461, 0xd532b670, 0x486c5c74, 0xd0b85742)
+
+T6 = (0x5051f4a7, 0x537e4165, 0xc31a17a4, 0x963a275e,
+      0xcb3bab6b, 0xf11f9d45, 0xabacfa58, 0x934be303,
+      0x552030fa, 0xf6ad766d, 0x9188cc76, 0x25f5024c,
+      0xfc4fe5d7, 0xd7c52acb, 0x80263544, 0x8fb562a3,
+      0x49deb15a, 0x6725ba1b, 0x9845ea0e, 0xe15dfec0,
+      0x2c32f75, 0x12814cf0, 0xa38d4697, 0xc66bd3f9,
+      0xe7038f5f, 0x9515929c, 0xebbf6d7a, 0xda955259,
+      0x2dd4be83, 0xd3587421, 0x2949e069, 0x448ec9c8,
+      0x6a75c289, 0x78f48e79, 0x6b99583e, 0xdd27b971,
+      0xb6bee14f, 0x17f088ad, 0x66c920ac, 0xb47dce3a,
+      0x1863df4a, 0x82e51a31, 0x60975133, 0x4562537f,
+      0xe0b16477, 0x84bb6bae, 0x1cfe81a0, 0x94f9082b,
+      0x58704868, 0x198f45fd, 0x8794de6c, 0xb7527bf8,
+      0x23ab73d3, 0xe2724b02, 0x57e31f8f, 0x2a6655ab,
+      0x7b2eb28, 0x32fb5c2, 0x9a86c57b, 0xa5d33708,
+      0xf2302887, 0xb223bfa5, 0xba02036a, 0x5ced1682,
+      0x2b8acf1c, 0x92a779b4, 0xf0f307f2, 0xa14e69e2,
+      0xcd65daf4, 0xd50605be, 0x1fd13462, 0x8ac4a6fe,
+      0x9d342e53, 0xa0a2f355, 0x32058ae1, 0x75a4f6eb,
+      0x390b83ec, 0xaa4060ef, 0x65e719f, 0x51bd6e10,
+      0xf93e218a, 0x3d96dd06, 0xaedd3e05, 0x464de6bd,
+      0xb591548d, 0x571c45d, 0x6f0406d4, 0xff605015,
+      0x241998fb, 0x97d6bde9, 0xcc894043, 0x7767d99e,
+      0xbdb0e842, 0x8807898b, 0x38e7195b, 0xdb79c8ee,
+      0x47a17c0a, 0xe97c420f, 0xc9f8841e, 0x0,
+      0x83098086, 0x48322bed, 0xac1e1170, 0x4e6c5a72,
+      0xfbfd0eff, 0x560f8538, 0x1e3daed5, 0x27362d39,
+      0x640a0fd9, 0x21685ca6, 0xd19b5b54, 0x3a24362e,
+      0xb10c0a67, 0xf9357e7, 0xd2b4ee96, 0x9e1b9b91,
+      0x4f80c0c5, 0xa261dc20, 0x695a774b, 0x161c121a,
+      0xae293ba, 0xe5c0a02a, 0x433c22e0, 0x1d121b17,
+      0xb0e090d, 0xadf28bc7, 0xb92db6a8, 0xc8141ea9,
+      0x8557f119, 0x4caf7507, 0xbbee99dd, 0xfda37f60,
+      0x9ff70126, 0xbc5c72f5, 0xc544663b, 0x345bfb7e,
+      0x768b4329, 0xdccb23c6, 0x68b6edfc, 0x63b8e4f1,
+      0xcad731dc, 0x10426385, 0x40139722, 0x2084c611,
+      0x7d854a24, 0xf8d2bb3d, 0x11aef932, 0x6dc729a1,
+      0x4b1d9e2f, 0xf3dcb230, 0xec0d8652, 0xd077c1e3,
+      0x6c2bb316, 0x99a970b9, 0xfa119448, 0x2247e964,
+      0xc4a8fc8c, 0x1aa0f03f, 0xd8567d2c, 0xef223390,
+      0xc787494e, 0xc1d938d1, 0xfe8ccaa2, 0x3698d40b,
+      0xcfa6f581, 0x28a57ade, 0x26dab78e, 0xa43fadbf,
+      0xe42c3a9d, 0xd507892, 0x9b6a5fcc, 0x62547e46,
+      0xc2f68d13, 0xe890d8b8, 0x5e2e39f7, 0xf582c3af,
+      0xbe9f5d80, 0x7c69d093, 0xa96fd52d, 0xb3cf2512,
+      0x3bc8ac99, 0xa710187d, 0x6ee89c63, 0x7bdb3bbb,
+      0x9cd2678, 0xf46e5918, 0x1ec9ab7, 0xa8834f9a,
+      0x65e6956e, 0x7eaaffe6, 0x821bccf, 0xe6ef15e8,
+      0xd9bae79b, 0xce4a6f36, 0xd4ea9f09, 0xd629b07c,
+      0xaf31a4b2, 0x312a3f23, 0x30c6a594, 0xc035a266,
+      0x37744ebc, 0xa6fc82ca, 0xb0e090d0, 0x1533a7d8,
+      0x4af10498, 0xf741ecda, 0xe7fcd50, 0x2f1791f6,
+      0x8d764dd6, 0x4d43efb0, 0x54ccaa4d, 0xdfe49604,
+      0xe39ed1b5, 0x1b4c6a88, 0xb8c12c1f, 0x7f466551,
+      0x49d5eea, 0x5d018c35, 0x73fa8774, 0x2efb0b41,
+      0x5ab3671d, 0x5292dbd2, 0x33e91056, 0x136dd647,
+      0x8c9ad761, 0x7a37a10c, 0x8e59f814, 0x89eb133c,
+      0xeecea927, 0x35b761c9, 0xede11ce5, 0x3c7a47b1,
+      0x599cd2df, 0x3f55f273, 0x791814ce, 0xbf73c737,
+      0xea53f7cd, 0x5b5ffdaa, 0x14df3d6f, 0x867844db,
+      0x81caaff3, 0x3eb968c4, 0x2c382434, 0x5fc2a340,
+      0x72161dc3, 0xcbce225, 0x8b283c49, 0x41ff0d95,
+      0x7139a801, 0xde080cb3, 0x9cd8b4e4, 0x906456c1,
+      0x617bcb84, 0x70d532b6, 0x74486c5c, 0x42d0b857)
+
+T7 = (0xa75051f4, 0x65537e41, 0xa4c31a17, 0x5e963a27,
+      0x6bcb3bab, 0x45f11f9d, 0x58abacfa, 0x3934be3,
+      0xfa552030, 0x6df6ad76, 0x769188cc, 0x4c25f502,
+      0xd7fc4fe5, 0xcbd7c52a, 0x44802635, 0xa38fb562,
+      0x5a49deb1, 0x1b6725ba, 0xe9845ea, 0xc0e15dfe,
+      0x7502c32f, 0xf012814c, 0x97a38d46, 0xf9c66bd3,
+      0x5fe7038f, 0x9c951592, 0x7aebbf6d, 0x59da9552,
+      0x832dd4be, 0x21d35874, 0x692949e0, 0xc8448ec9,
+      0x896a75c2, 0x7978f48e, 0x3e6b9958, 0x71dd27b9,
+      0x4fb6bee1, 0xad17f088, 0xac66c920, 0x3ab47dce,
+      0x4a1863df, 0x3182e51a, 0x33609751, 0x7f456253,
+      0x77e0b164, 0xae84bb6b, 0xa01cfe81, 0x2b94f908,
+      0x68587048, 0xfd198f45, 0x6c8794de, 0xf8b7527b,
+      0xd323ab73, 0x2e2724b, 0x8f57e31f, 0xab2a6655,
+      0x2807b2eb, 0xc2032fb5, 0x7b9a86c5, 0x8a5d337,
+      0x87f23028, 0xa5b223bf, 0x6aba0203, 0x825ced16,
+      0x1c2b8acf, 0xb492a779, 0xf2f0f307, 0xe2a14e69,
+      0xf4cd65da, 0xbed50605, 0x621fd134, 0xfe8ac4a6,
+      0x539d342e, 0x55a0a2f3, 0xe132058a, 0xeb75a4f6,
+      0xec390b83, 0xefaa4060, 0x9f065e71, 0x1051bd6e,
+      0x8af93e21, 0x63d96dd, 0x5aedd3e, 0xbd464de6,
+      0x8db59154, 0x5d0571c4, 0xd46f0406, 0x15ff6050,
+      0xfb241998, 0xe997d6bd, 0x43cc8940, 0x9e7767d9,
+      0x42bdb0e8, 0x8b880789, 0x5b38e719, 0xeedb79c8,
+      0xa47a17c, 0xfe97c42, 0x1ec9f884, 0x0,
+      0x86830980, 0xed48322b, 0x70ac1e11, 0x724e6c5a,
+      0xfffbfd0e, 0x38560f85, 0xd51e3dae, 0x3927362d,
+      0xd9640a0f, 0xa621685c, 0x54d19b5b, 0x2e3a2436,
+      0x67b10c0a, 0xe70f9357, 0x96d2b4ee, 0x919e1b9b,
+      0xc54f80c0, 0x20a261dc, 0x4b695a77, 0x1a161c12,
+      0xba0ae293, 0x2ae5c0a0, 0xe0433c22, 0x171d121b,
+      0xd0b0e09, 0xc7adf28b, 0xa8b92db6, 0xa9c8141e,
+      0x198557f1, 0x74caf75, 0xddbbee99, 0x60fda37f,
+      0x269ff701, 0xf5bc5c72, 0x3bc54466, 0x7e345bfb,
+      0x29768b43, 0xc6dccb23, 0xfc68b6ed, 0xf163b8e4,
+      0xdccad731, 0x85104263, 0x22401397, 0x112084c6,
+      0x247d854a, 0x3df8d2bb, 0x3211aef9, 0xa16dc729,
+      0x2f4b1d9e, 0x30f3dcb2, 0x52ec0d86, 0xe3d077c1,
+      0x166c2bb3, 0xb999a970, 0x48fa1194, 0x642247e9,
+      0x8cc4a8fc, 0x3f1aa0f0, 0x2cd8567d, 0x90ef2233,
+      0x4ec78749, 0xd1c1d938, 0xa2fe8cca, 0xb3698d4,
+      0x81cfa6f5, 0xde28a57a, 0x8e26dab7, 0xbfa43fad,
+      0x9de42c3a, 0x920d5078, 0xcc9b6a5f, 0x4662547e,
+      0x13c2f68d, 0xb8e890d8, 0xf75e2e39, 0xaff582c3,
+      0x80be9f5d, 0x937c69d0, 0x2da96fd5, 0x12b3cf25,
+      0x993bc8ac, 0x7da71018, 0x636ee89c, 0xbb7bdb3b,
+      0x7809cd26, 0x18f46e59, 0xb701ec9a, 0x9aa8834f,
+      0x6e65e695, 0xe67eaaff, 0xcf0821bc, 0xe8e6ef15,
+      0x9bd9bae7, 0x36ce4a6f, 0x9d4ea9f, 0x7cd629b0,
+      0xb2af31a4, 0x23312a3f, 0x9430c6a5, 0x66c035a2,
+      0xbc37744e, 0xcaa6fc82, 0xd0b0e090, 0xd81533a7,
+      0x984af104, 0xdaf741ec, 0x500e7fcd, 0xf62f1791,
+      0xd68d764d, 0xb04d43ef, 0x4d54ccaa, 0x4dfe496,
+      0xb5e39ed1, 0x881b4c6a, 0x1fb8c12c, 0x517f4665,
+      0xea049d5e, 0x355d018c, 0x7473fa87, 0x412efb0b,
+      0x1d5ab367, 0xd25292db, 0x5633e910, 0x47136dd6,
+      0x618c9ad7, 0xc7a37a1, 0x148e59f8, 0x3c89eb13,
+      0x27eecea9, 0xc935b761, 0xe5ede11c, 0xb13c7a47,
+      0xdf599cd2, 0x733f55f2, 0xce791814, 0x37bf73c7,
+      0xcdea53f7, 0xaa5b5ffd, 0x6f14df3d, 0xdb867844,
+      0xf381caaf, 0xc43eb968, 0x342c3824, 0x405fc2a3,
+      0xc372161d, 0x250cbce2, 0x498b283c, 0x9541ff0d,
+      0x17139a8, 0xb3de080c, 0xe49cd8b4, 0xc1906456,
+      0x84617bcb, 0xb670d532, 0x5c74486c, 0x5742d0b8)
+
+T8 = (0xf4a75051, 0x4165537e, 0x17a4c31a, 0x275e963a,
+      0xab6bcb3b, 0x9d45f11f, 0xfa58abac, 0xe303934b,
+      0x30fa5520, 0x766df6ad, 0xcc769188, 0x24c25f5,
+      0xe5d7fc4f, 0x2acbd7c5, 0x35448026, 0x62a38fb5,
+      0xb15a49de, 0xba1b6725, 0xea0e9845, 0xfec0e15d,
+      0x2f7502c3, 0x4cf01281, 0x4697a38d, 0xd3f9c66b,
+      0x8f5fe703, 0x929c9515, 0x6d7aebbf, 0x5259da95,
+      0xbe832dd4, 0x7421d358, 0xe0692949, 0xc9c8448e,
+      0xc2896a75, 0x8e7978f4, 0x583e6b99, 0xb971dd27,
+      0xe14fb6be, 0x88ad17f0, 0x20ac66c9, 0xce3ab47d,
+      0xdf4a1863, 0x1a3182e5, 0x51336097, 0x537f4562,
+      0x6477e0b1, 0x6bae84bb, 0x81a01cfe, 0x82b94f9,
+      0x48685870, 0x45fd198f, 0xde6c8794, 0x7bf8b752,
+      0x73d323ab, 0x4b02e272, 0x1f8f57e3, 0x55ab2a66,
+      0xeb2807b2, 0xb5c2032f, 0xc57b9a86, 0x3708a5d3,
+      0x2887f230, 0xbfa5b223, 0x36aba02, 0x16825ced,
+      0xcf1c2b8a, 0x79b492a7, 0x7f2f0f3, 0x69e2a14e,
+      0xdaf4cd65, 0x5bed506, 0x34621fd1, 0xa6fe8ac4,
+      0x2e539d34, 0xf355a0a2, 0x8ae13205, 0xf6eb75a4,
+      0x83ec390b, 0x60efaa40, 0x719f065e, 0x6e1051bd,
+      0x218af93e, 0xdd063d96, 0x3e05aedd, 0xe6bd464d,
+      0x548db591, 0xc45d0571, 0x6d46f04, 0x5015ff60,
+      0x98fb2419, 0xbde997d6, 0x4043cc89, 0xd99e7767,
+      0xe842bdb0, 0x898b8807, 0x195b38e7, 0xc8eedb79,
+      0x7c0a47a1, 0x420fe97c, 0x841ec9f8, 0x0,
+      0x80868309, 0x2bed4832, 0x1170ac1e, 0x5a724e6c,
+      0xefffbfd, 0x8538560f, 0xaed51e3d, 0x2d392736,
+      0xfd9640a, 0x5ca62168, 0x5b54d19b, 0x362e3a24,
+      0xa67b10c, 0x57e70f93, 0xee96d2b4, 0x9b919e1b,
+      0xc0c54f80, 0xdc20a261, 0x774b695a, 0x121a161c,
+      0x93ba0ae2, 0xa02ae5c0, 0x22e0433c, 0x1b171d12,
+      0x90d0b0e, 0x8bc7adf2, 0xb6a8b92d, 0x1ea9c814,
+      0xf1198557, 0x75074caf, 0x99ddbbee, 0x7f60fda3,
+      0x1269ff7, 0x72f5bc5c, 0x663bc544, 0xfb7e345b,
+      0x4329768b, 0x23c6dccb, 0xedfc68b6, 0xe4f163b8,
+      0x31dccad7, 0x63851042, 0x97224013, 0xc6112084,
+      0x4a247d85, 0xbb3df8d2, 0xf93211ae, 0x29a16dc7,
+      0x9e2f4b1d, 0xb230f3dc, 0x8652ec0d, 0xc1e3d077,
+      0xb3166c2b, 0x70b999a9, 0x9448fa11, 0xe9642247,
+      0xfc8cc4a8, 0xf03f1aa0, 0x7d2cd856, 0x3390ef22,
+      0x494ec787, 0x38d1c1d9, 0xcaa2fe8c, 0xd40b3698,
+      0xf581cfa6, 0x7ade28a5, 0xb78e26da, 0xadbfa43f,
+      0x3a9de42c, 0x78920d50, 0x5fcc9b6a, 0x7e466254,
+      0x8d13c2f6, 0xd8b8e890, 0x39f75e2e, 0xc3aff582,
+      0x5d80be9f, 0xd0937c69, 0xd52da96f, 0x2512b3cf,
+      0xac993bc8, 0x187da710, 0x9c636ee8, 0x3bbb7bdb,
+      0x267809cd, 0x5918f46e, 0x9ab701ec, 0x4f9aa883,
+      0x956e65e6, 0xffe67eaa, 0xbccf0821, 0x15e8e6ef,
+      0xe79bd9ba, 0x6f36ce4a, 0x9f09d4ea, 0xb07cd629,
+      0xa4b2af31, 0x3f23312a, 0xa59430c6, 0xa266c035,
+      0x4ebc3774, 0x82caa6fc, 0x90d0b0e0, 0xa7d81533,
+      0x4984af1, 0xecdaf741, 0xcd500e7f, 0x91f62f17,
+      0x4dd68d76, 0xefb04d43, 0xaa4d54cc, 0x9604dfe4,
+      0xd1b5e39e, 0x6a881b4c, 0x2c1fb8c1, 0x65517f46,
+      0x5eea049d, 0x8c355d01, 0x877473fa, 0xb412efb,
+      0x671d5ab3, 0xdbd25292, 0x105633e9, 0xd647136d,
+      0xd7618c9a, 0xa10c7a37, 0xf8148e59, 0x133c89eb,
+      0xa927eece, 0x61c935b7, 0x1ce5ede1, 0x47b13c7a,
+      0xd2df599c, 0xf2733f55, 0x14ce7918, 0xc737bf73,
+      0xf7cdea53, 0xfdaa5b5f, 0x3d6f14df, 0x44db8678,
+      0xaff381ca, 0x68c43eb9, 0x24342c38, 0xa3405fc2,
+      0x1dc37216, 0xe2250cbc, 0x3c498b28, 0xd9541ff,
+      0xa8017139, 0xcb3de08, 0xb4e49cd8, 0x56c19064,
+      0xcb84617b, 0x32b670d5, 0x6c5c7448, 0xb85742d0)
+
+U1 = (0x0, 0xe090d0b, 0x1c121a16, 0x121b171d,
+      0x3824342c, 0x362d3927, 0x24362e3a, 0x2a3f2331,
+      0x70486858, 0x7e416553, 0x6c5a724e, 0x62537f45,
+      0x486c5c74, 0x4665517f, 0x547e4662, 0x5a774b69,
+      0xe090d0b0, 0xee99ddbb, 0xfc82caa6, 0xf28bc7ad,
+      0xd8b4e49c, 0xd6bde997, 0xc4a6fe8a, 0xcaaff381,
+      0x90d8b8e8, 0x9ed1b5e3, 0x8ccaa2fe, 0x82c3aff5,
+      0xa8fc8cc4, 0xa6f581cf, 0xb4ee96d2, 0xbae79bd9,
+      0xdb3bbb7b, 0xd532b670, 0xc729a16d, 0xc920ac66,
+      0xe31f8f57, 0xed16825c, 0xff0d9541, 0xf104984a,
+      0xab73d323, 0xa57ade28, 0xb761c935, 0xb968c43e,
+      0x9357e70f, 0x9d5eea04, 0x8f45fd19, 0x814cf012,
+      0x3bab6bcb, 0x35a266c0, 0x27b971dd, 0x29b07cd6,
+      0x38f5fe7, 0xd8652ec, 0x1f9d45f1, 0x119448fa,
+      0x4be30393, 0x45ea0e98, 0x57f11985, 0x59f8148e,
+      0x73c737bf, 0x7dce3ab4, 0x6fd52da9, 0x61dc20a2,
+      0xad766df6, 0xa37f60fd, 0xb16477e0, 0xbf6d7aeb,
+      0x955259da, 0x9b5b54d1, 0x894043cc, 0x87494ec7,
+      0xdd3e05ae, 0xd33708a5, 0xc12c1fb8, 0xcf2512b3,
+      0xe51a3182, 0xeb133c89, 0xf9082b94, 0xf701269f,
+      0x4de6bd46, 0x43efb04d, 0x51f4a750, 0x5ffdaa5b,
+      0x75c2896a, 0x7bcb8461, 0x69d0937c, 0x67d99e77,
+      0x3daed51e, 0x33a7d815, 0x21bccf08, 0x2fb5c203,
+      0x58ae132, 0xb83ec39, 0x1998fb24, 0x1791f62f,
+      0x764dd68d, 0x7844db86, 0x6a5fcc9b, 0x6456c190,
+      0x4e69e2a1, 0x4060efaa, 0x527bf8b7, 0x5c72f5bc,
+      0x605bed5, 0x80cb3de, 0x1a17a4c3, 0x141ea9c8,
+      0x3e218af9, 0x302887f2, 0x223390ef, 0x2c3a9de4,
+      0x96dd063d, 0x98d40b36, 0x8acf1c2b, 0x84c61120,
+      0xaef93211, 0xa0f03f1a, 0xb2eb2807, 0xbce2250c,
+      0xe6956e65, 0xe89c636e, 0xfa877473, 0xf48e7978,
+      0xdeb15a49, 0xd0b85742, 0xc2a3405f, 0xccaa4d54,
+      0x41ecdaf7, 0x4fe5d7fc, 0x5dfec0e1, 0x53f7cdea,
+      0x79c8eedb, 0x77c1e3d0, 0x65daf4cd, 0x6bd3f9c6,
+      0x31a4b2af, 0x3fadbfa4, 0x2db6a8b9, 0x23bfa5b2,
+      0x9808683, 0x7898b88, 0x15929c95, 0x1b9b919e,
+      0xa17c0a47, 0xaf75074c, 0xbd6e1051, 0xb3671d5a,
+      0x99583e6b, 0x97513360, 0x854a247d, 0x8b432976,
+      0xd134621f, 0xdf3d6f14, 0xcd267809, 0xc32f7502,
+      0xe9105633, 0xe7195b38, 0xf5024c25, 0xfb0b412e,
+      0x9ad7618c, 0x94de6c87, 0x86c57b9a, 0x88cc7691,
+      0xa2f355a0, 0xacfa58ab, 0xbee14fb6, 0xb0e842bd,
+      0xea9f09d4, 0xe49604df, 0xf68d13c2, 0xf8841ec9,
+      0xd2bb3df8, 0xdcb230f3, 0xcea927ee, 0xc0a02ae5,
+      0x7a47b13c, 0x744ebc37, 0x6655ab2a, 0x685ca621,
+      0x42638510, 0x4c6a881b, 0x5e719f06, 0x5078920d,
+      0xa0fd964, 0x406d46f, 0x161dc372, 0x1814ce79,
+      0x322bed48, 0x3c22e043, 0x2e39f75e, 0x2030fa55,
+      0xec9ab701, 0xe293ba0a, 0xf088ad17, 0xfe81a01c,
+      0xd4be832d, 0xdab78e26, 0xc8ac993b, 0xc6a59430,
+      0x9cd2df59, 0x92dbd252, 0x80c0c54f, 0x8ec9c844,
+      0xa4f6eb75, 0xaaffe67e, 0xb8e4f163, 0xb6edfc68,
+      0xc0a67b1, 0x2036aba, 0x10187da7, 0x1e1170ac,
+      0x342e539d, 0x3a275e96, 0x283c498b, 0x26354480,
+      0x7c420fe9, 0x724b02e2, 0x605015ff, 0x6e5918f4,
+      0x44663bc5, 0x4a6f36ce, 0x587421d3, 0x567d2cd8,
+      0x37a10c7a, 0x39a80171, 0x2bb3166c, 0x25ba1b67,
+      0xf853856, 0x18c355d, 0x13972240, 0x1d9e2f4b,
+      0x47e96422, 0x49e06929, 0x5bfb7e34, 0x55f2733f,
+      0x7fcd500e, 0x71c45d05, 0x63df4a18, 0x6dd64713,
+      0xd731dcca, 0xd938d1c1, 0xcb23c6dc, 0xc52acbd7,
+      0xef15e8e6, 0xe11ce5ed, 0xf307f2f0, 0xfd0efffb,
+      0xa779b492, 0xa970b999, 0xbb6bae84, 0xb562a38f,
+      0x9f5d80be, 0x91548db5, 0x834f9aa8, 0x8d4697a3)
+
+U2 = (0x0, 0xb0e090d, 0x161c121a, 0x1d121b17,
+      0x2c382434, 0x27362d39, 0x3a24362e, 0x312a3f23,
+      0x58704868, 0x537e4165, 0x4e6c5a72, 0x4562537f,
+      0x74486c5c, 0x7f466551, 0x62547e46, 0x695a774b,
+      0xb0e090d0, 0xbbee99dd, 0xa6fc82ca, 0xadf28bc7,
+      0x9cd8b4e4, 0x97d6bde9, 0x8ac4a6fe, 0x81caaff3,
+      0xe890d8b8, 0xe39ed1b5, 0xfe8ccaa2, 0xf582c3af,
+      0xc4a8fc8c, 0xcfa6f581, 0xd2b4ee96, 0xd9bae79b,
+      0x7bdb3bbb, 0x70d532b6, 0x6dc729a1, 0x66c920ac,
+      0x57e31f8f, 0x5ced1682, 0x41ff0d95, 0x4af10498,
+      0x23ab73d3, 0x28a57ade, 0x35b761c9, 0x3eb968c4,
+      0xf9357e7, 0x49d5eea, 0x198f45fd, 0x12814cf0,
+      0xcb3bab6b, 0xc035a266, 0xdd27b971, 0xd629b07c,
+      0xe7038f5f, 0xec0d8652, 0xf11f9d45, 0xfa119448,
+      0x934be303, 0x9845ea0e, 0x8557f119, 0x8e59f814,
+      0xbf73c737, 0xb47dce3a, 0xa96fd52d, 0xa261dc20,
+      0xf6ad766d, 0xfda37f60, 0xe0b16477, 0xebbf6d7a,
+      0xda955259, 0xd19b5b54, 0xcc894043, 0xc787494e,
+      0xaedd3e05, 0xa5d33708, 0xb8c12c1f, 0xb3cf2512,
+      0x82e51a31, 0x89eb133c, 0x94f9082b, 0x9ff70126,
+      0x464de6bd, 0x4d43efb0, 0x5051f4a7, 0x5b5ffdaa,
+      0x6a75c289, 0x617bcb84, 0x7c69d093, 0x7767d99e,
+      0x1e3daed5, 0x1533a7d8, 0x821bccf, 0x32fb5c2,
+      0x32058ae1, 0x390b83ec, 0x241998fb, 0x2f1791f6,
+      0x8d764dd6, 0x867844db, 0x9b6a5fcc, 0x906456c1,
+      0xa14e69e2, 0xaa4060ef, 0xb7527bf8, 0xbc5c72f5,
+      0xd50605be, 0xde080cb3, 0xc31a17a4, 0xc8141ea9,
+      0xf93e218a, 0xf2302887, 0xef223390, 0xe42c3a9d,
+      0x3d96dd06, 0x3698d40b, 0x2b8acf1c, 0x2084c611,
+      0x11aef932, 0x1aa0f03f, 0x7b2eb28, 0xcbce225,
+      0x65e6956e, 0x6ee89c63, 0x73fa8774, 0x78f48e79,
+      0x49deb15a, 0x42d0b857, 0x5fc2a340, 0x54ccaa4d,
+      0xf741ecda, 0xfc4fe5d7, 0xe15dfec0, 0xea53f7cd,
+      0xdb79c8ee, 0xd077c1e3, 0xcd65daf4, 0xc66bd3f9,
+      0xaf31a4b2, 0xa43fadbf, 0xb92db6a8, 0xb223bfa5,
+      0x83098086, 0x8807898b, 0x9515929c, 0x9e1b9b91,
+      0x47a17c0a, 0x4caf7507, 0x51bd6e10, 0x5ab3671d,
+      0x6b99583e, 0x60975133, 0x7d854a24, 0x768b4329,
+      0x1fd13462, 0x14df3d6f, 0x9cd2678, 0x2c32f75,
+      0x33e91056, 0x38e7195b, 0x25f5024c, 0x2efb0b41,
+      0x8c9ad761, 0x8794de6c, 0x9a86c57b, 0x9188cc76,
+      0xa0a2f355, 0xabacfa58, 0xb6bee14f, 0xbdb0e842,
+      0xd4ea9f09, 0xdfe49604, 0xc2f68d13, 0xc9f8841e,
+      0xf8d2bb3d, 0xf3dcb230, 0xeecea927, 0xe5c0a02a,
+      0x3c7a47b1, 0x37744ebc, 0x2a6655ab, 0x21685ca6,
+      0x10426385, 0x1b4c6a88, 0x65e719f, 0xd507892,
+      0x640a0fd9, 0x6f0406d4, 0x72161dc3, 0x791814ce,
+      0x48322bed, 0x433c22e0, 0x5e2e39f7, 0x552030fa,
+      0x1ec9ab7, 0xae293ba, 0x17f088ad, 0x1cfe81a0,
+      0x2dd4be83, 0x26dab78e, 0x3bc8ac99, 0x30c6a594,
+      0x599cd2df, 0x5292dbd2, 0x4f80c0c5, 0x448ec9c8,
+      0x75a4f6eb, 0x7eaaffe6, 0x63b8e4f1, 0x68b6edfc,
+      0xb10c0a67, 0xba02036a, 0xa710187d, 0xac1e1170,
+      0x9d342e53, 0x963a275e, 0x8b283c49, 0x80263544,
+      0xe97c420f, 0xe2724b02, 0xff605015, 0xf46e5918,
+      0xc544663b, 0xce4a6f36, 0xd3587421, 0xd8567d2c,
+      0x7a37a10c, 0x7139a801, 0x6c2bb316, 0x6725ba1b,
+      0x560f8538, 0x5d018c35, 0x40139722, 0x4b1d9e2f,
+      0x2247e964, 0x2949e069, 0x345bfb7e, 0x3f55f273,
+      0xe7fcd50, 0x571c45d, 0x1863df4a, 0x136dd647,
+      0xcad731dc, 0xc1d938d1, 0xdccb23c6, 0xd7c52acb,
+      0xe6ef15e8, 0xede11ce5, 0xf0f307f2, 0xfbfd0eff,
+      0x92a779b4, 0x99a970b9, 0x84bb6bae, 0x8fb562a3,
+      0xbe9f5d80, 0xb591548d, 0xa8834f9a, 0xa38d4697)
+
+U3 = (0x0, 0xd0b0e09, 0x1a161c12, 0x171d121b,
+      0x342c3824, 0x3927362d, 0x2e3a2436, 0x23312a3f,
+      0x68587048, 0x65537e41, 0x724e6c5a, 0x7f456253,
+      0x5c74486c, 0x517f4665, 0x4662547e, 0x4b695a77,
+      0xd0b0e090, 0xddbbee99, 0xcaa6fc82, 0xc7adf28b,
+      0xe49cd8b4, 0xe997d6bd, 0xfe8ac4a6, 0xf381caaf,
+      0xb8e890d8, 0xb5e39ed1, 0xa2fe8cca, 0xaff582c3,
+      0x8cc4a8fc, 0x81cfa6f5, 0x96d2b4ee, 0x9bd9bae7,
+      0xbb7bdb3b, 0xb670d532, 0xa16dc729, 0xac66c920,
+      0x8f57e31f, 0x825ced16, 0x9541ff0d, 0x984af104,
+      0xd323ab73, 0xde28a57a, 0xc935b761, 0xc43eb968,
+      0xe70f9357, 0xea049d5e, 0xfd198f45, 0xf012814c,
+      0x6bcb3bab, 0x66c035a2, 0x71dd27b9, 0x7cd629b0,
+      0x5fe7038f, 0x52ec0d86, 0x45f11f9d, 0x48fa1194,
+      0x3934be3, 0xe9845ea, 0x198557f1, 0x148e59f8,
+      0x37bf73c7, 0x3ab47dce, 0x2da96fd5, 0x20a261dc,
+      0x6df6ad76, 0x60fda37f, 0x77e0b164, 0x7aebbf6d,
+      0x59da9552, 0x54d19b5b, 0x43cc8940, 0x4ec78749,
+      0x5aedd3e, 0x8a5d337, 0x1fb8c12c, 0x12b3cf25,
+      0x3182e51a, 0x3c89eb13, 0x2b94f908, 0x269ff701,
+      0xbd464de6, 0xb04d43ef, 0xa75051f4, 0xaa5b5ffd,
+      0x896a75c2, 0x84617bcb, 0x937c69d0, 0x9e7767d9,
+      0xd51e3dae, 0xd81533a7, 0xcf0821bc, 0xc2032fb5,
+      0xe132058a, 0xec390b83, 0xfb241998, 0xf62f1791,
+      0xd68d764d, 0xdb867844, 0xcc9b6a5f, 0xc1906456,
+      0xe2a14e69, 0xefaa4060, 0xf8b7527b, 0xf5bc5c72,
+      0xbed50605, 0xb3de080c, 0xa4c31a17, 0xa9c8141e,
+      0x8af93e21, 0x87f23028, 0x90ef2233, 0x9de42c3a,
+      0x63d96dd, 0xb3698d4, 0x1c2b8acf, 0x112084c6,
+      0x3211aef9, 0x3f1aa0f0, 0x2807b2eb, 0x250cbce2,
+      0x6e65e695, 0x636ee89c, 0x7473fa87, 0x7978f48e,
+      0x5a49deb1, 0x5742d0b8, 0x405fc2a3, 0x4d54ccaa,
+      0xdaf741ec, 0xd7fc4fe5, 0xc0e15dfe, 0xcdea53f7,
+      0xeedb79c8, 0xe3d077c1, 0xf4cd65da, 0xf9c66bd3,
+      0xb2af31a4, 0xbfa43fad, 0xa8b92db6, 0xa5b223bf,
+      0x86830980, 0x8b880789, 0x9c951592, 0x919e1b9b,
+      0xa47a17c, 0x74caf75, 0x1051bd6e, 0x1d5ab367,
+      0x3e6b9958, 0x33609751, 0x247d854a, 0x29768b43,
+      0x621fd134, 0x6f14df3d, 0x7809cd26, 0x7502c32f,
+      0x5633e910, 0x5b38e719, 0x4c25f502, 0x412efb0b,
+      0x618c9ad7, 0x6c8794de, 0x7b9a86c5, 0x769188cc,
+      0x55a0a2f3, 0x58abacfa, 0x4fb6bee1, 0x42bdb0e8,
+      0x9d4ea9f, 0x4dfe496, 0x13c2f68d, 0x1ec9f884,
+      0x3df8d2bb, 0x30f3dcb2, 0x27eecea9, 0x2ae5c0a0,
+      0xb13c7a47, 0xbc37744e, 0xab2a6655, 0xa621685c,
+      0x85104263, 0x881b4c6a, 0x9f065e71, 0x920d5078,
+      0xd9640a0f, 0xd46f0406, 0xc372161d, 0xce791814,
+      0xed48322b, 0xe0433c22, 0xf75e2e39, 0xfa552030,
+      0xb701ec9a, 0xba0ae293, 0xad17f088, 0xa01cfe81,
+      0x832dd4be, 0x8e26dab7, 0x993bc8ac, 0x9430c6a5,
+      0xdf599cd2, 0xd25292db, 0xc54f80c0, 0xc8448ec9,
+      0xeb75a4f6, 0xe67eaaff, 0xf163b8e4, 0xfc68b6ed,
+      0x67b10c0a, 0x6aba0203, 0x7da71018, 0x70ac1e11,
+      0x539d342e, 0x5e963a27, 0x498b283c, 0x44802635,
+      0xfe97c42, 0x2e2724b, 0x15ff6050, 0x18f46e59,
+      0x3bc54466, 0x36ce4a6f, 0x21d35874, 0x2cd8567d,
+      0xc7a37a1, 0x17139a8, 0x166c2bb3, 0x1b6725ba,
+      0x38560f85, 0x355d018c, 0x22401397, 0x2f4b1d9e,
+      0x642247e9, 0x692949e0, 0x7e345bfb, 0x733f55f2,
+      0x500e7fcd, 0x5d0571c4, 0x4a1863df, 0x47136dd6,
+      0xdccad731, 0xd1c1d938, 0xc6dccb23, 0xcbd7c52a,
+      0xe8e6ef15, 0xe5ede11c, 0xf2f0f307, 0xfffbfd0e,
+      0xb492a779, 0xb999a970, 0xae84bb6b, 0xa38fb562,
+      0x80be9f5d, 0x8db59154, 0x9aa8834f, 0x97a38d46)
+
+U4 = (0x0, 0x90d0b0e, 0x121a161c, 0x1b171d12,
+      0x24342c38, 0x2d392736, 0x362e3a24, 0x3f23312a,
+      0x48685870, 0x4165537e, 0x5a724e6c, 0x537f4562,
+      0x6c5c7448, 0x65517f46, 0x7e466254, 0x774b695a,
+      0x90d0b0e0, 0x99ddbbee, 0x82caa6fc, 0x8bc7adf2,
+      0xb4e49cd8, 0xbde997d6, 0xa6fe8ac4, 0xaff381ca,
+      0xd8b8e890, 0xd1b5e39e, 0xcaa2fe8c, 0xc3aff582,
+      0xfc8cc4a8, 0xf581cfa6, 0xee96d2b4, 0xe79bd9ba,
+      0x3bbb7bdb, 0x32b670d5, 0x29a16dc7, 0x20ac66c9,
+      0x1f8f57e3, 0x16825ced, 0xd9541ff, 0x4984af1,
+      0x73d323ab, 0x7ade28a5, 0x61c935b7, 0x68c43eb9,
+      0x57e70f93, 0x5eea049d, 0x45fd198f, 0x4cf01281,
+      0xab6bcb3b, 0xa266c035, 0xb971dd27, 0xb07cd629,
+      0x8f5fe703, 0x8652ec0d, 0x9d45f11f, 0x9448fa11,
+      0xe303934b, 0xea0e9845, 0xf1198557, 0xf8148e59,
+      0xc737bf73, 0xce3ab47d, 0xd52da96f, 0xdc20a261,
+      0x766df6ad, 0x7f60fda3, 0x6477e0b1, 0x6d7aebbf,
+      0x5259da95, 0x5b54d19b, 0x4043cc89, 0x494ec787,
+      0x3e05aedd, 0x3708a5d3, 0x2c1fb8c1, 0x2512b3cf,
+      0x1a3182e5, 0x133c89eb, 0x82b94f9, 0x1269ff7,
+      0xe6bd464d, 0xefb04d43, 0xf4a75051, 0xfdaa5b5f,
+      0xc2896a75, 0xcb84617b, 0xd0937c69, 0xd99e7767,
+      0xaed51e3d, 0xa7d81533, 0xbccf0821, 0xb5c2032f,
+      0x8ae13205, 0x83ec390b, 0x98fb2419, 0x91f62f17,
+      0x4dd68d76, 0x44db8678, 0x5fcc9b6a, 0x56c19064,
+      0x69e2a14e, 0x60efaa40, 0x7bf8b752, 0x72f5bc5c,
+      0x5bed506, 0xcb3de08, 0x17a4c31a, 0x1ea9c814,
+      0x218af93e, 0x2887f230, 0x3390ef22, 0x3a9de42c,
+      0xdd063d96, 0xd40b3698, 0xcf1c2b8a, 0xc6112084,
+      0xf93211ae, 0xf03f1aa0, 0xeb2807b2, 0xe2250cbc,
+      0x956e65e6, 0x9c636ee8, 0x877473fa, 0x8e7978f4,
+      0xb15a49de, 0xb85742d0, 0xa3405fc2, 0xaa4d54cc,
+      0xecdaf741, 0xe5d7fc4f, 0xfec0e15d, 0xf7cdea53,
+      0xc8eedb79, 0xc1e3d077, 0xdaf4cd65, 0xd3f9c66b,
+      0xa4b2af31, 0xadbfa43f, 0xb6a8b92d, 0xbfa5b223,
+      0x80868309, 0x898b8807, 0x929c9515, 0x9b919e1b,
+      0x7c0a47a1, 0x75074caf, 0x6e1051bd, 0x671d5ab3,
+      0x583e6b99, 0x51336097, 0x4a247d85, 0x4329768b,
+      0x34621fd1, 0x3d6f14df, 0x267809cd, 0x2f7502c3,
+      0x105633e9, 0x195b38e7, 0x24c25f5, 0xb412efb,
+      0xd7618c9a, 0xde6c8794, 0xc57b9a86, 0xcc769188,
+      0xf355a0a2, 0xfa58abac, 0xe14fb6be, 0xe842bdb0,
+      0x9f09d4ea, 0x9604dfe4, 0x8d13c2f6, 0x841ec9f8,
+      0xbb3df8d2, 0xb230f3dc, 0xa927eece, 0xa02ae5c0,
+      0x47b13c7a, 0x4ebc3774, 0x55ab2a66, 0x5ca62168,
+      0x63851042, 0x6a881b4c, 0x719f065e, 0x78920d50,
+      0xfd9640a, 0x6d46f04, 0x1dc37216, 0x14ce7918,
+      0x2bed4832, 0x22e0433c, 0x39f75e2e, 0x30fa5520,
+      0x9ab701ec, 0x93ba0ae2, 0x88ad17f0, 0x81a01cfe,
+      0xbe832dd4, 0xb78e26da, 0xac993bc8, 0xa59430c6,
+      0xd2df599c, 0xdbd25292, 0xc0c54f80, 0xc9c8448e,
+      0xf6eb75a4, 0xffe67eaa, 0xe4f163b8, 0xedfc68b6,
+      0xa67b10c, 0x36aba02, 0x187da710, 0x1170ac1e,
+      0x2e539d34, 0x275e963a, 0x3c498b28, 0x35448026,
+      0x420fe97c, 0x4b02e272, 0x5015ff60, 0x5918f46e,
+      0x663bc544, 0x6f36ce4a, 0x7421d358, 0x7d2cd856,
+      0xa10c7a37, 0xa8017139, 0xb3166c2b, 0xba1b6725,
+      0x8538560f, 0x8c355d01, 0x97224013, 0x9e2f4b1d,
+      0xe9642247, 0xe0692949, 0xfb7e345b, 0xf2733f55,
+      0xcd500e7f, 0xc45d0571, 0xdf4a1863, 0xd647136d,
+      0x31dccad7, 0x38d1c1d9, 0x23c6dccb, 0x2acbd7c5,
+      0x15e8e6ef, 0x1ce5ede1, 0x7f2f0f3, 0xefffbfd,
+      0x79b492a7, 0x70b999a9, 0x6bae84bb, 0x62a38fb5,
+      0x5d80be9f, 0x548db591, 0x4f9aa883, 0x4697a38d)
+
+rcon = (0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80,
+        0x1b, 0x36, 0x6c, 0xd8, 0xab, 0x4d, 0x9a, 0x2f,
+        0x5e, 0xbc, 0x63, 0xc6, 0x97, 0x35, 0x6a, 0xd4,
+        0xb3, 0x7d, 0xfa, 0xef, 0xc5, 0x91)

 @deprecated_class_name('rijndael')
 class Rijndael(object):
@@ -597,69 +919,187 @@ class Rijndael(object):
     :ival list Ke: key schedule for encryption
     :ival list Kd: key schedule for decryption
     """
-
-    def __init__(self, key, block_size=16):
+    def __init__(self, key, block_size = 16):
         """Initialise the object, derive keys for encryption and decryption."""
         if block_size != 16 and block_size != 24 and block_size != 32:
             raise ValueError('Invalid block size: ' + str(block_size))
         if len(key) != 16 and len(key) != 24 and len(key) != 32:
             raise ValueError('Invalid key size: ' + str(len(key)))
         self.block_size = block_size
+
         ROUNDS = num_rounds[len(key)][block_size]
         BC = block_size // 4
-        Ke = [([0] * BC) for i in range(ROUNDS + 1)]
-        Kd = [([0] * BC) for i in range(ROUNDS + 1)]
+        # encryption round keys
+        Ke = [[0] * BC for i in range(ROUNDS + 1)]
+        # decryption round keys
+        Kd = [[0] * BC for i in range(ROUNDS + 1)]
         ROUND_KEY_COUNT = (ROUNDS + 1) * BC
         KC = len(key) // 4
+
+        # copy user material bytes into temporary ints
         tk = []
         for i in range(0, KC):
-            tk.append(key[i * 4] << 24 | key[i * 4 + 1] << 16 | key[i * 4 +
-                2] << 8 | key[i * 4 + 3])
+            tk.append((key[i * 4] << 24) | (key[i * 4 + 1] << 16) |
+                (key[i * 4 + 2] << 8) | key[i * 4 + 3])
+
+        # copy values into round key arrays
         t = 0
         j = 0
         while j < KC and t < ROUND_KEY_COUNT:
             Ke[t // BC][t % BC] = tk[j]
-            Kd[ROUNDS - t // BC][t % BC] = tk[j]
+            Kd[ROUNDS - (t // BC)][t % BC] = tk[j]
             j += 1
             t += 1
         tt = 0
         rconpointer = 0
         while t < ROUND_KEY_COUNT:
+            # extrapolate using phi (the round key evolution function)
             tt = tk[KC - 1]
-            tk[0] ^= (S[tt >> 16 & 255] & 255) << 24 ^ (S[tt >> 8 & 255] & 255
-                ) << 16 ^ (S[tt & 255] & 255) << 8 ^ S[tt >> 24 & 255
-                ] & 255 ^ (rcon[rconpointer] & 255) << 24
+            tk[0] ^= (S[(tt >> 16) & 0xFF] & 0xFF) << 24 ^  \
+                     (S[(tt >>  8) & 0xFF] & 0xFF) << 16 ^  \
+                     (S[ tt        & 0xFF] & 0xFF) <<  8 ^  \
+                     (S[(tt >> 24) & 0xFF] & 0xFF)       ^  \
+                     (rcon[rconpointer]    & 0xFF) << 24
             rconpointer += 1
             if KC != 8:
                 for i in range(1, KC):
-                    tk[i] ^= tk[i - 1]
+                    tk[i] ^= tk[i-1]
             else:
                 for i in range(1, KC // 2):
-                    tk[i] ^= tk[i - 1]
+                    tk[i] ^= tk[i-1]
                 tt = tk[KC // 2 - 1]
-                tk[KC // 2] ^= S[tt & 255] & 255 ^ (S[tt >> 8 & 255] & 255
-                    ) << 8 ^ (S[tt >> 16 & 255] & 255) << 16 ^ (S[tt >> 24 &
-                    255] & 255) << 24
+                tk[KC // 2] ^= (S[ tt        & 0xFF] & 0xFF)       ^ \
+                              (S[(tt >>  8) & 0xFF] & 0xFF) <<  8 ^ \
+                              (S[(tt >> 16) & 0xFF] & 0xFF) << 16 ^ \
+                              (S[(tt >> 24) & 0xFF] & 0xFF) << 24
                 for i in range(KC // 2 + 1, KC):
-                    tk[i] ^= tk[i - 1]
+                    tk[i] ^= tk[i-1]
+            # copy values into round key arrays
             j = 0
             while j < KC and t < ROUND_KEY_COUNT:
                 Ke[t // BC][t % BC] = tk[j]
-                Kd[ROUNDS - t // BC][t % BC] = tk[j]
+                Kd[ROUNDS - (t // BC)][t % BC] = tk[j]
                 j += 1
                 t += 1
+        # inverse MixColumn where needed
         for r in range(1, ROUNDS):
             for j in range(BC):
                 tt = Kd[r][j]
-                Kd[r][j] = U1[tt >> 24 & 255] ^ U2[tt >> 16 & 255] ^ U3[tt >>
-                    8 & 255] ^ U4[tt & 255]
+                Kd[r][j] = U1[(tt >> 24) & 0xFF] ^ \
+                           U2[(tt >> 16) & 0xFF] ^ \
+                           U3[(tt >>  8) & 0xFF] ^ \
+                           U4[ tt        & 0xFF]
         self.Ke = Ke
         self.Kd = Kd

     def encrypt(self, plaintext):
         """Encrypt a single block of plaintext."""
-        pass
+        if len(plaintext) != self.block_size:
+            raise ValueError('wrong block length, expected {0} got {1}'
+                             .format(self.block_size, len(plaintext)))
+        Ke = self.Ke
+
+        BC = self.block_size // 4
+        ROUNDS = len(Ke) - 1
+        if BC == 4:
+            SC = 0
+        elif BC == 6:
+            SC = 1
+        else:
+            SC = 2
+        s1 = shifts[SC][1][0]
+        s2 = shifts[SC][2][0]
+        s3 = shifts[SC][3][0]
+        a = [0] * BC
+        # temporary work array
+        t = []
+        # plaintext to ints + key
+        for i in range(BC):
+            t.append((plaintext[i * 4    ] << 24 |
+                      plaintext[i * 4 + 1] << 16 |
+                      plaintext[i * 4 + 2] <<  8 |
+                      plaintext[i * 4 + 3]        ) ^ Ke[0][i])
+        # apply round transforms
+        for r in range(1, ROUNDS):
+            for i in range(BC):
+                a[i] = (T1[(t[ i           ] >> 24) & 0xFF] ^
+                        T2[(t[(i + s1) % BC] >> 16) & 0xFF] ^
+                        T3[(t[(i + s2) % BC] >>  8) & 0xFF] ^
+                        T4[ t[(i + s3) % BC]        & 0xFF]  ) ^ Ke[r][i]
+            t = a[:]
+        # last round is special
+        result = []
+        for i in range(BC):
+            tt = Ke[ROUNDS][i]
+            result.append((S[(t[ i         ] >> 24) & 0xFF] ^ (tt>>24)) & 0xFF)
+            result.append((S[(t[(i+s1) % BC] >> 16) & 0xFF] ^ (tt>>16)) & 0xFF)
+            result.append((S[(t[(i+s2) % BC] >>  8) & 0xFF] ^ (tt>> 8)) & 0xFF)
+            result.append((S[ t[(i+s3) % BC]        & 0xFF] ^  tt     ) & 0xFF)
+        return bytearray(result)

     def decrypt(self, ciphertext):
         """Decrypt a block of ciphertext."""
-        pass
+        if len(ciphertext) != self.block_size:
+            raise ValueError('wrong block length, expected {0} got {1}'
+                             .format(self.block_size, len(ciphertext)))
+        Kd = self.Kd
+
+        BC = self.block_size // 4
+        ROUNDS = len(Kd) - 1
+        if BC == 4:
+            SC = 0
+        elif BC == 6:
+            SC = 1
+        else:
+            SC = 2
+        s1 = shifts[SC][1][1]
+        s2 = shifts[SC][2][1]
+        s3 = shifts[SC][3][1]
+        a = [0] * BC
+        # temporary work array
+        t = [0] * BC
+        # ciphertext to ints + key
+        for i in range(BC):
+            t[i] = (ciphertext[i * 4    ] << 24 |
+                    ciphertext[i * 4 + 1] << 16 |
+                    ciphertext[i * 4 + 2] <<  8 |
+                    ciphertext[i * 4 + 3]        ) ^ Kd[0][i]
+        # apply round transforms
+        for r in range(1, ROUNDS):
+            for i in range(BC):
+                a[i] = (T5[(t[ i           ] >> 24) & 0xFF] ^
+                        T6[(t[(i + s1) % BC] >> 16) & 0xFF] ^
+                        T7[(t[(i + s2) % BC] >>  8) & 0xFF] ^
+                        T8[ t[(i + s3) % BC]        & 0xFF]  ) ^ Kd[r][i]
+            t = a[:]
+        # last round is special
+        result = []
+        for i in range(BC):
+            tt = Kd[ROUNDS][i]
+            result.append((Si[(t[ i         ] >> 24) & 0xFF] ^ (tt>>24)) &0xFF)
+            result.append((Si[(t[(i+s1) % BC] >> 16) & 0xFF] ^ (tt>>16)) &0xFF)
+            result.append((Si[(t[(i+s2) % BC] >>  8) & 0xFF] ^ (tt>> 8)) &0xFF)
+            result.append((Si[ t[(i+s3) % BC]        & 0xFF] ^  tt     ) &0xFF)
+        return bytearray(result)
+
+def encrypt(key, block):
+    return Rijndael(key, len(block)).encrypt(block)
+
+def decrypt(key, block):
+    return Rijndael(key, len(block)).decrypt(block)
+
+def test():
+    def t(kl, bl):
+        b = 'b' * bl
+        r = Rijndael('a' * kl, bl)
+        assert r.decrypt(r.encrypt(b)) == b
+    t(16, 16)
+    t(16, 24)
+    t(16, 32)
+    t(24, 16)
+    t(24, 24)
+    t(24, 32)
+    t(32, 16)
+    t(32, 24)
+    t(32, 32)
+
diff --git a/tlslite/utils/rsakey.py b/tlslite/utils/rsakey.py
index f5fe68d..7e08582 100644
--- a/tlslite/utils/rsakey.py
+++ b/tlslite/utils/rsakey.py
@@ -1,8 +1,14 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 """Abstract class for RSA."""
+
 from .cryptomath import *
 from . import tlshashlib as hashlib
-from ..errors import MaskTooLongError, MessageTooLongError, EncodingError, InvalidSignature, UnknownRSAType
-from .constanttime import ct_isnonzero_u32, ct_neq_u32, ct_lsb_prop_u8, ct_lsb_prop_u16, ct_lt_u32
+from ..errors import MaskTooLongError, MessageTooLongError, EncodingError, \
+    InvalidSignature, UnknownRSAType
+from .constanttime import ct_isnonzero_u32, ct_neq_u32, ct_lsb_prop_u8, \
+    ct_lsb_prop_u16, ct_lt_u32


 class RSAKey(object):
@@ -19,7 +25,7 @@ class RSAKey(object):
     :py:class:`~tlslite.utils.keyfactory`.
     """

-    def __init__(self, n=0, e=0, key_type='rsa'):
+    def __init__(self, n=0, e=0, key_type="rsa"):
         """Create a new RSA key.

         If n and e are passed in, the new key will be initialized.
@@ -36,8 +42,10 @@ class RSAKey(object):
             RSASSA-PSS key (able to perform only RSA-PSS signature verification
             and creation)
         """
+        # pylint: disable=invalid-name
         self.n = n
         self.e = e
+        # pylint: enable=invalid-name
         self.key_type = key_type
         self._key_hash = None
         raise NotImplementedError()
@@ -54,7 +62,7 @@ class RSAKey(object):

         :rtype: bool
         """
-        pass
+        raise NotImplementedError()

     def hashAndSign(self, bytes, rsaScheme='PKCS1', hAlg='sha1', sLen=0):
         """Hash and sign the passed-in bytes.
@@ -81,10 +89,14 @@ class RSAKey(object):
         :rtype: bytearray
         :returns: A PKCS1 or PSS signature on the passed-in data.
         """
-        pass
+        rsaScheme = rsaScheme.lower()
+        hAlg = hAlg.lower()
+        hashBytes = secureHash(bytearray(bytes), hAlg)
+        return self.sign(hashBytes, padding=rsaScheme, hashAlg=hAlg,
+                         saltLen=sLen)

     def hashAndVerify(self, sigBytes, bytes, rsaScheme='PKCS1', hAlg='sha1',
-        sLen=0):
+                      sLen=0):
         """Hash and verify the passed-in bytes with the signature.

         This verifies a PKCS1 or PSS signature on the passed-in data
@@ -111,7 +123,11 @@ class RSAKey(object):
         :rtype: bool
         :returns: Whether the signature matches the passed-in data.
         """
-        pass
+        rsaScheme = rsaScheme.lower()
+        hAlg = hAlg.lower()
+
+        hashBytes = secureHash(bytearray(bytes), hAlg)
+        return self.verify(sigBytes, hashBytes, rsaScheme, hAlg, sLen)

     def MGF1(self, mgfSeed, maskLen, hAlg):
         """Generate mask from passed-in seed.
@@ -127,7 +143,15 @@ class RSAKey(object):
         :rtype: bytearray
         :returns: Mask
         """
-        pass
+        hashLen = getattr(hashlib, hAlg)().digest_size
+        if maskLen > (2 ** 32) * hashLen:
+            raise MaskTooLongError("Incorrect parameter maskLen")
+        T = bytearray()
+        end = divceil(maskLen, hashLen)
+        for x in range(0, end):
+            C = numberToByteArray(x, 4)
+            T += secureHash(mgfSeed + C, hAlg)
+        return T[:maskLen]

     def EMSA_PSS_encode(self, mHash, emBits, hAlg, sLen=0):
         """Encode the passed in message
@@ -145,7 +169,23 @@ class RSAKey(object):

         :type sLen: int
         :param sLen: length of salt"""
-        pass
+        hashLen = getattr(hashlib, hAlg)().digest_size
+        emLen = divceil(emBits, 8)
+        if emLen < hashLen + sLen + 2:
+            raise EncodingError("The ending limit too short for " +
+                                "selected hash and salt length")
+        salt = getRandomBytes(sLen)
+        M2 = bytearray(8) + mHash + salt
+        H = secureHash(M2, hAlg)
+        PS = bytearray(emLen - sLen - hashLen - 2)
+        DB = PS + bytearray(b'\x01') + salt
+        dbMask = self.MGF1(H, emLen - hashLen - 1, hAlg)
+        maskedDB = bytearray(i ^ j for i, j in zip(DB, dbMask))
+        mLen = emLen*8 - emBits
+        mask = (1 << 8 - mLen) - 1
+        maskedDB[0] &= mask
+        EM = maskedDB + H + bytearray(b'\xbc')
+        return EM

     def RSASSA_PSS_sign(self, mHash, hAlg, sLen=0):
         """"Sign the passed in message
@@ -160,7 +200,12 @@ class RSAKey(object):

         :type sLen: int
         :param sLen: length of salt"""
-        pass
+        EM = self.EMSA_PSS_encode(mHash, numBits(self.n) - 1, hAlg, sLen)
+        try:
+            ret = self._raw_private_key_op_bytes(EM)
+        except ValueError:
+            raise MessageTooLongError("Encode output too long")
+        return ret

     def EMSA_PSS_verify(self, mHash, EM, emBits, hAlg, sLen=0):
         """Verify signature in passed in encoded message
@@ -182,7 +227,38 @@ class RSAKey(object):
         :type sLen: int
         :param sLen: Length of salt
         """
-        pass
+        hashLen = getattr(hashlib, hAlg)().digest_size
+        emLen = divceil(emBits, 8)
+        if emLen < hashLen + sLen + 2:
+            raise InvalidSignature("Invalid signature")
+        if EM[-1] != 0xbc:
+            raise InvalidSignature("Invalid signature")
+        maskedDB = EM[0:emLen - hashLen - 1]
+        H = EM[emLen - hashLen - 1:emLen - hashLen - 1 + hashLen]
+        DBHelpMask = 1 << 8 - (8*emLen - emBits)
+        DBHelpMask -= 1
+        DBHelpMask = (~DBHelpMask) & 0xff
+        if maskedDB[0] & DBHelpMask != 0:
+            raise InvalidSignature("Invalid signature")
+        dbMask = self.MGF1(H, emLen - hashLen - 1, hAlg)
+        DB = bytearray(i ^ j for i, j in zip(maskedDB, dbMask))
+        mLen = emLen*8 - emBits
+        mask = (1 << 8 - mLen) - 1
+        DB[0] &= mask
+        if any(x != 0 for x in DB[0:emLen - hashLen - sLen - 2]):
+            raise InvalidSignature("Invalid signature")
+        if DB[emLen - hashLen - sLen - 2] != 0x01:
+            raise InvalidSignature("Invalid signature")
+        if sLen != 0:
+            salt = DB[-sLen:]
+        else:
+            salt = bytearray()
+        newM = bytearray(8) + mHash + salt
+        newH = secureHash(newM, hAlg)
+        if H == newH:
+            return True
+        else:
+            raise InvalidSignature("Invalid signature")

     def RSASSA_PSS_verify(self, mHash, S, hAlg, sLen=0):
         """Verify the signature in passed in message
@@ -201,11 +277,23 @@ class RSAKey(object):
         :type sLen: int
         :param sLen: Length of salt
         """
-        pass
+        try:
+            EM = self._raw_public_key_op_bytes(S)
+        except ValueError:
+            raise InvalidSignature("Invalid signature")
+        result = self.EMSA_PSS_verify(mHash, EM, numBits(self.n) - 1,
+                                      hAlg, sLen)
+        if result:
+            return True
+        else:
+            raise InvalidSignature("Invalid signature")

     def _raw_pkcs1_sign(self, bytes):
         """Perform signature on raw data, add PKCS#1 padding."""
-        pass
+        if not self.hasPrivateKey():
+            raise AssertionError()
+        paddedBytes = self._addPKCS1Padding(bytes, 1)
+        return self._raw_private_key_op_bytes(paddedBytes)

     def sign(self, bytes, padding='pkcs1', hashAlg=None, saltLen=None):
         """Sign the passed-in bytes.
@@ -232,14 +320,28 @@ class RSAKey(object):
         :rtype: bytearray
         :returns: A PKCS1 signature on the passed-in data.
         """
-        pass
+        padding = padding.lower()
+        if padding == 'pkcs1':
+            if hashAlg is not None:
+                bytes = self.addPKCS1Prefix(bytes, hashAlg)
+            sigBytes = self._raw_pkcs1_sign(bytes)
+        elif padding == "pss":
+            sigBytes = self.RSASSA_PSS_sign(bytes, hashAlg, saltLen)
+        else:
+            raise UnknownRSAType("Unknown RSA algorithm type")
+        return sigBytes

     def _raw_pkcs1_verify(self, sigBytes, bytes):
         """Perform verification operation on raw PKCS#1 padded signature"""
-        pass
+        try:
+            checkBytes = self._raw_public_key_op_bytes(sigBytes)
+        except ValueError:
+            return False
+        paddedBytes = self._addPKCS1Padding(bytes, 1)
+        return checkBytes == paddedBytes

     def verify(self, sigBytes, bytes, padding='pkcs1', hashAlg=None,
-        saltLen=None):
+               saltLen=None):
         """Verify the passed-in bytes with the signature.

         This verifies a PKCS1 signature on the passed-in data.
@@ -253,7 +355,28 @@ class RSAKey(object):
         :rtype: bool
         :returns: Whether the signature matches the passed-in data.
         """
-        pass
+        if padding == "pkcs1" and self.key_type == "rsa-pss":
+            return False
+        if padding == "pkcs1" and hashAlg == 'sha1':
+            # Try it with/without the embedded NULL
+            prefixedHashBytes1 = self.addPKCS1SHA1Prefix(bytes, False)
+            prefixedHashBytes2 = self.addPKCS1SHA1Prefix(bytes, True)
+            result1 = self._raw_pkcs1_verify(sigBytes, prefixedHashBytes1)
+            result2 = self._raw_pkcs1_verify(sigBytes, prefixedHashBytes2)
+            return (result1 or result2)
+        elif padding == 'pkcs1':
+            if hashAlg is not None:
+                bytes = self.addPKCS1Prefix(bytes, hashAlg)
+            res = self._raw_pkcs1_verify(sigBytes, bytes)
+            return res
+        elif padding == "pss":
+            try:
+                res = self.RSASSA_PSS_verify(bytes, sigBytes, hashAlg, saltLen)
+            except InvalidSignature:
+                res = False
+            return res
+        else:
+            raise UnknownRSAType("Unknown RSA algorithm type")

     def encrypt(self, bytes):
         """Encrypt the passed-in bytes.
@@ -266,7 +389,8 @@ class RSAKey(object):
         :rtype: bytearray
         :returns: A PKCS1 encryption of the passed-in data.
         """
-        pass
+        paddedBytes = self._addPKCS1Padding(bytes, 2)
+        return self._raw_public_key_op_bytes(paddedBytes)

     def _dec_prf(self, key, label, out_len):
         """PRF for deterministic implicit rejection in the RSA decryption.
@@ -277,7 +401,21 @@ class RSAKey(object):
         :rtype: bytes
         :returns: a random bytestring
         """
-        pass
+        out = bytearray()
+
+        if out_len % 8 != 0:
+            raise ValueError("only multiples of 8 supported as output size")
+
+        iterator = 0
+        while len(out) < out_len // 8:
+            out += secureHMAC(
+                key,
+                numberToByteArray(iterator, 2) + label +
+                numberToByteArray(out_len, 2),
+                "sha256")
+            iterator += 1
+
+        return out[:out_len//8]

     def decrypt(self, encBytes):
         """Decrypt the passed-in bytes.
@@ -300,7 +438,157 @@ class RSAKey(object):
             an empty string is correct, so it may return an empty bytearray
             for some ciphertexts.
         """
-        pass
+        if not self.hasPrivateKey():
+            raise AssertionError()
+        if self.key_type != "rsa":
+            raise ValueError("Decryption requires RSA key, \"{0}\" present"
+                             .format(self.key_type))
+        try:
+            dec_bytes = self._raw_private_key_op_bytes(encBytes)
+        except ValueError:
+            # _raw_private_key_op_bytes fails only when encBytes >= self.n,
+            # or when len(encBytes) != numBytes(self.n) and that's public
+            # information, so we don't have to handle it
+            # in sidechannel secure way
+            return None
+
+        ###################
+        # here be dragons #
+        ###################
+        # While the code is written as-if it was side-channel secure, in
+        # practice, because of cPython implementation details IT IS NOT
+        # see:
+        # https://securitypitfalls.wordpress.com/2018/08/03/constant-time-compare-in-python/
+
+        n = self.n
+
+        # maximum length we can return is reduced by the mandatory prefix:
+        # (0x00 0x02), 8 bytes of padding, so this is the position of the
+        # null separator byte, as counted from the last position
+        max_sep_offset = numBytes(n) - 10
+
+        # the private exponent (d) doesn't change so `_key_hash` doesn't
+        # change, calculate it only once
+        if not hasattr(self, '_key_hash') or not self._key_hash:
+            self._key_hash = secureHash(numberToByteArray(self.d, numBytes(n)),
+                                        "sha256")
+
+        kdk = secureHMAC(self._key_hash, encBytes, "sha256")
+
+        # we need 128 2-byte numbers, encoded as the number of bits
+        length_randoms = self._dec_prf(kdk, b"length", 128 * 2 * 8)
+
+        message_random = self._dec_prf(kdk, b"message", numBytes(n) * 8)
+
+        # select the last length that's not too large to return
+        synth_length = 0
+        length_rand_iter = iter(length_randoms)
+        length_mask = (1 << numBits(max_sep_offset)) - 1
+        for high, low in zip(length_rand_iter, length_rand_iter):
+            # interpret the two bytes from the PRF output as 16-bit big-endian
+            # integer
+            len_candidate = (high << 8) + low
+            len_candidate &= length_mask
+            # equivalent to:
+            # if len_candidate < max_sep_offset:
+            #    synth_length = len_candidate
+            mask = ct_lt_u32(len_candidate, max_sep_offset)
+            mask = ct_lsb_prop_u16(mask)
+            synth_length = synth_length & (0xffff ^ mask) \
+                | len_candidate & mask
+
+        synth_msg_start = numBytes(n) - synth_length
+
+        error_detected = 0
+
+        # enumerate over all decrypted bytes
+        em_bytes = enumerate(dec_bytes)
+        # first check if first two bytes specify PKCS#1 v1.5 encryption padding
+        _, val = next(em_bytes)
+        error_detected |= ct_isnonzero_u32(val)
+        _, val = next(em_bytes)
+        error_detected |= ct_neq_u32(val, 0x02)
+        # then look for for the null separator byte among the padding bytes
+        # but inspect all decrypted bytes, even if we already find the
+        # separator earlier
+        msg_start = 0
+        for pos, val in em_bytes:
+            # padding must be at least 8 bytes long, fail if any of the first
+            # 8 bytes of it are zero
+            # equivalent to:
+            # if pos < 10 and not val:
+            #     error_detected = 0x01
+            error_detected |= ct_lt_u32(pos, 10) & (1 ^ ct_isnonzero_u32(val))
+
+            # update the msg_start only once; when it's 0
+            # (pos+1) because we want to skip the null separator
+            # equivalent to:
+            # if pos >= 10 and not msg_start and not val:
+            #     msg_start = pos+1
+            mask = (1 ^ ct_lt_u32(pos, 10)) & (1 ^ ct_isnonzero_u32(val)) \
+                & (1 ^ ct_isnonzero_u32(msg_start))
+            mask = ct_lsb_prop_u16(mask)
+            msg_start = msg_start & (0xffff ^ mask) | (pos+1) & mask
+
+        # if separator wasn't found, it's an error
+        # equivalent to:
+        # if not msg_start:
+        #     error_detected = 0x01
+        error_detected |= 1 ^ ct_isnonzero_u32(msg_start)
+
+        # equivalent to:
+        # if error_detected:
+        #     ret_msg_start = synth_msg_start
+        # else:
+        #     ret_msg_start = msg_start
+        mask = ct_lsb_prop_u16(error_detected)
+        ret_msg_start = msg_start & (0xffff ^ mask) | synth_msg_start & mask
+
+        # as at this point the length doesn't leak the information if the
+        # padding was correct or not, we don't have to worry about the
+        # length of the returned value (and thus the size of the buffer we
+        # pass to the caller); but we still need to read both buffers
+        # to ensure that the memory access patern is preserved (that both
+        # buffers are accessed, not just the one we return)
+
+        # equivalent to:
+        # if error_detected:
+        #     return message_random[ret_msg_start:]
+        # else:
+        #     return dec_bytes[ret_msg_start:]
+        mask = ct_lsb_prop_u8(error_detected)
+        not_mask = 0xff ^ mask
+        ret = bytearray(
+            x & not_mask | y & mask for x, y in
+            zip(dec_bytes[ret_msg_start:], message_random[ret_msg_start:]))
+
+        return ret
+
+    def _rawPrivateKeyOp(self, message):
+        raise NotImplementedError()
+
+    def _rawPublicKeyOp(self, ciphertext):
+        raise NotImplementedError()
+
+    def _raw_private_key_op_bytes(self, message):
+        n = self.n
+        if len(message) != numBytes(n):
+            raise ValueError("Message has incorrect length for the key size")
+        m_int = bytesToNumber(message)
+        if m_int >= n:
+            raise ValueError("Provided message value exceeds modulus")
+        dec_int = self._rawPrivateKeyOp(m_int)
+        return numberToByteArray(dec_int, numBytes(n))
+
+    def _raw_public_key_op_bytes(self, ciphertext):
+        n = self.n
+        if len(ciphertext) != numBytes(n):
+            raise ValueError("Message has incorrect length for the key size")
+        c_int = bytesToNumber(ciphertext)
+        if c_int >= n:
+            raise ValueError("Provided message value exceeds modulus")
+        enc_int = self._rawPublicKeyOp(c_int)
+        return numberToByteArray(enc_int, numBytes(n))

     def acceptsPassword(self):
         """Return True if the write() method accepts a password for use
@@ -308,7 +596,7 @@ class RSAKey(object):

         :rtype: bool
         """
-        pass
+        raise NotImplementedError()

     def write(self, password=None):
         """Return a string containing the key.
@@ -317,30 +605,83 @@ class RSAKey(object):
         :returns: A string describing the key, in whichever format (PEM)
             is native to the implementation.
         """
-        pass
+        raise NotImplementedError()

     @staticmethod
-    def generate(bits, key_type='rsa'):
+    def generate(bits, key_type="rsa"):
         """Generate a new key with the specified bit length.

         :rtype: ~tlslite.utils.RSAKey.RSAKey
         """
-        pass
+        raise NotImplementedError()
+
+
+    # **************************************************************************
+    # Helper Functions for RSA Keys
+    # **************************************************************************

     @classmethod
     def addPKCS1SHA1Prefix(cls, hashBytes, withNULL=True):
         """Add PKCS#1 v1.5 algorithm identifier prefix to SHA1 hash bytes"""
-        pass
-    _pkcs1Prefixes = {'md5': bytearray([48, 32, 48, 12, 6, 8, 42, 134, 72, 
-        134, 247, 13, 2, 5, 5, 0, 4, 16]), 'sha1': bytearray([48, 33, 48, 9,
-        6, 5, 43, 14, 3, 2, 26, 5, 0, 4, 20]), 'sha224': bytearray([48, 45,
-        48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 4, 5, 0, 4, 28]),
-        'sha256': bytearray([48, 49, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 
-        4, 2, 1, 5, 0, 4, 32]), 'sha384': bytearray([48, 65, 48, 13, 6, 9, 
-        96, 134, 72, 1, 101, 3, 4, 2, 2, 5, 0, 4, 48]), 'sha512': bytearray
-        ([48, 81, 48, 13, 6, 9, 96, 134, 72, 1, 101, 3, 4, 2, 3, 5, 0, 4, 64])}
+        # There is a long history of confusion over whether the SHA1 
+        # algorithmIdentifier should be encoded with a NULL parameter or 
+        # with the parameter omitted.  While the original intention was 
+        # apparently to omit it, many toolkits went the other way.  TLS 1.2
+        # specifies the NULL should be included, and this behavior is also
+        # mandated in recent versions of PKCS #1, and is what tlslite has
+        # always implemented.  Anyways, verification code should probably 
+        # accept both.
+        if not withNULL:
+            prefixBytes = bytearray([0x30, 0x1f, 0x30, 0x07, 0x06, 0x05, 0x2b,
+                                     0x0e, 0x03, 0x02, 0x1a, 0x04, 0x14])
+        else:
+            prefixBytes = cls._pkcs1Prefixes['sha1']
+        prefixedBytes = prefixBytes + hashBytes
+        return prefixedBytes
+
+    _pkcs1Prefixes = {'md5' : bytearray([0x30, 0x20, 0x30, 0x0c, 0x06, 0x08,
+                                         0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d,
+                                         0x02, 0x05, 0x05, 0x00, 0x04, 0x10]),
+                      'sha1' : bytearray([0x30, 0x21, 0x30, 0x09, 0x06, 0x05,
+                                          0x2b, 0x0e, 0x03, 0x02, 0x1a, 0x05,
+                                          0x00, 0x04, 0x14]),
+                      'sha224' : bytearray([0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09,
+                                            0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
+                                            0x04, 0x02, 0x04, 0x05, 0x00, 0x04,
+                                            0x1c]),
+                      'sha256' : bytearray([0x30, 0x31, 0x30, 0x0d, 0x06, 0x09,
+                                            0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
+                                            0x04, 0x02, 0x01, 0x05, 0x00, 0x04,
+                                            0x20]),
+                      'sha384' : bytearray([0x30, 0x41, 0x30, 0x0d, 0x06, 0x09,
+                                            0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
+                                            0x04, 0x02, 0x02, 0x05, 0x00, 0x04,
+                                            0x30]),
+                      'sha512' : bytearray([0x30, 0x51, 0x30, 0x0d, 0x06, 0x09,
+                                            0x60, 0x86, 0x48, 0x01, 0x65, 0x03,
+                                            0x04, 0x02, 0x03, 0x05, 0x00, 0x04,
+                                            0x40])}

     @classmethod
     def addPKCS1Prefix(cls, data, hashName):
         """Add the PKCS#1 v1.5 algorithm identifier prefix to hash bytes"""
-        pass
+        hashName = hashName.lower()
+        assert hashName in cls._pkcs1Prefixes
+        prefixBytes = cls._pkcs1Prefixes[hashName]
+        return prefixBytes + data
+
+    def _addPKCS1Padding(self, bytes, blockType):
+        padLength = (numBytes(self.n) - (len(bytes)+3))
+        if blockType == 1: #Signature padding
+            pad = [0xFF] * padLength
+        elif blockType == 2: #Encryption padding
+            pad = bytearray(0)
+            while len(pad) < padLength:
+                padBytes = getRandomBytes(padLength * 2)
+                pad = [b for b in padBytes if b]
+                pad = pad[:padLength]
+        else:
+            raise AssertionError()
+
+        padding = bytearray([0,blockType] + pad + [0])
+        return padding + bytes
diff --git a/tlslite/utils/tackwrapper.py b/tlslite/utils/tackwrapper.py
index d9bb9c4..4eb39ef 100644
--- a/tlslite/utils/tackwrapper.py
+++ b/tlslite/utils/tackwrapper.py
@@ -1,7 +1,11 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 try:
     from tack.structures.Tack import Tack
     from tack.structures.TackExtension import TackExtension
     from tack.tls.TlsCertificate import TlsCertificate
+    
     tackpyLoaded = True
 except ImportError:
     tackpyLoaded = False
diff --git a/tlslite/utils/tlshashlib.py b/tlslite/utils/tlshashlib.py
index 080311b..346f545 100644
--- a/tlslite/utils/tlshashlib.py
+++ b/tlslite/utils/tlshashlib.py
@@ -1,18 +1,32 @@
+# Author: Hubert Kario (c) 2015
+# see LICENCE file for legal information regarding use of this file
+
 """hashlib that handles FIPS mode."""
+
+# Because we are extending the hashlib module, we need to import all its
+# fields to suppport the same uses
+# pylint: disable=unused-wildcard-import, wildcard-import
 from hashlib import *
+# pylint: enable=unused-wildcard-import, wildcard-import
 import hashlib


 def _fipsFunction(func, *args, **kwargs):
     """Make hash function support FIPS mode."""
-    pass
+    try:
+        return func(*args, **kwargs)
+    except ValueError:
+        return func(*args, usedforsecurity=False, **kwargs)


+# redefining the function is exactly what we intend to do
+# pylint: disable=function-redefined
 def md5(*args, **kwargs):
     """MD5 constructor that works in FIPS mode."""
-    pass
+    return _fipsFunction(hashlib.md5, *args, **kwargs)


 def new(*args, **kwargs):
     """General constructor that works in FIPS mode."""
-    pass
+    return _fipsFunction(hashlib.new, *args, **kwargs)
+# pylint: enable=function-redefined
diff --git a/tlslite/utils/tlshmac.py b/tlslite/utils/tlshmac.py
index 02030ea..789df72 100644
--- a/tlslite/utils/tlshmac.py
+++ b/tlslite/utils/tlshmac.py
@@ -1,23 +1,31 @@
+# Author: Hubert Kario (c) 2019
+# see LICENCE file for legal information regarding use of this file
+
 """
 HMAC module that works in FIPS mode.

 Note that this makes this code FIPS non-compliant!
 """
+
+# Because we are extending the hashlib module, we need to import all its
+# fields to suppport the same uses
 from . import tlshashlib
 from .compat import compatHMAC
 try:
     from hmac import compare_digest
-    __all__ = ['new', 'compare_digest', 'HMAC']
+    __all__ = ["new", "compare_digest", "HMAC"]
 except ImportError:
-    __all__ = ['new', 'HMAC']
+    __all__ = ["new", "HMAC"]
+
 try:
     from hmac import HMAC, new
+    # if we can calculate HMAC on MD5, then use the built-in HMAC
+    # implementation
     _val = HMAC(b'some key', b'msg', 'md5')
     _val.digest()
     del _val
 except Exception:
-
-
+    # fallback only when MD5 doesn't work
     class HMAC(object):
         """Hacked version of HMAC that works in FIPS mode even with MD5."""

@@ -45,8 +53,8 @@ except Exception:
             if len(key) < self.block_size:
                 key = key + b'\x00' * (self.block_size - len(key))
             key = bytearray(key)
-            ipad = bytearray(b'6' * self.block_size)
-            opad = bytearray(b'\\' * self.block_size)
+            ipad = bytearray(b'\x36' * self.block_size)
+            opad = bytearray(b'\x5c' * self.block_size)
             i_key = bytearray(i ^ j for i, j in zip(key, ipad))
             self._o_key = bytearray(i ^ j for i, j in zip(key, opad))
             self._context = digestmod.copy()
@@ -54,6 +62,27 @@ except Exception:
             if msg:
                 self._context.update(compatHMAC(msg))

+        def update(self, msg):
+            self._context.update(compatHMAC(msg))
+
+        def digest(self):
+            i_digest = self._context.digest()
+            o_hash = self.digestmod.copy()
+            o_hash.update(compatHMAC(self._o_key))
+            o_hash.update(compatHMAC(i_digest))
+            return o_hash.digest()
+
+        def copy(self):
+            new = HMAC.__new__(HMAC)
+            new.key = self.key
+            new.digestmod = self.digestmod
+            new.block_size = self.block_size
+            new.digest_size = self.digest_size
+            new._o_key = self._o_key
+            new._context = self._context.copy()
+            return new
+
+
     def new(*args, **kwargs):
         """General constructor that works in FIPS mode."""
-        pass
+        return HMAC(*args, **kwargs)
diff --git a/tlslite/utils/tripledes.py b/tlslite/utils/tripledes.py
index 70cffe5..ddcdcad 100644
--- a/tlslite/utils/tripledes.py
+++ b/tlslite/utils/tripledes.py
@@ -1,8 +1,9 @@
-"""Abstract class for 3DES."""
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.

+"""Abstract class for 3DES."""

 class TripleDES(object):
-
     def __init__(self, key, mode, IV, implementation):
         if len(key) != 24:
             raise ValueError()
@@ -14,4 +15,14 @@ class TripleDES(object):
         self.isAEAD = False
         self.block_size = 8
         self.implementation = implementation
-        self.name = '3des'
+        self.name = "3des"
+
+    #CBC-Mode encryption, returns ciphertext
+    #WARNING: *MAY* modify the input as well
+    def encrypt(self, plaintext):
+        assert(len(plaintext) % 8 == 0)
+
+    #CBC-Mode decryption, returns plaintext
+    #WARNING: *MAY* modify the input as well
+    def decrypt(self, ciphertext):
+        assert(len(ciphertext) % 8 == 0)
diff --git a/tlslite/utils/x25519.py b/tlslite/utils/x25519.py
index c8f6173..334daad 100644
--- a/tlslite/utils/x25519.py
+++ b/tlslite/utils/x25519.py
@@ -1,28 +1,51 @@
+# Authors:
+#   Hubert Kario (2017)
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """Handling X25519 and X448 curve based key agreement protocol."""
+
 from .cryptomath import bytesToNumber, numberToByteArray, divceil
+# the names of the variables come directly from RFC 7748 so changing them
+# would make the code harder to audit/compare
+# pylint: disable=invalid-name


 def decodeUCoordinate(u, bits):
     """Function to decode the public U coordinate of X25519-family curves."""
-    pass
+    if bits not in (255, 448):
+        raise ValueError("Invalid number of expected bits")
+    if bits % 8:
+        u[-1] &= (1 << (bits % 8)) - 1
+    return bytesToNumber(u, endian="little")


 def decodeScalar22519(k):
     """Function to decode the private K parameter of the x25519 function."""
-    pass
+    k[0] &= 248
+    k[31] &= 127
+    k[31] |= 64
+    return bytesToNumber(k, endian="little")


 def decodeScalar448(k):
     """Function to decode the private K parameter of the X448 function."""
-    pass
+    k[0] &= 252
+    k[55] |= 128
+    return bytesToNumber(k, endian="little")


 def cswap(swap, x_2, x_3):
     """Conditional swap function."""
-    pass
+    if swap:
+        return x_3, x_2
+    else:
+        return x_2, x_3
+
+
+X25519_G = numberToByteArray(9, 32, endian="little")


-X25519_G = numberToByteArray(9, 32, endian='little')
 X25519_ORDER_SIZE = 32


@@ -38,10 +61,19 @@ def x25519(k, u):

     :rtype: bytearray
     """
-    pass
+    bits = 255
+    k = decodeScalar22519(k)
+    u = decodeUCoordinate(u, bits)
+
+    a24 = 121665
+    p = 2**255 - 19
+
+    return _x25519_generic(k, u, bits, a24, p)
+
+
+X448_G = numberToByteArray(5, 56, endian="little")


-X448_G = numberToByteArray(5, 56, endian='little')
 X448_ORDER_SIZE = 56


@@ -57,9 +89,48 @@ def x448(k, u):

     :rtype: bytearray
     """
-    pass
+    bits = 448
+    k = decodeScalar448(k)
+    u = decodeUCoordinate(u, bits)
+
+    a24 = 39081
+    p = 2**448 - 2**224 - 1
+
+    return _x25519_generic(k, u, bits, a24, p)


 def _x25519_generic(k, u, bits, a24, p):
     """Generic Montgomery ladder implementation of the x25519 algorithm."""
-    pass
+    x_1 = u
+    x_2 = 1
+    z_2 = 0
+    x_3 = u
+    z_3 = 1
+    swap = 0
+
+    for t in range(bits-1, -1, -1):
+        k_t = (k >> t) & 1
+        swap ^= k_t
+        x_2, x_3 = cswap(swap, x_2, x_3)
+        z_2, z_3 = cswap(swap, z_2, z_3)
+        swap = k_t
+
+        A = (x_2 + z_2) % p
+        AA = pow(A, 2, p)
+        B = (x_2 - z_2) % p
+        BB = pow(B, 2, p)
+        E = (AA - BB) % p
+        C = (x_3 + z_3) % p
+        D = (x_3 - z_3) % p
+        DA = (D * A) % p
+        CB = (C * B) % p
+        x_3 = pow(DA + CB, 2, p)
+        z_3 = (x_1 * pow(DA - CB, 2, p)) % p
+        x_2 = (AA * BB) % p
+        z_2 = (E * (AA + a24 * E)) % p
+
+    x_2, x_3 = cswap(swap, x_2, x_3)
+    z_2, z_3 = cswap(swap, z_2, z_3)
+    ret = (x_2 * pow(z_2, p - 2, p)) % p
+    return numberToByteArray(ret, divceil(bits, 8), endian="little")
+# pylint: enable=invalid-name
diff --git a/tlslite/verifierdb.py b/tlslite/verifierdb.py
index 4264045..0711170 100644
--- a/tlslite/verifierdb.py
+++ b/tlslite/verifierdb.py
@@ -1,10 +1,13 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 """Class for storing SRP password verifiers."""
+
 from .utils.cryptomath import *
 from .utils.compat import *
 from tlslite import mathtls
 from .basedb import BaseDB

-
 class VerifierDB(BaseDB):
     """This class represent an in-memory or on-disk database of SRP
     password verifiers.
@@ -14,7 +17,6 @@ class VerifierDB(BaseDB):

     This class is thread-safe.
     """
-
     def __init__(self, filename=None):
         """Create a new VerifierDB instance.

@@ -24,7 +26,15 @@ class VerifierDB(BaseDB):
             this with a call to open().  To create a new on-disk database,
             follow this with a call to create().
         """
-        BaseDB.__init__(self, filename, b'verifier')
+        BaseDB.__init__(self, filename, b"verifier")
+
+    def _getItem(self, username, valueStr):
+        (N, g, salt, verifier) = valueStr.split(b" ")
+        N = bytesToNumber(a2b_base64(N))
+        g = bytesToNumber(a2b_base64(g))
+        salt = a2b_base64(salt)
+        verifier = bytesToNumber(a2b_base64(verifier))
+        return (N, g, salt, verifier)

     def __setitem__(self, username, verifierEntry):
         """Add a verifier entry to the database.
@@ -41,6 +51,24 @@ class VerifierDB(BaseDB):
         """
         BaseDB.__setitem__(self, username, verifierEntry)

+
+    def _setItem(self, username, value):
+        if len(username)>=256:
+            raise ValueError("username too long")
+        N, g, salt, verifier = value
+        N = b2a_base64(numberToByteArray(N)).encode("ascii")
+        g = b2a_base64(numberToByteArray(g)).encode("ascii")
+        salt = b2a_base64(salt).encode("ascii")
+        verifier = b2a_base64(numberToByteArray(verifier)).encode("ascii")
+        valueStr = b" ".join((N, g, salt, verifier))
+        return valueStr
+
+    def _checkItem(self, value, username, param):
+        (N, g, salt, verifier) = value
+        x = mathtls.makeX(salt, username, param)
+        v = powMod(g, x, N)
+        return (verifier == v)
+
     @staticmethod
     def makeVerifier(username, password, bits):
         """Create a verifier entry which can be stored in a VerifierDB.
@@ -61,4 +89,12 @@ class VerifierDB(BaseDB):
         :rtype: tuple
         :returns: A tuple which may be stored in a VerifierDB.
         """
-        pass
+        if isinstance(username, str):
+            usernameBytes = bytearray(username, "utf-8")
+        else:
+            usernameBytes = bytearray(username)
+        if isinstance(password, str):
+            passwordBytes = bytearray(password, "utf-8")
+        else:
+            passwordBytes = bytearray(password)
+        return mathtls.makeVerifier(usernameBytes, passwordBytes, bits)
diff --git a/tlslite/x509.py b/tlslite/x509.py
index 5c37622..5fe53f8 100644
--- a/tlslite/x509.py
+++ b/tlslite/x509.py
@@ -1,8 +1,17 @@
+# Authors:
+#   Trevor Perrin
+#   Google - parsing subject field
+#
+# See the LICENSE file for legal information regarding use of this file.
+
 """Class representing an X.509 certificate."""
+
 from ecdsa.keys import VerifyingKey
+
 from .utils.asn1parser import ASN1Parser
 from .utils.cryptomath import *
-from .utils.keyfactory import _createPublicRSAKey, _create_public_ecdsa_key, _create_public_dsa_key, _create_public_eddsa_key
+from .utils.keyfactory import _createPublicRSAKey, _create_public_ecdsa_key, \
+    _create_public_dsa_key, _create_public_eddsa_key
 from .utils.pem import *
 from .utils.compat import compatHMAC, b2a_hex
 from .constants import AlgorithmOID, RSA_PSS_OID
@@ -43,13 +52,13 @@ class X509(object):

     def __eq__(self, other):
         """Compare other object for equality."""
-        if not hasattr(other, 'bytes'):
+        if not hasattr(other, "bytes"):
             return NotImplemented
         return self.bytes == other.bytes

     def __ne__(self, other):
         """Compare with other object for inequality."""
-        if not hasattr(other, 'bytes'):
+        if not hasattr(other, "bytes"):
             return NotImplemented
         return not self == other

@@ -62,7 +71,9 @@ class X509(object):
             certificate wrapped with "-----BEGIN CERTIFICATE-----" and
             "-----END CERTIFICATE-----" tags).
         """
-        pass
+        bytes = dePem(s, "CERTIFICATE")
+        self.parseBinary(bytes)
+        return self

     def parseBinary(self, cert_bytes):
         """
@@ -71,7 +82,94 @@ class X509(object):
         :type bytes: L{str} (in python2) or L{bytearray} of unsigned bytes
         :param bytes: A DER-encoded X.509 certificate.
         """
-        pass
+        self.bytes = bytearray(cert_bytes)
+        parser = ASN1Parser(self.bytes)
+
+        # Get the SignatureAlgorithm
+        signature_algorithm_identifier = parser.getChild(1)
+        self.sigalg = bytes(signature_algorithm_identifier.getChildBytes(0))
+
+        # Finally get the (hash, signature) pair coresponding to it
+        # If it is rsa-pss we need to check the aditional parameters field
+        # to extract the hash algorithm
+        if self.sigalg == RSA_PSS_OID:
+            sigalg_hash = signature_algorithm_identifier.getChild(1)
+            sigalg_hash = bytes(sigalg_hash.getChild(0).value)
+            self.sigalg = AlgorithmOID.oid[sigalg_hash]
+        else:
+            self.sigalg = AlgorithmOID.oid[self.sigalg]
+
+        # Get the tbsCertificate
+        tbs_certificate = parser.getChild(0)
+        # Is the optional version field present?
+        # This determines which index the key is at.
+        if tbs_certificate.value[0] == 0xA0:
+            serial_number_index = 1
+            subject_public_key_info_index = 6
+        else:
+            serial_number_index = 0
+            subject_public_key_info_index = 5
+
+        # Get serial number
+        self.serial_number = bytesToNumber(tbs_certificate.getChild(serial_number_index).value)
+
+        # Get the issuer
+        self.issuer = tbs_certificate.getChildBytes(
+            subject_public_key_info_index - 3)
+
+        # Get the subject
+        self.subject = tbs_certificate.getChildBytes(
+            subject_public_key_info_index - 1)
+
+        # Get the subjectPublicKeyInfo
+        subject_public_key_info = tbs_certificate.getChild(
+            subject_public_key_info_index)
+
+        # Get the AlgorithmIdentifier
+        alg_identifier = subject_public_key_info.getChild(0)
+        alg_identifier_len = alg_identifier.getChildCount()
+
+        # first item of AlgorithmIdentifier is the algorithm
+        alg = alg_identifier.getChild(0)
+        alg_oid = alg.value
+        if list(alg_oid) == [42, 134, 72, 134, 247, 13, 1, 1, 1]:
+            self.certAlg = "rsa"
+        elif list(alg_oid) == [42, 134, 72, 134, 247, 13, 1, 1, 10]:
+            self.certAlg = "rsa-pss"
+        elif list(alg_oid) == [42, 134, 72, 206, 56, 4, 1]:
+            self.certAlg = "dsa"
+        elif list(alg_oid) == [42, 134, 72, 206, 61, 2, 1]:
+            self.certAlg = "ecdsa"
+        elif list(alg_oid) == [43, 101, 112]:
+            self.certAlg = "Ed25519"
+        elif list(alg_oid) == [43, 101, 113]:
+            self.certAlg = "Ed448"
+        else:
+            raise SyntaxError("Unrecognized AlgorithmIdentifier")
+
+        # for RSA the parameters of AlgorithmIdentifier shuld be a NULL
+        if self.certAlg == "rsa":
+            if alg_identifier_len != 2:
+                raise SyntaxError("Missing parameters in AlgorithmIdentifier")
+            params = alg_identifier.getChild(1)
+            if params.value != bytearray(0):
+                raise SyntaxError("Unexpected non-NULL parameters in "
+                                  "AlgorithmIdentifier")
+        elif self.certAlg == "ecdsa":
+            self._ecdsa_pubkey_parsing(
+                tbs_certificate.getChildBytes(subject_public_key_info_index))
+            return
+        elif self.certAlg == "dsa":
+            self._dsa_pubkey_parsing(subject_public_key_info)
+            return
+        elif self.certAlg == "Ed25519" or self.certAlg == "Ed448":
+            self._eddsa_pubkey_parsing(
+                tbs_certificate.getChildBytes(subject_public_key_info_index))
+            return
+        else:  # rsa-pss
+            pass  # ignore parameters, if any - don't apply key restrictions
+
+        self._rsa_pubkey_parsing(subject_public_key_info)

     def _eddsa_pubkey_parsing(self, subject_public_key_info):
         """
@@ -80,7 +178,15 @@ class X509(object):
         :param subject_public_key_info: bytes like object with the DER encoded
             public key in it
         """
-        pass
+        try:
+            # python ecdsa knows how to parse curve OIDs so re-use that
+            # code
+            public_key = VerifyingKey.from_der(compatHMAC(
+                subject_public_key_info))
+        except Exception:
+            raise SyntaxError("Malformed or unsupported public key in "
+                              "certificate")
+        self.publicKey = _create_public_eddsa_key(public_key)

     def _rsa_pubkey_parsing(self, subject_public_key_info):
         """
@@ -89,7 +195,31 @@ class X509(object):
         :param subject_public_key_info: ASN1Parser object with subject
             public key info of X.509 certificate
         """
-        pass
+
+        # Get the subjectPublicKey
+        subject_public_key = subject_public_key_info.getChild(1)
+        self.subject_public_key = subject_public_key_info.getChildBytes(1)
+        self.subject_public_key = ASN1Parser(self.subject_public_key).value[1:]
+
+        # Adjust for BIT STRING encapsulation
+        if subject_public_key.value[0]:
+            raise SyntaxError()
+        subject_public_key = ASN1Parser(subject_public_key.value[1:])
+
+        # Get the modulus and exponent
+        modulus = subject_public_key.getChild(0)
+        public_exponent = subject_public_key.getChild(1)
+
+        # Decode them into numbers
+        # pylint: disable=invalid-name
+        # 'n' and 'e' are the universally used parameters in RSA algorithm
+        # definition
+        n = bytesToNumber(modulus.value)
+        e = bytesToNumber(public_exponent.value)
+
+        # Create a public key instance
+        self.publicKey = _createPublicRSAKey(n, e, self.certAlg)
+        # pylint: enable=invalid-name

     def _ecdsa_pubkey_parsing(self, subject_public_key_info):
         """
@@ -98,7 +228,18 @@ class X509(object):
         :param subject_public_key_info: bytes like object with DER encoded
             public key in it
         """
-        pass
+        try:
+            # python ecdsa knows how to parse curve OIDs so re-use that
+            # code
+            public_key = VerifyingKey.from_der(compatHMAC(
+                subject_public_key_info))
+        except Exception:
+            raise SyntaxError("Malformed or unsupported public key in "
+                              "certificate")
+        x = public_key.pubkey.point.x()
+        y = public_key.pubkey.point.y()
+        curve_name = public_key.curve.name
+        self.publicKey = _create_public_ecdsa_key(x, y, curve_name)

     def _dsa_pubkey_parsing(self, subject_public_key_info):
         """
@@ -107,7 +248,28 @@ class X509(object):
         :param subject_public_key_info: bytes like object with DER encoded
           global parameters and public key in it
         """
-        pass
+        global_parameters = (subject_public_key_info.getChild(0)).getChild(1)
+        # Get the subjectPublicKey
+        public_key = subject_public_key_info.getChild(1)
+
+        # Adjust for BIT STRING encapsulation and get hex value
+        if public_key.value[0]:
+            raise SyntaxError()
+        y = ASN1Parser(public_key.value[1:])
+
+        # Get the {A, p, q}
+        p = global_parameters.getChild(0)
+        q = global_parameters.getChild(1)
+        g = global_parameters.getChild(2)
+
+        # Decode them into numbers
+        y = bytesToNumber(y.value)
+        p = bytesToNumber(p.value)
+        q = bytesToNumber(q.value)
+        g = bytesToNumber(g.value)
+
+        # Create a public key instance
+        self.publicKey = _create_public_dsa_key(p, q, g, y)

     def getFingerprint(self):
         """
@@ -116,8 +278,10 @@ class X509(object):
         :rtype: str
         :returns: A hex-encoded fingerprint.
         """
-        pass
+        return b2a_hex(SHA1(self.bytes))

     def writeBytes(self):
         """Serialise object to a DER encoded string."""
-        pass
+        return self.bytes
+
+
diff --git a/tlslite/x509certchain.py b/tlslite/x509certchain.py
index 54183ce..0d1232b 100644
--- a/tlslite/x509certchain.py
+++ b/tlslite/x509certchain.py
@@ -1,10 +1,13 @@
+# Author: Trevor Perrin
+# See the LICENSE file for legal information regarding use of this file.
+
 """Class representing an X.509 certificate chain."""
+
 from .utils import cryptomath
 from .utils.tackwrapper import *
 from .utils.pem import *
 from .x509 import X509

-
 class X509CertChain(object):
     """This class represents a chain of X.509 certificates.

@@ -33,13 +36,13 @@ class X509CertChain(object):

     def __eq__(self, other):
         """Compare objects with each-other."""
-        if not hasattr(other, 'x509List'):
+        if not hasattr(other, "x509List"):
             return NotImplemented
         return self.x509List == other.x509List

     def __ne__(self, other):
         """Compare object for inequality."""
-        if not hasattr(other, 'x509List'):
+        if not hasattr(other, "x509List"):
             return NotImplemented
         return self.x509List != other.x509List

@@ -48,21 +51,29 @@ class X509CertChain(object):

         Raise a SyntaxError if input is malformed.
         """
-        pass
+        x509List = []
+        bList = dePemList(s, "CERTIFICATE")
+        for b in bList:
+            x509 = X509()
+            x509.parseBinary(b)
+            x509List.append(x509)
+        self.x509List = x509List

     def getNumCerts(self):
         """Get the number of certificates in this chain.

         :rtype: int
         """
-        pass
+        return len(self.x509List)

     def getEndEntityPublicKey(self):
         """Get the public key from the end-entity certificate.

         :rtype: ~tlslite.utils.rsakey.RSAKey`
         """
-        pass
+        if self.getNumCerts() == 0:
+            raise AssertionError()
+        return self.x509List[0].publicKey

     def getFingerprint(self):
         """Get the hex-encoded fingerprint of the end-entity certificate.
@@ -70,8 +81,27 @@ class X509CertChain(object):
         :rtype: str
         :returns: A hex-encoded fingerprint.
         """
-        pass
-
+        if self.getNumCerts() == 0:
+            raise AssertionError()
+        return self.x509List[0].getFingerprint()
+
+    def checkTack(self, tack):
+        if self.x509List:
+            tlsCert = TlsCertificate(self.x509List[0].bytes)
+            if tlsCert.matches(tack):
+                return True
+        return False
+        
     def getTackExt(self):
         """Get the TACK and/or Break Sigs from a TACK Cert in the chain."""
-        pass
+        tackExt = None
+        # Search list in backwards order
+        for x509 in self.x509List[::-1]:
+            tlsCert = TlsCertificate(x509.bytes)
+            if tlsCert.tackExt:
+                if tackExt:
+                    raise SyntaxError("Multiple TACK Extensions")
+                else:
+                    tackExt = tlsCert.tackExt
+        return tackExt
+