diff --git a/.travis/test.sh b/.travis/test.sh
index 40d67ac1382120839438e4a6189646079734daa6..2c27c3e8dbc914761c7b67c4b1cc3e258f95a662 100644
--- a/.travis/test.sh
+++ b/.travis/test.sh
@@ -12,11 +12,9 @@ then
   SCAPY_SUDO=""
 fi
 
-# AES-CCM not implemented yet in Cryptography
-# See
-#  - https://github.com/pyca/cryptography/issues/2968
-#  - https://github.com/pyca/cryptography/issues/1141
-UT_FLAGS+=" -K combined_modes_ccm"
+# AES-CCM, ChaCha20Poly1305 and X25519 were added to Cryptography v2.0
+# but the minimal version mandated by scapy is v1.7
+UT_FLAGS+=" -K crypto_advanced"
 
 if python --version 2>&1 | grep -q PyPy
 then
diff --git a/scapy/config.py b/scapy/config.py
index a57bdb3f165c2b3dfc04ef828308a022d7e8ef92..3e73b7e6eaa090af77bcf1432ddc3a84f55b250e 100755
--- a/scapy/config.py
+++ b/scapy/config.py
@@ -285,7 +285,7 @@ class LogLevel(object):
 def isCryptographyValid():
     """
     Check if the cryptography library is present, and if it is recent enough
-    (v1.7 or later).
+    for most usages in scapy (v1.7 or later).
     """
     try:
         import cryptography
@@ -295,6 +295,29 @@ def isCryptographyValid():
     return LooseVersion(cryptography.__version__) >= LooseVersion("1.7")
 
 
+def isCryptographyAdvanced():
+    """
+    Check if the cryptography library is present, and if it supports X25519,
+    ChaCha20Poly1305 and such (v2.0 or later).
+    """
+    try:
+        import cryptography
+    except ImportError:
+        return False
+    from distutils.version import LooseVersion
+    lib_valid = LooseVersion(cryptography.__version__) >= LooseVersion("2.0")
+    if not lib_valid:
+        return False
+
+    try:
+        from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey
+        X25519PrivateKey.generate()
+    except:
+        return False
+    else:
+        return True
+
+
 def _prompt_changer(attr,val):
     prompt = conf.prompt
     try:
@@ -416,6 +439,7 @@ debug_tls:When 1, print some TLS session secrets when they are computed.
                    "sctp", "vrrp", "ipsec", "lltd", "vxlan"]
     contribs = dict()
     crypto_valid = isCryptographyValid()
+    crypto_valid_advanced = isCryptographyAdvanced()
 
 
 if not Conf.ipv6_enabled:
diff --git a/scapy/fields.py b/scapy/fields.py
index 796f858ce062d23414a538ea77ce85b7d8e160a5..822fd100835c57d0284c5a3c148d763359dfc843 100644
--- a/scapy/fields.py
+++ b/scapy/fields.py
@@ -562,6 +562,8 @@ class XStrField(StrField):
     """
 
     def i2repr(self, pkt, x):
+        if not x:
+            return repr(x)
         return x.encode("hex")
 
 class XStrLenField(StrLenField):
@@ -570,6 +572,8 @@ class XStrLenField(StrLenField):
     """
 
     def i2repr(self, pkt, x):
+        if not x:
+            return repr(x)
         return x[:self.length_from(pkt)].encode("hex")
 
 class XStrFixedLenField(StrFixedLenField):
@@ -578,6 +582,8 @@ class XStrFixedLenField(StrFixedLenField):
     """
 
     def i2repr(self, pkt, x):
+        if not x:
+            return repr(x)
         return x[:self.length_from(pkt)].encode("hex")
 
 class StrLenFieldUtf16(StrLenField):
diff --git a/scapy/layers/tls/__init__.py b/scapy/layers/tls/__init__.py
index cb6a6dd07fc58d458aa9b4b636d62ca923507262..4b76353f8b7230d4f59a731649c12d24f01427d0 100644
--- a/scapy/layers/tls/__init__.py
+++ b/scapy/layers/tls/__init__.py
@@ -1,6 +1,6 @@
 ## This file is part of Scapy
 ## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard <arno@natisbad.com>
-##                     2015, 2016 Maxence Tury <maxence.tury@ssi.gouv.fr>
+##               2015, 2016, 2017 Maxence Tury <maxence.tury@ssi.gouv.fr>
 ## This program is published under a GPLv2 license
 
 """
@@ -18,17 +18,17 @@ Main features:
     - RSA & ECDSA keys sign/verify methods.
 
     - TLS records and sublayers (handshake...) parsing/building. Works with
-      versions SSLv3, TLS 1.0, 1.1 and 1.2. This may be enhanced by a TLS
-      context. For instance, if scapy reads a ServerHello with version TLS 1.2
-      and a cipher suite using AES, it will assume the presence of IVs
-      prepending the data. See test/tls.uts for real examples.
-
-    - TLS encryption/decryption capabilities with the usual ciphersuites. Once
-      again, the TLS context enables scapy to transparently send/receive
-      protected data if it learnt the session secrets. Note that if scapy acts
-      as one side of the handshake (e.g. reads all server-related packets and
-      builds all client-related packets), it will indeed compute the session
-      secrets.
+      versions SSLv2 to TLS 1.2. This may be enhanced by a TLS context. For
+      instance, if Scapy reads a ServerHello with version TLS 1.2 and a cipher
+      suite using AES, it will assume the presence of IVs prepending the data.
+      See test/tls.uts for real examples.
+
+    - TLS encryption/decryption capabilities with many ciphersuites, including
+      some which may be deemed dangerous. Once again, the TLS context enables
+      Scapy to transparently send/receive protected data if it learnt the
+      session secrets. Note that if Scapy acts as one side of the handshake
+      (e.g. reads all server-related packets and builds all client-related
+      packets), it will indeed compute the session secrets.
 
     - TLS client & server basic automatons, provided for testing and tweaking
       purposes. These make for a very primitive TLS stack.
@@ -43,6 +43,10 @@ Unit tests:
 
     - Reading a TLS handshake between a Firefox client and a GitHub server.
 
+    - Reading TLS 1.3 handshakes from test vectors of a draft RFC.
+
+    - Reading a SSLv2 handshake between s_client and s_server, without PFS.
+
     - Test our TLS server against s_client with different cipher suites.
 
     - Test our TLS client against our TLS server (s_server is unscriptable).
@@ -52,39 +56,21 @@ TODO list (may it be carved away by good souls):
 
     - Features to add (or wait for) in the cryptography library:
 
-        - no limitation on FFDH generator size;
-          (remove line 88 in cryptography/hazmat/primitives/asymmetric/dh.py)
-
-        - CCM and CHACHA20-POLY1305 ciphers;
-
-        - ECDH curves (x25519 and x448) from RFC 7748;
-
-        - FFDH groups from RFC 7919;
-
-        - the so-called 'tls' hash used with SSLv3 and TLS 1.0;
+        - X448 from RFC 7748 (no support in openssl yet);
 
         - the compressed EC point format.
 
 
     - About the automatons:
 
-        - Enrich the automatons. The client should be able to receive data at
-          any time, and to send as much data as wanted from stdin (for now,
-          only one predefined data message may be sent following the
-          handshake). The server should stay online even after the first client
-          leaves. Then we could look at more complicated behaviours like
-          renegotiation and resumption. We might get some help from
-          tintinweb/scapy-ssl_tls.
+        - Add resumption support, through session IDs or session tickets.
+
+        - Add various checks for discrepancies between client and server.
+          Is the ServerHello ciphersuite ok? What about the SKE params? Etc.
 
         - Add some examples which illustrate how the automatons could be used.
           Typically, we could showcase this with Heartbleed.
 
-        - Split up parts of the automaton, e.g. when our server builds the
-          ServerHello, Certificate, ServerKeyExchange and ServerHelloDone in
-          the same should_REPLY_TO_CH method.
-
-        - Make the automatons tests more robust and less consuming.
-
         - Allow the server to store both one RSA key and one ECDSA key, and
           select the right one to use according to the ClientHello suites.
 
@@ -94,19 +80,13 @@ TODO list (may it be carved away by good souls):
 
     - Miscellaneous:
 
-        - Implement TLS 1.3 structures and automatons. :D
-
-        - Implement SSLv2 structures and automatons. xD
-
-        - Mostly unused features : DSS, fixed DH, SRP, IDEA, char2 curves...
-
-        - Check FFDH and ECDH parameters at SKE/CKE reception.
-
-        - Go through the kx_algs and see what may be commented out.
+        - Enhance PSK and session ticket support.
 
         - Define several Certificate Transparency objects.
 
-        - Enhance PSK and session ticket support.
+        - Add the extended master secret and encrypt-then-mac logic.
+
+        - Mostly unused features : DSS, fixed DH, SRP, char2 curves...
 """
 
 from scapy.config import conf
diff --git a/scapy/layers/tls/all.py b/scapy/layers/tls/all.py
index 3409b88c4f3af36de1798da23e2922c71cec3b74..480d6d392bc417cc5ce951dd8898bd6519fa8c13 100644
--- a/scapy/layers/tls/all.py
+++ b/scapy/layers/tls/all.py
@@ -1,6 +1,6 @@
 ## This file is part of Scapy
 ## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
-##                     2015, 2016 Maxence Tury
+##               2015, 2016, 2017 Maxence Tury
 ## This program is published under a GPLv2 license
 
 """
@@ -9,10 +9,16 @@ Aggregate top level objects from all TLS modules.
 
 from scapy.layers.tls.cert import *
 
-from scapy.layers.tls.automaton import *
+from scapy.layers.tls.automaton_cli import *
+from scapy.layers.tls.automaton_srv import *
+from scapy.layers.tls.extensions import *
 from scapy.layers.tls.handshake import *
+from scapy.layers.tls.handshake_sslv2 import *
 from scapy.layers.tls.keyexchange import *
+from scapy.layers.tls.keyexchange_tls13 import *
 from scapy.layers.tls.record import *
+from scapy.layers.tls.record_sslv2 import *
+from scapy.layers.tls.record_tls13 import *
 from scapy.layers.tls.session import *
 
 from scapy.layers.tls.crypto.all import *
diff --git a/scapy/layers/tls/automaton.py b/scapy/layers/tls/automaton.py
index 8aa0fd70711aa10814a2b36955fe41eb2a13da6e..0e5d8457f63e3606cd06725faba6d39bed87a4a6 100644
--- a/scapy/layers/tls/automaton.py
+++ b/scapy/layers/tls/automaton.py
@@ -1,1083 +1,223 @@
 ## This file is part of Scapy
 ## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
-##                     2015, 2016 Maxence Tury
+##               2015, 2016, 2017 Maxence Tury
 ## This program is published under a GPLv2 license
 
 """
-TLS automatons. This makes for a primitive TLS stack.
-SSLv3 is not guaranteed, and SSLv2 is not supported.
-Obviously you need rights for network access.
-
-
-Launch a server on tcp/4433:
-
-from scapy.all import *
-t = TLSServerAutomaton(mycert='<cert.pem>', mykey='<key.pem>')
-t.run()
-
-
-Launch a client to tcp/50000 with one cipher suite of your choice:
-
-from scapy.all import *
-ch = TLSClientHello(ciphers=<int code of the cipher suite>)
-t = TLSClientAutomaton(dport=50000, client_hello=ch)
-t.run()
+The _TLSAutomaton class provides methods common to both TLS client and server.
 """
 
 from __future__ import print_function
-import socket
 import struct
 
-from scapy.error import warning
-from scapy.automaton import Automaton, ATMT
-from scapy.layers.tls.cert import Cert, PrivKey, PrivKeyRSA, PrivKeyECDSA
-from scapy.layers.tls.basefields import _tls_version
-from scapy.layers.tls.session import tlsSession
-from scapy.layers.tls.handshake import *
-from scapy.layers.tls.record import (TLS, TLSAlert, TLSChangeCipherSpec,
-                                     TLSApplicationData)
-from scapy.layers.tls.crypto.suites import (_tls_cipher_suites_cls,
-                                            _tls_cipher_suites,
-                                            get_usable_ciphersuites)
+from scapy.automaton import Automaton
+from scapy.packet import Raw
+from scapy.layers.tls.basefields import _tls_type
+from scapy.layers.tls.cert import Cert, PrivKey
+from scapy.layers.tls.record import TLS
+from scapy.layers.tls.record_sslv2 import SSLv2
+from scapy.layers.tls.record_tls13 import TLS13
 
 
-###############################################################################
-### Client automaton                                                        ###
-###############################################################################
-
-class TLSClientAutomaton(Automaton):
+class _TLSAutomaton(Automaton):
     """
-    The TLS client automaton.
-
-    - server : default value is '127.0.0.1';
-    - dport : default value is 4433;
-    - server_name : default value is None;
-    - mycert : optional when there is no client authentication;
-    - mykey : optional when there is no client authentication;
-    - client_hello : optional definition of the ClientHello to be sent to the
-      server, this is faster than automaton overloading and enables quick
-      cipher suite choice (make sure it is usable, though);
-    - data : optional application_data to be sent after the handshake, if this
-      is not defined we send a simple GET request.
+    SSLv3 and TLS 1.0-1.2 typically need a 2-RTT handshake:
+
+    Client        Server
+      | --------->>> |    C1 - ClientHello
+      | <<<--------- |    S1 - ServerHello
+      | <<<--------- |    S1 - Certificate
+      | <<<--------- |    S1 - ServerKeyExchange
+      | <<<--------- |    S1 - ServerHelloDone
+      | --------->>> |    C2 - ClientKeyExchange
+      | --------->>> |    C2 - ChangeCipherSpec
+      | --------->>> |    C2 - Finished [encrypted]
+      | <<<--------- |    S2 - ChangeCipherSpec
+      | <<<--------- |    S2 - Finished [encrypted]
+
+    We call these successive groups of messages:
+    ClientFlight1, ServerFlight1, ClientFlight2 and ServerFlight2.
+
+    We want to send our messages from the same flight all at once through the
+    socket. This is achieved by managing a list of records in 'buffer_out'.
+    We may put several messages (i.e. what RFC 5246 calls the record fragments)
+    in the same record when possible, but we may need several records for the
+    same flight, as with ClientFlight2.
+
+    However, note that the flights from the opposite side may be spread wildly
+    accross TLS records and TCP packets. This is why we use a 'get_next_msg'
+    method for feeding a list of received messages, 'buffer_in'. Raw data
+    which has not yet been interpreted as a TLS record is kept in 'remain_in'.
     """
+    def parse_args(self, mycert=None, mykey=None, **kargs):
 
-    def parse_args(self, server="127.0.0.1", dport=4433,
-                   server_name=None, mycert=None, mykey=None,
-                   client_hello=None, data=None, **kargs):
-        Automaton.parse_args(self, **kargs)
-
-        tmp = socket.getaddrinfo(server, dport)
-        self.remote_name = None
-        try:
-            if ':' in server:
-                socket.inet_pton(socket.AF_INET6, server)
-            else:
-                socket.inet_pton(socket.AF_INET, server)
-        except:
-            self.remote_name = socket.getfqdn(server)
-            if self.remote_name != server:
-                tmp = socket.getaddrinfo(self.remote_name, dport)
-
-        if server_name:
-            self.remote_name = server_name
-        self.remote_family = tmp[0][0]
-        self.remote_ip = tmp[0][4][0]
-        self.remote_port = dport
-        self.local_ip = None
-        self.local_port = None
-
-        self.cur_pkt = None
-        self.cur_session = None
-        self.msg_list = []
-
-        self.remain = ""
+        super(_TLSAutomaton, self).parse_args(**kargs)
 
         self.socket = None
+        self.remain_in = ""
+        self.buffer_in = []         # these are 'fragments' inside records
+        self.buffer_out = []        # these are records
 
-        self.cert_req = None
-
-        self.client_hello = client_hello
-        self.data = data
+        self.cur_session = None
+        self.cur_pkt = None         # this is usually the latest parsed packet
 
-        if mycert and mykey:
+        if mycert:
             self.mycert = Cert(mycert)
-            self.mykey  = PrivKey(mykey)
         else:
             self.mycert = None
-            self.mykey  = None
 
+        if mykey:
+            self.mykey = PrivKey(mykey)
+        else:
+            self.mykey = None
+
+        self.verbose = kargs.get("verbose", True)
 
-    def get_next_msg(self, socket_timeout=5, retry=5):
+
+    def get_next_msg(self, socket_timeout=2, retry=2):
         """
         The purpose of the function is to make next message(s) available in
-        self.msg_list. If the list is not empty, nothing is done. If not, in
+        self.buffer_in. If the list is not empty, nothing is done. If not, in
         order to fill it, the function uses the data already available in
-        self.remain from a previous call and waits till there are enough to
-        dissect a TLS packet (expected length is in the 5 first bytes of the
-        packet). Once dissected, the content of the TLS packet (carried
-        messages) is appended to self.msg_list.
+        self.remain_in from a previous call and waits till there are enough to
+        dissect a TLS packet. Once dissected, the content of the TLS packet
+        (carried messages, or 'fragments') is appended to self.buffer_in.
 
-        We have to grab enough data to dissect a TLS packet, i.e. at least
-        5 bytes in order to access the expected length of the TLS packet.
+        We have to grab enough data to dissect a TLS packet. We start by
+        reading the first 2 bytes. Unless we get anything different from
+        \\x14\\x03, \\x15\\x03, \\x16\\x03 or \\x17\\x03 (which might indicate
+        an SSLv2 record, whose first 2 bytes encode the length), we retrieve
+        3 more bytes in order to get the length of the TLS record, and
+        finally we can retrieve the remaining of the record.
         """
-
-        if self.msg_list:       # a message is already available
+        if self.buffer_in:
+            # A message is already available.
             return
 
         self.socket.settimeout(socket_timeout)
-        grablen = 5
-        while retry and (grablen == 5 or len(self.remain) < grablen):
-            if grablen == 5 and len(self.remain) >= 5:
-                grablen = struct.unpack('!H', self.remain[3:5])[0] + 5
-
-            if grablen == len(self.remain):
-                break
-
-            try:
-                tmp = self.socket.recv(grablen - len(self.remain))
-                if not tmp:
-                    retry -= 1
+        is_sslv2_msg = False
+        still_getting_len = True
+        grablen = 2
+        while retry and (still_getting_len or len(self.remain_in) < grablen):
+            if not is_sslv2_msg and grablen == 5 and len(self.remain_in) >= 5:
+                grablen = struct.unpack('!H', self.remain_in[3:5])[0] + 5
+                still_getting_len = False
+            elif grablen == 2 and len(self.remain_in) >= 2:
+                byte0 = struct.unpack("B", self.remain_in[0])[0]
+                byte1 = struct.unpack("B", self.remain_in[1])[0]
+                if (byte0 in _tls_type) and (byte1 == 3):
+                    # Retry following TLS scheme. This will cause failure
+                    # for SSLv2 packets with length 0x1{4-7}03.
+                    grablen = 5
                 else:
-                    self.remain += tmp
-            except:
-                retry -= 1
-
-        if self.remain < 5 or len(self.remain) != grablen:
-            # Remote peer is not willing to respond
-            return
-
-        # Instantiate the TLS packet (record header only, at this point)
-        p = TLS(self.remain, tls_session=self.cur_session)
-        self.cur_session = p.tls_session
-        self.remain = ""
-        self.msg_list += p.msg
-
-        while p.payload:
-            if isinstance(p.payload, Raw):
-                self.remain += p.payload.load
-                p = p.payload
-            elif isinstance(p.payload, TLS):
-                p = p.payload
-                self.msg_list += p.msg
-
-
-    @ATMT.state(initial=True)
-    def INITIAL(self):
-        raise self.INIT_TLS_SESSION()
-
-    @ATMT.state()
-    def INIT_TLS_SESSION(self):
-        self.cur_session = tlsSession()
-        self.cur_session.client_certs = self.mycert
-        self.cur_session.client_key = self.mykey
-        raise self.CONNECT()
-
-    @ATMT.state()
-    def CONNECT(self):
-        s = socket.socket(self.remote_family, socket.SOCK_STREAM)
-        s.connect((self.remote_ip, self.remote_port))
-        self.socket = s
-        self.local_ip, self.local_port = self.socket.getsockname()[:2]
-        raise self.PREPARE_FIRST_PKT()
-
-    @ATMT.state()
-    def PREPARE_FIRST_PKT(self):
-        self.cur_pkt = TLS(tls_session=self.cur_session)
-
-    @ATMT.condition(PREPARE_FIRST_PKT)
-    def should_add_ClientHello(self):
-        raise self.ADDED_ClientHello()
-
-    @ATMT.action(should_add_ClientHello, prio=1)
-    def add_ClientHello(self):
-        """
-        Default TLSClientHello() offers only TLS_DHE_RSA_WITH_AES_128_CBC_SHA.
-
-        For fast server testing, typical alternatives (DHE only, RSAkx with CBC
-        only, ECDHE with appropriate extensions) may be found in tls.uts,
-        and then brought here through the client_hello argument.
-        """
-        p = self.client_hello or TLSClientHello()
-        self.cur_pkt.msg.append(p)
-
-    @ATMT.state()
-    def ADDED_ClientHello(self):
-        pass
-
-    @ATMT.condition(ADDED_ClientHello)
-    def should_send_ClientHello(self):
-        raise self.SENT_ClientHello()
-
-    @ATMT.action(should_send_ClientHello, prio=1)
-    def send_ClientHello(self):
-        self.socket.send(str(self.cur_pkt))
-        self.cur_pkt = None
-
-    @ATMT.state()
-    def SENT_ClientHello(self):
-        raise self.WAITING_FOR_ServerHello()
-
-    @ATMT.state()
-    def WAITING_FOR_ServerHello(self):
-        self.get_next_msg()
-        raise self.PREPROCESS_ServerHello()
-
-    @ATMT.state()
-    def PREPROCESS_ServerHello(self):
-        pass
-
-    @ATMT.condition(PREPROCESS_ServerHello, prio=1)
-    def should_HANDLE_ServerHello(self):
-        """
-        XXX We should check the ServerHello attributes for discrepancies with
-        our own ClientHello.
-        """
-        if (not self.msg_list or
-            not isinstance(self.msg_list[0], TLSServerHello)):
-            return
-        p = self.msg_list[0]
-        self.msg_list = self.msg_list[1:]
-        raise self.HANDLE_SH()
-
-    @ATMT.state()
-    def HANDLE_SH(self):
-        pass
-
-    @ATMT.condition(PREPROCESS_ServerHello, prio=2)
-    def missing_server_hello(self):
-        raise self.MISSING_SH()
-
-    @ATMT.state(final=True)
-    def MISSING_SH(self):
-        print("Missing TLS Server Hello message")
-
-    @ATMT.condition(HANDLE_SH, prio=1)
-    def should_HANDLE_CERT(self):
-        self.get_next_msg()
-        if (not self.msg_list or
-            not isinstance(self.msg_list[0], TLSCertificate)):
-            return
-        p = self.msg_list[0]
-        self.msg_list = self.msg_list[1:]
-        raise self.HANDLE_CERT()
-
-    @ATMT.state()
-    def HANDLE_CERT(self):
-        pass
-
-    @ATMT.condition(HANDLE_SH, prio=2)
-    def missing_certificate(self):
-        raise self.MISSING_CERT()
-
-    @ATMT.state(final=True)
-    def MISSING_CERT(self):
-        print("Missing TLS Certificate message")
-
-    @ATMT.state()
-    def HANDLE_CERT_REQ(self):
-        pass
-
-    @ATMT.condition(HANDLE_CERT, prio=1)
-    def should_HANDLE_SKE_from_CERT(self):
-        """
-        XXX We should check the ServerKeyExchange attributes for discrepancies
-        with our own ClientHello, along with the ServerHello and Certificate.
-        """
-        self.get_next_msg()
-        if (not self.msg_list or
-            not isinstance(self.msg_list[0], TLSServerKeyExchange)):
-            return
-        p = self.msg_list[0]
-        self.msg_list = self.msg_list[1:]
-        raise self.HANDLE_SKE()
-
-    @ATMT.state(final=True)
-    def MISSING_SKE(self):
-        pass
-
-    @ATMT.condition(HANDLE_CERT, prio=2)
-    def expected_server_key_exchange(self):
-        if self.cur_session.prcs.key_exchange.server_kx_msg_cls:
-            # Should have received a SKE
-            raise self.MISSING_SKE()
-
-    @ATMT.state()
-    def HANDLE_SKE(self):
-        # XXX Move that refill code somewhere else
-        self.get_next_msg()
-
-    @ATMT.condition(HANDLE_SKE, prio=2)
-    def should_HANDLE_CERT_REQ_from_SKE(self):
-        self.get_next_msg()
-        """
-        XXX We should check the CertificateRequest attributes for discrepancies
-        with the cipher suite, etc.
-        """
-        if (not self.msg_list or
-            not isinstance(self.msg_list[0], TLSCertificateRequest)):
-            return
-        p = self.msg_list[0]
-        self.msg_list = self.msg_list[1:]
-        self.cert_req = p
-        raise self.HANDLE_CERT_REQ()
-
-    @ATMT.condition(HANDLE_CERT, prio=3)
-    def should_HANDLE_CERT_REQ(self):
-        """
-        XXX We should check the CertificateRequest attributes for discrepancies
-        with the cipher suite, etc.
-        """
-        if (not self.msg_list or
-            not isinstance(self.msg_list[0], TLSCertificateRequest)):
-            return
-        p = self.msg_list[0]
-        self.msg_list = self.msg_list[1:]
-        self.cert_req = p
-        raise self.HANDLE_CERT_REQ()
-
-    @ATMT.condition(HANDLE_SKE, prio=1)
-    def should_HANDLE_SHD(self):
-        self.get_next_msg()
-        if (not self.msg_list or
-            not isinstance(self.msg_list[0], TLSServerHelloDone)):
-            return
-        p = self.msg_list[0]
-        self.msg_list = self.msg_list[1:]
-        raise self.HANDLE_SHD()
-
-    @ATMT.condition(HANDLE_CERT_REQ, prio=4)
-    def should_HANDLE_SHD_from_CERT_REQ(self):
-        if (not self.msg_list or
-            not isinstance(self.msg_list[0], TLSServerHelloDone)):
-            return
-        p = self.msg_list[0]
-        self.msg_list = self.msg_list[1:]
-        raise self.HANDLE_SHD()
-
-    @ATMT.condition(HANDLE_CERT)
-    def should_HANDLE_SHD_from_CERT(self):
-        self.get_next_msg()
-        if (not self.msg_list or
-            not isinstance(self.msg_list[0], TLSServerHelloDone)):
-            return
-        p = self.msg_list[0]
-        self.msg_list = self.msg_list[1:]
-        raise self.HANDLE_SHD()
-
-    @ATMT.state()
-    def HANDLE_SHD(self):
-        raise self.PREPARE_PKT2()
-
-    # Second packet sent by us
-    @ATMT.state()
-    def PREPARE_PKT2(self):
-        pass
-
-    @ATMT.condition(PREPARE_PKT2, prio=1)
-    def should_ADD_CLIENT_CERT(self):
-        """
-        If the server sent a CertificateRequest, we send a Certificate message.
-        If no certificate is available, an empty Certificate message is sent:
-        - this is a SHOULD in RFC 4346 (Section 7.4.6)
-        - this is a MUST in RFC 5246 (Section 7.4.6)
-
-        XXX We may want to add a complete chain.
-        """
-        if not self.cert_req:
-            return
-        certs = []
-        if self.mycert:
-            certs = [self.mycert]
-        self.cur_pkt = TLS(tls_session=self.cur_session, msg=[])
-        p = TLSCertificate(certs=certs)
-        self.cur_pkt.msg.append(p)
-        self.socket.send(str(self.cur_pkt))
-        self.cur_pkt = None
-        raise self.ADD_CLIENT_CERT()
-
-    @ATMT.state()
-    def ADD_CLIENT_CERT(self):
-        pass
-
-    @ATMT.condition(PREPARE_PKT2, prio=2)
-    def should_ADD_CKE_from_PREPARE_PKT2(self):
-        self.cur_pkt = TLS(tls_session=self.cur_session, msg=[])
-        p = TLSClientKeyExchange()
-        self.cur_pkt.msg.append(p)
-        self.socket.send(str(self.cur_pkt))
-        self.cur_pkt = None
-        raise self.ADD_CKE()
-
-    @ATMT.condition(ADD_CLIENT_CERT, prio=2)
-    def should_ADD_CKE_from_ADD_CLIENT_CERT(self):
-        self.cur_pkt = TLS(tls_session=self.cur_session, msg=[])
-        p = TLSClientKeyExchange()
-        self.cur_pkt.msg.append(p)
-        self.socket.send(str(self.cur_pkt))
-        self.cur_pkt = None
-        raise self.ADD_CKE()
-
-    @ATMT.state()
-    def ADD_CKE(self):
-        pass
-
-    @ATMT.condition(ADD_CKE, prio=1)
-    def should_ADD_CV_from_ADD_CKE(self):
-        """
-        XXX Section 7.4.7.1 of RFC 5246 states that the CertificateVerify
-        message is only sent following a client certificate that has signing
-        capability (i.e. not those containing fixed DH params).
-        We should verify that before adding the message. We should also handle
-        the case when the Certificate message was empty.
-        """
-        if (not self.cert_req or
-            self.mycert is None or
-            self.mykey is None):
-            return
-        self.cur_pkt = TLS(tls_session=self.cur_session, msg=[])
-        p = TLSCertificateVerify()
-        self.cur_pkt.msg.append(p)
-        self.socket.send(str(self.cur_pkt))
-        self.cur_pkt = None
-        raise self.ADD_CV()
-
-    @ATMT.state()
-    def ADD_CV(self):
-        pass
-
-    @ATMT.condition(ADD_CV)
-    def should_ADD_CCS_from_ADD_CV(self):
-        self.cur_pkt = TLS(type=20, tls_session=self.cur_session, msg=[])
-        p = TLSChangeCipherSpec()
-        self.cur_pkt.msg.append(p)
-        self.socket.send(str(self.cur_pkt))
-        self.cur_pkt = None
-        raise self.ADD_CCS()
-
-    @ATMT.condition(ADD_CKE, prio=2)
-    def should_ADD_CCS_from_ADD_CKE(self):
-        self.cur_pkt = TLS(type=20, tls_session=self.cur_session, msg=[])
-        p = TLSChangeCipherSpec()
-        self.cur_pkt.msg.append(p)
-        self.socket.send(str(self.cur_pkt))
-        self.cur_pkt = None
-        raise self.ADD_CCS()
-
-    @ATMT.state()
-    def ADD_CCS(self):
-        pass
-
-    @ATMT.condition(ADD_CCS)
-    def should_ADD_FINISHED(self):
-        self.cur_pkt = TLS(tls_session=self.cur_session, msg=[])
-        p = TLSFinished()
-        self.cur_pkt.msg.append(p)
-        self.socket.send(str(self.cur_pkt))
-        self.cur_pkt = None
-        raise self.ADD_FINISHED()
-
-    @ATMT.state()
-    def ADD_FINISHED(self):
-        pass
-
-    @ATMT.condition(ADD_FINISHED)
-    def should_SEND_SECOND_PKT(self):
-        raise self.SEND_SECOND_PKT()
-
-    @ATMT.state()
-    def SEND_SECOND_PKT(self):
-        raise self.WAIT_FOR_RESP2()
-
-    @ATMT.state()
-    def WAIT_FOR_RESP2(self):
-        self.socket.settimeout(10)
-        s = self.socket.recv(100000)
-        p = TLS(s, tls_session=self.cur_session)
-        self.msg_list = p.msg
-        while p.payload:
-            if isinstance(p.payload, Raw):
-                self.remain += p.payload.load
-                p = p.payload
-            elif isinstance(p.payload, TLS):
-                p = p.payload
-                self.msg_list += p.msg
-        raise self.PREPROCESS_RESP2()
-
-    # Second response from the server
-    @ATMT.state()
-    def PREPROCESS_RESP2(self):
-        pass
-
-    @ATMT.condition(PREPROCESS_RESP2)
-    def should_HANDLE_CCS(self):
-        self.get_next_msg()
-        if (not self.msg_list or
-            not isinstance(self.msg_list[0], TLSChangeCipherSpec)):
-            return
-        p = self.msg_list[0]
-        self.msg_list = self.msg_list[1:]
-        raise self.HANDLE_CCS()
-
-    @ATMT.state()
-    def HANDLE_CCS(self):
-        pass
-
-    @ATMT.condition(HANDLE_CCS)
-    def should_HANDLE_FINISHED(self):
-        self.get_next_msg()
-        if (not self.msg_list or
-            not isinstance(self.msg_list[0], TLSFinished)):
-            return
-        p = self.msg_list[0]
-        self.msg_list = self.msg_list[1:]
-        raise self.HANDLE_FINISHED()
-
-    @ATMT.state()
-    def HANDLE_FINISHED(self):
-        pass
-
-    @ATMT.condition(HANDLE_FINISHED)
-    def should_test_connection(self):
-        raise self.TESTED_CONNECTION()
-
-    @ATMT.action(should_test_connection, prio=1)
-    def send_recv_data(self):
-        """
-        XXX No live input from the user ; one unique send for now.
-        XXX We might want not to send any ApplicationData message.
-        XXX We do not wait very long for server answer.
-        """
-        txt = self.data or "GET /\r\n\r\n"  # GET HTTP/1.1\r\n\r\n"
-        p = TLS(type=23, tls_session=self.cur_session, msg=[Raw(load=txt)])
-        self.socket.send(str(p))
-        print("Sent to server: \n%r" % txt)
-
-        self.get_next_msg(1, 0)
-        if self.msg_list:
-            p = self.msg_list[0]
-            self.msg_list = self.msg_list[1:]
-            if isinstance(p, Raw):
-                print("Received from server: \n%s" % p.load)
-            else:
-                print("Received from server: \n%s" % p)
-
-    @ATMT.state()
-    def TESTED_CONNECTION(self):
-        pass
-
-    @ATMT.condition(TESTED_CONNECTION)
-    def should_close_session(self):
-        raise self.CLOSED_TLS_SESSION()
-
-    @ATMT.action(should_close_session, prio=1)
-    def close_session(self):
-        """
-        We end the session properly after 2 seconds,
-        with a TLS Alert (warning, close_notify).
-        """
-        time.sleep(2)
-        self.cur_pkt = TLS(type=21, msg=[], tls_session=self.cur_session)
-        p = TLSAlert(level=1, descr=0)
-        self.cur_pkt.msg.append(p)
-        try:
-            self.socket.send(str(self.cur_pkt))
-        except:
-            print("Could not send termination Alert (maybe the server stopped)")
-        self.cur_pkt = None
-
-    @ATMT.state()
-    def CLOSED_TLS_SESSION(self):
-        raise self.FINAL()
-
-    @ATMT.state(final=True)
-    def FINAL(self):
-        """
-        We might call shutdown, but it may happen that the server
-        did not wait for us to shutdown after answering our data query.
-        #self.socket.shutdown(1)
-        """
-        self.socket.close()
-
-
-###############################################################################
-### Server automaton                                                        ###
-###############################################################################
-
-class TLSServerAutomaton(Automaton):
-    """
-    The TLS client automaton.
-
-    - server : default value is '127.0.0.1';
-    - sport : default value is 4433;
-    - mycert : optional when there is no client authentication;
-    - mykey : optional when there is no client authentication;
-    - preferred_ciphersuite : optional cipher suite to be selected should the
-      client offer it through its ClientHello.
-    """
-
-    def parse_args(self, server="127.0.0.1", sport=4433,
-                   mycert=None, mykey=None,
-                   preferred_ciphersuite=None, **kargs):
-        Automaton.parse_args(self, **kargs)
-
-        self.mycert = Cert(mycert)
-        self.mykey  = PrivKey(mykey)
-
-        try:
-            if ':' in server:
-                socket.inet_pton(socket.AF_INET6, server)
-            else:
-                socket.inet_pton(socket.AF_INET, server)
-            tmp = socket.getaddrinfo(server, sport)
-        except:
-            tmp = socket.getaddrinfo(socket.getfqdn(server), sport)
-
-        self.ip_family = tmp[0][0]
-        self.local_ip = tmp[0][4][0]
-        self.local_port = sport
-        self.remote_ip = None
-        self.remote_port = None
-
-        self.cur_pkt = None
-        self.cur_session = None
-        self.msg_list = []
-
-        self.remain = ""
-
-        self.socket = None
-
-        self.cert_req = None
-
-        self.preferred_ciphersuite = preferred_ciphersuite
-
-
-    def get_next_msg(self):
-        """
-        The purpose of the function is to make next message(s) available
-        in self.msg_list. If the list is not empty, nothing is done. If
-        not, in order to fill it, the function uses the data already
-        available in self.remain from a previous call and waits till there
-        are enough to dissect a TLS packet (expected length is in the 5
-        first bytes of the packet). Once dissected, the content of the
-        TLS packet (carried messages) is appended to self.msg_list.
-
-        We have to grab enough data to dissect a TLS packet, i.e. at least
-        5 bytes in order to access the expected length of the TLS packet.
-        """
-
-        if self.msg_list:       # a message is already available
-            return
-
-        self.socket.settimeout(5)
-        retry = 5
-        grablen = 5
-        while retry and (grablen == 5 or len(self.remain) < grablen):
-            if grablen == 5 and len(self.remain) >= 5:
-                grablen = struct.unpack('!H', self.remain[3:5])[0] + 5
-            if grablen == len(self.remain):
+                    # Extract the SSLv2 length.
+                    is_sslv2_msg = True
+                    still_getting_len = False
+                    if byte0 & 0x80:
+                        grablen = 2 + 0 + ((byte0 & 0x7f) << 8) + byte1
+                    else:
+                        grablen = 2 + 1 + ((byte0 & 0x3f) << 8) + byte1
+            elif not is_sslv2_msg and grablen == 5 and len(self.remain_in) >= 5:
+                grablen = struct.unpack('!H', self.remain_in[3:5])[0] + 5
+
+            if grablen == len(self.remain_in):
                 break
 
             try:
-                tmp = self.socket.recv(grablen - len(self.remain))
+                tmp = self.socket.recv(grablen - len(self.remain_in))
                 if not tmp:
                     retry -= 1
                 else:
-                    self.remain += tmp
+                    self.remain_in += tmp
             except:
                 retry -= 1
 
-        if self.remain < 5 or len(self.remain) != grablen:
+        if self.remain_in < 2 or len(self.remain_in) != grablen:
             # Remote peer is not willing to respond
             return
 
-        # Instantiate TLS packet (record header only, at this point)
-        p = TLS(self.remain, tls_session=self.cur_session)
+        p = TLS(self.remain_in, tls_session=self.cur_session)
         self.cur_session = p.tls_session
-        self.remain = ""
-        self.msg_list += p.msg
+        self.remain_in = ""
+        if isinstance(p, SSLv2) and not p.msg:
+            p.msg = Raw("")
+        if self.cur_session.tls_version < 0x0304:
+            self.buffer_in += p.msg
+        else:
+            if isinstance(p, TLS13):
+                self.buffer_in += p.inner.msg
+            else:
+                # should be TLS13ServerHello only
+                self.buffer_in += p.msg
 
         while p.payload:
             if isinstance(p.payload, Raw):
-                self.remain += p.payload.load
+                self.remain_in += p.payload.load
                 p = p.payload
             elif isinstance(p.payload, TLS):
                 p = p.payload
-                self.msg_list += p.msg
-
-    @ATMT.state(initial=True)
-    def INITIAL(self):
-        raise self.INIT_TLS_SESSION()
-
-    @ATMT.state()
-    def INIT_TLS_SESSION(self):
-        """
-        XXX We should offer the right key according to the client's suites. For
-        now server_rsa_key is only used for RSAkx, but we should try to replace
-        every server_key with both server_rsa_key and server_ecdsa_key.
-        """
-        self.cur_session = tlsSession(connection_end="server")
-        self.cur_session.server_certs = [self.mycert]
-        self.cur_session.server_key = self.mykey
-        if isinstance(self.mykey, PrivKeyRSA):
-            self.cur_session.server_rsa_key = self.mykey
-        #elif isinstance(self.mykey, PrivKeyECDSA):
-        #    self.cur_session.server_ecdsa_key = self.mykey
-        raise self.BIND_AND_WAIT()
-
-    @ATMT.state()
-    def BIND_AND_WAIT(self):
-        s = socket.socket(self.ip_family, socket.SOCK_STREAM)
-        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
-        try:
-            s.bind((self.local_ip, self.local_port))
-            s.listen(1)
-        except:
-            print("Unable to bind on address %s and port %d" % (self.local_ip,
-                                                                self.local_port))
-            return
-        self.socket, addr = s.accept()
-        if not isinstance(addr, tuple):
-            addr = self.socket.getpeername()
-        if len(addr) > 2:
-            addr = (addr[0], addr[1])
-        self.remote_ip, self.remote_port = addr
-
-        raise self.WAITING_FOR_ClientHello()
-
-    @ATMT.state()
-    def WAITING_FOR_ClientHello(self):
-        self.get_next_msg()
-
-        raise self.PREPROCESS_ClientHello()
-
-    @ATMT.state()
-    def PREPROCESS_ClientHello(self):
-        pass
-
-    @ATMT.condition(PREPROCESS_ClientHello, prio=1)
-    def should_HANDLE_ClientHello(self):
-        if (not self.msg_list or
-            not isinstance(self.msg_list[0], TLSClientHello)):
-            return
-        self.cur_pkt = self.msg_list[0]
-        self.msg_list = self.msg_list[1:]
-        raise self.HANDLE_CH()
-
-    @ATMT.state()
-    def HANDLE_CH(self):
-        pass
-
-    @ATMT.condition(HANDLE_CH, prio=1)
-    def should_NO_USABLE_CIPHERSUITE(self):
-        """
-        We extract cipher suites candidates from the client's proposition.
-        """
-        l = self.cur_pkt.ciphers
-
-        if isinstance(self.mykey, PrivKeyRSA):
-            kx = "RSA"
-        elif isinstance(self.mykey, PrivKeyECDSA):
-            kx = "ECDSA"
-        l = get_usable_ciphersuites(l, kx)
-
-        if l:
-            return
-
-        raise self.NO_USABLE_CIPHERSUITE()
-
-    @ATMT.state(final=True)
-    def NO_USABLE_CIPHERSUITE(self):
-        """
-        If there is no available cipher suite, close the session with an Alert.
-        """
-        print("No usable cipher suite, closing connection")
-        self.cur_pkt = TLS(type=21, msg=[], tls_session=self.cur_session)
-        p = TLSAlert(level=1, descr=0)
-        self.cur_pkt.msg.append(p)
-        self.socket.send(str(self.cur_pkt))
-        self.cur_pkt = None
-
-    @ATMT.condition(PREPROCESS_ClientHello, prio=2)
-    def missing_client_hello(self):
-        raise self.MISSING_CH()
-
-    @ATMT.state(final=True)
-    def MISSING_CH(self):
-        print("Missing TLS Client Hello message")
-
-    @ATMT.condition(HANDLE_CH, prio=2)
-    def should_REPLY_TO_CH(self):
-        """
-        XXX Several enhancements needed here.
-
-        Selecting a cipher suite should be no trouble as we already caught the
-        None case previously. However, regarding the protocol version, we
-        might want to try resending a ClientHello when the advertised
-        version is not deemed satisfying.
-
-        Then, the sending of ServerHello, Certificate, ServerKeyExchange and
-        ServerHelloDone should be split into multiple states, in order for the
-        user to overload only the ones he's interested in.
-
-        Also, we do not manage extensions at all.
-        """
-        if isinstance(self.mykey, PrivKeyRSA):
-            kx = "RSA"
-        elif isinstance(self.mykey, PrivKeyECDSA):
-            kx = "ECDSA"
-        usable_suites = get_usable_ciphersuites(self.cur_pkt.ciphers, kx)
-        c = usable_suites[0]
-        if self.preferred_ciphersuite in usable_suites:
-            c = self.preferred_ciphersuite
-
-        comp = 0
-        if self.cur_pkt.comp and 1 in self.cur_pkt.comp:
-            comp = 1
-
-        self.cur_session.advertised_tls_version = self.cur_pkt.version
-        self.cur_session.tls_version = self.cur_pkt.version
-        #XXX there should be some checks on this version from the ClientHello
-        v = self.cur_session.tls_version
-        print("\nVersion: " + _tls_version[v])
-        print("Cipher suite: " + _tls_cipher_suites[c])
-
-        self.cur_pkt = TLS(tls_session=self.cur_session, msg=[])
-
-        p = TLSServerHello(cipher=c, comp=[comp])
-        self.cur_pkt.msg.append(p)
-
-        p = TLSCertificate(certs=self.cur_session.server_certs)
-        self.cur_pkt.msg.append(p)
-
-        if not _tls_cipher_suites_cls[c].kx_alg.no_ske:
-            p = TLSServerKeyExchange()
-            self.cur_pkt.msg.append(p)
-
-        p = TLSServerHelloDone()
-        self.cur_pkt.msg.append(p)
-
-        self.socket.send(str(self.cur_pkt))
-        self.cur_pkt = None
-        raise self.SENT_SH()
-
-    @ATMT.state()
-    def SENT_SH(self):
-        pass
-
-    @ATMT.condition(SENT_SH, prio=1)
-    def should_HANDLE_CKE(self):
-        self.get_next_msg()
-        if (not self.msg_list or
-            not isinstance(self.msg_list[0], TLSClientKeyExchange)):
-            return
-        self.cur_pkt = self.msg_list[0]
-        self.msg_list = self.msg_list[1:]
-        raise self.HANDLE_CKE()
-
-    @ATMT.state()
-    def HANDLE_CKE(self):
-        pass
-
-    @ATMT.condition(SENT_SH, prio=2)
-    def should_HANDLE_ALERT_INSTEAD_OF_CKE(self):
-        self.get_next_msg()
-        if (not self.msg_list or
-            not isinstance(self.msg_list[0], TLSAlert)):
-            return
-        self.cur_pkt = self.msg_list[0]
-        self.msg_list = self.msg_list[1:]
-        raise self.HANDLE_ALERT_INSTEAD_OF_CKE()
-
-    @ATMT.state()
-    def HANDLE_ALERT_INSTEAD_OF_CKE(self):
-        print("Received Alert message instead of CKE")
-
-    @ATMT.condition(SENT_SH, prio=3)
-    def should_HANDLE_MISSING_CKE(self):
-        raise self.HANDLE_MISSING_CKE()
-
-    @ATMT.state()
-    def HANDLE_MISSING_CKE(self):
-        print("Missing CKE in client's reply")
-
-
-    @ATMT.condition(HANDLE_CKE, prio=1)
-    def should_HANDLE_CCS(self):
-        self.get_next_msg()
-        if (not self.msg_list or
-            not isinstance(self.msg_list[0], TLSChangeCipherSpec)):
-            return
-        self.cur_pkt = self.msg_list[0]
-        self.msg_list = self.msg_list[1:]
-        raise self.HANDLE_CCS()
-
-    @ATMT.state()
-    def HANDLE_CCS(self):
-        pass
-
-    @ATMT.condition(HANDLE_CKE, prio=2)
-    def should_HANDLE_ALERT_INSTEAD_OF_CCS(self):
-        self.get_next_msg()
-        if (not self.msg_list or
-            not isinstance(self.msg_list[0], TLSAlert)):
-            return
-        self.cur_pkt = self.msg_list[0]
-        self.msg_list = self.msg_list[1:]
-
-        raise self.HANDLE_ALERT_INSTEAD_OF_CCS()
-
-    @ATMT.state()
-    def HANDLE_ALERT_INSTEAD_OF_CCS(self):
-        print("Received Alert message instead of CCS")
-
-    @ATMT.condition(HANDLE_CKE, prio=3)
-    def should_HANDLE_MISSING_CCS(self):
-        raise self.HANDLE_MISSING_CCS()
-
-    @ATMT.state()
-    def HANDLE_MISSING_CCS(self):
-        print("Missing CCS in client's reply")
-
-    @ATMT.condition(HANDLE_CCS, prio=1)
-    def should_HANDLE_Finished(self):
-        self.get_next_msg()
-        if (not self.msg_list or
-            not isinstance(self.msg_list[0], TLSFinished)):
-            return
-        self.cur_pkt = self.msg_list[0]
-        self.msg_list = self.msg_list[1:]
-        raise self.HANDLE_FINISHED()
-
-    @ATMT.state()
-    def HANDLE_FINISHED(self):
-        pass
-
-    @ATMT.condition(HANDLE_CCS, prio=2)
-    def should_HANDLE_ALERT_INSTEAD_OF_Finished(self):
-        self.get_next_msg()
-        if (not self.msg_list or
-            not isinstance(self.msg_list[0], TLSAlert)):
-            return
-        self.cur_pkt = self.msg_list[0]
-        self.msg_list = self.msg_list[1:]
-        raise self.HANDLE_ALERT_INSTEAD_OF_FINISHED()
-
-    @ATMT.state()
-    def HANDLE_ALERT_INSTEAD_OF_FINISHED(self):
-        print("Received Alert message instead of Finished")
-
-    @ATMT.condition(HANDLE_CCS, prio=3)
-    def should_HANDLE_MISSING_FINISHED(self):
-        raise self.HANDLE_MISSING_FINISHED()
-
-    @ATMT.state()
-    def HANDLE_MISSING_FINISHED(self):
-        print("Missing Finished in client's reply")
-
-    @ATMT.condition(HANDLE_FINISHED, prio=1)
-    def should_SEND_CCS(self):
-        ccs = TLSChangeCipherSpec()
-        self.cur_pkt = TLS(type=20, msg=[ccs], tls_session=self.cur_session)
-        self.socket.send(str(self.cur_pkt))
-        self.cur_pkt = None
-        raise self.SEND_CCS()
-
-    @ATMT.state()
-    def SEND_CCS(self):
-        pass
-
-    @ATMT.condition(SEND_CCS, prio=2)
-    def should_SEND_FINISHED(self):
-        p = TLSFinished()
-        self.cur_pkt = TLS(tls_session=self.cur_session, msg=[p])
-        self.socket.send(str(self.cur_pkt))
-        self.cur_pkt = None
-        raise self.FINISHED_SENT()
-
-    @ATMT.state()
-    def FINISHED_SENT(self):
-        pass
-
-    @ATMT.condition(FINISHED_SENT, prio=0)
-    def should_HANDLE_NO_CLIENT(self):
-        self.get_next_msg()
-        if self.msg_list:
-            return
-        print("Client left. Closing connection...")
-        raise self.FINAL()
-
-    @ATMT.condition(FINISHED_SENT, prio=1)
-    def should_HANDLE_ALERT_FROM_FINISHED(self):
-        self.get_next_msg()
-        if (not self.msg_list or
-            not isinstance(self.msg_list[0], TLSAlert)):
-            return
-        raise self.HANDLE_ALERT_FROM_FINISHED_SENT()
-
-    @ATMT.state()
-    def HANDLE_ALERT_FROM_FINISHED_SENT(self):
-        self.cur_pkt = self.msg_list[0]
-        self.msg_list = self.msg_list[1:]
-        print("Received Alert Message after sending Finished")
-        print("Closing connection")
-        #XXX no support for new connections, for now
-        raise self.FINAL()
-
-    @ATMT.condition(FINISHED_SENT, prio=2)
-    def should_WAIT_DATA(self):
-        self.get_next_msg()
-        if self.msg_list:
-            return
-        # Client did not send anything, let's wait
-        raise self.FINISHED_SENT()
-
-    @ATMT.condition(FINISHED_SENT, prio=3)
-    def should_PROCESS_DATA(self):
-        self.get_next_msg()
-        if (not self.msg_list or
-            not isinstance(self.msg_list[0], TLSApplicationData)):
-            return
-        raise self.PROCESS_DATA()
+                if self.cur_session.tls_version < 0x0304:
+                    self.buffer_in += p.msg
+                else:
+                    self.buffer_in += p.inner.msg
+
+    def raise_on_packet(self, pkt_cls, state, get_next_msg=True):
+        """
+        If the next message to be processed has type 'pkt_cls', raise 'state'.
+        If there is no message waiting to be processed, we try to get one with
+        the default 'get_next_msg' parameters.
+        """
+        # Maybe we already parsed the expected packet, maybe not.
+        if get_next_msg:
+            self.get_next_msg()
+        if (not self.buffer_in or
+            not isinstance(self.buffer_in[0], pkt_cls)):
+            return
+        self.cur_pkt = self.buffer_in[0]
+        self.buffer_in = self.buffer_in[1:]
+        raise state()
+
+    def add_record(self, is_sslv2=None, is_tls13=None):
+        """
+        Add a new TLS or SSLv2 or TLS 1.3 record to the packets buffered out.
+        """
+        if is_sslv2 is None and is_tls13 is None:
+            v = (self.cur_session.tls_version or
+                 self.cur_session.advertised_tls_version)
+            if v in [0x0200, 0x0002]:
+                is_sslv2 = True
+            elif v >= 0x0304:
+                is_tls13 = True
+        if is_sslv2:
+            self.buffer_out.append(SSLv2(tls_session=self.cur_session))
+        elif is_tls13:
+            self.buffer_out.append(TLS13(tls_session=self.cur_session))
+        else:
+            self.buffer_out.append(TLS(tls_session=self.cur_session))
 
-    @ATMT.state()
-    def PROCESS_DATA(self):
+    def add_msg(self, pkt):
         """
-        In the beginning, we return a small page with useful information.
-        Then, we act as an echo server.
+        Add a TLS message (e.g. TLSClientHello or TLSApplicationData)
+        inside the latest record to be sent through the socket.
+        We believe a good automaton should not use the first test.
         """
-        self.cur_pkt = self.msg_list[0]
-        self.msg_list = self.msg_list[1:]
-
-        recv_data = self.cur_pkt.data
-        print("Received %s" % repr(recv_data))
-
-        if recv_data.startswith("GET / HTTP/1."):
-            header  = "HTTP/1.1 200 OK\r\n"
-            header += "Server: Scapy TLS Extension\r\n"
-            header += "Content-type: text/html\r\n"
-            header += "Content-length: %d\r\n\r\n"
-            s  = "Information on current TLS session:\n\n"
-            s += "Local end      : %s:%d\n" % (self.local_ip, self.local_port)
-            s += "Remote end     : %s:%d\n" % (self.remote_ip, self.remote_port)
-            v = self.cur_session.advertised_tls_version
-            v = "%s (0x%04x)" % (_tls_version[v], v)
-            s += "TLS version    : %s\n" % v
-            s += repr(self.cur_session.wcs)
-            body = "<html><body><pre>%s</pre></body></html>\r\n\r\n" % s
-            page = (header+body) % len(body)
+        if not self.buffer_out:
+            self.add_record()
+        r = self.buffer_out[-1]
+        if isinstance(r, TLS13):
+            self.buffer_out[-1].inner.msg.append(pkt)
         else:
-            page = recv_data
-
-        p = Raw(load=page)
-        self.cur_pkt = TLS(type=23, msg=[p], tls_session=self.cur_session)
-        self.socket.send(str(self.cur_pkt))
-        raise self.FINISHED_SENT()
+            self.buffer_out[-1].msg.append(pkt)
 
-    @ATMT.state(final=True)
-    def FINAL(self):
+    def flush_records(self):
         """
-        We might call shutdown, but unit tests with s_client fail with this.
-        #self.socket.shutdown(1)
+        Send all buffered records and update the session accordingly.
         """
-        self.socket.close()
+        s = "".join([p.str_stateful() for p in self.buffer_out])
+        self.socket.send(s)
+        self.buffer_out = []
+
+    def vprint(self, s=""):
+        if self.verbose:
+            print("> %s" % s)
 
diff --git a/scapy/layers/tls/automaton_cli.py b/scapy/layers/tls/automaton_cli.py
new file mode 100644
index 0000000000000000000000000000000000000000..0a4bb807dc8987c99a2e4332c08429cd4acef784
--- /dev/null
+++ b/scapy/layers/tls/automaton_cli.py
@@ -0,0 +1,946 @@
+## This file is part of Scapy
+## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
+##               2015, 2016, 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+TLS client automaton. This makes for a primitive TLS stack.
+Obviously you need rights for network access.
+
+We support versions SSLv2 to TLS 1.2, along with many features.
+There is no session resumption mechanism for now.
+
+In order to run a client to tcp/50000 with one cipher suite of your choice:
+> from scapy.all import *
+> ch = TLSClientHello(ciphers=<int code of the cipher suite>)
+> t = TLSClientAutomaton(dport=50000, client_hello=ch)
+> t.run()
+"""
+
+from __future__ import print_function
+import socket
+
+from scapy.utils import randstring
+from scapy.automaton import ATMT
+from scapy.layers.tls.automaton import _TLSAutomaton
+from scapy.layers.tls.basefields import _tls_version, _tls_version_options
+from scapy.layers.tls.session import tlsSession
+from scapy.layers.tls.extensions import (TLS_Ext_SupportedGroups,
+                                         TLS_Ext_SupportedVersions,
+                                         TLS_Ext_SignatureAlgorithms,
+                                         TLS_Ext_ServerName, ServerName)
+from scapy.layers.tls.handshake import *
+from scapy.layers.tls.handshake_sslv2 import *
+from scapy.layers.tls.keyexchange_tls13 import (TLS_Ext_KeyShare_CH,
+                                                KeyShareEntry)
+from scapy.layers.tls.record import (TLS, TLSAlert, TLSChangeCipherSpec,
+                                     TLSApplicationData)
+
+
+class TLSClientAutomaton(_TLSAutomaton):
+    """
+    A simple TLS test client automaton. Try to overload some states or
+    conditions and see what happens on the other side.
+
+    Rather than with an interruption, the best way to stop this client is by
+    typing 'quit'. This won't be a message sent to the server.
+
+    _'mycert' and 'mykey' may be provided as filenames. They will be used in
+    the handshake, should the server ask for client authentication.
+    _'server_name' does not need to be set.
+    _'client_hello' may hold a TLSClientHello or SSLv2ClientHello to be sent
+    to the server. This is particularly useful for extensions tweaking.
+    _'version' is a quicker way to advertise a protocol version ("sslv2",
+    "tls1", "tls12", etc.) It may be overriden by the previous 'client_hello'.
+    _'data' is a list of raw data to be sent to the server once the handshake
+    has been completed. Both 'stop_server' and 'quit' will work this way.
+    """
+
+    def parse_args(self, server="127.0.0.1", dport=4433, server_name=None,
+                         mycert=None, mykey=None,
+                         client_hello=None, version=None,
+                         data=None,
+                         **kargs):
+
+        super(TLSClientAutomaton, self).parse_args(mycert=mycert,
+                                                   mykey=mykey,
+                                                   **kargs)
+        tmp = socket.getaddrinfo(server, dport)
+        self.remote_name = None
+        try:
+            if ':' in server:
+                socket.inet_pton(socket.AF_INET6, server)
+            else:
+                socket.inet_pton(socket.AF_INET, server)
+        except:
+            self.remote_name = socket.getfqdn(server)
+            if self.remote_name != server:
+                tmp = socket.getaddrinfo(self.remote_name, dport)
+
+        if server_name:
+            self.remote_name = server_name
+        self.remote_family = tmp[0][0]
+        self.remote_ip = tmp[0][4][0]
+        self.remote_port = dport
+        self.local_ip = None
+        self.local_port = None
+        self.socket = None
+
+        self.client_hello = client_hello
+        self.advertised_tls_version = None
+        if version:
+            v = _tls_version_options.get(version, None)
+            if not v:
+                self.vprint("Unrecognized TLS version option.")
+            else:
+                self.advertised_tls_version = v
+
+        self.linebreak = False
+        if isinstance(data, str):
+            self.data_to_send = [data]
+        elif isinstance(data, list):
+            # parse_args is called two times, this is why we have to copy
+            # the data list for reversing it afterwards...
+            self.data_to_send = list(data)
+            self.data_to_send.reverse()
+        else:
+            self.data_to_send = []
+
+
+    def vprint_sessioninfo(self):
+        if self.verbose:
+            s = self.cur_session
+            v = _tls_version[s.tls_version]
+            self.vprint("Version       : %s" % v)
+            cs = s.wcs.ciphersuite.name
+            self.vprint("Cipher suite  : %s" % cs)
+            if s.tls_version >= 0x0304:
+                ms = s.tls13_master_secret
+            else:
+                ms = s.master_secret
+            self.vprint("Master secret : %s" % repr_hex(ms))
+            if s.server_certs:
+                self.vprint("Server certificate chain: %r" % s.server_certs)
+            self.vprint()
+
+
+    @ATMT.state(initial=True)
+    def INITIAL(self):
+        self.vprint("Starting TLS client automaton.")
+        raise self.INIT_TLS_SESSION()
+
+    @ATMT.state()
+    def INIT_TLS_SESSION(self):
+        self.cur_session = tlsSession(connection_end="client")
+        self.cur_session.client_certs = self.mycert
+        self.cur_session.client_key = self.mykey
+        v = self.advertised_tls_version
+        if v:
+            self.cur_session.advertised_tls_version = v
+        else:
+            default_version = self.cur_session.advertised_tls_version
+            self.advertised_tls_version = default_version
+        raise self.CONNECT()
+
+    @ATMT.state()
+    def CONNECT(self):
+        s = socket.socket(self.remote_family, socket.SOCK_STREAM)
+        self.vprint()
+        self.vprint("Trying to connect on %s:%d" % (self.remote_ip,
+                                                    self.remote_port))
+        s.connect((self.remote_ip, self.remote_port))
+        self.socket = s
+        self.local_ip, self.local_port = self.socket.getsockname()[:2]
+        self.vprint()
+        if self.cur_session.advertised_tls_version in [0x0200, 0x0002]:
+            raise self.SSLv2_PREPARE_CLIENTHELLO()
+        elif self.cur_session.advertised_tls_version >= 0x0304:
+            raise self.TLS13_START()
+        else:
+            raise self.PREPARE_CLIENTFLIGHT1()
+
+    ########################### TLS handshake #################################
+
+    @ATMT.state()
+    def PREPARE_CLIENTFLIGHT1(self):
+        self.add_record()
+
+    @ATMT.condition(PREPARE_CLIENTFLIGHT1)
+    def should_add_ClientHello(self):
+        self.add_msg(self.client_hello or TLSClientHello())
+        raise self.ADDED_CLIENTHELLO()
+
+    @ATMT.state()
+    def ADDED_CLIENTHELLO(self):
+        pass
+
+    @ATMT.condition(ADDED_CLIENTHELLO)
+    def should_send_ClientFlight1(self):
+        self.flush_records()
+        raise self.SENT_CLIENTFLIGHT1()
+
+    @ATMT.state()
+    def SENT_CLIENTFLIGHT1(self):
+        raise self.WAITING_SERVERFLIGHT1()
+
+    @ATMT.state()
+    def WAITING_SERVERFLIGHT1(self):
+        self.get_next_msg()
+        raise self.RECEIVED_SERVERFLIGHT1()
+
+    @ATMT.state()
+    def RECEIVED_SERVERFLIGHT1(self):
+        pass
+
+    @ATMT.condition(RECEIVED_SERVERFLIGHT1, prio=1)
+    def should_handle_ServerHello(self):
+        """
+        XXX We should check the ServerHello attributes for discrepancies with
+        our own ClientHello.
+        """
+        self.raise_on_packet(TLSServerHello,
+                             self.HANDLED_SERVERHELLO)
+
+    @ATMT.state()
+    def HANDLED_SERVERHELLO(self):
+        pass
+
+    @ATMT.condition(RECEIVED_SERVERFLIGHT1, prio=2)
+    def missing_ServerHello(self):
+        raise self.MISSING_SERVERHELLO()
+
+    @ATMT.state()
+    def MISSING_SERVERHELLO(self):
+        self.vprint("Missing TLS ServerHello message!")
+        raise self.CLOSE_NOTIFY()
+
+    @ATMT.condition(HANDLED_SERVERHELLO, prio=1)
+    def should_handle_ServerCertificate(self):
+        if not self.cur_session.prcs.key_exchange.anonymous:
+            self.raise_on_packet(TLSCertificate,
+                                 self.HANDLED_SERVERCERTIFICATE)
+        raise self.HANDLED_SERVERCERTIFICATE()
+
+    @ATMT.state()
+    def HANDLED_SERVERCERTIFICATE(self):
+        pass
+
+    @ATMT.condition(HANDLED_SERVERHELLO, prio=2)
+    def missing_ServerCertificate(self):
+        raise self.MISSING_SERVERCERTIFICATE()
+
+    @ATMT.state()
+    def MISSING_SERVERCERTIFICATE(self):
+        self.vprint("Missing TLS Certificate message!")
+        raise self.CLOSE_NOTIFY()
+
+    @ATMT.state()
+    def HANDLED_CERTIFICATEREQUEST(self):
+        self.vprint("Server asked for a certificate...")
+        if not self.mykey or not self.mycert:
+            self.vprint("No client certificate to send!")
+            self.vprint("Will try and send an empty Certificate message...")
+
+    @ATMT.condition(HANDLED_SERVERCERTIFICATE, prio=1)
+    def should_handle_ServerKeyExchange_from_ServerCertificate(self):
+        """
+        XXX We should check the ServerKeyExchange attributes for discrepancies
+        with our own ClientHello, along with the ServerHello and Certificate.
+        """
+        self.raise_on_packet(TLSServerKeyExchange,
+                             self.HANDLED_SERVERKEYEXCHANGE)
+
+    @ATMT.state(final=True)
+    def MISSING_SERVERKEYEXCHANGE(self):
+        pass
+
+    @ATMT.condition(HANDLED_SERVERCERTIFICATE, prio=2)
+    def missing_ServerKeyExchange(self):
+        if not self.cur_session.prcs.key_exchange.no_ske:
+            raise self.MISSING_SERVERKEYEXCHANGE()
+
+    @ATMT.state()
+    def HANDLED_SERVERKEYEXCHANGE(self):
+        pass
+
+    def should_handle_CertificateRequest(self):
+        """
+        XXX We should check the CertificateRequest attributes for discrepancies
+        with the cipher suite, etc.
+        """
+        self.raise_on_packet(TLSCertificateRequest,
+                             self.HANDLED_CERTIFICATEREQUEST)
+
+    @ATMT.condition(HANDLED_SERVERKEYEXCHANGE, prio=2)
+    def should_handle_CertificateRequest_from_ServerKeyExchange(self):
+        self.should_handle_CertificateRequest()
+
+    @ATMT.condition(HANDLED_SERVERCERTIFICATE, prio=3)
+    def should_handle_CertificateRequest_from_ServerCertificate(self):
+        self.should_handle_CertificateRequest()
+
+    def should_handle_ServerHelloDone(self):
+        self.raise_on_packet(TLSServerHelloDone,
+                             self.HANDLED_SERVERHELLODONE)
+
+    @ATMT.condition(HANDLED_SERVERKEYEXCHANGE, prio=1)
+    def should_handle_ServerHelloDone_from_ServerKeyExchange(self):
+        return self.should_handle_ServerHelloDone()
+
+    @ATMT.condition(HANDLED_CERTIFICATEREQUEST, prio=4)
+    def should_handle_ServerHelloDone_from_CertificateRequest(self):
+        return self.should_handle_ServerHelloDone()
+
+    @ATMT.condition(HANDLED_SERVERCERTIFICATE, prio=4)
+    def should_handle_ServerHelloDone_from_ServerCertificate(self):
+        return self.should_handle_ServerHelloDone()
+
+    @ATMT.state()
+    def HANDLED_SERVERHELLODONE(self):
+        raise self.PREPARE_CLIENTFLIGHT2()
+
+    @ATMT.state()
+    def PREPARE_CLIENTFLIGHT2(self):
+        self.add_record()
+
+    @ATMT.condition(PREPARE_CLIENTFLIGHT2, prio=1)
+    def should_add_ClientCertificate(self):
+        """
+        If the server sent a CertificateRequest, we send a Certificate message.
+        If no certificate is available, an empty Certificate message is sent:
+        - this is a SHOULD in RFC 4346 (Section 7.4.6)
+        - this is a MUST in RFC 5246 (Section 7.4.6)
+
+        XXX We may want to add a complete chain.
+        """
+        hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed]
+        if not TLSCertificateRequest in hs_msg:
+            return
+        certs = []
+        if self.mycert:
+            certs = [self.mycert]
+        self.add_msg(TLSCertificate(certs=certs))
+        raise self.ADDED_CLIENTCERTIFICATE()
+
+    @ATMT.state()
+    def ADDED_CLIENTCERTIFICATE(self):
+        pass
+
+    def should_add_ClientKeyExchange(self):
+        self.add_msg(TLSClientKeyExchange())
+        raise self.ADDED_CLIENTKEYEXCHANGE()
+
+    @ATMT.condition(PREPARE_CLIENTFLIGHT2, prio=2)
+    def should_add_ClientKeyExchange_from_ClientFlight2(self):
+        return self.should_add_ClientKeyExchange()
+
+    @ATMT.condition(ADDED_CLIENTCERTIFICATE)
+    def should_add_ClientKeyExchange_from_ClientCertificate(self):
+        return self.should_add_ClientKeyExchange()
+
+    @ATMT.state()
+    def ADDED_CLIENTKEYEXCHANGE(self):
+        pass
+
+    @ATMT.condition(ADDED_CLIENTKEYEXCHANGE, prio=1)
+    def should_add_ClientVerify(self):
+        """
+        XXX Section 7.4.7.1 of RFC 5246 states that the CertificateVerify
+        message is only sent following a client certificate that has signing
+        capability (i.e. not those containing fixed DH params).
+        We should verify that before adding the message. We should also handle
+        the case when the Certificate message was empty.
+        """
+        hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed]
+        if (not TLSCertificateRequest in hs_msg or
+            self.mycert is None or
+            self.mykey is None):
+            return
+        self.add_msg(TLSCertificateVerify())
+        raise self.ADDED_CERTIFICATEVERIFY()
+
+    @ATMT.state()
+    def ADDED_CERTIFICATEVERIFY(self):
+        pass
+
+    @ATMT.condition(ADDED_CERTIFICATEVERIFY)
+    def should_add_ChangeCipherSpec_from_CertificateVerify(self):
+        self.add_record()
+        self.add_msg(TLSChangeCipherSpec())
+        raise self.ADDED_CHANGECIPHERSPEC()
+
+    @ATMT.condition(ADDED_CLIENTKEYEXCHANGE, prio=2)
+    def should_add_ChangeCipherSpec_from_ClientKeyExchange(self):
+        self.add_record()
+        self.add_msg(TLSChangeCipherSpec())
+        raise self.ADDED_CHANGECIPHERSPEC()
+
+    @ATMT.state()
+    def ADDED_CHANGECIPHERSPEC(self):
+        pass
+
+    @ATMT.condition(ADDED_CHANGECIPHERSPEC)
+    def should_add_ClientFinished(self):
+        self.add_record()
+        self.add_msg(TLSFinished())
+        raise self.ADDED_CLIENTFINISHED()
+
+    @ATMT.state()
+    def ADDED_CLIENTFINISHED(self):
+        pass
+
+    @ATMT.condition(ADDED_CLIENTFINISHED)
+    def should_send_ClientFlight2(self):
+        self.flush_records()
+        raise self.SENT_CLIENTFLIGHT2()
+
+    @ATMT.state()
+    def SENT_CLIENTFLIGHT2(self):
+        raise self.WAITING_SERVERFLIGHT2()
+
+    @ATMT.state()
+    def WAITING_SERVERFLIGHT2(self):
+        self.get_next_msg()
+        raise self.RECEIVED_SERVERFLIGHT2()
+
+    @ATMT.state()
+    def RECEIVED_SERVERFLIGHT2(self):
+        pass
+
+    @ATMT.condition(RECEIVED_SERVERFLIGHT2)
+    def should_handle_ChangeCipherSpec(self):
+        self.raise_on_packet(TLSChangeCipherSpec,
+                             self.HANDLED_CHANGECIPHERSPEC)
+
+    @ATMT.state()
+    def HANDLED_CHANGECIPHERSPEC(self):
+        pass
+
+    @ATMT.condition(HANDLED_CHANGECIPHERSPEC)
+    def should_handle_Finished(self):
+        self.raise_on_packet(TLSFinished,
+                             self.HANDLED_SERVERFINISHED)
+
+    @ATMT.state()
+    def HANDLED_SERVERFINISHED(self):
+        self.vprint("TLS handshake completed!")
+        self.vprint_sessioninfo()
+        self.vprint("You may send data or use 'quit'.")
+
+    ####################### end of TLS handshake ##############################
+
+    @ATMT.condition(HANDLED_SERVERFINISHED)
+    def should_wait_ClientData(self):
+        raise self.WAIT_CLIENTDATA()
+
+    @ATMT.state()
+    def WAIT_CLIENTDATA(self):
+        pass
+
+    @ATMT.condition(WAIT_CLIENTDATA, prio=1)
+    def add_ClientData(self):
+        """
+        The user may type in:
+        GET / HTTP/1.1\r\nHost: testserver.com\r\n\r\n
+        Special characters are handled so that it becomes a valid HTTP request.
+        """
+        if not self.data_to_send:
+            data = raw_input()
+            data = data.replace("\\r", "\r")
+            data = data.replace("\\n", "\n")
+        else:
+            data = self.data_to_send.pop()
+        if data == "quit":
+            return
+        if self.linebreak:
+            data += "\n"
+        self.add_record()
+        self.add_msg(TLSApplicationData(data=data))
+        raise self.ADDED_CLIENTDATA()
+
+    @ATMT.condition(WAIT_CLIENTDATA, prio=2)
+    def no_more_ClientData(self):
+        raise self.CLOSE_NOTIFY()
+
+    @ATMT.state()
+    def ADDED_CLIENTDATA(self):
+        pass
+
+    @ATMT.condition(ADDED_CLIENTDATA)
+    def should_send_ClientData(self):
+        self.flush_records()
+        raise self.SENT_CLIENTDATA()
+
+    @ATMT.state()
+    def SENT_CLIENTDATA(self):
+        raise self.WAITING_SERVERDATA()
+
+    @ATMT.state()
+    def WAITING_SERVERDATA(self):
+        self.get_next_msg(0.3, 1)
+        raise self.RECEIVED_SERVERDATA()
+
+    @ATMT.state()
+    def RECEIVED_SERVERDATA(self):
+        pass
+
+    @ATMT.condition(RECEIVED_SERVERDATA, prio=1)
+    def should_handle_ServerData(self):
+        if not self.buffer_in:
+            raise self.WAIT_CLIENTDATA()
+        p = self.buffer_in[0]
+        if isinstance(p, TLSApplicationData):
+            print("> Received: %s" % p.data)
+        elif isinstance(p, TLSAlert):
+            print("> Received: %r" % p)
+            raise self.CLOSE_NOTIFY()
+        else:
+            print("> Received: %r" % p)
+        self.buffer_in = self.buffer_in[1:]
+        raise self.HANDLED_SERVERDATA()
+
+    @ATMT.state()
+    def HANDLED_SERVERDATA(self):
+        raise self.WAIT_CLIENTDATA()
+
+    @ATMT.state()
+    def CLOSE_NOTIFY(self):
+        self.vprint()
+        self.vprint("Trying to send a TLSAlert to the server...")
+
+    @ATMT.condition(CLOSE_NOTIFY)
+    def close_session(self):
+        self.add_record()
+        self.add_msg(TLSAlert(level=1, descr=0))
+        try:
+            self.flush_records()
+        except:
+            self.vprint("Could not send termination Alert, maybe the server stopped?")
+        raise self.FINAL()
+
+    ########################## SSLv2 handshake ################################
+
+    @ATMT.state()
+    def SSLv2_PREPARE_CLIENTHELLO(self):
+        pass
+
+    @ATMT.condition(SSLv2_PREPARE_CLIENTHELLO)
+    def sslv2_should_add_ClientHello(self):
+        self.add_record(is_sslv2=True)
+        p = self.client_hello or SSLv2ClientHello(challenge=randstring(16))
+        self.add_msg(p)
+        raise self.SSLv2_ADDED_CLIENTHELLO()
+
+    @ATMT.state()
+    def SSLv2_ADDED_CLIENTHELLO(self):
+        pass
+
+    @ATMT.condition(SSLv2_ADDED_CLIENTHELLO)
+    def sslv2_should_send_ClientHello(self):
+        self.flush_records()
+        raise self.SSLv2_SENT_CLIENTHELLO()
+
+    @ATMT.state()
+    def SSLv2_SENT_CLIENTHELLO(self):
+        raise self.SSLv2_WAITING_SERVERHELLO()
+
+    @ATMT.state()
+    def SSLv2_WAITING_SERVERHELLO(self):
+        self.get_next_msg()
+        raise self.SSLv2_RECEIVED_SERVERHELLO()
+
+    @ATMT.state()
+    def SSLv2_RECEIVED_SERVERHELLO(self):
+        pass
+
+    @ATMT.condition(SSLv2_RECEIVED_SERVERHELLO, prio=1)
+    def sslv2_should_handle_ServerHello(self):
+        self.raise_on_packet(SSLv2ServerHello,
+                             self.SSLv2_HANDLED_SERVERHELLO)
+
+    @ATMT.state()
+    def SSLv2_HANDLED_SERVERHELLO(self):
+        pass
+
+    @ATMT.condition(SSLv2_RECEIVED_SERVERHELLO, prio=2)
+    def sslv2_missing_ServerHello(self):
+        raise self.SSLv2_MISSING_SERVERHELLO()
+
+    @ATMT.state()
+    def SSLv2_MISSING_SERVERHELLO(self):
+        self.vprint("Missing SSLv2 ServerHello message!")
+        raise self.SSLv2_CLOSE_NOTIFY()
+
+    @ATMT.condition(SSLv2_HANDLED_SERVERHELLO)
+    def sslv2_should_add_ClientMasterKey(self):
+        self.add_record(is_sslv2=True)
+        self.add_msg(SSLv2ClientMasterKey())
+        raise self.SSLv2_ADDED_CLIENTMASTERKEY()
+
+    @ATMT.state()
+    def SSLv2_ADDED_CLIENTMASTERKEY(self):
+        pass
+
+    @ATMT.condition(SSLv2_ADDED_CLIENTMASTERKEY)
+    def sslv2_should_send_ClientMasterKey(self):
+        self.flush_records()
+        raise self.SSLv2_SENT_CLIENTMASTERKEY()
+
+    @ATMT.state()
+    def SSLv2_SENT_CLIENTMASTERKEY(self):
+        raise self.SSLv2_WAITING_SERVERVERIFY()
+
+    @ATMT.state()
+    def SSLv2_WAITING_SERVERVERIFY(self):
+        # We give the server 0.5 second to send his ServerVerify.
+        # Else we assume that he's waiting for our ClientFinished.
+        self.get_next_msg(0.5, 0)
+        raise self.SSLv2_RECEIVED_SERVERVERIFY()
+
+    @ATMT.state()
+    def SSLv2_RECEIVED_SERVERVERIFY(self):
+        pass
+
+    @ATMT.condition(SSLv2_RECEIVED_SERVERVERIFY, prio=1)
+    def sslv2_should_handle_ServerVerify(self):
+        self.raise_on_packet(SSLv2ServerVerify,
+                             self.SSLv2_HANDLED_SERVERVERIFY,
+                             get_next_msg=False)
+
+    @ATMT.state()
+    def SSLv2_HANDLED_SERVERVERIFY(self):
+        pass
+
+    def sslv2_should_add_ClientFinished(self):
+        hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed]
+        if SSLv2ClientFinished in hs_msg:
+            return
+        self.add_record(is_sslv2=True)
+        self.add_msg(SSLv2ClientFinished())
+        raise self.SSLv2_ADDED_CLIENTFINISHED()
+
+    @ATMT.condition(SSLv2_HANDLED_SERVERVERIFY, prio=1)
+    def sslv2_should_add_ClientFinished_from_ServerVerify(self):
+        return self.sslv2_should_add_ClientFinished()
+
+    @ATMT.condition(SSLv2_HANDLED_SERVERVERIFY, prio=2)
+    def sslv2_should_wait_ServerFinished_from_ServerVerify(self):
+        raise self.SSLv2_WAITING_SERVERFINISHED()
+
+    @ATMT.condition(SSLv2_RECEIVED_SERVERVERIFY, prio=2)
+    def sslv2_should_add_ClientFinished_from_NoServerVerify(self):
+        return self.sslv2_should_add_ClientFinished()
+
+    @ATMT.condition(SSLv2_RECEIVED_SERVERVERIFY, prio=3)
+    def sslv2_missing_ServerVerify(self):
+        raise self.SSLv2_MISSING_SERVERVERIFY()
+
+    @ATMT.state(final=True)
+    def SSLv2_MISSING_SERVERVERIFY(self):
+        self.vprint("Missing SSLv2 ServerVerify message!")
+        raise self.SSLv2_CLOSE_NOTIFY()
+
+    @ATMT.state()
+    def SSLv2_ADDED_CLIENTFINISHED(self):
+        pass
+
+    @ATMT.condition(SSLv2_ADDED_CLIENTFINISHED)
+    def sslv2_should_send_ClientFinished(self):
+        self.flush_records()
+        raise self.SSLv2_SENT_CLIENTFINISHED()
+
+    @ATMT.state()
+    def SSLv2_SENT_CLIENTFINISHED(self):
+        hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed]
+        if SSLv2ServerVerify in hs_msg:
+            raise self.SSLv2_WAITING_SERVERFINISHED()
+        else:
+            self.get_next_msg()
+            raise self.SSLv2_RECEIVED_SERVERVERIFY()
+
+    @ATMT.state()
+    def SSLv2_WAITING_SERVERFINISHED(self):
+        self.get_next_msg()
+        raise self.SSLv2_RECEIVED_SERVERFINISHED()
+
+    @ATMT.state()
+    def SSLv2_RECEIVED_SERVERFINISHED(self):
+        pass
+
+    @ATMT.condition(SSLv2_RECEIVED_SERVERFINISHED, prio=1)
+    def sslv2_should_handle_ServerFinished(self):
+        self.raise_on_packet(SSLv2ServerFinished,
+                             self.SSLv2_HANDLED_SERVERFINISHED)
+
+    ####################### SSLv2 client authentication #######################
+
+    @ATMT.condition(SSLv2_RECEIVED_SERVERFINISHED, prio=2)
+    def sslv2_should_handle_RequestCertificate(self):
+        self.raise_on_packet(SSLv2RequestCertificate,
+                             self.SSLv2_HANDLED_REQUESTCERTIFICATE)
+
+    @ATMT.state()
+    def SSLv2_HANDLED_REQUESTCERTIFICATE(self):
+        self.vprint("Server asked for a certificate...")
+        if not self.mykey or not self.mycert:
+            self.vprint("No client certificate to send!")
+            raise self.SSLv2_CLOSE_NOTIFY()
+
+    @ATMT.condition(SSLv2_HANDLED_REQUESTCERTIFICATE)
+    def sslv2_should_add_ClientCertificate(self):
+        self.add_record(is_sslv2=True)
+        self.add_msg(SSLv2ClientCertificate(certdata=self.mycert))
+        raise self.SSLv2_ADDED_CLIENTCERTIFICATE()
+
+    @ATMT.state()
+    def SSLv2_ADDED_CLIENTCERTIFICATE(self):
+        pass
+
+    @ATMT.condition(SSLv2_ADDED_CLIENTCERTIFICATE)
+    def sslv2_should_send_ClientCertificate(self):
+        self.flush_records()
+        raise self.SSLv2_SENT_CLIENTCERTIFICATE()
+
+    @ATMT.state()
+    def SSLv2_SENT_CLIENTCERTIFICATE(self):
+        raise self.SSLv2_WAITING_SERVERFINISHED()
+
+    ################### end of SSLv2 client authentication ####################
+
+    @ATMT.state()
+    def SSLv2_HANDLED_SERVERFINISHED(self):
+        self.vprint("SSLv2 handshake completed!")
+        self.vprint_sessioninfo()
+        self.vprint("You may send data or use 'quit'.")
+
+    @ATMT.condition(SSLv2_RECEIVED_SERVERFINISHED, prio=3)
+    def sslv2_missing_ServerFinished(self):
+        raise self.SSLv2_MISSING_SERVERFINISHED()
+
+    @ATMT.state()
+    def SSLv2_MISSING_SERVERFINISHED(self):
+        self.vprint("Missing SSLv2 ServerFinished message!")
+        raise self.SSLv2_CLOSE_NOTIFY()
+
+    ######################## end of SSLv2 handshake ###########################
+
+    @ATMT.condition(SSLv2_HANDLED_SERVERFINISHED)
+    def sslv2_should_wait_ClientData(self):
+        raise self.SSLv2_WAITING_CLIENTDATA()
+
+    @ATMT.state()
+    def SSLv2_WAITING_CLIENTDATA(self):
+        pass
+
+    @ATMT.condition(SSLv2_WAITING_CLIENTDATA, prio=1)
+    def sslv2_add_ClientData(self):
+        if not self.data_to_send:
+            data = raw_input()
+            data = data.replace("\\r", "\r")
+            data = data.replace("\\n", "\n")
+        else:
+            data = self.data_to_send.pop()
+            self.vprint("> Read from list: %s" % data)
+        if data == "quit":
+            return
+        if self.linebreak:
+            data += "\n"
+        self.add_record(is_sslv2=True)
+        self.add_msg(Raw(data))
+        raise self.SSLv2_ADDED_CLIENTDATA()
+
+    @ATMT.condition(SSLv2_WAITING_CLIENTDATA, prio=2)
+    def sslv2_no_more_ClientData(self):
+        raise self.SSLv2_CLOSE_NOTIFY()
+
+    @ATMT.state()
+    def SSLv2_ADDED_CLIENTDATA(self):
+        pass
+
+    @ATMT.condition(SSLv2_ADDED_CLIENTDATA)
+    def sslv2_should_send_ClientData(self):
+        self.flush_records()
+        raise self.SSLv2_SENT_CLIENTDATA()
+
+    @ATMT.state()
+    def SSLv2_SENT_CLIENTDATA(self):
+        raise self.SSLv2_WAITING_SERVERDATA()
+
+    @ATMT.state()
+    def SSLv2_WAITING_SERVERDATA(self):
+        self.get_next_msg(0.3, 1)
+        raise self.SSLv2_RECEIVED_SERVERDATA()
+
+    @ATMT.state()
+    def SSLv2_RECEIVED_SERVERDATA(self):
+        pass
+
+    @ATMT.condition(SSLv2_RECEIVED_SERVERDATA)
+    def sslv2_should_handle_ServerData(self):
+        if not self.buffer_in:
+            raise self.SSLv2_WAITING_CLIENTDATA()
+        p = self.buffer_in[0]
+        print("> Received: %s" % p.load)
+        if p.load.startswith("goodbye"):
+            raise self.SSLv2_CLOSE_NOTIFY()
+        self.buffer_in = self.buffer_in[1:]
+        raise self.SSLv2_HANDLED_SERVERDATA()
+
+    @ATMT.state()
+    def SSLv2_HANDLED_SERVERDATA(self):
+        raise self.SSLv2_WAITING_CLIENTDATA()
+
+    @ATMT.state()
+    def SSLv2_CLOSE_NOTIFY(self):
+        """
+        There is no proper way to end an SSLv2 session.
+        We try and send a 'goodbye' message as a substitute.
+        """
+        self.vprint()
+        self.vprint("Trying to send a 'goodbye' to the server...")
+
+    @ATMT.condition(SSLv2_CLOSE_NOTIFY)
+    def sslv2_close_session(self):
+        self.add_record()
+        self.add_msg(Raw('goodbye'))
+        try:
+            self.flush_records()
+        except:
+            self.vprint("Could not send our goodbye. The server probably stopped.")
+        self.socket.close()
+        raise self.FINAL()
+
+    ######################### TLS 1.3 handshake ###############################
+
+    @ATMT.state()
+    def TLS13_START(self):
+        pass
+
+    @ATMT.condition(TLS13_START)
+    def tls13_should_add_ClientHello(self):
+        # we have to use the legacy, plaintext TLS record here
+        self.add_record(is_tls13=False)
+        if self.client_hello:
+            p = self.client_hello
+        else:
+            # When trying to connect to a public TLS 1.3 server,
+            # you will most likely need to provide an SNI extension.
+           #sn = ServerName(servername="<put server name here>")
+            ext = [TLS_Ext_SupportedGroups(groups=["secp256r1"]),
+                  #TLS_Ext_ServerName(servernames=[sn]),
+                   TLS_Ext_KeyShare_CH(client_shares=[KeyShareEntry(group=23)]),
+                   TLS_Ext_SupportedVersions(versions=["TLS 1.3-d18"]),
+                   TLS_Ext_SignatureAlgorithms(sig_algs=["sha256+rsapss",
+                                                         "sha256+rsa"]) ]
+            p = TLSClientHello(ciphers=0x1301, ext=ext)
+        self.add_msg(p)
+        raise self.TLS13_ADDED_CLIENTHELLO()
+
+    @ATMT.state()
+    def TLS13_ADDED_CLIENTHELLO(self):
+        pass
+
+    @ATMT.condition(TLS13_ADDED_CLIENTHELLO)
+    def tls13_should_send_ClientHello(self):
+        self.flush_records()
+        raise self.TLS13_SENT_CLIENTHELLO()
+
+    @ATMT.state()
+    def TLS13_SENT_CLIENTHELLO(self):
+        raise self.TLS13_WAITING_SERVERHELLO()
+
+    @ATMT.state()
+    def TLS13_WAITING_SERVERHELLO(self):
+        self.get_next_msg()
+
+    @ATMT.condition(TLS13_WAITING_SERVERHELLO)
+    def tls13_should_handle_ServerHello(self):
+        self.raise_on_packet(TLS13ServerHello,
+                             self.TLS13_WAITING_ENCRYPTEDEXTENSIONS)
+
+    @ATMT.state()
+    def TLS13_WAITING_ENCRYPTEDEXTENSIONS(self):
+        self.get_next_msg()
+
+    @ATMT.condition(TLS13_WAITING_ENCRYPTEDEXTENSIONS)
+    def tls13_should_handle_EncryptedExtensions(self):
+        self.raise_on_packet(TLSEncryptedExtensions,
+                             self.TLS13_WAITING_CERTIFICATE)
+
+    @ATMT.state()
+    def TLS13_WAITING_CERTIFICATE(self):
+        self.get_next_msg()
+
+    @ATMT.condition(TLS13_WAITING_CERTIFICATE, prio=1)
+    def tls13_should_handle_Certificate(self):
+        self.raise_on_packet(TLS13Certificate,
+                             self.TLS13_WAITING_CERTIFICATEVERIFY)
+
+    @ATMT.condition(TLS13_WAITING_CERTIFICATE, prio=2)
+    def tls13_should_handle_CertificateRequest(self):
+        hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed]
+        if TLSCertificateRequest in hs_msg:
+            self.vprint("TLSCertificateRequest already received!")
+        self.raise_on_packet(TLSCertificateRequest,
+                             self.TLS13_WAITING_CERTIFICATE)
+
+    @ATMT.condition(TLS13_WAITING_CERTIFICATE, prio=3)
+    def tls13_should_handle_ServerFinished_from_EncryptedExtensions(self):
+        self.raise_on_packet(TLSFinished,
+                             self.TLS13_CONNECTED)
+
+    @ATMT.condition(TLS13_WAITING_CERTIFICATE, prio=4)
+    def tls13_missing_Certificate(self):
+        self.vprint("Missing TLS 1.3 message after EncryptedExtensions!")
+        raise self.FINAL()
+
+    @ATMT.state()
+    def TLS13_WAITING_CERTIFICATEVERIFY(self):
+        self.get_next_msg()
+
+    @ATMT.condition(TLS13_WAITING_CERTIFICATEVERIFY)
+    def tls13_should_handle_CertificateVerify(self):
+        self.raise_on_packet(TLSCertificateVerify,
+                             self.TLS13_WAITING_SERVERFINISHED)
+
+    @ATMT.state()
+    def TLS13_WAITING_SERVERFINISHED(self):
+        self.get_next_msg()
+
+    @ATMT.condition(TLS13_WAITING_SERVERFINISHED)
+    def tls13_should_handle_ServerFinished_from_CertificateVerify(self):
+        self.raise_on_packet(TLSFinished,
+                             self.TLS13_PREPARE_CLIENTFLIGHT2)
+
+    @ATMT.state()
+    def TLS13_PREPARE_CLIENTFLIGHT2(self):
+        self.add_record(is_tls13=True)
+        #raise self.FINAL()
+
+    @ATMT.condition(TLS13_PREPARE_CLIENTFLIGHT2)
+    def tls13_should_add_ClientFinished(self):
+        self.add_msg(TLSFinished())
+        raise self.TLS13_ADDED_CLIENTFINISHED()
+
+    @ATMT.state()
+    def TLS13_ADDED_CLIENTFINISHED(self):
+        pass
+
+    @ATMT.condition(TLS13_ADDED_CLIENTFINISHED)
+    def tls13_should_send_ClientFlight2(self):
+        self.flush_records()
+        raise self.TLS13_SENT_CLIENTFLIGHT2()
+
+    @ATMT.state()
+    def TLS13_SENT_CLIENTFLIGHT2(self):
+        raise self.HANDLED_SERVERFINISHED()
+
+    @ATMT.state(final=True)
+    def FINAL(self):
+        # We might call shutdown, but it may happen that the server
+        # did not wait for us to shutdown after answering our data query.
+        #self.socket.shutdown(1)
+        self.vprint("Closing client socket...")
+        self.socket.close()
+        self.vprint("Ending TLS client automaton.")
+
diff --git a/scapy/layers/tls/automaton_srv.py b/scapy/layers/tls/automaton_srv.py
new file mode 100644
index 0000000000000000000000000000000000000000..f1f1a6f00dde42c7153efe08e390026c70bea42b
--- /dev/null
+++ b/scapy/layers/tls/automaton_srv.py
@@ -0,0 +1,883 @@
+## This file is part of Scapy
+## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
+##               2015, 2016, 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+TLS server automaton. This makes for a primitive TLS stack.
+Obviously you need rights for network access.
+
+We support versions SSLv2 to TLS 1.2, along with many features.
+There is no session resumption mechanism for now.
+
+In order to run a server listening on tcp/4433:
+> from scapy.all import *
+> t = TLSServerAutomaton(mycert='<cert.pem>', mykey='<key.pem>')
+> t.run()
+"""
+
+from __future__ import print_function
+import socket
+
+from scapy.utils import randstring, repr_hex
+from scapy.automaton import ATMT
+from scapy.layers.tls.automaton import _TLSAutomaton
+from scapy.layers.tls.cert import PrivKeyRSA, PrivKeyECDSA
+from scapy.layers.tls.basefields import _tls_version
+from scapy.layers.tls.session import tlsSession
+from scapy.layers.tls.handshake import *
+from scapy.layers.tls.handshake_sslv2 import *
+from scapy.layers.tls.record import (TLS, TLSAlert, TLSChangeCipherSpec,
+                                     TLSApplicationData)
+from scapy.layers.tls.crypto.suites import (_tls_cipher_suites_cls,
+                                            get_usable_ciphersuites)
+
+
+class TLSServerAutomaton(_TLSAutomaton):
+    """
+    A simple TLS test server automaton. Try to overload some states or
+    conditions and see what happens on the other side.
+
+    Because of socket and automaton limitations, for now, the best way to
+    interrupt the server is by sending him 'stop_server'. Interruptions with
+    Ctrl-Z should work, but this might leave a loose listening socket behind.
+
+    In case the server receives a TLSAlert (whatever its type), or a 'goodbye'
+    message in a SSLv2 version, he will close the client session with a
+    similar message, and start waiting for new client connections.
+
+    _'mycert' and 'mykey' may be provided as filenames. They are needed for any
+    server authenticated handshake.
+    _'preferred_ciphersuite' allows the automaton to choose a cipher suite when
+    offered in the ClientHello. If absent, another one will be chosen.
+    _'client_auth' means the client has to provide a certificate.
+    _'is_echo_server' means that everything received will be sent back.
+    _'max_client_idle_time' is the maximum silence duration from the client.
+    Once this limit has been reached, the client (if still here) is dropped,
+    and we wait for a new connection.
+    """
+    def parse_args(self, server="127.0.0.1", sport=4433,
+                         mycert=None, mykey=None,
+                         preferred_ciphersuite=None,
+                         client_auth=False,
+                         is_echo_server=True,
+                         max_client_idle_time=60,
+                         **kargs):
+
+        super(TLSServerAutomaton, self).parse_args(mycert=mycert,
+                                                   mykey=mykey,
+                                                   **kargs)
+        try:
+            if ':' in server:
+                socket.inet_pton(socket.AF_INET6, server)
+            else:
+                socket.inet_pton(socket.AF_INET, server)
+            tmp = socket.getaddrinfo(server, sport)
+        except:
+            tmp = socket.getaddrinfo(socket.getfqdn(server), sport)
+
+        self.serversocket = None
+        self.ip_family = tmp[0][0]
+        self.local_ip = tmp[0][4][0]
+        self.local_port = sport
+        self.remote_ip = None
+        self.remote_port = None
+
+        self.preferred_ciphersuite = preferred_ciphersuite
+        self.client_auth = client_auth
+        self.is_echo_server = is_echo_server
+        self.max_client_idle_time = max_client_idle_time
+
+
+    def vprint_sessioninfo(self):
+        if self.verbose:
+            s = self.cur_session
+            v = _tls_version[s.tls_version]
+            self.vprint("Version       : %s" % v)
+            cs = s.wcs.ciphersuite.name
+            self.vprint("Cipher suite  : %s" % cs)
+            ms = s.master_secret
+            self.vprint("Master secret : %s" % repr_hex(ms))
+            if s.client_certs:
+                self.vprint("Client certificate chain: %r" % s.client_certs)
+            self.vprint()
+
+    def http_sessioninfo(self):
+        header  = "HTTP/1.1 200 OK\r\n"
+        header += "Server: Scapy TLS Extension\r\n"
+        header += "Content-type: text/html\r\n"
+        header += "Content-length: %d\r\n\r\n"
+        s = "----- Scapy TLS Server Automaton -----\n\n"
+        s += "Information on current TLS session:\n\n"
+        s += "Local end     : %s:%d\n" % (self.local_ip, self.local_port)
+        s += "Remote end    : %s:%d\n" % (self.remote_ip, self.remote_port)
+        v = _tls_version[self.cur_session.tls_version]
+        s += "Version       : %s\n" % v
+        cs = self.cur_session.wcs.ciphersuite.name
+        s += "Cipher suite  : %s\n" % cs
+        ms = self.cur_session.master_secret
+        s += "Master secret : %s\n" % repr_hex(ms)
+        body = "<html><body><pre>%s</pre></body></html>\r\n\r\n" % s
+        answer = (header+body) % len(body)
+        return answer
+
+
+    @ATMT.state(initial=True)
+    def INITIAL(self):
+        self.vprint("Starting TLS server automaton.")
+        self.vprint("Receiving 'stop_server' will cause a graceful exit.")
+        self.vprint("Interrupting with Ctrl-Z might leave a loose socket hanging.")
+        raise self.BIND()
+
+    @ATMT.state()
+    def BIND(self):
+        s = socket.socket(self.ip_family, socket.SOCK_STREAM)
+        self.serversocket = s
+        s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
+        try:
+            s.bind((self.local_ip, self.local_port))
+            s.listen(1)
+        except:
+            m = "Unable to bind on %s:%d!" % (self.local_ip, self.local_port)
+            self.vprint()
+            self.vprint(m)
+            self.vprint("Maybe some server is already listening there?")
+            self.vprint()
+            raise self.FINAL()
+        raise self.WAITING_CLIENT()
+
+    @ATMT.state()
+    def WAITING_CLIENT(self):
+        self.vprint()
+        self.vprint("Waiting for a new client on %s:%d" % (self.local_ip,
+                                                           self.local_port))
+        self.socket, addr = self.serversocket.accept()
+        if not isinstance(addr, tuple):
+            addr = self.socket.getpeername()
+        if len(addr) > 2:
+            addr = (addr[0], addr[1])
+        self.remote_ip, self.remote_port = addr
+        self.vprint("Accepted connection from %s:%d" % (self.remote_ip,
+                                                        self.remote_port))
+        self.vprint()
+        raise self.INIT_TLS_SESSION()
+
+    @ATMT.state()
+    def INIT_TLS_SESSION(self):
+        """
+        XXX We should offer the right key according to the client's suites. For
+        now server_rsa_key is only used for RSAkx, but we should try to replace
+        every server_key with both server_rsa_key and server_ecdsa_key.
+        """
+        self.cur_session = tlsSession(connection_end="server")
+        self.cur_session.server_certs = [self.mycert]
+        self.cur_session.server_key = self.mykey
+        if isinstance(self.mykey, PrivKeyRSA):
+            self.cur_session.server_rsa_key = self.mykey
+        #elif isinstance(self.mykey, PrivKeyECDSA):
+        #    self.cur_session.server_ecdsa_key = self.mykey
+        raise self.WAITING_CLIENTFLIGHT1()
+
+    @ATMT.state()
+    def WAITING_CLIENTFLIGHT1(self):
+        self.get_next_msg()
+        raise self.RECEIVED_CLIENTFLIGHT1()
+
+    @ATMT.state()
+    def RECEIVED_CLIENTFLIGHT1(self):
+        pass
+
+    ########################### TLS handshake #################################
+
+    @ATMT.condition(RECEIVED_CLIENTFLIGHT1, prio=1)
+    def should_handle_ClientHello(self):
+        self.raise_on_packet(TLSClientHello,
+                             self.HANDLED_CLIENTHELLO)
+
+    @ATMT.state()
+    def HANDLED_CLIENTHELLO(self):
+        raise self.PREPARE_SERVERFLIGHT1()
+
+    @ATMT.condition(HANDLED_CLIENTHELLO)
+    def should_check_ciphersuites(self):
+        """
+        We extract cipher suites candidates from the client's proposition.
+        """
+        if isinstance(self.mykey, PrivKeyRSA):
+            kx = "RSA"
+        elif isinstance(self.mykey, PrivKeyECDSA):
+            kx = "ECDSA"
+        if get_usable_ciphersuites(self.cur_pkt.ciphers, kx):
+            return
+        raise self.NO_USABLE_CIPHERSUITE()
+
+    @ATMT.state()
+    def NO_USABLE_CIPHERSUITE(self):
+        self.vprint("No usable cipher suite!")
+        raise self.CLOSE_NOTIFY()
+
+    @ATMT.condition(RECEIVED_CLIENTFLIGHT1, prio=3)
+    def missing_ClientHello(self):
+        raise self.MISSING_CLIENTHELLO()
+
+    @ATMT.state(final=True)
+    def MISSING_CLIENTHELLO(self):
+        self.vprint("Missing ClientHello message!")
+        raise self.CLOSE_NOTIFY()
+
+    @ATMT.state()
+    def PREPARE_SERVERFLIGHT1(self):
+        self.add_record()
+
+    @ATMT.condition(PREPARE_SERVERFLIGHT1)
+    def should_add_ServerHello(self):
+        """
+        Selecting a cipher suite should be no trouble as we already caught
+        the None case previously.
+
+        Also, we do not manage extensions at all.
+        """
+        if isinstance(self.mykey, PrivKeyRSA):
+            kx = "RSA"
+        elif isinstance(self.mykey, PrivKeyECDSA):
+            kx = "ECDSA"
+        usable_suites = get_usable_ciphersuites(self.cur_pkt.ciphers, kx)
+        c = usable_suites[0]
+        if self.preferred_ciphersuite in usable_suites:
+            c = self.preferred_ciphersuite
+        self.add_msg(TLSServerHello(cipher=c))
+        raise self.ADDED_SERVERHELLO()
+
+    @ATMT.state()
+    def ADDED_SERVERHELLO(self):
+        pass
+
+    @ATMT.condition(ADDED_SERVERHELLO)
+    def should_add_Certificate(self):
+        c = self.buffer_out[-1].msg[0].cipher
+        if not _tls_cipher_suites_cls[c].kx_alg.anonymous:
+            self.add_msg(TLSCertificate(certs=self.cur_session.server_certs))
+        raise self.ADDED_CERTIFICATE()
+
+    @ATMT.state()
+    def ADDED_CERTIFICATE(self):
+        pass
+
+    @ATMT.condition(ADDED_CERTIFICATE)
+    def should_add_ServerKeyExchange(self):
+        c = self.buffer_out[-1].msg[0].cipher
+        if not _tls_cipher_suites_cls[c].kx_alg.no_ske:
+            self.add_msg(TLSServerKeyExchange())
+        raise self.ADDED_SERVERKEYEXCHANGE()
+
+    @ATMT.state()
+    def ADDED_SERVERKEYEXCHANGE(self):
+        pass
+
+    @ATMT.condition(ADDED_SERVERKEYEXCHANGE)
+    def should_add_CertificateRequest(self):
+        if self.client_auth:
+            self.add_msg(TLSCertificateRequest())
+        raise self.ADDED_CERTIFICATEREQUEST()
+
+    @ATMT.state()
+    def ADDED_CERTIFICATEREQUEST(self):
+        pass
+
+    @ATMT.condition(ADDED_CERTIFICATEREQUEST)
+    def should_add_ServerHelloDone(self):
+        self.add_msg(TLSServerHelloDone())
+        raise self.ADDED_SERVERHELLODONE()
+
+    @ATMT.state()
+    def ADDED_SERVERHELLODONE(self):
+        pass
+
+    @ATMT.condition(ADDED_SERVERHELLODONE)
+    def should_send_ServerFlight1(self):
+        self.flush_records()
+        raise self.WAITING_CLIENTFLIGHT2()
+
+    @ATMT.state()
+    def WAITING_CLIENTFLIGHT2(self):
+        self.get_next_msg()
+        raise self.RECEIVED_CLIENTFLIGHT2()
+
+    @ATMT.state()
+    def RECEIVED_CLIENTFLIGHT2(self):
+        pass
+
+    @ATMT.condition(RECEIVED_CLIENTFLIGHT2, prio=1)
+    def should_handle_ClientCertificate(self):
+        self.raise_on_packet(TLSCertificate,
+                             self.HANDLED_CLIENTCERTIFICATE)
+
+    @ATMT.condition(RECEIVED_CLIENTFLIGHT2, prio=2)
+    def no_ClientCertificate(self):
+        if self.client_auth:
+            raise self.MISSING_CLIENTCERTIFICATE()
+        raise self.HANDLED_CLIENTCERTIFICATE()
+
+    @ATMT.state()
+    def MISSING_CLIENTCERTIFICATE(self):
+        self.vprint("Missing ClientCertificate!")
+        raise self.CLOSE_NOTIFY()
+
+    @ATMT.state()
+    def HANDLED_CLIENTCERTIFICATE(self):
+        if self.client_auth:
+            self.vprint("Received client certificate chain...")
+
+    @ATMT.condition(HANDLED_CLIENTCERTIFICATE, prio=1)
+    def should_handle_ClientKeyExchange(self):
+        self.raise_on_packet(TLSClientKeyExchange,
+                             self.HANDLED_CLIENTKEYEXCHANGE)
+
+    @ATMT.state()
+    def HANDLED_CLIENTKEYEXCHANGE(self):
+        pass
+
+    @ATMT.condition(HANDLED_CLIENTCERTIFICATE, prio=2)
+    def should_handle_Alert_from_ClientCertificate(self):
+        self.raise_on_packet(TLSAlert,
+                             self.HANDLED_ALERT_FROM_CLIENTCERTIFICATE)
+
+    @ATMT.state()
+    def HANDLED_ALERT_FROM_CLIENTCERTIFICATE(self):
+        self.vprint("Received Alert message instead of ClientKeyExchange!")
+        raise self.CLOSE_NOTIFY()
+
+    @ATMT.condition(HANDLED_CLIENTCERTIFICATE, prio=3)
+    def missing_ClientKeyExchange(self):
+        raise self.MISSING_CLIENTKEYEXCHANGE()
+
+    @ATMT.state()
+    def MISSING_CLIENTKEYEXCHANGE(self):
+        self.vprint("Missing ClientKeyExchange!")
+        raise self.CLOSE_NOTIFY()
+
+    @ATMT.condition(HANDLED_CLIENTKEYEXCHANGE, prio=1)
+    def should_handle_CertificateVerify(self):
+        self.raise_on_packet(TLSCertificateVerify,
+                             self.HANDLED_CERTIFICATEVERIFY)
+
+    @ATMT.condition(HANDLED_CLIENTKEYEXCHANGE, prio=2)
+    def no_CertificateVerify(self):
+        if self.client_auth:
+            raise self.MISSING_CERTIFICATEVERIFY()
+        raise self.HANDLED_CERTIFICATEVERIFY()
+
+    @ATMT.state()
+    def MISSING_CERTIFICATEVERIFY(self):
+        self.vprint("Missing CertificateVerify!")
+        raise self.CLOSE_NOTIFY()
+
+    @ATMT.state()
+    def HANDLED_CERTIFICATEVERIFY(self):
+        pass
+
+    @ATMT.condition(HANDLED_CERTIFICATEVERIFY, prio=1)
+    def should_handle_ChangeCipherSpec(self):
+        self.raise_on_packet(TLSChangeCipherSpec,
+                             self.HANDLED_CHANGECIPHERSPEC)
+
+    @ATMT.state()
+    def HANDLED_CHANGECIPHERSPEC(self):
+        pass
+
+    @ATMT.condition(HANDLED_CERTIFICATEVERIFY, prio=2)
+    def should_handle_Alert_from_ClientKeyExchange(self):
+        self.raise_on_packet(TLSAlert,
+                             self.HANDLED_ALERT_FROM_CLIENTKEYEXCHANGE)
+
+    @ATMT.state()
+    def HANDLED_ALERT_FROM_CLIENTKEYEXCHANGE(self):
+        self.vprint("Received Alert message instead of ChangeCipherSpec!")
+        raise self.CLOSE_NOTIFY()
+
+    @ATMT.condition(HANDLED_CERTIFICATEVERIFY, prio=3)
+    def missing_ChangeCipherSpec(self):
+        raise self.MISSING_CHANGECIPHERSPEC()
+
+    @ATMT.state()
+    def MISSING_CHANGECIPHERSPEC(self):
+        self.vprint("Missing ChangeCipherSpec!")
+        raise self.CLOSE_NOTIFY()
+
+    @ATMT.condition(HANDLED_CHANGECIPHERSPEC, prio=1)
+    def should_handle_ClientFinished(self):
+        self.raise_on_packet(TLSFinished,
+                             self.HANDLED_CLIENTFINISHED)
+
+    @ATMT.state()
+    def HANDLED_CLIENTFINISHED(self):
+        raise self.PREPARE_SERVERFLIGHT2()
+
+    @ATMT.condition(HANDLED_CHANGECIPHERSPEC, prio=2)
+    def should_handle_Alert_from_ClientFinished(self):
+        self.raise_on_packet(TLSAlert,
+                             self.HANDLED_ALERT_FROM_CHANGECIPHERSPEC)
+
+    @ATMT.state()
+    def HANDLED_ALERT_FROM_CHANGECIPHERSPEC(self):
+        self.vprint("Received Alert message instead of Finished!")
+        raise self.CLOSE_NOTIFY()
+
+    @ATMT.condition(HANDLED_CHANGECIPHERSPEC, prio=3)
+    def missing_ClientFinished(self):
+        raise self.MISSING_CLIENTFINISHED()
+
+    @ATMT.state()
+    def MISSING_CLIENTFINISHED(self):
+        self.vprint("Missing Finished!")
+        raise self.CLOSE_NOTIFY()
+
+    @ATMT.state()
+    def PREPARE_SERVERFLIGHT2(self):
+        self.add_record()
+
+    @ATMT.condition(PREPARE_SERVERFLIGHT2)
+    def should_add_ChangeCipherSpec(self):
+        self.add_msg(TLSChangeCipherSpec())
+        raise self.ADDED_CHANGECIPHERSPEC()
+
+    @ATMT.state()
+    def ADDED_CHANGECIPHERSPEC(self):
+        pass
+
+    @ATMT.condition(ADDED_CHANGECIPHERSPEC)
+    def should_add_ServerFinished(self):
+        self.add_record()
+        self.add_msg(TLSFinished())
+        raise self.ADDED_SERVERFINISHED()
+
+    @ATMT.state()
+    def ADDED_SERVERFINISHED(self):
+        pass
+
+    @ATMT.condition(ADDED_SERVERFINISHED)
+    def should_send_ServerFlight2(self):
+        self.flush_records()
+        raise self.SENT_SERVERFLIGHT2()
+
+    @ATMT.state()
+    def SENT_SERVERFLIGHT2(self):
+        self.vprint("TLS handshake completed!")
+        self.vprint_sessioninfo()
+        if self.is_echo_server:
+            self.vprint("Will now act as a simple echo server.")
+        raise self.WAITING_CLIENTDATA()
+
+    ####################### end of TLS handshake ##############################
+
+    @ATMT.state()
+    def WAITING_CLIENTDATA(self):
+        self.get_next_msg(self.max_client_idle_time, 1)
+        raise self.RECEIVED_CLIENTDATA()
+
+    @ATMT.state()
+    def RECEIVED_CLIENTDATA(self):
+        pass
+
+    @ATMT.condition(RECEIVED_CLIENTDATA)
+    def should_handle_ClientData(self):
+        if not self.buffer_in:
+            self.vprint("Client idle time maxed out.")
+            raise self.CLOSE_NOTIFY()
+        p = self.buffer_in[0]
+        self.buffer_in = self.buffer_in[1:]
+
+        recv_data = ""
+        if isinstance(p, TLSApplicationData):
+            print("> Received: %s" % p.data)
+            recv_data = p.data
+            lines = recv_data.split("\n")
+            stop = False
+            for l in lines:
+                if l.startswith("stop_server"):
+                    stop = True
+                    break
+            if stop:
+                raise self.CLOSE_NOTIFY_FINAL()
+        elif isinstance(p, TLSAlert):
+            print("> Received: %r" % p)
+            raise self.CLOSE_NOTIFY()
+        else:
+            print("> Received: %r" % p)
+
+        if recv_data.startswith("GET / HTTP/1.1"):
+            p = TLSApplicationData(data=self.http_sessioninfo())
+
+        if self.is_echo_server or recv_data.startswith("GET / HTTP/1.1"):
+            self.add_record()
+            self.add_msg(p)
+            raise self.ADDED_SERVERDATA()
+
+        raise self.HANDLED_CLIENTDATA()
+
+    @ATMT.state()
+    def HANDLED_CLIENTDATA(self):
+        raise self.WAITING_CLIENTDATA()
+
+    @ATMT.state()
+    def ADDED_SERVERDATA(self):
+        pass
+
+    @ATMT.condition(ADDED_SERVERDATA)
+    def should_send_ServerData(self):
+        self.flush_records()
+        raise self.SENT_SERVERDATA()
+
+    @ATMT.state()
+    def SENT_SERVERDATA(self):
+        raise self.WAITING_CLIENTDATA()
+
+    @ATMT.state()
+    def CLOSE_NOTIFY(self):
+        self.vprint()
+        self.vprint("Sending a TLSAlert to the client...")
+
+    @ATMT.condition(CLOSE_NOTIFY)
+    def close_session(self):
+        self.add_record()
+        self.add_msg(TLSAlert(level=1, descr=0))
+        try:
+            self.flush_records()
+        except:
+            self.vprint("Could not send termination Alert, maybe the client left?")
+            self.buffer_out = []
+        self.socket.close()
+        raise self.WAITING_CLIENT()
+
+    @ATMT.state()
+    def CLOSE_NOTIFY_FINAL(self):
+        self.vprint()
+        self.vprint("Sending a TLSAlert to the client...")
+
+    @ATMT.condition(CLOSE_NOTIFY_FINAL)
+    def close_session_final(self):
+        self.add_record()
+        self.add_msg(TLSAlert(level=1, descr=0))
+        try:
+            self.flush_records()
+        except:
+            self.vprint("Could not send termination Alert, maybe the client left?")
+        # We might call shutdown, but unit tests with s_client fail with this.
+        #self.socket.shutdown(1)
+        self.socket.close()
+        raise self.FINAL()
+
+    ########################## SSLv2 handshake ################################
+
+    @ATMT.condition(RECEIVED_CLIENTFLIGHT1, prio=2)
+    def sslv2_should_handle_ClientHello(self):
+        self.raise_on_packet(SSLv2ClientHello,
+                             self.SSLv2_HANDLED_CLIENTHELLO)
+
+    @ATMT.state()
+    def SSLv2_HANDLED_CLIENTHELLO(self):
+        pass
+
+    @ATMT.condition(SSLv2_HANDLED_CLIENTHELLO)
+    def sslv2_should_add_ServerHello(self):
+        self.add_record(is_sslv2=True)
+        cert = self.mycert
+        ciphers = [0x010080, 0x020080, 0x030080, 0x040080,
+                   0x050080, 0x060040, 0x0700C0]
+        connection_id = randstring(16)
+        p = SSLv2ServerHello(cert=cert,
+                             ciphers=ciphers,
+                             connection_id=connection_id)
+        self.add_msg(p)
+        raise self.SSLv2_ADDED_SERVERHELLO()
+
+    @ATMT.state()
+    def SSLv2_ADDED_SERVERHELLO(self):
+        pass
+
+    @ATMT.condition(SSLv2_ADDED_SERVERHELLO)
+    def sslv2_should_send_ServerHello(self):
+        self.flush_records()
+        raise self.SSLv2_SENT_SERVERHELLO()
+
+    @ATMT.state()
+    def SSLv2_SENT_SERVERHELLO(self):
+        raise self.SSLv2_WAITING_CLIENTMASTERKEY()
+
+    @ATMT.state()
+    def SSLv2_WAITING_CLIENTMASTERKEY(self):
+        self.get_next_msg()
+        raise self.SSLv2_RECEIVED_CLIENTMASTERKEY()
+
+    @ATMT.state()
+    def SSLv2_RECEIVED_CLIENTMASTERKEY(self):
+        pass
+
+    @ATMT.condition(SSLv2_RECEIVED_CLIENTMASTERKEY, prio=1)
+    def sslv2_should_handle_ClientMasterKey(self):
+        self.raise_on_packet(SSLv2ClientMasterKey,
+                             self.SSLv2_HANDLED_CLIENTMASTERKEY)
+
+    @ATMT.condition(SSLv2_RECEIVED_CLIENTMASTERKEY, prio=2)
+    def missing_ClientMasterKey(self):
+        raise self.SSLv2_MISSING_CLIENTMASTERKEY()
+
+    @ATMT.state()
+    def SSLv2_MISSING_CLIENTMASTERKEY(self):
+        self.vprint("Missing SSLv2 ClientMasterKey!")
+        raise self.SSLv2_CLOSE_NOTIFY()
+
+    @ATMT.state()
+    def SSLv2_HANDLED_CLIENTMASTERKEY(self):
+        raise self.SSLv2_RECEIVED_CLIENTFINISHED()
+
+    @ATMT.state()
+    def SSLv2_RECEIVED_CLIENTFINISHED(self):
+        pass
+
+    @ATMT.condition(SSLv2_RECEIVED_CLIENTFINISHED, prio=1)
+    def sslv2_should_handle_ClientFinished(self):
+        self.raise_on_packet(SSLv2ClientFinished,
+                             self.SSLv2_HANDLED_CLIENTFINISHED)
+
+    @ATMT.state()
+    def SSLv2_HANDLED_CLIENTFINISHED(self):
+        pass
+
+    @ATMT.condition(SSLv2_HANDLED_CLIENTFINISHED, prio=1)
+    def sslv2_should_add_ServerVerify_from_ClientFinished(self):
+        hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed]
+        if SSLv2ServerVerify in hs_msg:
+            return
+        self.add_record(is_sslv2=True)
+        p = SSLv2ServerVerify(challenge=self.cur_session.sslv2_challenge)
+        self.add_msg(p)
+        raise self.SSLv2_ADDED_SERVERVERIFY()
+
+    @ATMT.condition(SSLv2_RECEIVED_CLIENTFINISHED, prio=2)
+    def sslv2_should_add_ServerVerify_from_NoClientFinished(self):
+        hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed]
+        if SSLv2ServerVerify in hs_msg:
+            return
+        self.add_record(is_sslv2=True)
+        p = SSLv2ServerVerify(challenge=self.cur_session.sslv2_challenge)
+        self.add_msg(p)
+        raise self.SSLv2_ADDED_SERVERVERIFY()
+
+    @ATMT.condition(SSLv2_RECEIVED_CLIENTFINISHED, prio=3)
+    def sslv2_missing_ClientFinished(self):
+        raise self.SSLv2_MISSING_CLIENTFINISHED()
+
+    @ATMT.state()
+    def SSLv2_MISSING_CLIENTFINISHED(self):
+        self.vprint("Missing SSLv2 ClientFinished!")
+        raise self.SSLv2_CLOSE_NOTIFY()
+
+    @ATMT.state()
+    def SSLv2_ADDED_SERVERVERIFY(self):
+        pass
+
+    @ATMT.condition(SSLv2_ADDED_SERVERVERIFY)
+    def sslv2_should_send_ServerVerify(self):
+        self.flush_records()
+        raise self.SSLv2_SENT_SERVERVERIFY()
+
+    @ATMT.state()
+    def SSLv2_SENT_SERVERVERIFY(self):
+        hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed]
+        if SSLv2ClientFinished in hs_msg:
+            raise self.SSLv2_HANDLED_CLIENTFINISHED()
+        else:
+            raise self.SSLv2_RECEIVED_CLIENTFINISHED()
+
+    ####################### SSLv2 client authentication #######################
+
+    @ATMT.condition(SSLv2_HANDLED_CLIENTFINISHED, prio=2)
+    def sslv2_should_add_RequestCertificate(self):
+        hs_msg = [type(m) for m in self.cur_session.handshake_messages_parsed]
+        if not self.client_auth or SSLv2RequestCertificate in hs_msg:
+            return
+        self.add_record(is_sslv2=True)
+        self.add_msg(SSLv2RequestCertificate(challenge=randstring(16)))
+        raise self.SSLv2_ADDED_REQUESTCERTIFICATE()
+
+    @ATMT.state()
+    def SSLv2_ADDED_REQUESTCERTIFICATE(self):
+        pass
+
+    @ATMT.condition(SSLv2_ADDED_REQUESTCERTIFICATE)
+    def sslv2_should_send_RequestCertificate(self):
+        self.flush_records()
+        raise self.SSLv2_SENT_REQUESTCERTIFICATE()
+
+    @ATMT.state()
+    def SSLv2_SENT_REQUESTCERTIFICATE(self):
+        raise self.SSLv2_WAITING_CLIENTCERTIFICATE()
+
+    @ATMT.state()
+    def SSLv2_WAITING_CLIENTCERTIFICATE(self):
+        self.get_next_msg()
+        raise self.SSLv2_RECEIVED_CLIENTCERTIFICATE()
+
+    @ATMT.state()
+    def SSLv2_RECEIVED_CLIENTCERTIFICATE(self):
+        pass
+
+    @ATMT.condition(SSLv2_RECEIVED_CLIENTCERTIFICATE, prio=1)
+    def sslv2_should_handle_ClientCertificate(self):
+        self.raise_on_packet(SSLv2ClientCertificate,
+                             self.SSLv2_HANDLED_CLIENTCERTIFICATE)
+
+    @ATMT.condition(SSLv2_RECEIVED_CLIENTCERTIFICATE, prio=2)
+    def sslv2_missing_ClientCertificate(self):
+        raise self.SSLv2_MISSING_CLIENTCERTIFICATE()
+
+    @ATMT.state()
+    def SSLv2_MISSING_CLIENTCERTIFICATE(self):
+        self.vprint("Missing SSLv2 ClientCertificate!")
+        raise self.SSLv2_CLOSE_NOTIFY()
+
+    @ATMT.state()
+    def SSLv2_HANDLED_CLIENTCERTIFICATE(self):
+        selv.vprint("Received client certificate...")
+        # We could care about the client CA, but we don't.
+        raise self.SSLv2_HANDLED_CLIENTFINISHED()
+
+    ################### end of SSLv2 client authentication ####################
+
+    @ATMT.condition(SSLv2_HANDLED_CLIENTFINISHED, prio=3)
+    def sslv2_should_add_ServerFinished(self):
+        self.add_record(is_sslv2=True)
+        self.add_msg(SSLv2ServerFinished(sid=randstring(16)))
+        raise self.SSLv2_ADDED_SERVERFINISHED()
+
+    @ATMT.state()
+    def SSLv2_ADDED_SERVERFINISHED(self):
+        pass
+
+    @ATMT.condition(SSLv2_ADDED_SERVERFINISHED)
+    def sslv2_should_send_ServerFinished(self):
+        self.flush_records()
+        raise self.SSLv2_SENT_SERVERFINISHED()
+
+    @ATMT.state()
+    def SSLv2_SENT_SERVERFINISHED(self):
+        self.vprint("SSLv2 handshake completed!")
+        self.vprint_sessioninfo()
+        if self.is_echo_server:
+            self.vprint("Will now act as a simple echo server.")
+        raise self.SSLv2_WAITING_CLIENTDATA()
+
+    ######################## end of SSLv2 handshake ###########################
+
+    @ATMT.state()
+    def SSLv2_WAITING_CLIENTDATA(self):
+        self.get_next_msg(self.max_client_idle_time, 1)
+        raise self.SSLv2_RECEIVED_CLIENTDATA()
+
+    @ATMT.state()
+    def SSLv2_RECEIVED_CLIENTDATA(self):
+        pass
+
+    @ATMT.condition(SSLv2_RECEIVED_CLIENTDATA)
+    def sslv2_should_handle_ClientData(self):
+        if not self.buffer_in:
+            self.vprint("Client idle time maxed out.")
+            raise self.SSLv2_CLOSE_NOTIFY()
+        p = self.buffer_in[0]
+        self.buffer_in = self.buffer_in[1:]
+        if hasattr(p, "load"):
+            cli_data = p.load
+            self.vprint("Received: %s" % cli_data)
+            if cli_data.startswith("goodbye"):
+                self.vprint()
+                self.vprint("Seems like the client left...")
+                raise self.WAITING_CLIENT()
+        else:
+            cli_data = str(p)
+            self.vprint("Received: %r" % p)
+
+        lines = cli_data.split("\n")
+        stop = False
+        for l in lines:
+            if l.startswith("stop_server"):
+                stop = True
+                break
+        if stop:
+            raise self.SSLv2_CLOSE_NOTIFY_FINAL()
+
+        answer = ""
+        if cli_data.startswith("GET / HTTP/1.1"):
+            p = Raw(self.http_sessioninfo())
+
+        if self.is_echo_server or recv_data.startswith("GET / HTTP/1.1"):
+            self.add_record(is_sslv2=True)
+            self.add_msg(p)
+            raise self.SSLv2_ADDED_SERVERDATA()
+
+        raise self.SSLv2_HANDLED_CLIENTDATA()
+
+    @ATMT.state()
+    def SSLv2_HANDLED_CLIENTDATA(self):
+        raise self.SSLv2_WAITING_CLIENTDATA()
+
+    @ATMT.state()
+    def SSLv2_ADDED_SERVERDATA(self):
+        pass
+
+    @ATMT.condition(SSLv2_ADDED_SERVERDATA)
+    def sslv2_should_send_ServerData(self):
+        self.flush_records()
+        raise self.SSLv2_SENT_SERVERDATA()
+
+    @ATMT.state()
+    def SSLv2_SENT_SERVERDATA(self):
+        raise self.SSLv2_WAITING_CLIENTDATA()
+
+    @ATMT.state()
+    def SSLv2_CLOSE_NOTIFY(self):
+        """
+        There is no proper way to end an SSLv2 session.
+        We try and send a 'goodbye' message as a substitute.
+        """
+        self.vprint()
+        self.vprint("Trying to send 'goodbye' to the client...")
+
+    @ATMT.condition(SSLv2_CLOSE_NOTIFY)
+    def sslv2_close_session(self):
+        self.add_record()
+        self.add_msg(Raw('goodbye'))
+        try:
+            self.flush_records()
+        except:
+            self.vprint("Could not send our goodbye. The client probably left.")
+            self.buffer_out = []
+        self.socket.close()
+        raise self.WAITING_CLIENT()
+
+    @ATMT.state()
+    def SSLv2_CLOSE_NOTIFY_FINAL(self):
+        """
+        There is no proper way to end an SSLv2 session.
+        We try and send a 'goodbye' message as a substitute.
+        """
+        self.vprint()
+        self.vprint("Trying to send 'goodbye' to the client...")
+
+    @ATMT.condition(SSLv2_CLOSE_NOTIFY_FINAL)
+    def sslv2_close_session_final(self):
+        self.add_record()
+        self.add_msg(Raw('goodbye'))
+        try:
+            self.flush_records()
+        except:
+            self.vprint("Could not send our goodbye. The client probably left.")
+        self.socket.close()
+        raise self.FINAL()
+
+    @ATMT.state(final=True)
+    def FINAL(self):
+        self.vprint("Closing server socket...")
+        self.serversocket.close()
+        self.vprint("Ending TLS server automaton.")
+
diff --git a/scapy/layers/tls/basefields.py b/scapy/layers/tls/basefields.py
index c267d28a2a9014035a34369c8102d99057acf973..b82eff488f775a1e9a30b675a11ad34c6f69a3b0 100644
--- a/scapy/layers/tls/basefields.py
+++ b/scapy/layers/tls/basefields.py
@@ -1,6 +1,6 @@
 ## This file is part of Scapy
 ## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
-##                     2015, 2016 Maxence Tury
+##               2015, 2016, 2017 Maxence Tury
 ## This program is published under a GPLv2 license
 
 """
@@ -16,58 +16,88 @@ _tls_type = { 20: "change_cipher_spec",
               22: "handshake",
               23: "application_data" }
 
-_tls_version = { 0x0200: "SSLv2",
+_tls_version = { 0x0002: "SSLv2",
+                 0x0200: "SSLv2",
                  0x0300: "SSLv3",
                  0x0301: "TLS 1.0",
                  0x0302: "TLS 1.1",
-                 0x0303: "TLS 1.2" }
+                 0x0303: "TLS 1.2",
+                 0x7f12: "TLS 1.3-d18",
+                 0x7f13: "TLS 1.3-d19",
+                 0x0304: "TLS 1.3" }
+
+_tls_version_options = { "sslv2": 0x0002,
+                         "sslv3": 0x0300,
+                         "tls1" : 0x0301,
+                         "tls10": 0x0301,
+                         "tls11": 0x0302,
+                         "tls12": 0x0303,
+                         "tls13-d18": 0x7f12,
+                         "tls13-d19": 0x7f13,
+                         "tls13": 0x0304 }
+
+def _tls13_version_filter(version, legacy_version):
+    if version < 0x0304:
+        return version
+    else:
+        return legacy_version
 
-
-class _TLSVersionField(ShortEnumField):
+class _TLSClientVersionField(ShortEnumField):
     """
-    Behavior: if the user does not provide a value, we use the version provided
-    by tls_version parameter in packet's session, only if it is defined. In
-    that case, this is the version selected by the server. Otherwise, we use
-    the value provided by advertised_tls_version parameter in packet's session.
-    In that latter case, this is simply the version provided by the client.
+    We use the advertised_tls_version if it has been defined,
+    and the legacy 0x0303 for TLS 1.3 packets.
     """
     def i2h(self, pkt, x):
         if x is None:
-            if pkt.tls_session.tls_version:
-                return pkt.tls_session.tls_version
-            return pkt.tls_session.advertised_tls_version
+            v = pkt.tls_session.advertised_tls_version
+            if v:
+                return _tls13_version_filter(v, 0x0303)
+            return ""
         return x
 
     def i2m(self, pkt, x):
         if x is None:
-            if pkt.tls_session.tls_version:
-                return pkt.tls_session.tls_version
-            return pkt.tls_session.advertised_tls_version
+            v = pkt.tls_session.advertised_tls_version
+            if v:
+                return _tls13_version_filter(v, 0x0303)
+            return ""
         return x
 
 
-class _TLSClientVersionField(ShortEnumField):
+class _TLSVersionField(ShortEnumField):
     """
-    Unlike _TLSVersionField, we use advertised_tls_version preferentially,
-    and then tls_version if there was none advertised.
+    We use the tls_version if it has been defined, else the advertised version.
+    Also, the legacy 0x0301 is used for TLS 1.3 packets.
     """
     def i2h(self, pkt, x):
         if x is None:
-            if pkt.tls_session.advertised_tls_version:
-                return pkt.tls_session.advertised_tls_version
-            return pkt.tls_session.tls_version
+            v = pkt.tls_session.tls_version
+            if v:
+                return _tls13_version_filter(v, 0x0301)
+            else:
+                adv_v = pkt.tls_session.advertised_tls_version
+                return _tls13_version_filter(adv_v, 0x0301)
         return x
 
     def i2m(self, pkt, x):
         if x is None:
-            if pkt.tls_session.advertised_tls_version:
-                return pkt.tls_session.advertised_tls_version
-            return pkt.tls_session.tls_version
+            v = pkt.tls_session.tls_version
+            if v:
+                return _tls13_version_filter(v, 0x0301)
+            else:
+                adv_v = pkt.tls_session.advertised_tls_version
+                return _tls13_version_filter(adv_v, 0x0301)
         return x
 
 
 class _TLSLengthField(ShortField):
-    pass
+    def i2repr(self, pkt, x):
+        s = super(_TLSLengthField, self).i2repr(pkt, x)
+        if pkt.deciphered_len is not None:
+            dx = pkt.deciphered_len
+            ds = super(_TLSLengthField, self).i2repr(pkt, dx)
+            s += "    [deciphered_len= %s]" % ds
+        return s
 
 
 class _TLSIVField(StrField):
@@ -130,6 +160,10 @@ class _TLSMACField(StrField):
         return s
 
     def getfield(self, pkt, s):
+        if (pkt.tls_session.rcs.cipher.type != "aead" and
+            False in pkt.tls_session.rcs.cipher.ready.itervalues()):
+            #XXX Find a more proper way to handle the still-encrypted case
+            return s, ""
         l = pkt.tls_session.rcs.mac_len
         return s[l:], self.m2i(pkt, s[:l])
 
@@ -155,11 +189,12 @@ class _TLSPadField(StrField):
 
     def getfield(self, pkt, s):
         if pkt.tls_session.consider_read_padding():
-            # We get the length from the last byte of s which
-            # is either the first byte of padding or the padding
-            # length field itself is padding length is 0.
             # This should work with SSLv3 and also TLS versions.
-            l = ord(s[-1])
+            # Note that we need to retrieve pkt.padlen beforehand,
+            # because it's possible that the padding is followed by some data
+            # from another TLS record (hence the last byte from s would not be
+            # the last byte from the current record padding).
+            l = ord(s[pkt.padlen-1])
             return s[l:], self.m2i(pkt, s[:l])
         return s, None
 
@@ -178,3 +213,44 @@ class _TLSPadLenField(ByteField):
             return ByteField.getfield(self, pkt, s)
         return s, None
 
+
+### SSLv2 fields
+
+class _SSLv2LengthField(_TLSLengthField):
+    def i2repr(self, pkt, x):
+        s = super(_SSLv2LengthField, self).i2repr(pkt, x)
+        if pkt.with_padding:
+            x |= 0x8000
+        #elif pkt.with_escape:      #XXX no complete support for 'escape' yet
+        #   x |= 0x4000
+            s += "    [with padding: %s]" % hex(x)
+        return s
+
+    def getfield(self, pkt, s):
+        msglen = struct.unpack('!H', s[:2])[0]
+        pkt.with_padding = (msglen & 0x8000) == 0
+        if pkt.with_padding:
+            msglen_clean = msglen & 0x3fff
+        else:
+            msglen_clean = msglen & 0x7fff
+        return s[2:], msglen_clean
+
+
+class _SSLv2MACField(_TLSMACField):
+    pass
+
+
+class _SSLv2PadField(_TLSPadField):
+    def getfield(self, pkt, s):
+        if pkt.padlen is not None:
+            l = pkt.padlen
+            return s[l:], self.m2i(pkt, s[:l])
+        return s, None
+
+
+class _SSLv2PadLenField(_TLSPadLenField):
+    def getfield(self, pkt, s):
+        if pkt.with_padding:
+            return ByteField.getfield(self, pkt, s)
+        return s, None
+
diff --git a/scapy/layers/tls/cert.py b/scapy/layers/tls/cert.py
index 83ef15ec6ce7c96117c59bf8b55a22b5cff7064d..6228acd6a148d306ce303be75220193c5eaad395 100644
--- a/scapy/layers/tls/cert.py
+++ b/scapy/layers/tls/cert.py
@@ -1,7 +1,7 @@
 ## This file is part of Scapy
 ## Copyright (C) 2008 Arnaud Ebalard <arnaud.ebalard@eads.net>
 ##                                   <arno@natisbad.org>
-##         2015, 2016 Maxence Tury   <maxence.tury@ssi.gouv.fr>
+##   2015, 2016, 2017 Maxence Tury   <maxence.tury@ssi.gouv.fr>
 ## This program is published under a GPLv2 license
 
 """
@@ -37,8 +37,6 @@ if conf.crypto_valid:
     from cryptography.hazmat.backends import default_backend
     from cryptography.hazmat.primitives import serialization
     from cryptography.hazmat.primitives.asymmetric import rsa
-else:
-    default_backend = rsa = serialization = None
 
 from scapy.error import warning
 from scapy.utils import binrepr
@@ -49,7 +47,7 @@ from scapy.layers.x509 import (X509_SubjectPublicKeyInfo,
                                ECDSAPublicKey, ECDSAPrivateKey,
                                RSAPrivateKey_OpenSSL, ECDSAPrivateKey_OpenSSL,
                                X509_Cert, X509_CRL)
-from scapy.layers.tls.crypto.pkcs1 import (pkcs_os2ip, pkcs_i2osp, mapHashFunc,
+from scapy.layers.tls.crypto.pkcs1 import (pkcs_os2ip, pkcs_i2osp, _get_hash,
                                            _EncryptAndVerifyRSA,
                                            _DecryptAndSignRSA)
 
@@ -264,11 +262,11 @@ class PubKeyRSA(PubKey, _EncryptAndVerifyRSA):
             self.pubkey = private_key.public_key()
         else:
             real_modulusLen = len(binrepr(modulus))
-            if real_modulusLen != modulusLen:
+            if modulusLen and real_modulusLen != modulusLen:
                 warning("modulus and modulusLen do not match!")
             pubNum = rsa.RSAPublicNumbers(n=modulus, e=pubExp)
             self.pubkey = pubNum.public_key(default_backend())
-        #XXX lines below should be removed once pkcs1.py is cleaned of legacy
+        # Lines below are only useful for the legacy part of pkcs1.py
         pubNum = self.pubkey.public_numbers()
         self._modulusLen = real_modulusLen
         self._modulus = pubNum.n
@@ -293,14 +291,12 @@ class PubKeyRSA(PubKey, _EncryptAndVerifyRSA):
         pubExp     = pubkey.publicExponent.val
         self.fill_and_store(modulus=modulus, pubExp=pubExp)
 
-    def encrypt(self, msg, t=None, h=None, mgf=None, L=None):
+    def encrypt(self, msg, t="pkcs", h="sha256", mgf=None, L=None):
         # no ECDSA encryption support, hence no ECDSA specific keywords here
-        return _EncryptAndVerifyRSA.encrypt(self, msg, t=t, h=h, mgf=mgf, L=L)
+        return _EncryptAndVerifyRSA.encrypt(self, msg, t, h, mgf, L)
 
-    def verify(self, msg, sig, h=None,
-               t=None, mgf=None, sLen=None):
-        return _EncryptAndVerifyRSA.verify(self, msg, sig, h=h,
-                                           t=t, mgf=mgf, sLen=sLen)
+    def verify(self, msg, sig, t="pkcs", h="sha256", mgf=None, L=None):
+        return _EncryptAndVerifyRSA.verify(self, msg, sig, t, h, mgf, L)
 
 class PubKeyECDSA(PubKey):
     """
@@ -315,19 +311,18 @@ class PubKeyECDSA(PubKey):
 
     @crypto_validator
     def import_from_der(self, pubkey):
-        # XXX does the cryptography lib support explicit curves?
-        # check also for compressed points
+        # No lib support for explicit curves nor compressed points.
         self.pubkey = serialization.load_der_public_key(pubkey,
                                                     backend=default_backend())
 
-    def encrypt(self, msg, h=None, **kwargs):
+    def encrypt(self, msg, h="sha256", **kwargs):
         # cryptography lib does not support ECDSA encryption
         raise Exception("No ECDSA encryption support")
 
     @crypto_validator
-    def verify(self, msg, sig, h=None, **kwargs):
+    def verify(self, msg, sig, h="sha256", **kwargs):
         # 'sig' should be a DER-encoded signature, as per RFC 3279
-        verifier = self.pubkey.verifier(sig, ec.ECDSA(mapHashFunc(h)))
+        verifier = self.pubkey.verifier(sig, ec.ECDSA(_get_hash(h)))
         verifier.update(msg)
         return verifier.verify()
 
@@ -405,7 +400,7 @@ class PrivKey(six.with_metaclass(_PrivKeyFactory, object)):
     Provides common signTBSCert() and resignCert() methods.
     """
 
-    def signTBSCert(self, tbsCert, h=None):
+    def signTBSCert(self, tbsCert, h="sha256"):
         """
         Note that this will always copy the signature field from the
         tbsCertificate into the signatureAlgorithm field of the result,
@@ -463,7 +458,7 @@ class PrivKeyRSA(PrivKey, _EncryptAndVerifyRSA, _DecryptAndSignRSA):
             self.pubkey = self.key.public_key()
         else:
             real_modulusLen = len(binrepr(modulus))
-            if real_modulusLen != modulusLen:
+            if modulusLen and real_modulusLen != modulusLen:
                 warning("modulus and modulusLen do not match!")
             pubNum = rsa.RSAPublicNumbers(n=modulus, e=pubExp)
             privNum = rsa.RSAPrivateNumbers(p=prime1, q=prime2,
@@ -472,7 +467,8 @@ class PrivKeyRSA(PrivKey, _EncryptAndVerifyRSA, _DecryptAndSignRSA):
                                             public_numbers=pubNum)
             self.key = privNum.private_key(default_backend())
             self.pubkey = self.key.public_key()
-        #XXX lines below should be removed once pkcs1.py is cleaned of legacy
+
+        # Lines below are only useful for the legacy part of pkcs1.py
         pubNum = self.pubkey.public_numbers()
         self._modulusLen = real_modulusLen
         self._modulus = pubNum.n
@@ -492,16 +488,12 @@ class PrivKeyRSA(PrivKey, _EncryptAndVerifyRSA, _DecryptAndSignRSA):
                             exponent1=exponent1, exponent2=exponent2,
                             coefficient=coefficient)
 
-    def verify(self, msg, sig, h=None,
-               t=None, mgf=None, sLen=None):
+    def verify(self, msg, sig, t="pkcs", h="sha256", mgf=None, L=None):
         # Let's copy this from PubKeyRSA instead of adding another baseclass :)
-        return _EncryptAndVerifyRSA.verify(self, msg, sig, h=h,
-                                           t=t, mgf=mgf, sLen=sLen)
+        return _EncryptAndVerifyRSA.verify(self, msg, sig, t, h, mgf, L)
 
-    def sign(self, data, h=None,
-             t=None, mgf=None, sLen=None):
-        return _DecryptAndSignRSA.sign(self, data, h=h,
-                                       t=t, mgf=mgf, sLen=sLen)
+    def sign(self, data, t="pkcs", h="sha256", mgf=None, L=None):
+        return _DecryptAndSignRSA.sign(self, data, t, h, mgf, L)
 
 
 class PrivKeyECDSA(PrivKey):
@@ -522,15 +514,15 @@ class PrivKeyECDSA(PrivKey):
         self.pubkey = self.key.public_key()
 
     @crypto_validator
-    def verify(self, msg, sig, h=None, **kwargs):
+    def verify(self, msg, sig, h="sha256", **kwargs):
         # 'sig' should be a DER-encoded signature, as per RFC 3279
-        verifier = self.pubkey.verifier(sig, ec.ECDSA(mapHashFunc(h)))
+        verifier = self.pubkey.verifier(sig, ec.ECDSA(_get_hash(h)))
         verifier.update(msg)
         return verifier.verify()
 
     @crypto_validator
-    def sign(self, data, h=None, **kwargs):
-        signer = self.key.signer(ec.ECDSA(mapHashFunc(h)))
+    def sign(self, data, h="sha256", **kwargs):
+        signer = self.key.signer(ec.ECDSA(_get_hash(h)))
         signer.update(data)
         return signer.finalize()
 
@@ -641,14 +633,12 @@ class Cert(six.with_metaclass(_CertMaker, object)):
             return self.isIssuerCert(self)
         return False
 
-    def encrypt(self, msg, t=None, h=None, mgf=None, L=None):
+    def encrypt(self, msg, t="pkcs", h="sha256", mgf=None, L=None):
         # no ECDSA *encryption* support, hence only RSA specific keywords here
-        return self.pubKey.encrypt(msg, t=t, h=h, mgf=mgf, L=L)
+        return self.pubKey.encrypt(msg, t, h, mgf, L)
 
-    def verify(self, msg, sig, h=None,
-               t=None, mgf=None, sLen=None):
-        return self.pubKey.verify(msg, sig, h=h,
-                                  t=t, mgf=mgf, sLen=sLen)
+    def verify(self, msg, sig, t="pkcs", h="sha256", mgf=None, L=None):
+        return self.pubKey.verify(msg, sig, t, h, mgf, L)
 
     def remainingDays(self, now=None):
         """
@@ -985,3 +975,27 @@ class Chain(list):
             idx += 1
         return s
 
+
+##############################
+# Certificate export helpers #
+##############################
+
+def _create_ca_file(anchor_list, filename):
+    """
+    Concatenate all the certificates (PEM format for the export) in
+    'anchor_list' and write the result to file 'filename'. On success
+    'filename' is returned, None otherwise.
+
+    If you are used to OpenSSL tools, this function builds a CAfile
+    that can be used for certificate and CRL check.
+    """
+    try:
+        f = open(filename, "w")
+        for a in anchor_list:
+            s = a.output(fmt="PEM")
+            f.write(s)
+        f.close()
+    except IOError:
+        return None
+    return filename
+
diff --git a/scapy/layers/tls/crypto/all.py b/scapy/layers/tls/crypto/all.py
index e8192a55dcec9a67aa4c0b027f6fa16a5b40c2c7..51a42179251c3c306744833d73543b6549995fc7 100644
--- a/scapy/layers/tls/crypto/all.py
+++ b/scapy/layers/tls/crypto/all.py
@@ -1,15 +1,11 @@
 ## This file is part of Scapy
 ## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
-##                     2015, 2016 Maxence Tury
+##               2015, 2016, 2017 Maxence Tury
 ## This program is published under a GPLv2 license
 
 """
 Aggregate some TLS crypto objects.
 """
 
-# XXX This line should be removed once standard FFDH groups have been
-# registered in the cryptography library.
-from scapy.layers.tls.crypto.ffdh import *
-
 from scapy.layers.tls.crypto.suites import *
 
diff --git a/scapy/layers/tls/crypto/cipher_aead.py b/scapy/layers/tls/crypto/cipher_aead.py
index 5298299e9053997697285241a813bef8eb79af11..86dbe00c8b0430c91319c6a0a01f0d34fb244daf 100644
--- a/scapy/layers/tls/crypto/cipher_aead.py
+++ b/scapy/layers/tls/crypto/cipher_aead.py
@@ -1,6 +1,6 @@
 ## This file is part of Scapy
 ## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
-##                     2015, 2016 Maxence Tury
+##               2015, 2016, 2017 Maxence Tury
 ## This program is published under a GPLv2 license
 
 """
@@ -8,23 +8,27 @@ Authenticated Encryption with Associated Data ciphers.
 
 RFC 5288 introduces new ciphersuites for TLS 1.2 which are based on AES in
 Galois/Counter Mode (GCM). RFC 6655 in turn introduces AES_CCM ciphersuites.
-The related AEAD algorithms are defined in RFC 5116.
-
-For now the cryptography library only supports GCM mode.
-Their interface might (and should) be changed in the future.
+The related AEAD algorithms are defined in RFC 5116. Later on, RFC 7905
+introduced cipher suites based on a ChaCha20-Poly1305 construction.
 """
 
 from __future__ import absolute_import
 import struct
 
-from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
-from cryptography.hazmat.backends import default_backend
-from cryptography.exceptions import InvalidTag
-
+from scapy.config import conf
 from scapy.layers.tls.crypto.pkcs1 import pkcs_i2osp, pkcs_os2ip
 from scapy.layers.tls.crypto.ciphers import CipherError
+from scapy.utils import strxor
 import scapy.modules.six as six
 
+if conf.crypto_valid:
+    from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
+    from cryptography.hazmat.backends import default_backend
+    from cryptography.exceptions import InvalidTag
+if conf.crypto_valid_advanced:
+    from cryptography.hazmat.primitives.ciphers.aead import (AESCCM,
+                                                             ChaCha20Poly1305)
+
 
 tls_aead_cipher_algs = {}
 
@@ -34,37 +38,46 @@ class _AEADCipherMetaclass(type):
     Furthermore, their name attribute is extracted from their class name.
     """
     def __new__(cls, ciph_name, bases, dct):
-        if ciph_name != "_AEADCipher":
+        if not ciph_name.startswith("_AEADCipher"):
             dct["name"] = ciph_name[7:]     # remove leading "Cipher_"
         the_class = super(_AEADCipherMetaclass, cls).__new__(cls, ciph_name,
                                                              bases, dct)
-        if ciph_name != "_AEADCipher":
+        if not ciph_name.startswith("_AEADCipher"):
             tls_aead_cipher_algs[ciph_name[7:]] = the_class
         return the_class
 
 
 class AEADTagError(Exception):
     """
-    Raised when MAC verification fails. Hopefully you can access to the
-    deciphered (but unathenticated) plaintext as e.args.
+    Raised when MAC verification fails.
     """
     pass
 
 class _AEADCipher(six.with_metaclass(_AEADCipherMetaclass, object)):
+    """
+    The hasattr(self, "pc_cls") tests correspond to the legacy API of the
+    crypto library. With cryptography v2.0, both CCM and GCM should follow
+    the else case.
+
+    Note that the "fixed_iv" in TLS RFCs is called "salt" in the AEAD RFC 5116.
+    """
     type = "aead"
+    fixed_iv_len = 4
+    nonce_explicit_len = 8
 
-    def __init__(self, key=None, salt=None, nonce_explicit=None):
+    def __init__(self, key=None, fixed_iv=None, nonce_explicit=None):
         """
-        'key' and 'salt' are to be provided as strings, whereas the internal
+        'key' and 'fixed_iv' are to be provided as strings, whereas the internal
         'nonce_explicit' is an integer (it is simpler for incrementation).
+        /!\ The whole 'nonce' may be called IV in certain RFCs.
         """
-        self.ready = {"key":True, "salt":True, "nonce_explicit":True}
+        self.ready = {"key": True, "fixed_iv": True, "nonce_explicit": True}
         if key is None:
             self.ready["key"] = False
             key = b"\0" * self.key_len
-        if salt is None:
-            self.ready["salt"] = False
-            salt = b"\0" * self.salt_len
+        if fixed_iv is None:
+            self.ready["fixed_iv"] = False
+            fixed_iv = b"\0" * self.fixed_iv_len
         if nonce_explicit is None:
             self.ready["nonce_explicit"] = False
             nonce_explicit = 0
@@ -74,65 +87,79 @@ class _AEADCipher(six.with_metaclass(_AEADCipherMetaclass, object)):
 
         # we use super() in order to avoid any deadlock with __setattr__
         super(_AEADCipher, self).__setattr__("key", key)
-        super(_AEADCipher, self).__setattr__("salt", salt)
+        super(_AEADCipher, self).__setattr__("fixed_iv", fixed_iv)
         super(_AEADCipher, self).__setattr__("nonce_explicit", nonce_explicit)
 
-        iv = salt + pkcs_i2osp(nonce_explicit, self.nonce_explicit_len)
-        self._cipher = Cipher(self.pc_cls(key),
-                              self.pc_cls_mode(iv),
-                              backend=default_backend())
+        if hasattr(self, "pc_cls"):
+            self._cipher = Cipher(self.pc_cls(key),
+                                  self.pc_cls_mode(self._get_nonce()),
+                                  backend=default_backend())
+        else:
+            self._cipher = self.cipher_cls(key)
 
     def __setattr__(self, name, val):
         if name == "key":
             if self._cipher is not None:
-                self._cipher.algorithm.key = val
+                if hasattr(self, "pc_cls"):
+                    self._cipher.algorithm.key = val
+                else:
+                    self._cipher._key = val
             self.ready["key"] = True
-        elif name == "salt":
-            iv = val + pkcs_i2osp(self.nonce_explicit, self.nonce_explicit_len)
-            if self._cipher is not None:
-                self._cipher.mode._initialization_vector = iv
-            self.ready["salt"] = True
+        elif name == "fixed_iv":
+            self.ready["fixed_iv"] = True
         elif name == "nonce_explicit":
             if isinstance(val, str):
                 val = pkcs_os2ip(val)
-            iv = self.salt + pkcs_i2osp(val, self.nonce_explicit_len)
-            if self._cipher is not None:
-                self._cipher.mode._initialization_vector = iv
             self.ready["nonce_explicit"] = True
         super(_AEADCipher, self).__setattr__(name, val)
 
-    def _update_nonce(self):
+
+    def _get_nonce(self):
+        return (self.fixed_iv +
+                pkcs_i2osp(self.nonce_explicit, self.nonce_explicit_len))
+
+    def _update_nonce_explicit(self):
         """
         Increment the explicit nonce while avoiding any overflow.
         """
         ne = self.nonce_explicit + 1
         self.nonce_explicit = ne % 2**(self.nonce_explicit_len*8)
 
-    def auth_encrypt(self, P, A):
+    def auth_encrypt(self, P, A, seq_num=None):
         """
-        Encrypt the data, prepend the explicit part of the nonce,
-        and append the computed authentication code.
-        Additional data may be authenticated without encryption (as A).
+        Encrypt the data then prepend the explicit part of the nonce. The
+        authentication tag is directly appended with the most recent crypto
+        API. Additional data may be authenticated without encryption (as A).
 
-        Note that the cipher's authentication tag must be None when encrypting.
+        The 'seq_num' should never be used here, it is only a safeguard needed
+        because one cipher (ChaCha20Poly1305) using TLS 1.2 logic in record.py
+        actually is a _AEADCipher_TLS13 (even though others are not).
         """
         if False in six.itervalues(self.ready):
             raise CipherError, (P, A)
-        self._cipher.mode._tag = None
-        encryptor = self._cipher.encryptor()
-        encryptor.authenticate_additional_data(A)
-        res = encryptor.update(P) + encryptor.finalize()
-        res += encryptor.tag
+
+        if hasattr(self, "pc_cls"):
+            self._cipher.mode._initialization_vector = self._get_nonce()
+            self._cipher.mode._tag = None
+            encryptor = self._cipher.encryptor()
+            encryptor.authenticate_additional_data(A)
+            res = encryptor.update(P) + encryptor.finalize()
+            res += encryptor.tag
+        else:
+            if isinstance(self._cipher, AESCCM):
+                res = self._cipher.encrypt(self._get_nonce(), P, A,
+                                           tag_length=self.tag_len)
+            else:
+                res = self._cipher.encrypt(self._get_nonce(), P, A)
 
         nonce_explicit = pkcs_i2osp(self.nonce_explicit,
                                     self.nonce_explicit_len)
-        self._update_nonce()
+        self._update_nonce_explicit()
         return nonce_explicit + res
 
-    def auth_decrypt(self, A, C, add_length=True):
+    def auth_decrypt(self, A, C, seq_num=None, add_length=True):
         """
-        Decrypt the data and verify the authentication code (in this order).
-        When additional data was authenticated, it has to be passed (as A).
+        Decrypt the data and authenticate the associated data (i.e. A).
         If the verification fails, an AEADTagError is raised. It is the user's
         responsibility to catch it if deemed useful. If we lack the key, we
         raise a CipherError which contains the encrypted input.
@@ -143,6 +170,10 @@ class _AEADCipher(six.with_metaclass(_AEADCipherMetaclass, object)):
 
         The 'add_length' switch should always be True for TLS, but we provide
         it anyway (mostly for test cases, hum).
+
+        The 'seq_num' should never be used here, it is only a safeguard needed
+        because one cipher (ChaCha20Poly1305) using TLS 1.2 logic in record.py
+        actually is a _AEADCipher_TLS13 (even though others are not).
         """
         nonce_explicit_str, C, mac = (C[:self.nonce_explicit_len],
                                       C[self.nonce_explicit_len:-self.tag_len],
@@ -152,44 +183,230 @@ class _AEADCipher(six.with_metaclass(_AEADCipherMetaclass, object)):
             raise CipherError, (nonce_explicit_str, C, mac)
 
         self.nonce_explicit = pkcs_os2ip(nonce_explicit_str)
-        self._cipher.mode._tag = mac
-
-        decryptor = self._cipher.decryptor()
         if add_length:
             A += struct.pack("!H", len(C))
-        decryptor.authenticate_additional_data(A)
-
-        P = decryptor.update(C)
-        try:
-            decryptor.finalize()
-        except InvalidTag:
-            raise AEADTagError, (nonce_explicit_str, P, mac)
+        
+        if hasattr(self, "pc_cls"):
+            self._cipher.mode._initialization_vector = self._get_nonce()
+            self._cipher.mode._tag = mac
+            decryptor = self._cipher.decryptor()
+            decryptor.authenticate_additional_data(A)
+            P = decryptor.update(C)
+            try:
+                decryptor.finalize()
+            except InvalidTag:
+                raise AEADTagError, (nonce_explicit_str, P, mac)
+        else:
+            try:
+                if isinstance(self._cipher, AESCCM):
+                    P = self._cipher.decrypt(self._get_nonce(), C + mac, A,
+                                             tag_length=self.tag_len)
+                else:
+                    P = self._cipher.decrypt(self._get_nonce(), C + mac, A)
+            except InvalidTag:
+                raise AEADTagError, (nonce_explicit_str,
+                                     "<unauthenticated data>",
+                                     mac)
         return nonce_explicit_str, P, mac
 
+    def snapshot(self):
+        c = self.__class__(self.key, self.fixed_iv, self.nonce_explicit)
+        c.ready = self.ready.copy()
+        return c
 
-class Cipher_AES_128_GCM(_AEADCipher):
-    pc_cls = algorithms.AES
-    pc_cls_mode = modes.GCM
-    block_size = 16
-    key_len = 16
-    salt_len = 4
-    nonce_explicit_len = 8
-    tag_len = 16
-
-class Cipher_AES_256_GCM(Cipher_AES_128_GCM):
-    key_len = 32
-
-
-# no support for now in the cryptography library
-#class Cipher_AES_128_CCM(_AEADCipher):
-#    pc_cls_mode = modes.CCM
-#
-#class Cipher_AES_256_CCM(Cipher_AES_128_CCM):
-#    key_len = 32
-#
-#class Cipher_AES_128_CCM_8(Cipher_AES_128_CCM):
-#    tag_len = 8
-#
-#class Cipher_AES_256_CCM_8(Cipher_AES_128_CCM_8):
-#    key_len = 32
+
+if conf.crypto_valid:
+    class Cipher_AES_128_GCM(_AEADCipher):
+       #XXX use the new AESGCM if available
+       #if conf.crypto_valid_advanced:
+       #    cipher_cls = AESGCM
+       #else:
+        pc_cls = algorithms.AES
+        pc_cls_mode = modes.GCM
+        key_len = 16
+        tag_len = 16
+
+    class Cipher_AES_256_GCM(Cipher_AES_128_GCM):
+        key_len = 32
+
+
+if conf.crypto_valid_advanced:
+    class Cipher_AES_128_CCM(_AEADCipher):
+        cipher_cls = AESCCM
+        key_len = 16
+        tag_len = 16
+
+    class Cipher_AES_256_CCM(Cipher_AES_128_CCM):
+        key_len = 32
+
+    class Cipher_AES_128_CCM_8(Cipher_AES_128_CCM):
+        tag_len = 8
+
+    class Cipher_AES_256_CCM_8(Cipher_AES_128_CCM_8):
+        key_len = 32
+
+
+class _AEADCipher_TLS13(object):
+    """
+    The hasattr(self, "pc_cls") enable support for the legacy implementation
+    of GCM in the cryptography library. They should not be used, and might
+    eventually be removed, with cryptography v2.0. XXX
+    """
+    __metaclass__ = _AEADCipherMetaclass
+    type = "aead"
+
+    def __init__(self, key=None, fixed_iv=None, nonce_explicit=None):
+        """
+        'key' and 'fixed_iv' are to be provided as strings. This IV never
+        changes: it is either the client_write_IV or server_write_IV.
+
+        Note that 'nonce_explicit' is never used. It is only a safeguard for a
+        call in session.py to the TLS 1.2/ChaCha20Poly1305 case (see RFC 7905).
+        """
+        self.ready = {"key": True, "fixed_iv": True}
+        if key is None:
+            self.ready["key"] = False
+            key = "\0" * self.key_len
+        if fixed_iv is None:
+            self.ready["fixed_iv"] = False
+            fixed_iv = "\0" * self.fixed_iv_len
+
+        # we use super() in order to avoid any deadlock with __setattr__
+        super(_AEADCipher_TLS13, self).__setattr__("key", key)
+        super(_AEADCipher_TLS13, self).__setattr__("fixed_iv", fixed_iv)
+
+        if hasattr(self, "pc_cls"):
+            self._cipher = Cipher(self.pc_cls(key),
+                                  self.pc_cls_mode(fixed_iv),
+                                  backend=default_backend())
+        else:
+            self._cipher = self.cipher_cls(key)
+
+    def __setattr__(self, name, val):
+        if name == "key":
+            if self._cipher is not None:
+                if hasattr(self, "pc_cls"):
+                    self._cipher.algorithm.key = val
+                else:
+                    self._cipher._key = val
+            self.ready["key"] = True
+        elif name == "fixed_iv":
+            self.ready["fixed_iv"] = True
+        super(_AEADCipher_TLS13, self).__setattr__(name, val)
+
+    def _get_nonce(self, seq_num):
+        padlen = self.fixed_iv_len - len(seq_num)
+        padded_seq_num = "\x00" * padlen + seq_num
+        return strxor(padded_seq_num, self.fixed_iv)
+
+    def auth_encrypt(self, P, A, seq_num):
+        """
+        Encrypt the data, and append the computed authentication code.
+        TLS 1.3 does not use additional data, but we leave this option to the
+        user nonetheless.
+
+        Note that the cipher's authentication tag must be None when encrypting.
+        """
+        if False in self.ready.itervalues():
+            raise CipherError, (P, A)
+
+        if hasattr(self, "pc_cls"):
+            self._cipher.mode._tag = None
+            self._cipher.mode._initialization_vector = self._get_nonce(seq_num)
+            encryptor = self._cipher.encryptor()
+            encryptor.authenticate_additional_data(A)
+            res = encryptor.update(P) + encryptor.finalize()
+            res += encryptor.tag
+        else:
+            if (conf.crypto_valid_advanced and
+                isinstance(self._cipher, AESCCM)):
+                res = self._cipher.encrypt(self._get_nonce(seq_num), P, A,
+                                           tag_length=self.tag_len)
+            else:
+                res = self._cipher.encrypt(self._get_nonce(seq_num), P, A)
+        return res
+
+    def auth_decrypt(self, A, C, seq_num):
+        """
+        Decrypt the data and verify the authentication code (in this order).
+        Note that TLS 1.3 is not supposed to use any additional data A.
+        If the verification fails, an AEADTagError is raised. It is the user's
+        responsibility to catch it if deemed useful. If we lack the key, we
+        raise a CipherError which contains the encrypted input.
+        """
+        C, mac = C[:-self.tag_len], C[-self.tag_len:]
+        if False in self.ready.itervalues():
+            raise CipherError, (C, mac)
+
+        if hasattr(self, "pc_cls"):
+            self._cipher.mode._initialization_vector = self._get_nonce(seq_num)
+            self._cipher.mode._tag = mac
+            decryptor = self._cipher.decryptor()
+            decryptor.authenticate_additional_data(A)
+            P = decryptor.update(C)
+            try:
+                decryptor.finalize()
+            except InvalidTag:
+                raise AEADTagError, (P, mac)
+        else:
+            try:
+                if (conf.crypto_valid_advanced and
+                    isinstance(self._cipher, AESCCM)):
+                    P = self._cipher.decrypt(self._get_nonce(seq_num), C + mac, A,
+                                             tag_length=self.tag_len)
+                else:
+                    if (conf.crypto_valid_advanced and
+                        isinstance(self, Cipher_CHACHA20_POLY1305)):
+                        A += struct.pack("!H", len(C))
+                    P = self._cipher.decrypt(self._get_nonce(seq_num), C + mac, A)
+            except InvalidTag:
+                raise AEADTagError, ("<unauthenticated data>", mac)
+        return P, mac
+
+    def snapshot(self):
+        c = self.__class__(self.key, self.fixed_iv)
+        c.ready = self.ready.copy()
+        return c
+
+
+if conf.crypto_valid_advanced:
+    class Cipher_CHACHA20_POLY1305_TLS13(_AEADCipher_TLS13):
+        cipher_cls = ChaCha20Poly1305
+        key_len = 32
+        tag_len = 16
+        fixed_iv_len = 12
+        nonce_explicit_len = 0
+
+    class Cipher_CHACHA20_POLY1305(Cipher_CHACHA20_POLY1305_TLS13):
+        """
+        This TLS 1.2 cipher actually uses TLS 1.3 logic, as per RFC 7905.
+        Changes occur at the record layer (in record.py).
+        """
+        pass
+
+
+if conf.crypto_valid:
+    class Cipher_AES_128_GCM_TLS13(_AEADCipher_TLS13):
+       #XXX use the new AESGCM if available
+       #if conf.crypto_valid_advanced:
+       #    cipher_cls = AESGCM
+       #else:
+        pc_cls = algorithms.AES
+        pc_cls_mode = modes.GCM
+        key_len = 16
+        fixed_iv_len = 12
+        tag_len = 16
+
+    class Cipher_AES_256_GCM_TLS13(Cipher_AES_128_GCM_TLS13):
+        key_len = 32
+
+
+if conf.crypto_valid_advanced:
+    class Cipher_AES_128_CCM_TLS13(_AEADCipher_TLS13):
+        cipher_cls = AESCCM
+        key_len = 16
+        tag_len = 16
+
+    class Cipher_AES_128_CCM_8_TLS13(Cipher_AES_128_CCM_TLS13):
+        tag_len = 8
 
diff --git a/scapy/layers/tls/crypto/cipher_block.py b/scapy/layers/tls/crypto/cipher_block.py
index 74f1e677d216c7c2d5d46f39c190145a39ced137..bf947b9f8b04f167837f07533366e870d1a632c7 100644
--- a/scapy/layers/tls/crypto/cipher_block.py
+++ b/scapy/layers/tls/crypto/cipher_block.py
@@ -1,6 +1,6 @@
 ## This file is part of Scapy
 ## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
-##                     2015, 2016 Maxence Tury
+##               2015, 2016, 2017 Maxence Tury
 ## This program is published under a GPLv2 license
 
 """
@@ -8,13 +8,19 @@ Block ciphers.
 """
 
 from __future__ import absolute_import
-from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
-from cryptography.hazmat.backends import default_backend
-
+from scapy.config import conf
 from scapy.utils import strxor
 from scapy.layers.tls.crypto.ciphers import CipherError
 import scapy.modules.six as six
 
+if conf.crypto_valid:
+    from cryptography.utils import register_interface
+    from cryptography.hazmat.primitives.ciphers import (Cipher, algorithms, modes,
+                                                        BlockCipherAlgorithm,
+                                                        CipherAlgorithm)
+    from cryptography.hazmat.backends.openssl.backend import (backend,
+                                                              GetCipherByName)
+
 
 tls_block_cipher_algs = {}
 
@@ -37,7 +43,7 @@ class _BlockCipher(six.with_metaclass(_BlockCipherMetaclass, object)):
     type = "block"
 
     def __init__(self, key=None, iv=None):
-        self.ready = {"key":True, "iv":True}
+        self.ready = {"key": True, "iv": True}
         if key is None:
             self.ready["key"] = False
             if hasattr(self, "expanded_key_len"):
@@ -55,7 +61,7 @@ class _BlockCipher(six.with_metaclass(_BlockCipherMetaclass, object)):
 
         self._cipher = Cipher(self.pc_cls(key),
                               self.pc_cls_mode(iv),
-                              backend=default_backend())
+                              backend=backend)
 
     def __setattr__(self, name, val):
         if name == "key":
@@ -94,69 +100,124 @@ class _BlockCipher(six.with_metaclass(_BlockCipherMetaclass, object)):
         self.iv = data[-self.block_size:]
         return tmp
 
+    def snapshot(self):
+        c = self.__class__(self.key, self.iv)
+        c.ready = self.ready.copy()
+        return c
 
-class Cipher_AES_128_CBC(_BlockCipher):
-    pc_cls = algorithms.AES
-    pc_cls_mode = modes.CBC
-    block_size = 16
-    key_len = 16
 
-class Cipher_AES_256_CBC(Cipher_AES_128_CBC):
-    key_len = 32
+if conf.crypto_valid:
+    class Cipher_AES_128_CBC(_BlockCipher):
+        pc_cls = algorithms.AES
+        pc_cls_mode = modes.CBC
+        block_size = 16
+        key_len = 16
 
+    class Cipher_AES_256_CBC(Cipher_AES_128_CBC):
+        key_len = 32
 
-class Cipher_CAMELLIA_128_CBC(_BlockCipher):
-    pc_cls = algorithms.Camellia
-    pc_cls_mode = modes.CBC
-    block_size = 16
-    key_len = 16
 
-class Cipher_CAMELLIA_256_CBC(Cipher_CAMELLIA_128_CBC):
-    key_len = 32
+    class Cipher_CAMELLIA_128_CBC(_BlockCipher):
+        pc_cls = algorithms.Camellia
+        pc_cls_mode = modes.CBC
+        block_size = 16
+        key_len = 16
+
+    class Cipher_CAMELLIA_256_CBC(Cipher_CAMELLIA_128_CBC):
+        key_len = 32
 
 
 ### Mostly deprecated ciphers
 
-class Cipher_DES_CBC(_BlockCipher):
-    pc_cls = algorithms.TripleDES
-    pc_cls_mode = modes.CBC
-    block_size = 8
-    key_len = 8
+if conf.crypto_valid:
+    class Cipher_DES_CBC(_BlockCipher):
+        pc_cls = algorithms.TripleDES
+        pc_cls_mode = modes.CBC
+        block_size = 8
+        key_len = 8
 
-class Cipher_DES40_CBC(Cipher_DES_CBC):
-    """
-    This is an export cipher example. The key length has been weakened to 5
-    random bytes (i.e. 5 bytes will be extracted from the master_secret).
-    Yet, we still need to know the original length which will actually be
-    fed into the encryption algorithm. This is what expanded_key_len
-    is for, and it gets used in PRF.postprocess_key_for_export().
-    We never define this attribute with non-export ciphers.
-    """
-    expanded_key_len = 8
-    key_len = 5
-
-class Cipher_3DES_EDE_CBC(_BlockCipher):
-    pc_cls = algorithms.TripleDES
-    pc_cls_mode = modes.CBC
-    block_size = 8
-    key_len = 24
-
-class Cipher_IDEA_CBC(_BlockCipher):
-    pc_cls = algorithms.IDEA
-    pc_cls_mode = modes.CBC
-    block_size = 8
-    key_len = 16
-
-class Cipher_SEED_CBC(_BlockCipher):
-    pc_cls = algorithms.SEED
-    pc_cls_mode = modes.CBC
-    block_size = 16
-    key_len = 16
-
-#class Cipher_RC2_CBC_40(_BlockCipher): # RFC 2268
-#    pc_cls = ARC2              # no support in the cryptography library
-#    pc_cls_mode = modes.CBC
-#    block_size = 8
-#    key_len = 5
-#    expanded_key_len = 16
+    class Cipher_DES40_CBC(Cipher_DES_CBC):
+        """
+        This is an export cipher example. The key length has been weakened to 5
+        random bytes (i.e. 5 bytes will be extracted from the master_secret).
+        Yet, we still need to know the original length which will actually be
+        fed into the encryption algorithm. This is what expanded_key_len
+        is for, and it gets used in PRF.postprocess_key_for_export().
+        We never define this attribute with non-export ciphers.
+        """
+        expanded_key_len = 8
+        key_len = 5
+
+    class Cipher_3DES_EDE_CBC(_BlockCipher):
+        pc_cls = algorithms.TripleDES
+        pc_cls_mode = modes.CBC
+        block_size = 8
+        key_len = 24
+
+    class Cipher_IDEA_CBC(_BlockCipher):
+        pc_cls = algorithms.IDEA
+        pc_cls_mode = modes.CBC
+        block_size = 8
+        key_len = 16
+
+    class Cipher_SEED_CBC(_BlockCipher):
+        pc_cls = algorithms.SEED
+        pc_cls_mode = modes.CBC
+        block_size = 16
+        key_len = 16
+
+
+sslv2_block_cipher_algs = {}
+
+if conf.crypto_valid:
+    sslv2_block_cipher_algs.update({
+        "IDEA_128_CBC":     Cipher_IDEA_CBC,
+        "DES_64_CBC":       Cipher_DES_CBC,
+        "DES_192_EDE3_CBC": Cipher_3DES_EDE_CBC
+        })
+
+
+# We need some black magic for RC2, which is not registered by default
+# to the openssl backend of the cryptography library.
+# If the current version of openssl does not support rc2, the RC2 ciphers are
+# silently not declared, and the corresponding suites will have 'usable' False.
+
+if conf.crypto_valid:
+    @register_interface(BlockCipherAlgorithm)
+    @register_interface(CipherAlgorithm)
+    class _ARC2(object):
+        name = "RC2"
+        block_size = 64
+        key_sizes = frozenset([128])
+
+        def __init__(self, key):
+            self.key = algorithms._verify_key_size(self, key)
+
+        @property
+        def key_size(self):
+            return len(self.key) * 8
+
+
+    _gcbn_format = "{cipher.name}-{mode.name}"
+    if GetCipherByName(_gcbn_format)(backend, _ARC2, modes.CBC) != \
+            backend._ffi.NULL:
+
+        class Cipher_RC2_CBC(_BlockCipher):
+            pc_cls = _ARC2
+            pc_cls_mode = modes.CBC
+            block_size = 8
+            key_len = 16
+
+        class Cipher_RC2_CBC_40(Cipher_RC2_CBC):
+            expanded_key_len = 16
+            key_len = 5
+
+        backend.register_cipher_adapter(Cipher_RC2_CBC.pc_cls,
+                                        Cipher_RC2_CBC.pc_cls_mode,
+                                        GetCipherByName(_gcbn_format))
+
+        sslv2_block_cipher_algs["RC2_128_CBC"] = Cipher_RC2_CBC
+
+
+tls_block_cipher_algs.update(sslv2_block_cipher_algs)
 
diff --git a/scapy/layers/tls/crypto/cipher_stream.py b/scapy/layers/tls/crypto/cipher_stream.py
index 1ceb279a952d6004b10f98e2d3e59f692a72713c..cb49b89ce3bbdf6d634438854b69a54f5c5ddef7 100644
--- a/scapy/layers/tls/crypto/cipher_stream.py
+++ b/scapy/layers/tls/crypto/cipher_stream.py
@@ -1,6 +1,6 @@
 ## This file is part of Scapy
 ## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
-##                     2015, 2016 Maxence Tury
+##               2015, 2016, 2017 Maxence Tury
 ## This program is published under a GPLv2 license
 
 """
@@ -8,12 +8,14 @@ Stream ciphers.
 """
 
 from __future__ import absolute_import
-from cryptography.hazmat.primitives.ciphers import Cipher, algorithms
-from cryptography.hazmat.backends import default_backend
-
+from scapy.config import conf
 from scapy.layers.tls.crypto.ciphers import CipherError
 import scapy.modules.six as six
 
+if conf.crypto_valid:
+    from cryptography.hazmat.primitives.ciphers import Cipher, algorithms
+    from cryptography.hazmat.backends import default_backend
+
 
 tls_stream_cipher_algs = {}
 
@@ -39,8 +41,12 @@ class _StreamCipher(six.with_metaclass(_StreamCipherMetaclass, object)):
         """
         Note that we have to keep the encryption/decryption state in unique
         encryptor and decryptor objects. This differs from _BlockCipher.
+
+        In order to do connection state snapshots, we need to be able to
+        recreate past cipher contexts. This is why we feed _enc_updated_with
+        and _dec_updated_with every time encrypt() or decrypt() is called.
         """
-        self.ready = {"key":True}
+        self.ready = {"key": True}
         if key is None:
             self.ready["key"] = False
             if hasattr(self, "expanded_key_len"):
@@ -57,42 +63,68 @@ class _StreamCipher(six.with_metaclass(_StreamCipherMetaclass, object)):
                               backend=default_backend())
         self.encryptor = self._cipher.encryptor()
         self.decryptor = self._cipher.decryptor()
+        self._enc_updated_with = ""
+        self._dec_updated_with = ""
 
     def __setattr__(self, name, val):
+        """
+        We have to keep the encryptor/decryptor for a long time,
+        however they have to be updated every time the key is changed.
+        """
         if name == "key":
             if self._cipher is not None:
                 self._cipher.algorithm.key = val
+                self.encryptor = self._cipher.encryptor()
+                self.decryptor = self._cipher.decryptor()
             self.ready["key"] = True
         super(_StreamCipher, self).__setattr__(name, val)
 
+
     def encrypt(self, data):
         if False in six.itervalues(self.ready):
             raise CipherError, data
+        self._enc_updated_with += data
         return self.encryptor.update(data)
 
     def decrypt(self, data):
         if False in six.itervalues(self.ready):
             raise CipherError, data
+        self._dec_updated_with += data
         return self.decryptor.update(data)
 
+    def snapshot(self):
+        c = self.__class__(self.key)
+        c.ready = self.ready.copy()
+        c.encryptor.update(self._enc_updated_with)
+        c.decryptor.update(self._dec_updated_with)
+        c._enc_updated_with = self._enc_updated_with
+        c._dec_updated_with = self._dec_updated_with
+        return c
 
-class Cipher_RC4_128(_StreamCipher):
-    pc_cls = algorithms.ARC4
-    key_len = 16
 
-class Cipher_RC4_40(Cipher_RC4_128):
-    expanded_key_len = 16
-    key_len = 5
+if conf.crypto_valid:
+    class Cipher_RC4_128(_StreamCipher):
+        pc_cls = algorithms.ARC4
+        key_len = 16
+
+    class Cipher_RC4_40(Cipher_RC4_128):
+        expanded_key_len = 16
+        key_len = 5
 
 
 class Cipher_NULL(_StreamCipher):
     key_len = 0
 
     def __init__(self, key=None):
-        self.ready = {"key":True}
-        # we use super() in order to avoid any deadlock with __setattr__
-        super(_StreamCipher, self).__setattr__("key", key)
+        self.ready = {"key": True}
         self._cipher = None
+        # we use super() in order to avoid any deadlock with __setattr__
+        super(Cipher_NULL, self).__setattr__("key", key)
+
+    def snapshot(self):
+        c = self.__class__(self.key)
+        c.ready = self.ready.copy()
+        return c
 
     def encrypt(self, data):
         return data
diff --git a/scapy/layers/tls/crypto/curves.py b/scapy/layers/tls/crypto/curves.py
deleted file mode 100644
index bd6fe1222929964c73f4a600c9f37f4baed8dd75..0000000000000000000000000000000000000000
--- a/scapy/layers/tls/crypto/curves.py
+++ /dev/null
@@ -1,269 +0,0 @@
-## This file is part of Scapy
-## See http://www.secdev.org/projects/scapy for more informations
-## Copyright (C) 2016 Pascal Delaunay, Maxence Tury
-## This program is published under a GPLv2 license
-
-"""
-Implicit elliptic curves.
-
-Recommended curve parameters from www.secg.org/SEC2-Ver-1.0.pdf
-and www.ecc-brainpool.org/download/Domain-parameters.pdf
-
-This is a ghost file since the shift from 'ecdsa' to 'cryptography' lib.
-Part of the code has been kept, but commented out, in case anyone would like
-to improve ECC support in 'cryptography' (namely for the compressed point
-format and additional curves).
-"""
-
-#import math
-#
-#from scapy.utils import long_converter, binrepr
-#from scapy.layers.tls.crypto.pkcs1 import pkcs_i2osp, pkcs_os2ip
-#
-#
-#def encode_point(point, point_format=0):
-#    """
-#    Return a string representation of the Point p, according to point_format.
-#    """
-#    pLen = len(binrepr(point.curve().p()))
-#    x = pkcs_i2osp(point.x(), math.ceil(pLen/8))
-#    y = pkcs_i2osp(point.y(), math.ceil(pLen/8))
-#    if point_format == 0:
-#        frmt = b'\x04'
-#    elif point_format == 1:
-#        frmt = chr(2 + y%2)
-#        y = ''
-#    else:
-#        raise Exception("No support for point_format %d" % point_format)
-#    return frmt + x + y
-#
-#
-#try:
-#    import ecdsa
-#    ecdsa_support = True
-#except ImportError:
-#    import logging
-#    log_loading = logging.getLogger("scapy.loading")
-#    log_loading.info("Can't import python ecdsa lib. No curves.")
-#
-#
-#if ecdsa_support:
-#
-#    from ecdsa.ellipticcurve import CurveFp, Point
-#    from ecdsa.curves import Curve
-#    from ecdsa.numbertheory import square_root_mod_prime
-#
-#
-#    def extract_coordinates(g, curve):
-#        """
-#        Return the coordinates x and y as integers,
-#        regardless of the point format of string g.
-#        Second expected parameter is a CurveFp.
-#        """
-#        p = curve.p()
-#        point_format = g[0]
-#        point = g[1:]
-#        if point_format == b'\x04':
-#            point_len = len(point)
-#            if point_len % 2 != 0:
-#                raise Exception("Point length is not even.")
-#            x_bytes = point[:point_len>>1]
-#            x = pkcs_os2ip(x_bytes) % p
-#            y_bytes = point[point_len>>1:]
-#            y = pkcs_os2ip(y_bytes) % p
-#        elif point_format in [b'\x02', b'\x03']:
-#            x_bytes = point
-#            x = pkcs_os2ip(x_bytes) % p
-#            # perform the y coordinate computation with self.tls_ec
-#            y_square = (x*x*x + curve.a()*x + curve.b()) % p
-#            y = square_root_mod_prime(y_square, p)
-#            y_parity = ord(point_format) % 2    # \x02 means even, \x03 means odd
-#            if y % 2 != y_parity:
-#                y = -y % p
-#        else:
-#            raise Exception("Point starts with %s. This encoding "
-#                            "is not recognized." % repr(point_format))
-#        if not curve.contains_point(x, y):
-#            raise Exception("The point we extracted does not belong on the curve!")
-#        return x, y
-#
-#    def import_curve(p, a, b, g, r, name="dummyName", oid=(1, 3, 132, 0, 0xff)):
-#        """
-#        Create an ecdsa.curves.Curve from the usual parameters.
-#        Arguments may be either octet strings or integers,
-#        except g which we expect to be an octet string.
-#        """
-#        if isinstance(p, str):
-#            p = pkcs_os2ip(p)
-#        if isinstance(a, str):
-#            a = pkcs_os2ip(a)
-#        if isinstance(b, str):
-#            b = pkcs_os2ip(b)
-#        if isinstance(r, str):
-#            r = pkcs_os2ip(r)
-#        curve = CurveFp(p, a, b)
-#        x, y = extract_coordinates(g, curve)
-#        generator = Point(curve, x, y, r)
-#        return Curve(name, curve, generator, oid)
-
-
-    ### Named curves
-
-    # We always provide _a as a positive integer.
-
-#    _p          = long_converter("""
-#                  ffffffff ffffffff ffffffff fffffffe ffffac73""")
-#    _a          = 0
-#    _b          = 7
-#    _Gx         = long_converter("""
-#                  3b4c382c e37aa192 a4019e76 3036f4f5 dd4d7ebb""")
-#    _Gy         = long_converter("""
-#                  938cf935 318fdced 6bc28286 531733c3 f03c4fee""")
-#    _r          = long_converter("""01
-#                  00000000 00000000 0001b8fa 16dfab9a ca16b6b3""")
-#    curve       = CurveFp(_p, _a, _b)
-#    generator   = Point(curve, _Gx, _Gy, _r)
-#    SECP160k1   = Curve("SECP160k1", curve, generator,
-#                        (1, 3, 132, 0, 9), "secp160k1")
-
-#    _p          = long_converter("""
-#                  ffffffff ffffffff ffffffff ffffffff 7fffffff""")
-#    _a          = -3 % _p
-#    _b          = long_converter("""
-#                  1c97befc 54bd7a8b 65acf89f 81d4d4ad c565fa45""")
-#    _Gx         = long_converter("""
-#                  4a96b568 8ef57328 46646989 68c38bb9 13cbfc82""")
-#    _Gy         = long_converter("""
-#                  23a62855 3168947d 59dcc912 04235137 7ac5fb32""")
-#    _r          = long_converter("""01
-#                  00000000 00000000 0001f4c8 f927aed3 ca752257""")
-#    curve       = CurveFp(_p, _a, _b)
-#    generator   = Point(curve, _Gx, _Gy, _r)
-#    SECP160r1   = Curve("SECP160r1", curve, generator,
-#                        (1, 3, 132, 0, 8), "secp160r1")
-
-#    _p          = long_converter("""
-#                  ffffffff ffffffff ffffffff fffffffe ffffac73""")
-#    _a          = -3 % _p
-#    _b          = long_converter("""
-#                  b4e134d3 fb59eb8b ab572749 04664d5a f50388ba""")
-#    _Gx         = long_converter("""
-#                  52dcb034 293a117e 1f4ff11b 30f7199d 3144ce6d""")
-#    _Gy         = long_converter("""
-#                  feaffef2 e331f296 e071fa0d f9982cfe a7d43f2e""")
-#    _r          = long_converter("""01
-#                  00000000 00000000 0000351e e786a818 f3a1a16b""")
-#    curve       = CurveFp(_p, _a, _b)
-#    generator   = Point(curve, _Gx, _Gy, _r)
-#    SECP160r2   = Curve("SECP160r2", curve, generator,
-#                        (1, 3, 132, 0, 30), "secp160r2")
-
-#    _p          = long_converter("""
-#                  ffffffff ffffffff ffffffff ffffffff fffffffe ffffee37""")
-#    _a          = 0
-#    _b          = 3
-#    _Gx         = long_converter("""
-#                  db4ff10e c057e9ae 26b07d02 80b7f434 1da5d1b1 eae06c7d""")
-#    _Gy         = long_converter("""
-#                  9b2f2f6d 9c5628a7 844163d0 15be8634 4082aa88 d95e2f9d""")
-#    _r          = long_converter("""
-#                  ffffffff ffffffff fffffffe 26f2fc17 0f69466a 74defd8d""")
-#    curve       = CurveFp(_p, _a, _b)
-#    generator   = Point(curve, _Gx, _Gy, _r)
-#    SECP192k1   = Curve("SECP192k1", curve, generator,
-#                        (1, 3, 132, 0, 31), "secp192k1")
-
-#    _p          = long_converter("""
-#                  ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe
-#                  ffffe56d""")
-#    _a          = 0
-#    _b          = 5
-#    _Gx         = long_converter("""
-#                  a1455b33 4df099df 30fc28a1 69a467e9 e47075a9 0f7e650e
-#                  b6b7a45c""")
-#    _Gy         = long_converter("""
-#                  7e089fed 7fba3442 82cafbd6 f7e319f7 c0b0bd59 e2ca4bdb
-#                  556d61a5""")
-#    _r          = long_converter("""01
-#                  00000000 00000000 00000000 0001dce8 d2ec6184 caf0a971
-#                  769fb1f7""")
-#    curve       = CurveFp(_p, _a, _b)
-#    generator   = Point(curve, _Gx, _Gy, _r)
-#    SECP224k1   = Curve("SECP224k1", curve, generator,
-#                        (1, 3, 132, 0, 32), "secp224k1")
-
-#    _p          = long_converter("""
-#                  A9FB57DB A1EEA9BC 3E660A90 9D838D72 6E3BF623 D5262028
-#                  2013481D 1F6E5377""")
-#    _a          = long_converter("""
-#                  7D5A0975 FC2C3057 EEF67530 417AFFE7 FB8055C1 26DC5C6C
-#                  E94A4B44 F330B5D9""")
-#    _b          = long_converter("""
-#                  26DC5C6C E94A4B44 F330B5D9 BBD77CBF 95841629 5CF7E1CE
-#                  6BCCDC18 FF8C07B6""")
-#    _Gx         = long_converter("""
-#                  8BD2AEB9 CB7E57CB 2C4B482F FC81B7AF B9DE27E1 E3BD23C2
-#                  3A4453BD 9ACE3262""")
-#    _Gy         = long_converter("""
-#                  547EF835 C3DAC4FD 97F8461A 14611DC9 C2774513 2DED8E54
-#                  5C1D54C7 2F046997""")
-#    _r          = long_converter("""
-#                  A9FB57DB A1EEA9BC 3E660A90 9D838D71 8C397AA3 B561A6F7
-#                  901E0E82 974856A7""")
-#    curve       = CurveFp(_p, _a, _b)
-#    generator   = Point(curve, _Gx, _Gy, _r)
-#    BRNP256r1   = Curve("BRNP256r1", curve, generator,
-#                        (1, 3, 36, 3, 3, 2, 8, 1, 1, 7), "brainpoolP256r1")
-
-#    _p          = long_converter("""
-#                  8CB91E82 A3386D28 0F5D6F7E 50E641DF 152F7109 ED5456B4
-#                  12B1DA19 7FB71123 ACD3A729 901D1A71 87470013 3107EC53""")
-#    _a          = long_converter("""
-#                  7BC382C6 3D8C150C 3C72080A CE05AFA0 C2BEA28E 4FB22787
-#                  139165EF BA91F90F 8AA5814A 503AD4EB 04A8C7DD 22CE2826""")
-#    _b          = long_converter("""
-#                  04A8C7DD 22CE2826 8B39B554 16F0447C 2FB77DE1 07DCD2A6
-#                  2E880EA5 3EEB62D5 7CB43902 95DBC994 3AB78696 FA504C11""")
-#    _Gx         = long_converter("""
-#                  1D1C64F0 68CF45FF A2A63A81 B7C13F6B 8847A3E7 7EF14FE3
-#                  DB7FCAFE 0CBD10E8 E826E034 36D646AA EF87B2E2 47D4AF1E""")
-#    _Gy         = long_converter("""
-#                  8ABE1D75 20F9C2A4 5CB1EB8E 95CFD552 62B70B29 FEEC5864
-#                  E19C054F F9912928 0E464621 77918111 42820341 263C5315""")
-#    _r          = long_converter("""
-#                  8CB91E82 A3386D28 0F5D6F7E 50E641DF 152F7109 ED5456B3
-#                  1F166E6C AC0425A7 CF3AB6AF 6B7FC310 3B883202 E9046565""")
-#    curve       = CurveFp(_p, _a, _b)
-#    generator   = Point(curve, _Gx, _Gy, _r)
-#    BRNP384r1   = Curve("BRNP384r1", curve, generator,
-#                        (1, 3, 36, 3, 3, 2, 8, 1, 1, 11), "brainpoolP384r1")
-
-#    _p          = long_converter("""
-#                  AADD9DB8 DBE9C48B 3FD4E6AE 33C9FC07 CB308DB3 B3C9D20E
-#                  D6639CCA 70330871 7D4D9B00 9BC66842 AECDA12A E6A380E6
-#                  2881FF2F 2D82C685 28AA6056 583A48F3""")
-#    _a          = long_converter("""
-#                  7830A331 8B603B89 E2327145 AC234CC5 94CBDD8D 3DF91610
-#                  A83441CA EA9863BC 2DED5D5A A8253AA1 0A2EF1C9 8B9AC8B5
-#                  7F1117A7 2BF2C7B9 E7C1AC4D 77FC94CA""")
-#    _b          = long_converter("""
-#                  3DF91610 A83441CA EA9863BC 2DED5D5A A8253AA1 0A2EF1C9
-#                  8B9AC8B5 7F1117A7 2BF2C7B9 E7C1AC4D 77FC94CA DC083E67
-#                  984050B7 5EBAE5DD 2809BD63 8016F723""")
-#    _Gx         = long_converter("""
-#                  81AEE4BD D82ED964 5A21322E 9C4C6A93 85ED9F70 B5D916C1
-#                  B43B62EE F4D0098E FF3B1F78 E2D0D48D 50D1687B 93B97D5F
-#                  7C6D5047 406A5E68 8B352209 BCB9F822""")
-#    _Gy         = long_converter("""
-#                  7DDE385D 566332EC C0EABFA9 CF7822FD F209F700 24A57B1A
-#                  A000C55B 881F8111 B2DCDE49 4A5F485E 5BCA4BD8 8A2763AE
-#                  D1CA2B2F A8F05406 78CD1E0F 3AD80892""")
-#    _r          = long_converter("""
-#                  AADD9DB8 DBE9C48B 3FD4E6AE 33C9FC07 CB308DB3 B3C9D20E
-#                  D6639CCA 70330870 553E5C41 4CA92619 41866119 7FAC1047
-#                  1DB1D381 085DDADD B5879682 9CA90069""")
-#    curve       = CurveFp(_p, _a, _b)
-#    generator   = Point(curve, _Gx, _Gy, _r)
-#    BRNP512r1   = Curve("BRNP512r1", curve, generator,
-#                        (1, 3, 36, 3, 3, 2, 8, 1, 1, 13), "brainpoolP512r1")
-
diff --git a/scapy/layers/tls/crypto/ffdh.py b/scapy/layers/tls/crypto/ffdh.py
deleted file mode 100644
index fad685c21bf4f3dd12ed4e70a5993bf36e6ff8dd..0000000000000000000000000000000000000000
--- a/scapy/layers/tls/crypto/ffdh.py
+++ /dev/null
@@ -1,306 +0,0 @@
-## This file is part of Scapy
-## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
-##                     2015, 2016 Maxence Tury
-## This program is published under a GPLv2 license
-
-"""
-This is a register for DH groups from RFC 3526 and RFC 4306.
-XXX These groups (and the ones from RFC 7919) should be registered to
-the cryptography library. And this file should eventually be removed.
-"""
-
-from __future__ import absolute_import
-from scapy.config import conf
-if conf.crypto_valid:
-    from cryptography.hazmat.backends import default_backend
-    from cryptography.hazmat.primitives.asymmetric import dh
-else:
-    default_backend = dh = None
-
-from scapy.utils import long_converter
-import scapy.modules.six as six
-
-
-class modp768: # From RFC 4306
-    g = 0x02
-    m = long_converter("""
-    FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
-    8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B
-    302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9
-    A63A3620 FFFFFFFF FFFFFFFF""")
-    mLen = 768
-
-class modp1024: # From RFC 4306
-    g = 0x02
-    m = long_converter("""
-    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""")
-    mLen  = 1024
-
-class modp1536: # From RFC 3526
-    g = 0x02
-    m = long_converter("""
-    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 ECE45B3D
-    C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
-    83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
-    670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF""")
-    mLen  = 1536
-
-class modp2048: # From RFC 3526
-    g = 0x02
-    m = long_converter("""
-    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 ECE45B3D
-    C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
-    83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
-    670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B
-    E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9
-    DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510
-    15728E5A 8AACAA68 FFFFFFFF FFFFFFFF""")
-    mLen  = 2048
-
-class modp3072: # From RFC 3526
-    g = 0x02
-    m = long_converter("""
-    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 ECE45B3D
-    C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
-    83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
-    670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B
-    E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9
-    DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510
-    15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64
-    ECFB8504 58DBEF0A 8AEA7157 5D060C7D 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""")
-    mLen  = 3072
-
-class modp4096: # From RFC 3526
-    g = 0x02
-    m = long_converter("""
-    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 ECE45B3D
-    C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
-    83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
-    670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B
-    E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9
-    DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510
-    15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64
-    ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7
-    ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B
-    F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
-    BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31
-    43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7
-    88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA
-    2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6
-    287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED
-    1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9
-    93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34063199
-    FFFFFFFF FFFFFFFF""")
-    mLen  = 4096
-
-class modp6144: # From RFC 3526
-    g = 0x02
-    m = long_converter("""
-    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 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8
-    FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
-    670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C
-    180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718
-    3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D
-    04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D
-    B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226
-    1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
-    BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC
-    E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7 88719A10 BDBA5B26
-    99C32718 6AF4E23C 1A946834 B6150BDA 2583E9CA 2AD44CE8 DBBBC2DB
-    04DE8EF9 2E8EFC14 1FBECAA6 287C5947 4E6BC05D 99B2964F A090C3A2
-    233BA186 515BE7ED 1F612970 CEE2D7AF B81BDD76 2170481C D0069127
-    D5B05AA9 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492
-    36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD F8FF9406
-    AD9E530E E5DB382F 413001AE B06A53ED 9027D831 179727B0 865A8918
-    DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B DB7F1447 E6CC254B 33205151
-    2BD7AF42 6FB8F401 378CD2BF 5983CA01 C64B92EC F032EA15 D1721D03
-    F482D7CE 6E74FEF6 D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F
-    BEC7E8F3 23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA
-    CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328 06A1D58B
-    B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C DA56C9EC 2EF29632
-    387FE8D7 6E3C0468 043E8F66 3F4860EE 12BF2D5B 0B7474D6 E694F91E
-    6DCC4024 FFFFFFFF FFFFFFFF""")
-    mLen = 6144
-
-class modp8192: # From RFC 3526
-    g = 0x02
-    m = long_converter("""
-    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 ECE45B3D
-    C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
-    83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
-    670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B
-    E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9
-    DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510
-    15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64
-    ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7
-    ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B
-    F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
-    BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31
-    43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7
-    88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA
-    2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6
-    287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED
-    1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9
-    93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492
-    36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD
-    F8FF9406 AD9E530E E5DB382F 413001AE B06A53ED 9027D831
-    179727B0 865A8918 DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B
-    DB7F1447 E6CC254B 33205151 2BD7AF42 6FB8F401 378CD2BF
-    5983CA01 C64B92EC F032EA15 D1721D03 F482D7CE 6E74FEF6
-    D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F BEC7E8F3
-    23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA
-    CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328
-    06A1D58B B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C
-    DA56C9EC 2EF29632 387FE8D7 6E3C0468 043E8F66 3F4860EE
-    12BF2D5B 0B7474D6 E694F91E 6DBE1159 74A3926F 12FEE5E4
-    38777CB6 A932DF8C D8BEC4D0 73B931BA 3BC832B6 8D9DD300
-    741FA7BF 8AFC47ED 2576F693 6BA42466 3AAB639C 5AE4F568
-    3423B474 2BF1C978 238F16CB E39D652D E3FDB8BE FC848AD9
-    22222E04 A4037C07 13EB57A8 1A23F0C7 3473FC64 6CEA306B
-    4BCBC886 2F8385DD FA9D4B7F A2C087E8 79683303 ED5BDD3A
-    062B3CF5 B3A278A6 6D2A13F8 3F44F82D DF310EE0 74AB6A36
-    4597E899 A0255DC1 64F31CC5 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""")
-    mLen = 8192
-
-_ffdh_raw_params = { 'modp768' : modp768,
-                     'modp1024': modp1024,
-                     'modp1536': modp1536,
-                     'modp2048': modp2048,
-                     'modp3072': modp3072,
-                     'modp4096': modp4096,
-                     'modp6144': modp6144,
-                     'modp8192': modp8192  }
-
-FFDH_GROUPS = {}
-if dh and default_backend:
-    for name, group in six.iteritems(_ffdh_raw_params):
-        pn = dh.DHParameterNumbers(group.m, group.g)
-        params = pn.parameters(default_backend())
-        FFDH_GROUPS[name] = [params, group.mLen]
-
-
-#from scapy.layers.tls.crypto.pkcs1 import pkcs_os2ip, pkcs_i2osp
-#
-#
-#class FFDHParams(object):
-#    """
-#    Finite-Field Diffie-Hellman parameters.
-#    self.priv is an integer. Its value may remain unknown.
-#    self.pub, self.other_pub, and finally self.secret, are also integers.
-#    Default group parameters relate to the 2048-bit group from RFC 3526.
-#    """
-#    def __init__(self, g=ffdh_params[2048].g,
-#                       m=ffdh_params[2048].m,
-#                       mLen=ffdh_params[2048].mLen):
-#        """
-#           g: group (2, 5, ...). Can be provided as a string or long.
-#           m: prime modulus. Can be provided as a string or long.
-#        mLen: prime modulus length in bits.
-#        """
-#        if type(g) is str:
-#            g = pkcs_os2ip(g)
-#        if type(m) is str:
-#            m = pkcs_os2ip(m)
-#
-#        self.g = long(g)
-#        self.m = long(m)
-#        self.mLen = mLen
-#
-#        self.priv       = None
-#        self.pub        = None
-#        self.other_pub  = None
-#        self.secret     = None
-#
-#    def gen_public_params(self):
-#        """
-#        Generate FFDH public parameter, by choosing a random private
-#        value in ] 0, p-1 [ and then exponentiating the generator of
-#        the group with the private value. The public parameter is
-#        returned as an octet string. The private parameter is internally
-#        available for further secret generation (using .gen_secret()).
-#
-#        Note that 'secret' and 'other_pub' attribute of the instance
-#        are reset by the call.
-#        """
-#        self.other_pub  = None
-#        self.secret     = None
-#
-#        # Private key generation : 0 < x < p-1
-#        x = random.randint(1, self.m-2)
-#        self.priv = x
-#
-#        # Exponentiation
-#        y = pow(self.g, self.priv, self.m)
-#        self.pub = y
-#
-#        # Integer-to-octet-string conversion
-#        y = pkcs_i2osp(y, self.mLen/8)
-#
-#        return y
-#
-#    def gen_secret(self, other_pub):
-#        """
-#        Given the peer's public value 'other_pub' provided as an octet string,
-#        the shared secret is computed by exponentiating the value using
-#        internally stored private value (self.priv, generated during
-#        public_parameter generation using .gen_public_params()).
-#
-#        Computed secret is returned as a bitstring and stored internally.
-#
-#        No specific check is done on 'other_pub' before exponentiation.
-#        """
-#        if type(other_pub) is str:
-#            other_pub = pkcs_os2ip(other_pub)
-#
-#        # Octet-string-to-integer conversion
-#        self.other_pub = other_pub
-#
-#        # Exponentiation
-#        z = pow(other_pub, self.priv, self.m)
-#
-#        # Integer-to-octet-string conversion
-#        z = pkcs_i2osp(z, self.mLen/8)
-#        self.secret = z
-#
-#        return z
-#
-#    def check_params(self):
-#        #XXX Do me, maybe
-#        pass
-
diff --git a/scapy/layers/tls/crypto/groups.py b/scapy/layers/tls/crypto/groups.py
new file mode 100644
index 0000000000000000000000000000000000000000..9cef2a3fed41142d63cf773294ab79af721f20f5
--- /dev/null
+++ b/scapy/layers/tls/crypto/groups.py
@@ -0,0 +1,690 @@
+## This file is part of Scapy
+## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
+##               2015, 2016, 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+This is a register for DH groups from RFC 3526 and RFC 4306.
+At this time the groups from RFC 7919 have not been registered by openssl,
+thus they cannot be imported from the cryptography library.
+
+We also provide TLS identifiers for these DH groups and also the ECDH groups.
+(Note that the equivalent of _ffdh_groups for ECDH is ec._CURVE_TYPES.)
+"""
+
+from __future__ import absolute_import
+
+from scapy.config import conf
+from scapy.utils import long_converter
+if conf.crypto_valid:
+    from cryptography.hazmat.backends import default_backend
+    from cryptography.hazmat.primitives.asymmetric import dh
+import scapy.modules.six as six
+
+from scapy.config import conf
+from scapy.utils import long_converter
+
+# We have to start by a dirty hack in order to allow long generators,
+# which some versions of openssl love to use...
+
+if conf.crypto_valid:
+    from cryptography.hazmat.backends import default_backend
+    from cryptography.hazmat.primitives.asymmetric.dh import DHParameterNumbers
+
+    try:
+        # We test with dummy values whether the size limitation has been removed.
+        pn_test = DHParameterNumbers(2, 7)
+    except ValueError:
+        # We get rid of the limitation through the cryptography v1.9 __init__.
+        import six
+        def DHParameterNumbers__init__hack(self, p, g, q=None):
+            if (
+                not isinstance(p, six.integer_types) or
+                not isinstance(g, six.integer_types)
+            ):
+                raise TypeError("p and g must be integers")
+            if q is not None and not isinstance(q, six.integer_types):
+                raise TypeError("q must be integer or None")
+
+            self._p = p
+            self._g = g
+            self._q = q
+
+        DHParameterNumbers.__init__ = DHParameterNumbers__init__hack
+
+    # End of hack.
+
+
+_ffdh_groups = {}
+
+class _FFDHParamsMetaclass(type):
+    def __new__(cls, ffdh_name, bases, dct):
+        the_class = super(_FFDHParamsMetaclass, cls).__new__(cls, ffdh_name,
+                                                             bases, dct)
+        if conf.crypto_valid and ffdh_name != "_FFDHParams":
+            pn = DHParameterNumbers(the_class.m, the_class.g)
+            params = pn.parameters(default_backend())
+            _ffdh_groups[ffdh_name] = [params, the_class.mLen]
+        return the_class
+
+
+class _FFDHParams(object):
+    __metaclass__ = _FFDHParamsMetaclass
+
+
+class modp768(_FFDHParams):
+    g = 0x02
+    m = long_converter("""
+    FFFFFFFF FFFFFFFF C90FDAA2 2168C234 C4C6628B 80DC1CD1 29024E08
+    8A67CC74 020BBEA6 3B139B22 514A0879 8E3404DD EF9519B3 CD3A431B
+    302B0A6D F25F1437 4FE1356D 6D51C245 E485B576 625E7EC6 F44C42E9
+    A63A3620 FFFFFFFF FFFFFFFF""")
+    mLen = 768
+
+class modp1024(_FFDHParams): # From RFC 4306
+    g = 0x02
+    m = long_converter("""
+    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""")
+    mLen  = 1024
+
+class modp1536(_FFDHParams): # From RFC 3526
+    g = 0x02
+    m = long_converter("""
+    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 ECE45B3D
+    C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
+    83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
+    670C354E 4ABC9804 F1746C08 CA237327 FFFFFFFF FFFFFFFF""")
+    mLen  = 1536
+
+class modp2048(_FFDHParams): # From RFC 3526
+    g = 0x02
+    m = long_converter("""
+    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 ECE45B3D
+    C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
+    83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
+    670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B
+    E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9
+    DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510
+    15728E5A 8AACAA68 FFFFFFFF FFFFFFFF""")
+    mLen  = 2048
+
+class modp3072(_FFDHParams): # From RFC 3526
+    g = 0x02
+    m = long_converter("""
+    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 ECE45B3D
+    C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
+    83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
+    670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B
+    E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9
+    DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510
+    15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64
+    ECFB8504 58DBEF0A 8AEA7157 5D060C7D 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""")
+    mLen  = 3072
+
+class modp4096(_FFDHParams): # From RFC 3526
+    g = 0x02
+    m = long_converter("""
+    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 ECE45B3D
+    C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
+    83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
+    670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B
+    E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9
+    DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510
+    15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64
+    ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7
+    ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B
+    F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
+    BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31
+    43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7
+    88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA
+    2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6
+    287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED
+    1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9
+    93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34063199
+    FFFFFFFF FFFFFFFF""")
+    mLen  = 4096
+
+class modp6144(_FFDHParams): # From RFC 3526
+    g = 0x02
+    m = long_converter("""
+    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 ECE45B3D C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8
+    FD24CF5F 83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
+    670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B E39E772C
+    180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9 DE2BCBF6 95581718
+    3995497C EA956AE5 15D22618 98FA0510 15728E5A 8AAAC42D AD33170D
+    04507A33 A85521AB DF1CBA64 ECFB8504 58DBEF0A 8AEA7157 5D060C7D
+    B3970F85 A6E1E4C7 ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226
+    1AD2EE6B F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
+    BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31 43DB5BFC
+    E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7 88719A10 BDBA5B26
+    99C32718 6AF4E23C 1A946834 B6150BDA 2583E9CA 2AD44CE8 DBBBC2DB
+    04DE8EF9 2E8EFC14 1FBECAA6 287C5947 4E6BC05D 99B2964F A090C3A2
+    233BA186 515BE7ED 1F612970 CEE2D7AF B81BDD76 2170481C D0069127
+    D5B05AA9 93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492
+    36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD F8FF9406
+    AD9E530E E5DB382F 413001AE B06A53ED 9027D831 179727B0 865A8918
+    DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B DB7F1447 E6CC254B 33205151
+    2BD7AF42 6FB8F401 378CD2BF 5983CA01 C64B92EC F032EA15 D1721D03
+    F482D7CE 6E74FEF6 D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F
+    BEC7E8F3 23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA
+    CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328 06A1D58B
+    B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C DA56C9EC 2EF29632
+    387FE8D7 6E3C0468 043E8F66 3F4860EE 12BF2D5B 0B7474D6 E694F91E
+    6DCC4024 FFFFFFFF FFFFFFFF""")
+    mLen = 6144
+
+class modp8192(_FFDHParams): # From RFC 3526
+    g = 0x02
+    m = long_converter("""
+    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 ECE45B3D
+    C2007CB8 A163BF05 98DA4836 1C55D39A 69163FA8 FD24CF5F
+    83655D23 DCA3AD96 1C62F356 208552BB 9ED52907 7096966D
+    670C354E 4ABC9804 F1746C08 CA18217C 32905E46 2E36CE3B
+    E39E772C 180E8603 9B2783A2 EC07A28F B5C55DF0 6F4C52C9
+    DE2BCBF6 95581718 3995497C EA956AE5 15D22618 98FA0510
+    15728E5A 8AAAC42D AD33170D 04507A33 A85521AB DF1CBA64
+    ECFB8504 58DBEF0A 8AEA7157 5D060C7D B3970F85 A6E1E4C7
+    ABF5AE8C DB0933D7 1E8C94E0 4A25619D CEE3D226 1AD2EE6B
+    F12FFA06 D98A0864 D8760273 3EC86A64 521F2B18 177B200C
+    BBE11757 7A615D6C 770988C0 BAD946E2 08E24FA0 74E5AB31
+    43DB5BFC E0FD108E 4B82D120 A9210801 1A723C12 A787E6D7
+    88719A10 BDBA5B26 99C32718 6AF4E23C 1A946834 B6150BDA
+    2583E9CA 2AD44CE8 DBBBC2DB 04DE8EF9 2E8EFC14 1FBECAA6
+    287C5947 4E6BC05D 99B2964F A090C3A2 233BA186 515BE7ED
+    1F612970 CEE2D7AF B81BDD76 2170481C D0069127 D5B05AA9
+    93B4EA98 8D8FDDC1 86FFB7DC 90A6C08F 4DF435C9 34028492
+    36C3FAB4 D27C7026 C1D4DCB2 602646DE C9751E76 3DBA37BD
+    F8FF9406 AD9E530E E5DB382F 413001AE B06A53ED 9027D831
+    179727B0 865A8918 DA3EDBEB CF9B14ED 44CE6CBA CED4BB1B
+    DB7F1447 E6CC254B 33205151 2BD7AF42 6FB8F401 378CD2BF
+    5983CA01 C64B92EC F032EA15 D1721D03 F482D7CE 6E74FEF6
+    D55E702F 46980C82 B5A84031 900B1C9E 59E7C97F BEC7E8F3
+    23A97A7E 36CC88BE 0F1D45B7 FF585AC5 4BD407B2 2B4154AA
+    CC8F6D7E BF48E1D8 14CC5ED2 0F8037E0 A79715EE F29BE328
+    06A1D58B B7C5DA76 F550AA3D 8A1FBFF0 EB19CCB1 A313D55C
+    DA56C9EC 2EF29632 387FE8D7 6E3C0468 043E8F66 3F4860EE
+    12BF2D5B 0B7474D6 E694F91E 6DBE1159 74A3926F 12FEE5E4
+    38777CB6 A932DF8C D8BEC4D0 73B931BA 3BC832B6 8D9DD300
+    741FA7BF 8AFC47ED 2576F693 6BA42466 3AAB639C 5AE4F568
+    3423B474 2BF1C978 238F16CB E39D652D E3FDB8BE FC848AD9
+    22222E04 A4037C07 13EB57A8 1A23F0C7 3473FC64 6CEA306B
+    4BCBC886 2F8385DD FA9D4B7F A2C087E8 79683303 ED5BDD3A
+    062B3CF5 B3A278A6 6D2A13F8 3F44F82D DF310EE0 74AB6A36
+    4597E899 A0255DC1 64F31CC5 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""")
+    mLen = 8192
+
+class ffdhe2048(_FFDHParams): # From RFC 7919
+    g = 0x02
+    m = long_converter("""
+    FFFFFFFF FFFFFFFF ADF85458 A2BB4A9A AFDC5620 273D3CF1
+    D8B9C583 CE2D3695 A9E13641 146433FB CC939DCE 249B3EF9
+    7D2FE363 630C75D8 F681B202 AEC4617A D3DF1ED5 D5FD6561
+    2433F51F 5F066ED0 85636555 3DED1AF3 B557135E 7F57C935
+    984F0C70 E0E68B77 E2A689DA F3EFE872 1DF158A1 36ADE735
+    30ACCA4F 483A797A BC0AB182 B324FB61 D108A94B B2C8E3FB
+    B96ADAB7 60D7F468 1D4F42A3 DE394DF4 AE56EDE7 6372BB19
+    0B07A7C8 EE0A6D70 9E02FCE1 CDF7E2EC C03404CD 28342F61
+    9172FE9C E98583FF 8E4F1232 EEF28183 C3FE3B1B 4C6FAD73
+    3BB5FCBC 2EC22005 C58EF183 7D1683B2 C6F34A26 C1B2EFFA
+    886B4238 61285C97 FFFFFFFF FFFFFFFF
+    """)
+    mLen = 2048
+
+class ffdhe3072(_FFDHParams): # From RFC 7919
+    g = 0x02
+    m = long_converter("""
+    FFFFFFFF FFFFFFFF ADF85458 A2BB4A9A AFDC5620 273D3CF1
+    D8B9C583 CE2D3695 A9E13641 146433FB CC939DCE 249B3EF9
+    7D2FE363 630C75D8 F681B202 AEC4617A D3DF1ED5 D5FD6561
+    2433F51F 5F066ED0 85636555 3DED1AF3 B557135E 7F57C935
+    984F0C70 E0E68B77 E2A689DA F3EFE872 1DF158A1 36ADE735
+    30ACCA4F 483A797A BC0AB182 B324FB61 D108A94B B2C8E3FB
+    B96ADAB7 60D7F468 1D4F42A3 DE394DF4 AE56EDE7 6372BB19
+    0B07A7C8 EE0A6D70 9E02FCE1 CDF7E2EC C03404CD 28342F61
+    9172FE9C E98583FF 8E4F1232 EEF28183 C3FE3B1B 4C6FAD73
+    3BB5FCBC 2EC22005 C58EF183 7D1683B2 C6F34A26 C1B2EFFA
+    886B4238 611FCFDC DE355B3B 6519035B BC34F4DE F99C0238
+    61B46FC9 D6E6C907 7AD91D26 91F7F7EE 598CB0FA C186D91C
+    AEFE1309 85139270 B4130C93 BC437944 F4FD4452 E2D74DD3
+    64F2E21E 71F54BFF 5CAE82AB 9C9DF69E E86D2BC5 22363A0D
+    ABC52197 9B0DEADA 1DBF9A42 D5C4484E 0ABCD06B FA53DDEF
+    3C1B20EE 3FD59D7C 25E41D2B 66C62E37 FFFFFFFF FFFFFFFF
+    """)
+    mLen = 3072
+
+class ffdhe4096(_FFDHParams): # From RFC 7919
+    g = 0x02
+    m = long_converter("""
+    FFFFFFFF FFFFFFFF ADF85458 A2BB4A9A AFDC5620 273D3CF1
+    D8B9C583 CE2D3695 A9E13641 146433FB CC939DCE 249B3EF9
+    7D2FE363 630C75D8 F681B202 AEC4617A D3DF1ED5 D5FD6561
+    2433F51F 5F066ED0 85636555 3DED1AF3 B557135E 7F57C935
+    984F0C70 E0E68B77 E2A689DA F3EFE872 1DF158A1 36ADE735
+    30ACCA4F 483A797A BC0AB182 B324FB61 D108A94B B2C8E3FB
+    B96ADAB7 60D7F468 1D4F42A3 DE394DF4 AE56EDE7 6372BB19
+    0B07A7C8 EE0A6D70 9E02FCE1 CDF7E2EC C03404CD 28342F61
+    9172FE9C E98583FF 8E4F1232 EEF28183 C3FE3B1B 4C6FAD73
+    3BB5FCBC 2EC22005 C58EF183 7D1683B2 C6F34A26 C1B2EFFA
+    886B4238 611FCFDC DE355B3B 6519035B BC34F4DE F99C0238
+    61B46FC9 D6E6C907 7AD91D26 91F7F7EE 598CB0FA C186D91C
+    AEFE1309 85139270 B4130C93 BC437944 F4FD4452 E2D74DD3
+    64F2E21E 71F54BFF 5CAE82AB 9C9DF69E E86D2BC5 22363A0D
+    ABC52197 9B0DEADA 1DBF9A42 D5C4484E 0ABCD06B FA53DDEF
+    3C1B20EE 3FD59D7C 25E41D2B 669E1EF1 6E6F52C3 164DF4FB
+    7930E9E4 E58857B6 AC7D5F42 D69F6D18 7763CF1D 55034004
+    87F55BA5 7E31CC7A 7135C886 EFB4318A ED6A1E01 2D9E6832
+    A907600A 918130C4 6DC778F9 71AD0038 092999A3 33CB8B7A
+    1A1DB93D 7140003C 2A4ECEA9 F98D0ACC 0A8291CD CEC97DCF
+    8EC9B55A 7F88A46B 4DB5A851 F44182E1 C68A007E 5E655F6A
+    FFFFFFFF FFFFFFFF
+    """)
+    mLen = 4096
+
+class ffdhe6144(_FFDHParams): # From RFC 7919
+    g = 0x02
+    m = long_converter("""
+    FFFFFFFF FFFFFFFF ADF85458 A2BB4A9A AFDC5620 273D3CF1
+    D8B9C583 CE2D3695 A9E13641 146433FB CC939DCE 249B3EF9
+    7D2FE363 630C75D8 F681B202 AEC4617A D3DF1ED5 D5FD6561
+    2433F51F 5F066ED0 85636555 3DED1AF3 B557135E 7F57C935
+    984F0C70 E0E68B77 E2A689DA F3EFE872 1DF158A1 36ADE735
+    30ACCA4F 483A797A BC0AB182 B324FB61 D108A94B B2C8E3FB
+    B96ADAB7 60D7F468 1D4F42A3 DE394DF4 AE56EDE7 6372BB19
+    0B07A7C8 EE0A6D70 9E02FCE1 CDF7E2EC C03404CD 28342F61
+    9172FE9C E98583FF 8E4F1232 EEF28183 C3FE3B1B 4C6FAD73
+    3BB5FCBC 2EC22005 C58EF183 7D1683B2 C6F34A26 C1B2EFFA
+    886B4238 611FCFDC DE355B3B 6519035B BC34F4DE F99C0238
+    61B46FC9 D6E6C907 7AD91D26 91F7F7EE 598CB0FA C186D91C
+    AEFE1309 85139270 B4130C93 BC437944 F4FD4452 E2D74DD3
+    64F2E21E 71F54BFF 5CAE82AB 9C9DF69E E86D2BC5 22363A0D
+    ABC52197 9B0DEADA 1DBF9A42 D5C4484E 0ABCD06B FA53DDEF
+    3C1B20EE 3FD59D7C 25E41D2B 669E1EF1 6E6F52C3 164DF4FB
+    7930E9E4 E58857B6 AC7D5F42 D69F6D18 7763CF1D 55034004
+    87F55BA5 7E31CC7A 7135C886 EFB4318A ED6A1E01 2D9E6832
+    A907600A 918130C4 6DC778F9 71AD0038 092999A3 33CB8B7A
+    1A1DB93D 7140003C 2A4ECEA9 F98D0ACC 0A8291CD CEC97DCF
+    8EC9B55A 7F88A46B 4DB5A851 F44182E1 C68A007E 5E0DD902
+    0BFD64B6 45036C7A 4E677D2C 38532A3A 23BA4442 CAF53EA6
+    3BB45432 9B7624C8 917BDD64 B1C0FD4C B38E8C33 4C701C3A
+    CDAD0657 FCCFEC71 9B1F5C3E 4E46041F 388147FB 4CFDB477
+    A52471F7 A9A96910 B855322E DB6340D8 A00EF092 350511E3
+    0ABEC1FF F9E3A26E 7FB29F8C 183023C3 587E38DA 0077D9B4
+    763E4E4B 94B2BBC1 94C6651E 77CAF992 EEAAC023 2A281BF6
+    B3A739C1 22611682 0AE8DB58 47A67CBE F9C9091B 462D538C
+    D72B0374 6AE77F5E 62292C31 1562A846 505DC82D B854338A
+    E49F5235 C95B9117 8CCF2DD5 CACEF403 EC9D1810 C6272B04
+    5B3B71F9 DC6B80D6 3FDD4A8E 9ADB1E69 62A69526 D43161C1
+    A41D570D 7938DAD4 A40E329C D0E40E65 FFFFFFFF FFFFFFFF
+    """)
+    mLen = 6144
+
+class ffdhe8192(_FFDHParams): # From RFC 7919
+    g = 0x02
+    m = long_converter("""
+    FFFFFFFF FFFFFFFF ADF85458 A2BB4A9A AFDC5620 273D3CF1
+    D8B9C583 CE2D3695 A9E13641 146433FB CC939DCE 249B3EF9
+    7D2FE363 630C75D8 F681B202 AEC4617A D3DF1ED5 D5FD6561
+    2433F51F 5F066ED0 85636555 3DED1AF3 B557135E 7F57C935
+    984F0C70 E0E68B77 E2A689DA F3EFE872 1DF158A1 36ADE735
+    30ACCA4F 483A797A BC0AB182 B324FB61 D108A94B B2C8E3FB
+    B96ADAB7 60D7F468 1D4F42A3 DE394DF4 AE56EDE7 6372BB19
+    0B07A7C8 EE0A6D70 9E02FCE1 CDF7E2EC C03404CD 28342F61
+    9172FE9C E98583FF 8E4F1232 EEF28183 C3FE3B1B 4C6FAD73
+    3BB5FCBC 2EC22005 C58EF183 7D1683B2 C6F34A26 C1B2EFFA
+    886B4238 611FCFDC DE355B3B 6519035B BC34F4DE F99C0238
+    61B46FC9 D6E6C907 7AD91D26 91F7F7EE 598CB0FA C186D91C
+    AEFE1309 85139270 B4130C93 BC437944 F4FD4452 E2D74DD3
+    64F2E21E 71F54BFF 5CAE82AB 9C9DF69E E86D2BC5 22363A0D
+    ABC52197 9B0DEADA 1DBF9A42 D5C4484E 0ABCD06B FA53DDEF
+    3C1B20EE 3FD59D7C 25E41D2B 669E1EF1 6E6F52C3 164DF4FB
+    7930E9E4 E58857B6 AC7D5F42 D69F6D18 7763CF1D 55034004
+    87F55BA5 7E31CC7A 7135C886 EFB4318A ED6A1E01 2D9E6832
+    A907600A 918130C4 6DC778F9 71AD0038 092999A3 33CB8B7A
+    1A1DB93D 7140003C 2A4ECEA9 F98D0ACC 0A8291CD CEC97DCF
+    8EC9B55A 7F88A46B 4DB5A851 F44182E1 C68A007E 5E0DD902
+    0BFD64B6 45036C7A 4E677D2C 38532A3A 23BA4442 CAF53EA6
+    3BB45432 9B7624C8 917BDD64 B1C0FD4C B38E8C33 4C701C3A
+    CDAD0657 FCCFEC71 9B1F5C3E 4E46041F 388147FB 4CFDB477
+    A52471F7 A9A96910 B855322E DB6340D8 A00EF092 350511E3
+    0ABEC1FF F9E3A26E 7FB29F8C 183023C3 587E38DA 0077D9B4
+    763E4E4B 94B2BBC1 94C6651E 77CAF992 EEAAC023 2A281BF6
+    B3A739C1 22611682 0AE8DB58 47A67CBE F9C9091B 462D538C
+    D72B0374 6AE77F5E 62292C31 1562A846 505DC82D B854338A
+    E49F5235 C95B9117 8CCF2DD5 CACEF403 EC9D1810 C6272B04
+    5B3B71F9 DC6B80D6 3FDD4A8E 9ADB1E69 62A69526 D43161C1
+    A41D570D 7938DAD4 A40E329C CFF46AAA 36AD004C F600C838
+    1E425A31 D951AE64 FDB23FCE C9509D43 687FEB69 EDD1CC5E
+    0B8CC3BD F64B10EF 86B63142 A3AB8829 555B2F74 7C932665
+    CB2C0F1C C01BD702 29388839 D2AF05E4 54504AC7 8B758282
+    2846C0BA 35C35F5C 59160CC0 46FD8251 541FC68C 9C86B022
+    BB709987 6A460E74 51A8A931 09703FEE 1C217E6C 3826E52C
+    51AA691E 0E423CFC 99E9E316 50C1217B 624816CD AD9A95F9
+    D5B80194 88D9C0A0 A1FE3075 A577E231 83F81D4A 3F2FA457
+    1EFC8CE0 BA8A4FE8 B6855DFE 72B0A66E DED2FBAB FBE58A30
+    FAFABE1C 5D71A87E 2F741EF8 C1FE86FE A6BBFDE5 30677F0D
+    97D11D49 F7A8443D 0822E506 A9F4614E 011E2A94 838FF88C
+    D68C8BB7 C5C6424C FFFFFFFF FFFFFFFF
+    """)
+    mLen = 8192
+
+
+_tls_named_ffdh_groups = { 256: "ffdhe2048", 257: "ffdhe3072",
+                           258: "ffdhe4096", 259: "ffdhe6144",
+                           260: "ffdhe8192" }
+
+_tls_named_curves = {  1: "sect163k1",  2: "sect163r1",  3: "sect163r2",
+                       4: "sect193r1",  5: "sect193r2",  6: "sect233k1",
+                       7: "sect233r1",  8: "sect239k1",  9: "sect283k1",
+                      10: "sect283r1", 11: "sect409k1", 12: "sect409r1",
+                      13: "sect571k1", 14: "sect571r1", 15: "secp160k1",
+                      16: "secp160r1", 17: "secp160r2", 18: "secp192k1",
+                      19: "secp192r1", 20: "secp224k1", 21: "secp224r1",
+                      22: "secp256k1", 23: "secp256r1", 24: "secp384r1",
+                      25: "secp521r1", 26: "brainpoolP256r1",
+                      27: "brainpoolP384r1", 28: "brainpoolP512r1",
+                      29: "x25519",    30: "x448",
+                      0xff01: "arbitrary_explicit_prime_curves",
+                      0xff02: "arbitrary_explicit_char2_curves"}
+
+_tls_named_groups = {}
+_tls_named_groups.update(_tls_named_ffdh_groups)
+_tls_named_groups.update(_tls_named_curves)
+
+
+# Below lies ghost code since the shift from 'ecdsa' to 'cryptography' lib.
+# Part of the code has been kept, but commented out, in case anyone would like
+# to improve ECC support in 'cryptography' (namely for the compressed point
+# format and additional curves).
+# 
+# Recommended curve parameters from www.secg.org/SEC2-Ver-1.0.pdf
+# and www.ecc-brainpool.org/download/Domain-parameters.pdf
+#
+#
+#import math
+#
+#from scapy.utils import long_converter, binrepr
+#from scapy.layers.tls.crypto.pkcs1 import pkcs_i2osp, pkcs_os2ip
+#
+#
+#def encode_point(point, point_format=0):
+#    """
+#    Return a string representation of the Point p, according to point_format.
+#    """
+#    pLen = len(binrepr(point.curve().p()))
+#    x = pkcs_i2osp(point.x(), math.ceil(pLen/8))
+#    y = pkcs_i2osp(point.y(), math.ceil(pLen/8))
+#    if point_format == 0:
+#        frmt = b'\x04'
+#    elif point_format == 1:
+#        frmt = chr(2 + y%2)
+#        y = ''
+#    else:
+#        raise Exception("No support for point_format %d" % point_format)
+#    return frmt + x + y
+#
+#
+#try:
+#    import ecdsa
+#    ecdsa_support = True
+#except ImportError:
+#    import logging
+#    log_loading = logging.getLogger("scapy.loading")
+#    log_loading.info("Can't import python ecdsa lib. No curves.")
+#
+#
+#if ecdsa_support:
+#
+#    from ecdsa.ellipticcurve import CurveFp, Point
+#    from ecdsa.curves import Curve
+#    from ecdsa.numbertheory import square_root_mod_prime
+#
+#
+#    def extract_coordinates(g, curve):
+#        """
+#        Return the coordinates x and y as integers,
+#        regardless of the point format of string g.
+#        Second expected parameter is a CurveFp.
+#        """
+#        p = curve.p()
+#        point_format = g[0]
+#        point = g[1:]
+#        if point_format == b'\x04':
+#            point_len = len(point)
+#            if point_len % 2 != 0:
+#                raise Exception("Point length is not even.")
+#            x_bytes = point[:point_len>>1]
+#            x = pkcs_os2ip(x_bytes) % p
+#            y_bytes = point[point_len>>1:]
+#            y = pkcs_os2ip(y_bytes) % p
+#        elif point_format in [b'\x02', b'\x03']:
+#            x_bytes = point
+#            x = pkcs_os2ip(x_bytes) % p
+#            # perform the y coordinate computation with self.tls_ec
+#            y_square = (x*x*x + curve.a()*x + curve.b()) % p
+#            y = square_root_mod_prime(y_square, p)
+#            y_parity = ord(point_format) % 2    # \x02 means even, \x03 means odd
+#            if y % 2 != y_parity:
+#                y = -y % p
+#        else:
+#            raise Exception("Point starts with %s. This encoding "
+#                            "is not recognized." % repr(point_format))
+#        if not curve.contains_point(x, y):
+#            raise Exception("The point we extracted does not belong on the curve!")
+#        return x, y
+#
+#    def import_curve(p, a, b, g, r, name="dummyName", oid=(1, 3, 132, 0, 0xff)):
+#        """
+#        Create an ecdsa.curves.Curve from the usual parameters.
+#        Arguments may be either octet strings or integers,
+#        except g which we expect to be an octet string.
+#        """
+#        if isinstance(p, str):
+#            p = pkcs_os2ip(p)
+#        if isinstance(a, str):
+#            a = pkcs_os2ip(a)
+#        if isinstance(b, str):
+#            b = pkcs_os2ip(b)
+#        if isinstance(r, str):
+#            r = pkcs_os2ip(r)
+#        curve = CurveFp(p, a, b)
+#        x, y = extract_coordinates(g, curve)
+#        generator = Point(curve, x, y, r)
+#        return Curve(name, curve, generator, oid)
+
+
+    ### Named curves
+
+    # We always provide _a as a positive integer.
+
+#    _p          = long_converter("""
+#                  ffffffff ffffffff ffffffff fffffffe ffffac73""")
+#    _a          = 0
+#    _b          = 7
+#    _Gx         = long_converter("""
+#                  3b4c382c e37aa192 a4019e76 3036f4f5 dd4d7ebb""")
+#    _Gy         = long_converter("""
+#                  938cf935 318fdced 6bc28286 531733c3 f03c4fee""")
+#    _r          = long_converter("""01
+#                  00000000 00000000 0001b8fa 16dfab9a ca16b6b3""")
+#    curve       = CurveFp(_p, _a, _b)
+#    generator   = Point(curve, _Gx, _Gy, _r)
+#    SECP160k1   = Curve("SECP160k1", curve, generator,
+#                        (1, 3, 132, 0, 9), "secp160k1")
+
+#    _p          = long_converter("""
+#                  ffffffff ffffffff ffffffff ffffffff 7fffffff""")
+#    _a          = -3 % _p
+#    _b          = long_converter("""
+#                  1c97befc 54bd7a8b 65acf89f 81d4d4ad c565fa45""")
+#    _Gx         = long_converter("""
+#                  4a96b568 8ef57328 46646989 68c38bb9 13cbfc82""")
+#    _Gy         = long_converter("""
+#                  23a62855 3168947d 59dcc912 04235137 7ac5fb32""")
+#    _r          = long_converter("""01
+#                  00000000 00000000 0001f4c8 f927aed3 ca752257""")
+#    curve       = CurveFp(_p, _a, _b)
+#    generator   = Point(curve, _Gx, _Gy, _r)
+#    SECP160r1   = Curve("SECP160r1", curve, generator,
+#                        (1, 3, 132, 0, 8), "secp160r1")
+
+#    _p          = long_converter("""
+#                  ffffffff ffffffff ffffffff fffffffe ffffac73""")
+#    _a          = -3 % _p
+#    _b          = long_converter("""
+#                  b4e134d3 fb59eb8b ab572749 04664d5a f50388ba""")
+#    _Gx         = long_converter("""
+#                  52dcb034 293a117e 1f4ff11b 30f7199d 3144ce6d""")
+#    _Gy         = long_converter("""
+#                  feaffef2 e331f296 e071fa0d f9982cfe a7d43f2e""")
+#    _r          = long_converter("""01
+#                  00000000 00000000 0000351e e786a818 f3a1a16b""")
+#    curve       = CurveFp(_p, _a, _b)
+#    generator   = Point(curve, _Gx, _Gy, _r)
+#    SECP160r2   = Curve("SECP160r2", curve, generator,
+#                        (1, 3, 132, 0, 30), "secp160r2")
+
+#    _p          = long_converter("""
+#                  ffffffff ffffffff ffffffff ffffffff fffffffe ffffee37""")
+#    _a          = 0
+#    _b          = 3
+#    _Gx         = long_converter("""
+#                  db4ff10e c057e9ae 26b07d02 80b7f434 1da5d1b1 eae06c7d""")
+#    _Gy         = long_converter("""
+#                  9b2f2f6d 9c5628a7 844163d0 15be8634 4082aa88 d95e2f9d""")
+#    _r          = long_converter("""
+#                  ffffffff ffffffff fffffffe 26f2fc17 0f69466a 74defd8d""")
+#    curve       = CurveFp(_p, _a, _b)
+#    generator   = Point(curve, _Gx, _Gy, _r)
+#    SECP192k1   = Curve("SECP192k1", curve, generator,
+#                        (1, 3, 132, 0, 31), "secp192k1")
+
+#    _p          = long_converter("""
+#                  ffffffff ffffffff ffffffff ffffffff ffffffff fffffffe
+#                  ffffe56d""")
+#    _a          = 0
+#    _b          = 5
+#    _Gx         = long_converter("""
+#                  a1455b33 4df099df 30fc28a1 69a467e9 e47075a9 0f7e650e
+#                  b6b7a45c""")
+#    _Gy         = long_converter("""
+#                  7e089fed 7fba3442 82cafbd6 f7e319f7 c0b0bd59 e2ca4bdb
+#                  556d61a5""")
+#    _r          = long_converter("""01
+#                  00000000 00000000 00000000 0001dce8 d2ec6184 caf0a971
+#                  769fb1f7""")
+#    curve       = CurveFp(_p, _a, _b)
+#    generator   = Point(curve, _Gx, _Gy, _r)
+#    SECP224k1   = Curve("SECP224k1", curve, generator,
+#                        (1, 3, 132, 0, 32), "secp224k1")
+
+#    _p          = long_converter("""
+#                  A9FB57DB A1EEA9BC 3E660A90 9D838D72 6E3BF623 D5262028
+#                  2013481D 1F6E5377""")
+#    _a          = long_converter("""
+#                  7D5A0975 FC2C3057 EEF67530 417AFFE7 FB8055C1 26DC5C6C
+#                  E94A4B44 F330B5D9""")
+#    _b          = long_converter("""
+#                  26DC5C6C E94A4B44 F330B5D9 BBD77CBF 95841629 5CF7E1CE
+#                  6BCCDC18 FF8C07B6""")
+#    _Gx         = long_converter("""
+#                  8BD2AEB9 CB7E57CB 2C4B482F FC81B7AF B9DE27E1 E3BD23C2
+#                  3A4453BD 9ACE3262""")
+#    _Gy         = long_converter("""
+#                  547EF835 C3DAC4FD 97F8461A 14611DC9 C2774513 2DED8E54
+#                  5C1D54C7 2F046997""")
+#    _r          = long_converter("""
+#                  A9FB57DB A1EEA9BC 3E660A90 9D838D71 8C397AA3 B561A6F7
+#                  901E0E82 974856A7""")
+#    curve       = CurveFp(_p, _a, _b)
+#    generator   = Point(curve, _Gx, _Gy, _r)
+#    BRNP256r1   = Curve("BRNP256r1", curve, generator,
+#                        (1, 3, 36, 3, 3, 2, 8, 1, 1, 7), "brainpoolP256r1")
+
+#    _p          = long_converter("""
+#                  8CB91E82 A3386D28 0F5D6F7E 50E641DF 152F7109 ED5456B4
+#                  12B1DA19 7FB71123 ACD3A729 901D1A71 87470013 3107EC53""")
+#    _a          = long_converter("""
+#                  7BC382C6 3D8C150C 3C72080A CE05AFA0 C2BEA28E 4FB22787
+#                  139165EF BA91F90F 8AA5814A 503AD4EB 04A8C7DD 22CE2826""")
+#    _b          = long_converter("""
+#                  04A8C7DD 22CE2826 8B39B554 16F0447C 2FB77DE1 07DCD2A6
+#                  2E880EA5 3EEB62D5 7CB43902 95DBC994 3AB78696 FA504C11""")
+#    _Gx         = long_converter("""
+#                  1D1C64F0 68CF45FF A2A63A81 B7C13F6B 8847A3E7 7EF14FE3
+#                  DB7FCAFE 0CBD10E8 E826E034 36D646AA EF87B2E2 47D4AF1E""")
+#    _Gy         = long_converter("""
+#                  8ABE1D75 20F9C2A4 5CB1EB8E 95CFD552 62B70B29 FEEC5864
+#                  E19C054F F9912928 0E464621 77918111 42820341 263C5315""")
+#    _r          = long_converter("""
+#                  8CB91E82 A3386D28 0F5D6F7E 50E641DF 152F7109 ED5456B3
+#                  1F166E6C AC0425A7 CF3AB6AF 6B7FC310 3B883202 E9046565""")
+#    curve       = CurveFp(_p, _a, _b)
+#    generator   = Point(curve, _Gx, _Gy, _r)
+#    BRNP384r1   = Curve("BRNP384r1", curve, generator,
+#                        (1, 3, 36, 3, 3, 2, 8, 1, 1, 11), "brainpoolP384r1")
+
+#    _p          = long_converter("""
+#                  AADD9DB8 DBE9C48B 3FD4E6AE 33C9FC07 CB308DB3 B3C9D20E
+#                  D6639CCA 70330871 7D4D9B00 9BC66842 AECDA12A E6A380E6
+#                  2881FF2F 2D82C685 28AA6056 583A48F3""")
+#    _a          = long_converter("""
+#                  7830A331 8B603B89 E2327145 AC234CC5 94CBDD8D 3DF91610
+#                  A83441CA EA9863BC 2DED5D5A A8253AA1 0A2EF1C9 8B9AC8B5
+#                  7F1117A7 2BF2C7B9 E7C1AC4D 77FC94CA""")
+#    _b          = long_converter("""
+#                  3DF91610 A83441CA EA9863BC 2DED5D5A A8253AA1 0A2EF1C9
+#                  8B9AC8B5 7F1117A7 2BF2C7B9 E7C1AC4D 77FC94CA DC083E67
+#                  984050B7 5EBAE5DD 2809BD63 8016F723""")
+#    _Gx         = long_converter("""
+#                  81AEE4BD D82ED964 5A21322E 9C4C6A93 85ED9F70 B5D916C1
+#                  B43B62EE F4D0098E FF3B1F78 E2D0D48D 50D1687B 93B97D5F
+#                  7C6D5047 406A5E68 8B352209 BCB9F822""")
+#    _Gy         = long_converter("""
+#                  7DDE385D 566332EC C0EABFA9 CF7822FD F209F700 24A57B1A
+#                  A000C55B 881F8111 B2DCDE49 4A5F485E 5BCA4BD8 8A2763AE
+#                  D1CA2B2F A8F05406 78CD1E0F 3AD80892""")
+#    _r          = long_converter("""
+#                  AADD9DB8 DBE9C48B 3FD4E6AE 33C9FC07 CB308DB3 B3C9D20E
+#                  D6639CCA 70330870 553E5C41 4CA92619 41866119 7FAC1047
+#                  1DB1D381 085DDADD B5879682 9CA90069""")
+#    curve       = CurveFp(_p, _a, _b)
+#    generator   = Point(curve, _Gx, _Gy, _r)
+#    BRNP512r1   = Curve("BRNP512r1", curve, generator,
+#                        (1, 3, 36, 3, 3, 2, 8, 1, 1, 13), "brainpoolP512r1")
+
diff --git a/scapy/layers/tls/crypto/hkdf.py b/scapy/layers/tls/crypto/hkdf.py
new file mode 100644
index 0000000000000000000000000000000000000000..7085f50d88106f68ed5cdd4343be8cd83717afc4
--- /dev/null
+++ b/scapy/layers/tls/crypto/hkdf.py
@@ -0,0 +1,64 @@
+## This file is part of Scapy
+## Copyright (C) 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+Stateless HKDF for TLS 1.3.
+"""
+
+import struct
+
+from scapy.config import conf
+from scapy.layers.tls.crypto.pkcs1 import _get_hash
+
+if conf.crypto_valid:
+    from cryptography.hazmat.backends import default_backend
+    from cryptography.hazmat.primitives.kdf.hkdf import HKDF, HKDFExpand
+    from cryptography.hazmat.primitives.hashes import Hash
+    from cryptography.hazmat.primitives.hmac import HMAC
+
+
+class TLS13_HKDF(object):
+    def __init__(self, hash_name="sha256"):
+        self.hash = _get_hash(hash_name)
+
+    def extract(self, salt, ikm):
+        h = self.hash
+        hkdf = HKDF(h, h.digest_size, salt, None, default_backend())
+        if ikm is None:
+            ikm = "\x00" * h.digest_size
+        return hkdf._extract(ikm)
+
+    def expand(self, prk, info, L):
+        h = self.hash
+        hkdf = HKDFExpand(h, L, info, default_backend())
+        return hkdf.derive(prk)
+
+    def expand_label(self, secret, label, hash_value, length):
+        hkdf_label  = struct.pack("!H", length)
+        hkdf_label += struct.pack("B", 9 + len(label))
+        hkdf_label += "TLS 1.3, "
+        hkdf_label += label
+        hkdf_label += struct.pack("B", len(hash_value))
+        hkdf_label += hash_value
+        return self.expand(secret, hkdf_label, length)
+
+    def derive_secret(self, secret, label, messages):
+        h = Hash(self.hash, backend=default_backend())
+        h.update(messages)
+        hash_messages = h.finalize()
+        hash_len = self.hash.digest_size
+        return self.expand_label(secret, label, hash_messages, hash_len)
+
+    def compute_verify_data(self, basekey, handshake_context):
+        hash_len = self.hash.digest_size
+        finished_key = self.expand_label(basekey, "finished", "", hash_len)
+
+        h = Hash(self.hash, backend=default_backend())
+        h.update(handshake_context)
+        hash_value = h.finalize()
+
+        hm = HMAC(finished_key, self.hash, default_backend())
+        hm.update(hash_value)
+        return hm.finalize()
+
diff --git a/scapy/layers/tls/crypto/kx_algs.py b/scapy/layers/tls/crypto/kx_algs.py
index 61db97aa459cde7ee1648de0c7f04c4e2efba7b3..7f6c3632e8a4091c579a82ce8a1d626778ef2e93 100644
--- a/scapy/layers/tls/crypto/kx_algs.py
+++ b/scapy/layers/tls/crypto/kx_algs.py
@@ -1,12 +1,12 @@
 ## This file is part of Scapy
 ## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
-##                     2015, 2016 Maxence Tury
+##               2015, 2016, 2017 Maxence Tury
 ## This program is published under a GPLv2 license
 
 """
 Key Exchange algorithms as listed in appendix C of RFC 4346.
 
-XXX Incomplete support for static DH, DSS, PSK, SRP, KRB and anonymous kx.
+XXX No support yet for PSK (also, no static DH, DSS, SRP or KRB).
 """
 
 from __future__ import absolute_import
@@ -31,10 +31,10 @@ class _GenericKXMetaclass(type):
             dct["name"] = kx_name[3:]       # remove leading "KX_"
         the_class = super(_GenericKXMetaclass, cls).__new__(cls, kx_name,
                                                             bases, dct)
-        if kx_name:
+        if kx_name != "_GenericKX":
             the_class.export = kx_name.endswith("_EXPORT")
-            the_class.anonymous = "_anon_" in kx_name
-            the_class.no_ske = not ("DHE" in kx_name or "_anon_" in kx_name)
+            the_class.anonymous = "_anon" in kx_name
+            the_class.no_ske = not ("DHE" in kx_name or "_anon" in kx_name)
             the_class.no_ske &= not the_class.export
             tls_kx_algs[kx_name[3:]] = the_class
         return the_class
@@ -49,6 +49,16 @@ class KX_NULL(_GenericKX):
     server_kx_msg_cls = lambda _,m: None
     client_kx_msg_cls = None
 
+class KX_SSLv2(_GenericKX):
+    descr = "SSLv2 dummy key exchange class"
+    server_kx_msg_cls = lambda _,m: None
+    client_kx_msg_cls = None
+
+class KX_TLS13(_GenericKX):
+    descr = "TLS 1.3 dummy key exchange class"
+    server_kx_msg_cls = lambda _,m: None
+    client_kx_msg_cls = None
+
 
 ### Standard RSA-authenticated key exchange
 
@@ -166,16 +176,16 @@ class KX_ECDHE_ECDSA(_GenericKX):
 
 ### Unauthenticated key exchange (opportunistic encryption)
 
-# class KX_DH_anon(_GenericKX):
-#     descr = "Anonymous DH, no signatures"
-#     server_kx_msg_cls = lambda _,m: ServerDHParams
-#     client_kx_msg_cls = ClientDiffieHellmanPublic
+class KX_DH_anon(_GenericKX):
+    descr = "Anonymous DH, no signatures"
+    server_kx_msg_cls = lambda _,m: ServerDHParams
+    client_kx_msg_cls = ClientDiffieHellmanPublic
 
-# class KX_ECDH_anon(_GenericKX):
-#     descr = "ECDH anonymous key exchange"
-#     server_kx_msg_cls = lambda _,m: _tls_server_ecdh_cls_guess(m)
-#     client_kx_msg_cls = ClientDiffieHellmanPublic
+class KX_ECDH_anon(_GenericKX):
+    descr = "ECDH anonymous key exchange"
+    server_kx_msg_cls = lambda _,m: _tls_server_ecdh_cls_guess(m)
+    client_kx_msg_cls = ClientECDiffieHellmanPublic
 
-# class KX_DH_anon_EXPORT(KX_DH_anon):
-#     descr = "Anonymous DH, no signatures - Export version"
+class KX_DH_anon_EXPORT(KX_DH_anon):
+    descr = "Anonymous DH, no signatures - Export version"
 
diff --git a/scapy/layers/tls/crypto/pkcs1.py b/scapy/layers/tls/crypto/pkcs1.py
index ef479015b8d467a9bce341783f24ce92d7f806cc..baa8b7964e67f4ca37395d71646ccb31e6c864ae 100644
--- a/scapy/layers/tls/crypto/pkcs1.py
+++ b/scapy/layers/tls/crypto/pkcs1.py
@@ -1,30 +1,26 @@
 ## This file is part of Scapy
 ## Copyright (C) 2008 Arnaud Ebalard <arno@natisbad.org>
-##         2015, 2016 Maxence Tury <maxence.tury@ssi.gouv.fr>
+##   2015, 2016, 2017 Maxence Tury <maxence.tury@ssi.gouv.fr>
 ## This program is published under a GPLv2 license
 
 """
 PKCS #1 methods as defined in RFC 3447.
 
-XXX We cannot rely solely on the cryptography library, because it does not
-support our "tls" hash used with TLS 1.0. Once it is added to (or from) the
-library, most of the present module should be removed.
+We cannot rely solely on the cryptography library, because the openssl package
+used by the cryptography library may not implement the md5-sha1 hash, as with
+Ubuntu or OSX. This is why we reluctantly keep some legacy crypto here.
 """
 
 from __future__ import absolute_import
-import os, popen2, tempfile
-import math, random, struct
 
 from scapy.config import conf, crypto_validator
-from functools import reduce
-from scapy.modules.six.moves import range
 if conf.crypto_valid:
-    from cryptography.exceptions import InvalidSignature
+    from cryptography import utils
+    from cryptography.exceptions import InvalidSignature, UnsupportedAlgorithm
     from cryptography.hazmat.backends import default_backend
     from cryptography.hazmat.primitives import hashes
     from cryptography.hazmat.primitives.asymmetric import padding
-else:
-    InvalidSignature = dafault_backend = hashes = padding = None
+    from cryptography.hazmat.primitives.hashes import HashAlgorithm
 
 from scapy.utils import randstring, zerofree_randstring, strxor, strand
 from scapy.error import warning
@@ -34,39 +30,29 @@ from scapy.error import warning
 # Some helpers
 #####################################################################
 
-# OS2IP function defined in RFC 3447 for octet string to integer conversion
-def pkcs_os2ip(x):
+def pkcs_os2ip(s):
     """
-    Accepts a byte string as input parameter and return the associated long
-    value:
+    OS2IP conversion function from RFC 3447.
 
-    Input : x        octet string to be converted
-
-    Output: x        corresponding nonnegative integer
-
-    Reverse function is pkcs_i2osp()
+    Input : s        octet string to be converted
+    Output: n        corresponding nonnegative integer
     """
-    return int(x.encode("hex"), 16)
+    return int(s.encode("hex"), 16)
 
-# I2OSP function defined in RFC 3447 for integer to octet string conversion
-def pkcs_i2osp(x, xLen):
+def pkcs_i2osp(n, sLen):
     """
-    Converts a long (the first parameter) to the associated byte string
-    representation of length l (second parameter). Basically, the length
-    parameters allow the function to perform the associated padding.
-
-    Input : x        nonnegative integer to be converted
-            xLen     intended length of the resulting octet string
+    I2OSP conversion function from RFC 3447.
+    The length parameter allows the function to perform the padding needed.
+    Note that the user is responsible for providing a sufficient xLen.
 
-    Output: x        corresponding octet string
-
-    Reverse function is pkcs_os2ip().
+    Input : n        nonnegative integer to be converted
+            sLen     intended length of the resulting octet string
+    Output: s        corresponding octet string
     """
-    # The user is responsible for providing an appropriate xLen.
-    #if x >= 256**xLen:
-    #    raise Exception("Integer too large for provided xLen %d" % xLen)
-    fmt = "%%0%dx" % (2*xLen)
-    return (fmt % x).decode("hex")
+    #if n >= 256**sLen:
+    #    raise Exception("Integer too large for provided sLen %d" % sLen)
+    fmt = "%%0%dx" % (2*sLen)
+    return (fmt % n).decode("hex")
 
 def pkcs_ilen(n):
     """
@@ -79,802 +65,157 @@ def pkcs_ilen(n):
         i += 1
     return i
 
-
-#####################################################################
-# Hash functions
-#####################################################################
-
-# For every hash function a tuple is provided, giving access to
-# - hash output length in byte
-# - associated hash function that take data to be hashed as parameter
-#   XXX I do not provide update() at the moment.
-# - DER encoding of the leading bits of digestInfo (the hash value
-#   will be concatenated to create the complete digestInfo).
-#
-# Note that 'tls' is the concatenation of both md5 and sha1 hashes used by
-# SSL/TLS 1.0 when signing/verifying things.
-
-_hashFuncParams = {}
-if conf.crypto_valid:
-
-    def _hashWrapper(hash_algo, message, backend=default_backend()):
-        digest = hashes.Hash(hash_algo(), backend)
-        digest.update(message)
-        return digest.finalize()
-
-    _hashFuncParams = {
-        "md5"    : (16,
-                    hashes.MD5,
-                    lambda x: _hashWrapper(hashes.MD5, x),
-                    b'\x30\x20\x30\x0c\x06\x08\x2a\x86\x48\x86\xf7\x0d\x02\x05\x05\x00\x04\x10'),
-        "sha1"   : (20,
-                    hashes.SHA1,
-                    lambda x: _hashWrapper(hashes.SHA1, x),
-                    b'\x30\x21\x30\x09\x06\x05\x2b\x0e\x03\x02\x1a\x05\x00\x04\x14'),
-        "sha224" : (28,
-                    hashes.SHA224,
-                    lambda x: _hashWrapper(hashes.SHA224, x),
-                    b'\x30\x2d\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x04\x05\x00\x04\x1c'),
-        "sha256" : (32,
-                    hashes.SHA256,
-                    lambda x: _hashWrapper(hashes.SHA256, x),
-                    b'\x30\x31\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x01\x05\x00\x04\x20'),
-        "sha384" : (48,
-                    hashes.SHA384,
-                    lambda x: _hashWrapper(hashes.SHA384, x),
-                    b'\x30\x41\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x02\x05\x00\x04\x30'),
-        "sha512" : (64,
-                    hashes.SHA512,
-                    lambda x: _hashWrapper(hashes.SHA512, x),
-                    b'\x30\x51\x30\x0d\x06\x09\x60\x86\x48\x01\x65\x03\x04\x02\x03\x05\x00\x04\x40'),
-        "tls"    : (36,
-                    None,
-                    lambda x: _hashWrapper(hashes.MD5, x) + _hashWrapper(hashes.SHA1, x),
-                    '')
-        }
-
-def mapHashFunc(hashStr):
-    if hashStr == "tls":
-        raise Exception("mapHashFunc is not supposed to be called on 'tls'")
-    try:
-        return _hashFuncParams[hashStr][1]()
-    except:
-        raise Exception("Unknown hash function %s" % hashStr)
-
-
-#####################################################################
-# Some more PKCS helpers
-#####################################################################
-
-def pkcs_mgf1(mgfSeed, maskLen, h):
+@crypto_validator
+def _legacy_pkcs1_v1_5_encode_md5_sha1(M, emLen):
     """
-    Implements generic MGF1 Mask Generation function as described in
-    Appendix B.2.1 of RFC 3447. The hash function is passed by name.
-    valid values are 'md2', 'md4', 'md5', 'sha1', 'tls, 'sha256',
-    'sha384' and 'sha512'. Returns None on error.
-
-    Input:
-       mgfSeed: seed from which mask is generated, an octet string
-       maskLen: intended length in octets of the mask, at most 2^32 * hLen
-                hLen (see below)
-       h      : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
-                'sha256', 'sha384'). hLen denotes the length in octets of
-                the hash function output.
-
-    Output:
-       an octet string of length maskLen
+    Legacy method for PKCS1 v1.5 encoding with MD5-SHA1 hash.
     """
-
-    # steps are those of Appendix B.2.1
-    if h not in _hashFuncParams:
-        warning("pkcs_mgf1: invalid hash (%s) provided" % h)
-        return None
-    hLen = _hashFuncParams[h][0]
-    hFunc = _hashFuncParams[h][2]
-    if maskLen > 2**32 * hLen:                               # 1)
-        warning("pkcs_mgf1: maskLen > 2**32 * hLen")
-        return None
-    T = ""                                                   # 2)
-    maxCounter = math.ceil(float(maskLen) / float(hLen))     # 3)
-    counter = 0
-    while counter < maxCounter:
-        C = pkcs_i2osp(counter, 4)
-        T += hFunc(mgfSeed + C)
-        counter += 1
-    return T[:maskLen]
-
-
-def pkcs_emsa_pss_encode(M, emBits, h, mgf, sLen):
-    """
-    Implements EMSA-PSS-ENCODE() function described in Sect. 9.1.1 of RFC 3447
-
-    Input:
-       M     : message to be encoded, an octet string
-       emBits: maximal bit length of the integer resulting of pkcs_os2ip(EM),
-               where EM is the encoded message, output of the function.
-       h     : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
-               'sha256', 'sha384'). hLen denotes the length in octets of
-               the hash function output.
-       mgf   : the mask generation function f : seed, maskLen -> mask
-       sLen  : intended length in octets of the salt
-
-    Output:
-       encoded message, an octet string of length emLen = ceil(emBits/8)
-
-    On error, None is returned.
-    """
-
-    # 1) is not done
-    hLen = _hashFuncParams[h][0]                             # 2)
-    hFunc = _hashFuncParams[h][2]
-    mHash = hFunc(M)
-    emLen = int(math.ceil(emBits/8.))
-    if emLen < hLen + sLen + 2:                              # 3)
-        warning("encoding error (emLen < hLen + sLen + 2)")
-        return None
-    salt = randstring(sLen)                                  # 4)
-    MPrime = b'\x00'*8 + mHash + salt                         # 5)
-    H = hFunc(MPrime)                                        # 6)
-    PS = b'\x00'*(emLen - sLen - hLen - 2)                    # 7)
-    DB = PS + b'\x01' + salt                                  # 8)
-    dbMask = mgf(H, emLen - hLen - 1)                        # 9)
-    maskedDB = strxor(DB, dbMask)                            # 10)
-    l = (8*emLen - emBits)/8                                 # 11)
-    rem = 8*emLen - emBits - 8*l # additionnal bits
-    andMask = l*b'\x00'
-    if rem:
-        j = sum(1 << x for x in range(8 - rem))
-        andMask += j
-        l += 1
-    maskedDB = strand(maskedDB[:l], andMask) + maskedDB[l:]
-    EM = maskedDB + H + b'\xbc'                               # 12)
-    return EM                                                # 13)
-
-
-def pkcs_emsa_pss_verify(M, EM, emBits, h, mgf, sLen):
-    """
-    Implements EMSA-PSS-VERIFY() function described in Sect. 9.1.2 of RFC 3447
-
-    Input:
-       M     : message to be encoded, an octet string
-       EM    : encoded message, an octet string of length emLen=ceil(emBits/8)
-       emBits: maximal bit length of the integer resulting of pkcs_os2ip(EM)
-       h     : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
-               'sha256', 'sha384'). hLen denotes the length in octets of
-               the hash function output.
-       mgf   : the mask generation function f : seed, maskLen -> mask
-       sLen  : intended length in octets of the salt
-
-    Output:
-       True if the verification is ok, False otherwise.
-    """
-
-    # 1) is not done
-    hLen = _hashFuncParams[h][0]                             # 2)
-    hFunc = _hashFuncParams[h][2]
-    mHash = hFunc(M)
-    emLen = int(math.ceil(emBits/8.))                        # 3)
-    if emLen < hLen + sLen + 2:
-        return False
-    if EM[-1] != b'\xbc':                                     # 4)
-        return False
-    l = emLen - hLen - 1                                     # 5)
-    maskedDB = EM[:l]
-    H = EM[l:l+hLen]
-    l = (8*emLen - emBits)/8                                 # 6)
-    rem = 8*emLen - emBits - 8*l # additionnal bits
-    andMask = l*b'\xff'
-    if rem:
-        val = sum(1 << x for x in range(8 - rem))
-        j = chr(~val & 0xff)
-        andMask += j
-        l += 1
-    if strand(maskedDB[:l], andMask) != b'\x00'*l:
-        return False
-    dbMask = mgf(H, emLen - hLen - 1)                        # 7)
-    DB = strxor(maskedDB, dbMask)                            # 8)
-    l = (8*emLen - emBits)/8                                 # 9)
-    rem = 8*emLen - emBits - 8*l # additionnal bits
-    andMask = l*b'\x00'
-    if rem:
-        j = chr(sum(1 << x for x in range(8 - rem)))
-        andMask += j
-        l += 1
-    DB = strand(DB[:l], andMask) + DB[l:]
-    l = emLen - hLen - sLen - 1                              # 10)
-    if DB[:l] != b'\x00'*(l-1) + b'\x01':
-        return False
-    salt = DB[-sLen:]                                        # 11)
-    MPrime = b'\x00'*8 + mHash + salt                         # 12)
-    HPrime = hFunc(MPrime)                                   # 13)
-    return H == HPrime                                       # 14)
-
-
-def pkcs_emsa_pkcs1_v1_5_encode(M, emLen, h): # section 9.2 of RFC 3447
-    """
-    Implements EMSA-PKCS1-V1_5-ENCODE() function described in Sect.
-    9.2 of RFC 3447.
-
-    Input:
-       M    : message to be encode, an octet string
-       emLen: intended length in octets of the encoded message, at least
-              tLen + 11, where tLen is the octet length of the DER encoding
-              T of a certain value computed during the encoding operation.
-       h    : hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
-              'sha256', 'sha384'). hLen denotes the length in octets of
-              the hash function output.
-
-    Output:
-       encoded message, an octet string of length emLen
-
-    On error, None is returned.
-    """
-    hLen = _hashFuncParams[h][0]                             # 1)
-    hFunc = _hashFuncParams[h][2]
-    H = hFunc(M)
-    hLeadingDigestInfo = _hashFuncParams[h][3]               # 2)
-    T = hLeadingDigestInfo + H
-    tLen = len(T)
-    if emLen < tLen + 11:                                    # 3)
-        warning("pkcs_emsa_pkcs1_v1_5_encode:"
+    md5_hash = hashes.Hash(_get_hash("md5"), backend=default_backend())
+    md5_hash.update(M)
+    sha1_hash = hashes.Hash(_get_hash("sha1"), backend=default_backend())
+    sha1_hash.update(M)
+    H = md5_hash.finalize() + sha1_hash.finalize()
+    if emLen < 36 + 11:
+        warning("pkcs_emsa_pkcs1_v1_5_encode: "
                 "intended encoded message length too short")
         return None
-    PS = b'\xff'*(emLen - tLen - 3)                           # 4)
-    EM = b'\x00' + b'\x01' + PS + b'\x00' + T                   # 5)
-    return EM                                                # 6)
+    PS = '\xff'*(emLen - 36 - 3)
+    return '\x00' + '\x01' + PS + '\x00' + H
 
 
 #####################################################################
-# Asymmetric Cryptography wrappers
+# Hash and padding helpers
 #####################################################################
 
-class _EncryptAndVerifyRSA(object):
-
-    def _rsaep(self, m):
-        """
-        Internal method providing raw RSA encryption, i.e. simple modular
-        exponentiation of the given message representative 'm', a long
-        between 0 and n-1.
-
-        This is the encryption primitive RSAEP described in PKCS#1 v2.1,
-        i.e. RFC 3447 Sect. 5.1.1.
-
-        Input:
-           m: message representative, a long between 0 and n-1, where
-              n is the key modulus.
-
-        Output:
-           ciphertext representative, a long between 0 and n-1
-
-        Not intended to be used directly. Please, see encrypt() method.
-        """
-
-        n = self._modulus
-        if isinstance(m, int):
-            m = long(m)
-        if (not isinstance(m, long)) or m > n-1:
-            warning("Key._rsaep() expects a long between 0 and n-1")
-            return None
-
-        return pow(m, self._pubExp, n)
-
-
-    @crypto_validator
-    def encrypt(self, m, t="pkcs", h=None, mgf=None, L=None):
-        if h == "tls" or t is None:
-            #return self.encrypt_legacy(m, t=t, h=h, mgf=mgf, L=L)
-            warning("Cannot call encrypt_legacy anymore.")
-            return None
-
-        if h is not None:
-            h = mapHashFunc(h)
+_get_hash = None
+if conf.crypto_valid:
 
-        if t == "pkcs":
-            pad = padding.PKCS1v15()
-        elif t == "oaep":
-            pad = padding.OAEP(mgf=mgf(h), algorithm=h, label=L)
+    # first, we add the "md5-sha1" hash from openssl to python-cryptography
+    @utils.register_interface(HashAlgorithm)
+    class MD5_SHA1(object):
+        name = "md5-sha1"
+        digest_size = 36
+        block_size = 64
+
+    _hashes = {
+            "md5"      : hashes.MD5,
+            "sha1"     : hashes.SHA1,
+            "sha224"   : hashes.SHA224,
+            "sha256"   : hashes.SHA256,
+            "sha384"   : hashes.SHA384,
+            "sha512"   : hashes.SHA512,
+            "md5-sha1" : MD5_SHA1
+            }
+
+    def _get_hash(hashStr):
+        try:
+            return _hashes[hashStr]()
+        except KeyError:
+            raise KeyError("Unknown hash function %s" % hashStr)
+
+
+    def _get_padding(padStr, mgf=padding.MGF1, h=hashes.SHA256, label=None):
+        if padStr == "pkcs":
+            return padding.PKCS1v15()
+        elif padStr == "pss":
+            # Can't find where this is written, but we have to use the digest
+            # size instead of the automatic padding.PSS.MAX_LENGTH.
+            return padding.PSS(mgf=mgf(h), salt_length=h.digest_size)
+        elif padStr == "oaep":
+            return padding.OAEP(mgf=mgf(h), algorithm=h, label=label)
         else:
-            warning("Key.encrypt(): Unknown encryption type (%s) provided" % t)
+            warning("Key.encrypt(): Unknown padding type (%s)" % padStr)
             return None
-        return self.pubkey.encrypt(m, pad)
-
-
-    ### Below are verification related methods
-
-    def _rsavp1(self, s):
-        """
-        Internal method providing raw RSA verification, i.e. simple modular
-        exponentiation of the given signature representative 'c', an integer
-        between 0 and n-1.
-
-        This is the signature verification primitive RSAVP1 described in
-        PKCS#1 v2.1, i.e. RFC 3447 Sect. 5.2.2.
-
-        Input:
-          s: signature representative, an integer between 0 and n-1,
-             where n is the key modulus.
 
-        Output:
-           message representative, an integer between 0 and n-1
 
-        Not intended to be used directly. Please, see verify() method.
-        """
-        return self._rsaep(s)
+#####################################################################
+# Asymmetric Cryptography wrappers
+#####################################################################
 
-    def _rsassa_pss_verify(self, M, S, h=None, mgf=None, sLen=None):
-        """
-        Implements RSASSA-PSS-VERIFY() function described in Sect 8.1.2
-        of RFC 3447
+# Make sure that default values are consistent accross the whole TLS module,
+# lest they be explicitly set to None between cert.py and pkcs1.py.
 
-        Input:
-           M: message whose signature is to be verified
-           S: signature to be verified, an octet string of length k, where k
-              is the length in octets of the RSA modulus n.
+class _EncryptAndVerifyRSA(object):
 
-        Output:
-           True is the signature is valid. False otherwise.
-        """
+    @crypto_validator
+    def encrypt(self, m, t="pkcs", h="sha256", mgf=None, L=None):
+        mgf = mgf or padding.MGF1
+        h = _get_hash(h)
+        pad = _get_padding(t, mgf, h, L)
+        return self.pubkey.encrypt(m, pad)
 
-        # Set default parameters if not provided
-        if h is None: # By default, sha1
-            h = "sha1"
-        if h not in _hashFuncParams:
-            warning("Key._rsassa_pss_verify(): unknown hash function "
-                    "provided (%s)" % h)
-            return False
-        if mgf is None: # use mgf1 with underlying hash function
-            mgf = lambda x,y: pkcs_mgf1(x, y, h)
-        if sLen is None: # use Hash output length (A.2.3 of RFC 3447)
-            hLen = _hashFuncParams[h][0]
-            sLen = hLen
-
-        # 1) Length checking
-        modBits = self._modulusLen
-        k = modBits / 8
-        if len(S) != k:
+    @crypto_validator
+    def verify(self, M, S, t="pkcs", h="sha256", mgf=None, L=None):
+        mgf = mgf or padding.MGF1
+        h = _get_hash(h)
+        pad = _get_padding(t, mgf, h, L)
+        try:
+            try:
+                self.pubkey.verify(S, M, pad, h)
+            except UnsupportedAlgorithm:
+                if t != "pkcs" and h != "md5-sha1":
+                    raise UnsupportedAlgorithm("RSA verification with %s" % h)
+                self._legacy_verify_md5_sha1(M, S)
+            return True
+        except InvalidSignature:
             return False
 
-        # 2) RSA verification
-        s = pkcs_os2ip(S)                           # 2.a)
-        m = self._rsavp1(s)                         # 2.b)
-        emLen = math.ceil((modBits - 1) / 8.)       # 2.c)
-        EM = pkcs_i2osp(m, emLen)
-
-        # 3) EMSA-PSS verification
-        result = pkcs_emsa_pss_verify(M, EM, modBits - 1, h, mgf, sLen)
-
-        return result                               # 4)
-
-
-    def _rsassa_pkcs1_v1_5_verify(self, M, S, h):
-        """
-        Implements RSASSA-PKCS1-v1_5-VERIFY() function as described in
-        Sect. 8.2.2 of RFC 3447.
-
-        Input:
-           M: message whose signature is to be verified, an octet string
-           S: signature to be verified, an octet string of length k, where
-              k is the length in octets of the RSA modulus n
-           h: hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls',
-                'sha256', 'sha384').
-
-        Output:
-           True if the signature is valid. False otherwise.
-        """
-
-        # 1) Length checking
+    def _legacy_verify_md5_sha1(self, M, S):
         k = self._modulusLen / 8
         if len(S) != k:
             warning("invalid signature (len(S) != k)")
             return False
-
-        # 2) RSA verification
-        s = pkcs_os2ip(S)                           # 2.a)
-        m = self._rsavp1(s)                         # 2.b)
-        EM = pkcs_i2osp(m, k)                       # 2.c)
-
-        # 3) EMSA-PKCS1-v1_5 encoding
-        EMPrime = pkcs_emsa_pkcs1_v1_5_encode(M, k, h)
+        s = pkcs_os2ip(S)
+        n = self._modulus
+        if isinstance(s, int):
+            s = long(s)
+        if (not isinstance(s, long)) or s > n-1:
+            warning("Key._rsaep() expects a long between 0 and n-1")
+            return None
+        m = pow(s, self._pubExp, n)
+        EM = pkcs_i2osp(m, k)
+        EMPrime = _legacy_pkcs1_v1_5_encode_md5_sha1(M, k)
         if EMPrime is None:
             warning("Key._rsassa_pkcs1_v1_5_verify(): unable to encode.")
             return False
-
-        # 4) Comparison
         return EM == EMPrime
 
 
-    def verify_legacy(self, M, S, t=None, h=None, mgf=None, sLen=None):
-        """
-        Verify alleged signature 'S' is indeed the signature of message 'M'
-        using 't' signature scheme where 't' can be:
-
-        - None: the alleged signature 'S' is directly applied the RSAVP1
-                signature primitive, as described in PKCS#1 v2.1, i.e. RFC 3447
-                Sect 5.2.1. Simply put, the provided signature is applied a
-                modular exponentiation using the public key. Then, a comparison
-                of the result is done against 'M'. On match, True is returned.
-                Additional method parameters are just ignored.
-
-        -'pkcs': the alleged signature 'S' and message 'M' are applied
-                RSASSA-PKCS1-v1_5-VERIFY signature verification scheme as
-                described in Sect. 8.2.2 of RFC 3447. In that context, the hash
-                function name is passed using 'h'. Possible values are "md2",
-                "md4", "md5", "sha1", "tls", "sha224", "sha256", "sha384" and
-                "sha512". If none is provided, sha1 is used. Other additional
-                parameters are ignored.
-
-        -'pss': the alleged signature 'S' and message 'M' are applied
-                RSASSA-PSS-VERIFY signature scheme as described in Sect. 8.1.2.
-                of RFC 3447. In that context,
-
-                o 'h' parameter provides the name of the hash method to use.
-                   Possible values are "md2", "md4", "md5", "sha1", "tls",
-                   "sha224", "sha256", "sha384" and "sha512". If None is
-                   provided, sha1 is used.
-
-                o 'mgf' is the mask generation function. By default, mgf
-                   is derived from the provided hash function using the
-                   generic MGF1 (see pkcs_mgf1() for details).
-
-                o 'sLen' is the byte length of the salt. You can overload the
-                  default value (the byte length of the hash value for provided
-                  algorithm) by providing another one with that parameter.
-        """
-        if t is None: # RSAVP1
-            S = pkcs_os2ip(S)
-            n = self._modulus
-            if S > n-1:
-                warning("Signature to be verified is too long for key modulus")
-                return False
-            m = self._rsavp1(S)
-            if m is None:
-                return False
-            l = int(math.ceil(math.log(m, 2) / 8.)) # Hack
-            m = pkcs_i2osp(m, l)
-            return M == m
-        elif t == "pkcs": # RSASSA-PKCS1-v1_5-VERIFY
-            if h is None:
-                h = "sha1"
-            return self._rsassa_pkcs1_v1_5_verify(M, S, h)
-        elif t == "pss": # RSASSA-PSS-VERIFY
-            return self._rsassa_pss_verify(M, S, h, mgf, sLen)
-        else:
-            warning("Key.verify(): Unknown signature type (%s) provided" % t)
-            return None
-
-    @crypto_validator
-    def verify(self, M, S, t="pkcs", h=None, mgf=None, sLen=None):
-        if h == "tls" or t is None:
-            return self.verify_legacy(M, S, t=t, h=h, mgf=mgf, sLen=sLen)
-
-        if h is not None:
-            h = mapHashFunc(h)
-
-        if t == "pkcs": # RSASSA-PKCS1-v1_5-VERIFY
-            pad = padding.PKCS1v15()
-        elif t == "pss": # RSASSA-PSS-VERIFY
-            pad = padding.PSS(mgf=mgf(h), salt_length=sLen)
-        else:
-            warning("Key.verify(): Unknown signature type (%s) provided" % t)
-            return None
-
-        try:
-            self.pubkey.verify(signature=S, data=M, padding=pad, algorithm=h)
-            return True
-        except InvalidSignature:
-            return False
-
-
 class _DecryptAndSignRSA(object):
-    ### Below are decryption related methods. Encryption ones are inherited
-    ### from PubKey
-
-    def _rsadp(self, c):
-        """
-        Internal method providing raw RSA decryption, i.e. simple modular
-        exponentiation of the given ciphertext representative 'c', a long
-        between 0 and n-1.
-
-        This is the decryption primitive RSADP described in PKCS#1 v2.1,
-        i.e. RFC 3447 Sect. 5.1.2.
-
-        Input:
-           c: ciphertest representative, a long between 0 and n-1, where
-              n is the key modulus.
-
-        Output:
-           message representative, a long between 0 and n-1
-
-        Not intended to be used directly. Please, see decrypt() method.
-        """
 
-        n = self._modulus
-        if isinstance(c, int):
-            c = long(c)
-        if (not isinstance(c, long)) or c > n-1:
-            warning("Key._rsaep() expects a long between 0 and n-1")
-            return None
-
-        privExp = self.key.private_numbers().d
-        return pow(c, privExp, n)
-
-
-    def decrypt(self, C, t="pkcs", h=None, mgf=None, L=None):
-        if h == "tls" or t is None:
-            #return self.decrypt_legacy(C, t=t, h=h, mgf=mgf, L=L)
-            warning("Cannot call decrypt_legacy anymore.")
-            return None
-
-        if h is not None:
-            h = mapHashFunc(h)
-
-        if t == "pkcs":
-            pad = padding.PKCS1v15()
-        elif t == "oaep":
-            pad = padding.OAEP(mgf=mgf(h), algorithm=h, label=L)
-        else:
-            warning("Key.decrypt(): Unknown decryption type (%s) provided" % t)
-            return None
+    @crypto_validator
+    def decrypt(self, C, t="pkcs", h="sha256", mgf=None, L=None):
+        mgf = mgf or padding.MGF1
+        h = _get_hash(h)
+        pad = _get_padding(t, mgf, h, L)
         return self.key.decrypt(C, pad)
 
+    @crypto_validator
+    def sign(self, M, t="pkcs", h="sha256", mgf=None, L=None):
+        mgf = mgf or padding.MGF1
+        h = _get_hash(h)
+        pad = _get_padding(t, mgf, h, L)
+        try:
+            return self.key.sign(M, pad, h)
+        except UnsupportedAlgorithm:
+            if t != "pkcs" and h != "md5-sha1":
+                raise UnsupportedAlgorithm("RSA signature with %s" % h)
+            return self._legacy_sign_md5_sha1(M)
 
-    ### Below are signature related methods.
-    ### Verification methods are inherited from PubKey.
-
-    def _rsasp1(self, m):
-        """
-        Internal method providing raw RSA signature, i.e. simple modular
-        exponentiation of the given message representative 'm', an integer
-        between 0 and n-1.
-
-        This is the signature primitive RSASP1 described in PKCS#1 v2.1,
-        i.e. RFC 3447 Sect. 5.2.1.
-
-        Input:
-           m: message representative, an integer between 0 and n-1, where
-              n is the key modulus.
-
-        Output:
-           signature representative, an integer between 0 and n-1
-
-        Not intended to be used directly. Please, see sign() method.
-        """
-        return self._rsadp(m)
-
-
-    def _rsassa_pss_sign(self, M, h=None, mgf=None, sLen=None):
-        """
-        Implements RSASSA-PSS-SIGN() function described in Sect. 8.1.1 of
-        RFC 3447.
-
-        Input:
-           M: message to be signed, an octet string
-
-        Output:
-           signature, an octet string of length k, where k is the length in
-           octets of the RSA modulus n.
-
-        On error, None is returned.
-        """
-
-        # Set default parameters if not provided
-        if h is None: # By default, sha1
-            h = "sha1"
-        if h not in _hashFuncParams:
-            warning("Key._rsassa_pss_sign(): unknown hash function "
-                    "provided (%s)" % h)
-            return None
-        if mgf is None: # use mgf1 with underlying hash function
-            mgf = lambda x,y: pkcs_mgf1(x, y, h)
-        if sLen is None: # use Hash output length (A.2.3 of RFC 3447)
-            hLen = _hashFuncParams[h][0]
-            sLen = hLen
-
-        # 1) EMSA-PSS encoding
-        modBits = self._modulusLen
-        k = modBits / 8
-        EM = pkcs_emsa_pss_encode(M, modBits - 1, h, mgf, sLen)
-        if EM is None:
-            warning("Key._rsassa_pss_sign(): unable to encode")
-            return None
-
-        # 2) RSA signature
-        m = pkcs_os2ip(EM)                          # 2.a)
-        s = self._rsasp1(m)                         # 2.b)
-        S = pkcs_i2osp(s, k)                        # 2.c)
-
-        return S                                    # 3)
-
-
-    def _rsassa_pkcs1_v1_5_sign(self, M, h):
-        """
-        Implements RSASSA-PKCS1-v1_5-SIGN() function as described in
-        Sect. 8.2.1 of RFC 3447.
-
-        Input:
-           M: message to be signed, an octet string
-           h: hash function name (in 'md2', 'md4', 'md5', 'sha1', 'tls'
-                'sha256', 'sha384').
-
-        Output:
-           the signature, an octet string.
-        """
-
-        # 1) EMSA-PKCS1-v1_5 encoding
+    def _legacy_sign_md5_sha1(self, M):
         k = self._modulusLen / 8
-        EM = pkcs_emsa_pkcs1_v1_5_encode(M, k, h)
+        EM = _legacy_pkcs1_v1_5_encode_md5_sha1(M, k)
         if EM is None:
             warning("Key._rsassa_pkcs1_v1_5_sign(): unable to encode")
             return None
-
-        # 2) RSA signature
-        m = pkcs_os2ip(EM)                          # 2.a)
-        s = self._rsasp1(m)                         # 2.b)
-        S = pkcs_i2osp(s, k)                        # 2.c)
-
-        return S                                    # 3)
-
-
-    def sign_legacy(self, M, t=None, h=None, mgf=None, sLen=None):
-        """
-        Sign message 'M' using 't' signature scheme where 't' can be:
-
-        - None: the message 'M' is directly applied the RSASP1 signature
-                primitive, as described in PKCS#1 v2.1, i.e. RFC 3447 Sect
-                5.2.1. Simply put, the message undergo a modular exponentiation
-                using the private key. Additional method parameters are just
-                ignored.
-
-        - 'pkcs': the message 'M' is applied RSASSA-PKCS1-v1_5-SIGN signature
-                scheme as described in Sect. 8.2.1 of RFC 3447. In that
-                context, the hash function name is passed using 'h'. Possible
-                values are "md2", "md4", "md5", "sha1", "tls", "sha224",
-                "sha256", "sha384" and "sha512". If none is provided, sha1 is
-                used. Other additional parameters are ignored.
-
-        - 'pss' : the message 'M' is applied RSASSA-PSS-SIGN signature scheme
-                as described in Sect. 8.1.1. of RFC 3447. In that context,
-
-                o 'h' parameter provides the name of the hash method to use.
-                   Possible values are "md2", "md4", "md5", "sha1", "tls",
-                   "sha224", "sha256", "sha384" and "sha512". If None is
-                   provided, sha1 is used.
-
-                o 'mgf' is the mask generation function. By default, mgf
-                   is derived from the provided hash function using the
-                   generic MGF1 (see pkcs_mgf1() for details).
-
-                o 'sLen' is the byte length of the salt. You can overload the
-                  default value (the byte length of the hash value for provided
-                  algorithm) by providing another one with that parameter.
-        """
-        if t is None: # RSASP1
-            M = pkcs_os2ip(M)
-            n = self._modulus
-            if M > n-1:
-                warning("Message to be signed is too long for key modulus")
-                return None
-            s = self._rsasp1(M)
-            if s is None:
-                return None
-            return pkcs_i2osp(s, self._modulusLen/8)
-        elif t == "pkcs": # RSASSA-PKCS1-v1_5-SIGN
-            if h is None:
-                h = "sha1"
-            return self._rsassa_pkcs1_v1_5_sign(M, h)
-        elif t == "pss": # RSASSA-PSS-SIGN
-            return self._rsassa_pss_sign(M, h, mgf, sLen)
-        else:
-            warning("Key.sign(): Unknown signature type (%s) provided" % t)
-            return None
-
-    def sign(self, M, t="pkcs", h=None, mgf=None, sLen=None):
-        if h == "tls" or t is None:
-            return self.sign_legacy(M, t=t, h=h, mgf=mgf, sLen=sLen)
-
-        if h is not None:
-            h = mapHashFunc(h)
-
-        if t == "pkcs": # RSASSA-PKCS1-v1_5-SIGN
-            pad = padding.PKCS1v15()
-        elif t == "pss": # RSASSA-PSS-SIGN
-            pad = padding.PSS(mgf=mgf(h), salt_length=sLen)
-        else:
-            warning("Key.sign(): Unknown signature type (%s) provided" % t)
+        m = pkcs_os2ip(EM)
+        n = self._modulus
+        if isinstance(m, int):
+            m = long(m)
+        if (not isinstance(m, long)) or m > n-1:
+            warning("Key._rsaep() expects a long between 0 and n-1")
             return None
-        return self.key.sign(M, pad, h)
-
-
-
-#####################################################################
-# CA files helpers
-#####################################################################
-
-def create_ca_file(anchor_list, filename):
-    """
-    Concatenate all the certificates (PEM format for the export) in
-    'anchor_list' and write the result to file 'filename'. On success
-    'filename' is returned, None otherwise.
-
-    If you are used to OpenSSL tools, this function builds a CAfile
-    that can be used for certificate and CRL check.
-
-    Also see create_temporary_ca_file().
-    """
-    try:
-        f = open(filename, "w")
-        for a in anchor_list:
-            s = a.output(fmt="PEM")
-            f.write(s)
-        f.close()
-    except:
-        return None
-    return filename
-
-def create_temporary_ca_file(anchor_list):
-    """
-    Concatenate all the certificates (PEM format for the export) in
-    'anchor_list' and write the result to file to a temporary file
-    using mkstemp() from tempfile module. On success 'filename' is
-    returned, None otherwise.
-
-    If you are used to OpenSSL tools, this function builds a CAfile
-    that can be used for certificate and CRL check.
-    """
-    try:
-        f, fname = tempfile.mkstemp()
-        for a in anchor_list:
-            s = a.output(fmt="PEM")
-            l = os.write(f, s)
-        os.close(f)
-    except:
-        return None
-    return fname
-
-def create_temporary_ca_path(anchor_list, folder):
-    """
-    Create a CA path folder as defined in OpenSSL terminology, by
-    storing all certificates in 'anchor_list' list in PEM format
-    under provided 'folder' and then creating the associated links
-    using the hash as usually done by c_rehash.
-
-    Note that you can also include CRL in 'anchor_list'. In that
-    case, they will also be stored under 'folder' and associated
-    links will be created.
-
-    In folder, the files are created with names of the form
-    0...ZZ.pem. If you provide an empty list, folder will be created
-    if it does not already exist, but that's all.
-
-    The number of certificates written to folder is returned on
-    success, None on error.
-    """
-    # We should probably avoid writing duplicate anchors and also
-    # check if they are all certs.
-    try:
-        if not os.path.isdir(folder):
-            os.makedirs(folder)
-    except:
-        return None
-
-    l = len(anchor_list)
-    if l == 0:
-        return None
-    fmtstr = "%%0%sd.pem" % math.ceil(math.log(l, 10))
-    i = 0
-    try:
-        for a in anchor_list:
-            fname = os.path.join(folder, fmtstr % i)
-            f = open(fname, "w")
-            s = a.output(fmt="PEM")
-            f.write(s)
-            f.close()
-            i += 1
-    except:
-        return None
-
-    r,w=popen2.popen2("c_rehash %s" % folder)
-    r.close(); w.close()
-
-    return l
+        privExp = self.key.private_numbers().d
+        s = pow(m, privExp, n)
+        return pkcs_i2osp(s, k)
 
diff --git a/scapy/layers/tls/crypto/prf.py b/scapy/layers/tls/crypto/prf.py
index 111b7d64c47995e1947c83b3899f6adc4ab14604..03b7d9c1d5e33ba85075fdba886200bc767ab411 100644
--- a/scapy/layers/tls/crypto/prf.py
+++ b/scapy/layers/tls/crypto/prf.py
@@ -1,6 +1,6 @@
 ## This file is part of Scapy
 ## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
-##                     2015, 2016 Maxence Tury
+##               2015, 2016, 2017 Maxence Tury
 ## This program is published under a GPLv2 license
 
 """
@@ -35,7 +35,6 @@ def _tls_P_hash(secret, seed, req_len, hm):
            Hmac_MD5 or Hmac_SHA1 in TLS <= 1.1 or
            Hmac_SHA256 or Hmac_SHA384 in TLS 1.2)
     """
-
     hash_len = hm.hash_alg.hash_len
     n = (req_len + hash_len - 1) / hash_len
 
@@ -68,6 +67,22 @@ def _tls_P_SHA512(secret, seed, req_len):
 
 ### PRF functions, according to the protocol version
 
+def _sslv2_PRF(secret, seed, req_len):
+    hash_md5 = tls_hash_algs["MD5"]()
+    rounds = (req_len + hash_md5.hash_len - 1) / hash_md5.hash_len
+
+    res = ""
+    if rounds == 1:
+        res += hash_md5.digest(secret + seed)
+    else:
+        r = 0
+        while r < rounds:
+            label = str(r)
+            res += hash_md5.digest(secret + label + seed)
+            r += 1
+
+    return res[:req_len]
+
 def _ssl_PRF(secret, seed, req_len):
     """
     Provides the implementation of SSLv3 PRF function:
@@ -78,7 +93,6 @@ def _ssl_PRF(secret, seed, req_len):
         MD5(secret || SHA-1("CCC" || secret || seed)) || ...
 
     req_len should not be more than  26 x 16 = 416.
-
     """
     if req_len > 416:
         warning("_ssl_PRF() is not expected to provide more than 416 bytes")
@@ -87,13 +101,14 @@ def _ssl_PRF(secret, seed, req_len):
     d = ["A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M",
          "N", "O", "P", "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z"]
     res = ""
-    hash_md5 = tls_hash_algs["MD5"]
+    hash_sha1 = tls_hash_algs["SHA"]()
+    hash_md5 = tls_hash_algs["MD5"]()
     rounds = (req_len + hash_md5.hash_len - 1) / hash_md5.hash_len
 
     for i in range(rounds):
         label = d[i] * (i+1)
-        tmp = tls_hash_algs["SHA"]().digest(label + secret + seed)
-        res += tls_hash_algs["MD5"]().digest(secret + tmp)
+        tmp = hash_sha1.digest(label + secret + seed)
+        res += hash_md5.digest(secret + tmp)
 
     return res[:req_len]
 
@@ -113,9 +128,7 @@ def _tls_PRF(secret, label, seed, req_len):
              depending on the use of the generated PRF keystream
     - seed: the seed used by the expansion functions.
     - req_len: amount of keystream to be generated
-
     """
-
     l = (len(secret) + 1) / 2
     S1 = secret[:l]
     S2 = secret[-l:]
@@ -140,7 +153,6 @@ def _tls12_SHA256PRF(secret, label, seed, req_len):
              depending on the use of the generated PRF keystream
     - seed: the seed used by the expansion functions.
     - req_len: amount of keystream to be generated
-
     """
     return _tls_P_SHA256(secret, label+seed, req_len)
 
@@ -165,8 +177,9 @@ class PRF(object):
         self.tls_version = tls_version
         self.hash_name = hash_name
 
-        if (tls_version == 0x0200 or        # SSLv2
-            tls_version == 0x0300):         # SSLv3
+        if tls_version < 0x0300:            # SSLv2
+            self.prf = _sslv2_PRF
+        elif tls_version == 0x0300:         # SSLv3
             self.prf = _ssl_PRF
         elif (tls_version == 0x0301 or      # TLS 1.0
               tls_version == 0x0302):       # TLS 1.1
@@ -188,7 +201,9 @@ class PRF(object):
         client_random and server_random. See RFC 5246, section 6.3.
         """
         seed = client_random + server_random
-        if self.tls_version <= 0x0300:
+        if self.tls_version < 0x0300:
+            return None
+        elif self.tls_version == 0x0300:
             return self.prf(pre_master_secret, seed, 48)
         else:
             return self.prf(pre_master_secret, "master secret", seed, 48)
@@ -217,7 +232,9 @@ class PRF(object):
          prior to this document when TLS 1.2 is negotiated."
         Cipher suites using SHA-384 were defined later on.
         """
-        if self.tls_version <= 0x0300:
+        if self.tls_version < 0x0300:
+            return None
+        elif self.tls_version == 0x0300:
 
             if read_or_write == "write":
                 d = {"client": "CLNT", "server": "SRVR"}
@@ -273,7 +290,9 @@ class PRF(object):
         s = con_end + read_or_write
         s = (s == "clientwrite" or s == "serverread")
 
-        if self.tls_version == 0x0300:
+        if self.tls_version < 0x0300:
+            return None
+        elif self.tls_version == 0x0300:
             if s:
                 tbh = key + client_random + server_random
             else:
@@ -300,7 +319,9 @@ class PRF(object):
         s = con_end + read_or_write
         s = (s == "clientwrite" or s == "serverread")
 
-        if self.tls_version == 0x0300:
+        if self.tls_version < 0x0300:
+            return None
+        elif self.tls_version == 0x0300:
             if s:
                 tbh = client_random + server_random
             else:
diff --git a/scapy/layers/tls/crypto/suites.py b/scapy/layers/tls/crypto/suites.py
index f8a8aaa179b31d6cd68690f9370876a3d9efaf0a..c2cd40eeeb769d6e1b7dabefa81f3d2c9bd7fd7b 100644
--- a/scapy/layers/tls/crypto/suites.py
+++ b/scapy/layers/tls/crypto/suites.py
@@ -1,6 +1,6 @@
 ## This file is part of Scapy
 ## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
-##                     2015, 2016 Maxence Tury
+##               2015, 2016, 2017 Maxence Tury
 ## This program is published under a GPLv2 license
 
 """
@@ -23,31 +23,48 @@ def get_algs_from_ciphersuite_name(ciphersuite_name):
     Return the 3-tuple made of the Key Exchange Algorithm class, the Cipher
     class and the HMAC class, through the parsing of the ciphersuite name.
     """
-    s = ciphersuite_name[4:]
-
-    kx_name, s = s.split("_WITH_")
-    kx_alg = tls_kx_algs.get(kx_name)
-
-    if s.endswith("CCM") or s.endswith("CCM_8"):
-
-        hash_alg = tls_hash_algs.get("SHA256")
-        cipher_alg = tls_cipher_algs.get(s)
-        hmac_alg = None
-
-    else:
-
-        hash_name = s.split('_')[-1]
+    tls1_3 = False
+    if ciphersuite_name.startswith("TLS"):
+        s = ciphersuite_name[4:]
+    
+        if s.endswith("CCM") or s.endswith("CCM_8"):
+            kx_name, s = s.split("_WITH_")
+            kx_alg = tls_kx_algs.get(kx_name)
+            hash_alg = tls_hash_algs.get("SHA256")
+            cipher_alg = tls_cipher_algs.get(s)
+            hmac_alg = None
+    
+        else:
+            if "WITH" in s:
+                kx_name, s = s.split("_WITH_")
+                kx_alg = tls_kx_algs.get(kx_name)
+            else:
+                tls1_3 = True
+                kx_alg = tls_kx_algs.get("TLS13")
+    
+            hash_name = s.split('_')[-1]
+            hash_alg = tls_hash_algs.get(hash_name)
+    
+            cipher_name = s[:-(len(hash_name) + 1)]
+            if tls1_3:
+                cipher_name += "_TLS13"
+            cipher_alg = tls_cipher_algs.get(cipher_name)
+    
+            hmac_alg = None
+            if cipher_alg is not None and cipher_alg.type != "aead":
+                hmac_name = "HMAC-%s" % hash_name
+                hmac_alg = tls_hmac_algs.get(hmac_name)
+
+    elif ciphersuite_name.startswith("SSL"):
+        s = ciphersuite_name[7:]
+        kx_alg = tls_kx_algs.get("SSLv2")
+        cipher_name, hash_name = s.split("_WITH_")
+        cipher_alg = tls_cipher_algs.get(cipher_name.rstrip("_EXPORT40"))
+        kx_alg.export = cipher_name.endswith("_EXPORT40")
+        hmac_alg = tls_hmac_algs.get("HMAC-NULL")
         hash_alg = tls_hash_algs.get(hash_name)
 
-        cipher_name = s[:-(len(hash_name) + 1)]
-        cipher_alg = tls_cipher_algs.get(cipher_name)
-
-        hmac_alg = None
-        if cipher_alg is not None and cipher_alg.type != "aead":
-            hmac_name = "HMAC-%s" % hash_name
-            hmac_alg = tls_hmac_algs.get(hmac_name)
-
-    return kx_alg, cipher_alg, hmac_alg, hash_alg
+    return kx_alg, cipher_alg, hmac_alg, hash_alg, tls1_3
 
 
 _tls_cipher_suites = {}
@@ -64,17 +81,14 @@ class _GenericCipherSuiteMetaclass(type):
 
     Regarding the AEAD cipher suites, note that the 'hmac_alg' attribute will
     be set to None. Yet, we always need a 'hash_alg' for the PRF.
-
-    Also, if pycrypto 2.7a is not installed, there will be no AES.GCM nor
-    AES.CCM support, so the 'usable' attribute will be set to False.
     """
     def __new__(cls, cs_name, bases, dct):
         cs_val = dct.get("val")
 
         if cs_name != "_GenericCipherSuite":
-            kx, c, hm, h = get_algs_from_ciphersuite_name(cs_name)
+            kx, c, hm, h, tls1_3 = get_algs_from_ciphersuite_name(cs_name)
 
-            if (kx is None or c is None or h is None):
+            if c is None or h is None or (kx is None and not tls1_3):
                 dct["usable"] = False
             else:
                 dct["usable"] = True
@@ -84,27 +98,28 @@ class _GenericCipherSuiteMetaclass(type):
                 dct["hmac_alg"] = hm
                 dct["hash_alg"] = h
 
-                kb_len = 2*c.key_len
+                if not tls1_3:
+                    kb_len = 2*c.key_len
 
-                if c.type == "stream" or c.type == "block":
-                    kb_len += 2*hm.key_len
+                    if c.type == "stream" or c.type == "block":
+                        kb_len += 2*hm.key_len
 
-                kb_len_v1_0 = kb_len
-                if c.type == "block":
-                    kb_len_v1_0 += 2*c.block_size
-                    # no explicit IVs added for TLS 1.1+
-                elif c.type == "aead":
-                    kb_len_v1_0 += 2*c.salt_len
-                    kb_len += 2*c.salt_len
+                    kb_len_v1_0 = kb_len
+                    if c.type == "block":
+                        kb_len_v1_0 += 2*c.block_size
+                        # no explicit IVs added for TLS 1.1+
+                    elif c.type == "aead":
+                        kb_len_v1_0 += 2*c.fixed_iv_len
+                        kb_len += 2*c.fixed_iv_len
 
-                dct["_key_block_len_v1_0"] = kb_len_v1_0
-                dct["key_block_len"] = kb_len
+                    dct["_key_block_len_v1_0"] = kb_len_v1_0
+                    dct["key_block_len"] = kb_len
 
             _tls_cipher_suites[cs_val] = cs_name
         the_class = super(_GenericCipherSuiteMetaclass, cls).__new__(cls,
-                                                                    cs_name,
-                                                                    bases,
-                                                                    dct)
+                                                                     cs_name,
+                                                                     bases,
+                                                                     dct)
         if cs_name != "_GenericCipherSuite":
             _tls_cipher_suites_cls[cs_val] = the_class
         return the_class
@@ -924,32 +939,81 @@ class TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8(_GenericCipherSuite):
 class TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8(_GenericCipherSuite):
     val = 0xC0AF
 
-#class TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite):
-#    val = 0xCCA8
-#
-#class TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite):
-#    val = 0xCCA9
-#
-#class TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite):
-#    val = 0xCCAA
-#
-#class TLS_PSK_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite):
-#    val = 0xCCAB
-#
-#class TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite):
-#    val = 0xCCAC
-#
-#class TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite):
-#    val = 0xCCAD
-#
-#class TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite):
-#    val = 0xCCAE
+# the next 3 suites are from draft-agl-tls-chacha20poly1305-04
+class TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256_OLD(_GenericCipherSuite):
+    val = 0xCC13
+
+class TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256_OLD(_GenericCipherSuite):
+    val = 0xCC14
+
+class TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256_OLD(_GenericCipherSuite):
+    val = 0xCC15
+
+class TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite):
+    val = 0xCCA8
+
+class TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite):
+    val = 0xCCA9
+
+class TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite):
+    val = 0xCCAA
+
+class TLS_PSK_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite):
+    val = 0xCCAB
+
+class TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite):
+    val = 0xCCAC
+
+class TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite):
+    val = 0xCCAD
+
+class TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256(_GenericCipherSuite):
+    val = 0xCCAE
+
+
+class TLS_AES_128_GCM_SHA256(_GenericCipherSuite):
+    val = 0x1301
+
+class TLS_AES_256_GCM_SHA384(_GenericCipherSuite):
+    val = 0x1302
+
+class TLS_CHACHA20_POLY1305_SHA256(_GenericCipherSuite):
+    val = 0x1303
+
+class TLS_AES_128_CCM_SHA256(_GenericCipherSuite):
+    val = 0x1304
+
+class TLS_AES_128_CCM_8_SHA256(_GenericCipherSuite):
+    val = 0x1305
+
+
+class SSL_CK_RC4_128_WITH_MD5(_GenericCipherSuite):
+    val = 0x010080
+
+class SSL_CK_RC4_128_EXPORT40_WITH_MD5(_GenericCipherSuite):
+    val = 0x020080
+
+class SSL_CK_RC2_128_CBC_WITH_MD5(_GenericCipherSuite):
+    val = 0x030080
+
+class SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5(_GenericCipherSuite):
+    val = 0x040080
+
+class SSL_CK_IDEA_128_CBC_WITH_MD5(_GenericCipherSuite):
+    val = 0x050080
+
+class SSL_CK_DES_64_CBC_WITH_MD5(_GenericCipherSuite):
+    val = 0x060040
+
+class SSL_CK_DES_192_EDE3_CBC_WITH_MD5(_GenericCipherSuite):
+    val = 0x0700C0
 
 
 _tls_cipher_suites[0x00ff] = "TLS_EMPTY_RENEGOTIATION_INFO_SCSV"
 _tls_cipher_suites[0x5600] = "TLS_FALLBACK_SCSV"
 
 
+
 def get_usable_ciphersuites(l, kx):
     """
     From a list of proposed ciphersuites, this function returns a list of
@@ -965,7 +1029,7 @@ def get_usable_ciphersuites(l, kx):
             if ciph.usable:
                 #XXX select among RSA and ECDSA cipher suites
                 # according to the key(s) the server was given
-                if kx in ciph.kx_alg.name:
+                if ciph.kx_alg.anonymous or kx in ciph.kx_alg.name:
                     res.append(c)
     return res
 
diff --git a/scapy/layers/tls/extensions.py b/scapy/layers/tls/extensions.py
new file mode 100644
index 0000000000000000000000000000000000000000..00600fcba9a21c63b56df1b79277f9a986e50ee3
--- /dev/null
+++ b/scapy/layers/tls/extensions.py
@@ -0,0 +1,659 @@
+## This file is part of Scapy
+## Copyright (C) 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+TLS handshake extensions.
+"""
+
+from __future__ import print_function
+
+from scapy.fields import *
+from scapy.packet import Packet, Raw, Padding
+from scapy.layers.x509 import X509_Extensions
+from scapy.layers.tls.basefields import _tls_version
+from scapy.layers.tls.keyexchange import (SigAndHashAlgsLenField,
+                                          SigAndHashAlgsField, _tls_hash_sig)
+from scapy.layers.tls.session import _GenericTLSSessionInheritance
+from scapy.layers.tls.crypto.groups import _tls_named_groups
+
+
+_tls_ext = {  0: "server_name",             # RFC 4366
+              1: "max_fragment_length",     # RFC 4366
+              2: "client_certificate_url",  # RFC 4366
+              3: "trusted_ca_keys",         # RFC 4366
+              4: "truncated_hmac",          # RFC 4366
+              5: "status_request",          # RFC 4366
+              6: "user_mapping",            # RFC 4681
+              7: "client_authz",            # RFC 5878
+              8: "server_authz",            # RFC 5878
+              9: "cert_type",               # RFC 6091
+            #10: "elliptic_curves",         # RFC 4492
+             10: "supported_groups",
+             11: "ec_point_formats",        # RFC 4492
+             13: "signature_algorithms",    # RFC 5246
+             0x0f: "heartbeat",             # RFC 6520
+             0x10: "alpn",                  # RFC 7301
+             0x12: "signed_certificate_timestamp",  # RFC 6962
+             0x15: "padding",               # RFC 7685
+             0x16: "encrypt_then_mac",      # RFC 7366
+             0x17: "extended_master_secret",# RFC 7627
+             0x23: "session_ticket",        # RFC 5077
+             0x28: "key_share",
+             0x29: "pre_shared_key",
+             0x2a: "early_data",
+             0x2b: "supported_versions",
+             0x2c: "cookie",
+             0x2d: "psk_key_exchange_modes",
+             0x2e: "ticket_early_data_info",
+             0x2f: "certificate_authorities",
+             0x30: "oid_filters",
+             0x3374: "next_protocol_negotiation",
+                                            # RFC-draft-agl-tls-nextprotoneg-03
+             0xff01: "renegotiation_info"   # RFC 5746
+             }
+
+
+class TLS_Ext_Unknown(_GenericTLSSessionInheritance):
+    """
+    We put this here rather than in extensions.py in order to avoid
+    circular imports...
+    """
+    name = "TLS Extension - Scapy Unknown"
+    fields_desc = [ShortEnumField("type", None, _tls_ext),
+                   FieldLenField("len", None, fmt="!H", length_of="val"),
+                   StrLenField("val", "",
+                               length_from=lambda pkt: pkt.len) ]
+
+    def post_build(self, p, pay):
+        if self.len is None:
+            l = len(p) - 4
+            p = p[:2] + struct.pack("!H", l) + p[4:]
+        return p+pay
+
+
+###############################################################################
+### ClientHello/ServerHello extensions                                      ###
+###############################################################################
+
+# We provide these extensions mostly for packet manipulation purposes.
+# For now, most of them are not considered by our automaton.
+
+class TLS_Ext_PrettyPacketList(TLS_Ext_Unknown):
+    """
+    Dummy extension used for server_name/ALPN/NPN for a lighter representation:
+    the final field is showed as a 1-line list rather than as lots of packets.
+    XXX Define a new condition for packet lists in Packet._show_or_dump?
+    """
+    def _show_or_dump(self, dump=False, indent=3,
+                      lvl="", label_lvl="", first_call=True):
+        """ Reproduced from packet.py """
+        ct = AnsiColorTheme() if dump else conf.color_theme
+        s = "%s%s %s %s \n" % (label_lvl, ct.punct("###["),
+                               ct.layer_name(self.name), ct.punct("]###"))
+        for f in self.fields_desc[:-1]:
+            ncol = ct.field_name
+            vcol = ct.field_value
+            fvalue = self.getfieldval(f.name)
+            begn = "%s  %-10s%s " % (label_lvl+lvl, ncol(f.name),
+                                     ct.punct("="),)
+            reprval = f.i2repr(self,fvalue)
+            if isinstance(reprval, str):
+                reprval = reprval.replace("\n", "\n"+" "*(len(label_lvl)
+                                                          +len(lvl)
+                                                          +len(f.name)
+                                                          +4))
+            s += "%s%s\n" % (begn,vcol(reprval))
+        f = self.fields_desc[-1]
+        ncol = ct.field_name
+        vcol = ct.field_value
+        fvalue = self.getfieldval(f.name)
+        begn = "%s  %-10s%s " % (label_lvl+lvl, ncol(f.name), ct.punct("="),)
+        reprval = f.i2repr(self,fvalue)
+        if isinstance(reprval, str):
+            reprval = reprval.replace("\n", "\n"+" "*(len(label_lvl)
+                                                      +len(lvl)
+                                                      +len(f.name)
+                                                      +4))
+        s += "%s%s\n" % (begn,vcol(reprval))
+        if self.payload:
+            s += self.payload._show_or_dump(dump=dump, indent=indent,
+                                lvl=lvl+(" "*indent*self.show_indent),
+                                label_lvl=label_lvl, first_call=False)
+
+        if first_call and not dump:
+            print(s)
+        else:
+            return s
+
+
+_tls_server_name_types = { 0: "host_name" }
+
+class ServerName(Packet):
+    name = "HostName"
+    fields_desc = [ ByteEnumField("nametype", 0, _tls_server_name_types),
+                    FieldLenField("namelen", None, length_of="servername"),
+                    StrLenField("servername", "",
+                                length_from=lambda pkt: pkt.namelen) ]
+    def guess_payload_class(self, p):
+        return Padding
+
+class ServerListField(PacketListField):
+    def i2repr(self, pkt, x):
+        res = [p.servername for p in x]
+        return "[%s]" % ", ".join(res)
+
+class ServerLenField(FieldLenField):
+    """
+    There is no length when there are no servernames (as in a ServerHello).
+    """
+    def addfield(self, pkt, s, val):
+        if not val:
+            if not pkt.servernames:
+                return s
+        return super(ServerLenField, self).addfield(pkt, s, val)
+
+class TLS_Ext_ServerName(TLS_Ext_PrettyPacketList):                 # RFC 4366
+    name = "TLS Extension - Server Name"
+    fields_desc = [ShortEnumField("type", 0, _tls_ext),
+                   FieldLenField("len", None, length_of="servernames",
+                                 adjust=lambda pkt,x: x+2),
+                   ServerLenField("servernameslen", None,
+                                 length_of="servernames"),
+                   ServerListField("servernames", [], ServerName,
+                                   length_from=lambda pkt: pkt.servernameslen)]
+
+
+class TLS_Ext_MaxFragLen(TLS_Ext_Unknown):                          # RFC 4366
+    name = "TLS Extension - Max Fragment Length"
+    fields_desc = [ShortEnumField("type", 1, _tls_ext),
+                   ShortField("len", None),
+                   ByteEnumField("maxfraglen", 4, { 1: "2^9",
+                                                    2: "2^10",
+                                                    3: "2^11",
+                                                    4: "2^12" }) ]
+
+
+class TLS_Ext_ClientCertURL(TLS_Ext_Unknown):                       # RFC 4366
+    name = "TLS Extension - Client Certificate URL"
+    fields_desc = [ShortEnumField("type", 2, _tls_ext),
+                   ShortField("len", None) ]
+
+
+_tls_trusted_authority_types = {0: "pre_agreed",
+                                1: "key_sha1_hash",
+                                2: "x509_name",
+                                3: "cert_sha1_hash" }
+
+class TAPreAgreed(Packet):
+    name = "Trusted authority - pre_agreed"
+    fields_desc = [ ByteEnumField("idtype", 0, _tls_trusted_authority_types) ]
+    def guess_payload_class(self, p):
+        return Padding
+
+class TAKeySHA1Hash(Packet):
+    name = "Trusted authority - key_sha1_hash"
+    fields_desc = [ ByteEnumField("idtype", 1, _tls_trusted_authority_types),
+                    StrFixedLenField("id", None, 20) ]
+    def guess_payload_class(self, p):
+        return Padding
+
+class TAX509Name(Packet):
+    """
+    XXX Section 3.4 of RFC 4366. Implement a more specific DNField
+    rather than current StrLenField.
+    """
+    name = "Trusted authority - x509_name"
+    fields_desc = [ ByteEnumField("idtype", 2, _tls_trusted_authority_types),
+                    FieldLenField("dnlen", None, length_of="dn"),
+                    StrLenField("dn", "", length_from=lambda pkt: pkt.dnlen) ]
+    def guess_payload_class(self, p):
+        return Padding
+
+class TACertSHA1Hash(Packet):
+    name = "Trusted authority - cert_sha1_hash"
+    fields_desc = [ ByteEnumField("idtype", 3, _tls_trusted_authority_types),
+                    StrFixedLenField("id", None, 20) ]
+    def guess_payload_class(self, p):
+        return Padding
+
+_tls_trusted_authority_cls = {0: TAPreAgreed,
+                              1: TAKeySHA1Hash,
+                              2: TAX509Name,
+                              3: TACertSHA1Hash }
+
+class _TAListField(PacketListField):
+    """
+    Specific version that selects the right Trusted Authority (previous TA*)
+    class to be used for dissection based on idtype.
+    """
+    def m2i(self, pkt, m):
+        idtype = ord(m[0])
+        cls = self.cls
+        if idtype in _tls_trusted_authority_cls:
+            cls = _tls_trusted_authority_cls[idtype]
+        return cls(m)
+
+class TLS_Ext_TrustedCAInd(TLS_Ext_Unknown):                        # RFC 4366
+    name = "TLS Extension - Trusted CA Indication"
+    fields_desc = [ShortEnumField("type", 3, _tls_ext),
+                   ShortField("len", None),
+                   FieldLenField("talen", None, length_of="ta"),
+                   _TAListField("ta", [], Raw,
+                                length_from=lambda pkt: pkt.talen) ]
+
+
+class TLS_Ext_TruncatedHMAC(TLS_Ext_Unknown):                       # RFC 4366
+    name = "TLS Extension - Truncated HMAC"
+    fields_desc = [ShortEnumField("type", 4, _tls_ext),
+                   ShortField("len", None) ]
+
+
+class ResponderID(Packet):
+    name = "Responder ID structure"
+    fields_desc = [ FieldLenField("respidlen", None, length_of="respid"),
+                    StrLenField("respid", "",
+                                length_from=lambda pkt: pkt.respidlen)]
+    def guess_payload_class(self, p):
+        return Padding
+
+class OCSPStatusRequest(Packet):
+    """
+    This is the structure defined in RFC 6066, not in RFC 6960!
+    """
+    name = "OCSPStatusRequest structure"
+    fields_desc = [ FieldLenField("respidlen", None, length_of="respid"),
+                    PacketListField("respid", [], ResponderID,
+                                    length_from=lambda pkt: pkt.respidlen),
+                    FieldLenField("reqextlen", None, length_of="reqext"),
+                    PacketField("reqext", "", X509_Extensions) ]
+    def guess_payload_class(self, p):
+        return Padding
+
+_cert_status_type = { 1: "ocsp" }
+_cert_status_req_cls  = { 1: OCSPStatusRequest }
+
+class _StatusReqField(PacketListField):
+    def m2i(self, pkt, m):
+        idtype = pkt.stype
+        cls = self.cls
+        if idtype in _cert_status_req_cls:
+            cls = _cert_status_req_cls[idtype]
+        return cls(m)
+
+class TLS_Ext_CSR(TLS_Ext_Unknown):                                 # RFC 4366
+    name = "TLS Extension - Certificate Status Request"
+    fields_desc = [ShortEnumField("type", 5, _tls_ext),
+                   ShortField("len", None),
+                   ByteEnumField("stype", None, _cert_status_type),
+                   _StatusReqField("req", [], Raw,
+                                  length_from=lambda pkt: pkt.len - 1) ]
+
+
+class TLS_Ext_UserMapping(TLS_Ext_Unknown):                         # RFC 4681
+    name = "TLS Extension - User Mapping"
+    fields_desc = [ShortEnumField("type", 6, _tls_ext),
+                   ShortField("len", None),
+                   FieldLenField("umlen", None, fmt="B", length_of="um"),
+                   FieldListField("um", [],
+                                  ByteField("umtype", 0),
+                                  length_from=lambda pkt: pkt.umlen) ]
+
+
+class TLS_Ext_ClientAuthz(TLS_Ext_Unknown):                         # RFC 5878
+    """ XXX Unsupported """
+    name = "TLS Extension - Client Authz"
+    fields_desc = [ShortEnumField("type", 7, _tls_ext),
+                   ShortField("len", None),
+                   ]
+
+class TLS_Ext_ServerAuthz(TLS_Ext_Unknown):                         # RFC 5878
+    """ XXX Unsupported """
+    name = "TLS Extension - Server Authz"
+    fields_desc = [ShortEnumField("type", 8, _tls_ext),
+                   ShortField("len", None),
+                   ]
+
+
+_tls_cert_types = { 0: "X.509", 1: "OpenPGP" }
+
+class TLS_Ext_ClientCertType(TLS_Ext_Unknown):                      # RFC 5081
+    name = "TLS Extension - Certificate Type (client version)"
+    fields_desc = [ShortEnumField("type", 9, _tls_ext),
+                   ShortField("len", None),
+                   FieldLenField("ctypeslen", None, length_of="ctypes"),
+                   FieldListField("ctypes", [0, 1],
+                                  ByteEnumField("certtypes", None,
+                                                _tls_cert_types),
+                                  length_from=lambda pkt: pkt.ctypeslen) ]
+
+class TLS_Ext_ServerCertType(TLS_Ext_Unknown):                      # RFC 5081
+    name = "TLS Extension - Certificate Type (server version)"
+    fields_desc = [ShortEnumField("type", 9, _tls_ext),
+                   ShortField("len", None),
+                   ByteEnumField("ctype", None, _tls_cert_types) ]
+
+def _TLS_Ext_CertTypeDispatcher(m, *args, **kargs):
+    """
+    We need to select the correct one on dissection. We use the length for
+    that, as 1 for client version would emply an empty list.
+    """
+    l = struct.unpack("!H", m[2:4])[0]
+    if l == 1:
+        cls = TLS_Ext_ServerCertType
+    else:
+        cls = TLS_Ext_ClientCertType
+    return cls(m, *args, **kargs)
+
+
+class TLS_Ext_SupportedGroups(TLS_Ext_Unknown):
+    """
+    This extension was known as 'Supported Elliptic Curves' before TLS 1.3
+    merged both group selection mechanisms for ECDH and FFDH.
+    """
+    name = "TLS Extension - Supported Groups"
+    fields_desc = [ShortEnumField("type", 10, _tls_ext),
+                   ShortField("len", None),
+                   FieldLenField("groupslen", None, length_of="groups"),
+                   FieldListField("groups", [],
+                                  ShortEnumField("ng", None,
+                                                 _tls_named_groups),
+                                  length_from=lambda pkt: pkt.groupslen) ]
+
+class TLS_Ext_SupportedEllipticCurves(TLS_Ext_SupportedGroups):     # RFC 4492
+    pass
+
+
+_tls_ecpoint_format = { 0: "uncompressed",
+                        1: "ansiX962_compressed_prime",
+                        2: "ansiX962_compressed_char2" }
+
+class TLS_Ext_SupportedPointFormat(TLS_Ext_Unknown):                # RFC 4492
+    name = "TLS Extension - Supported Point Format"
+    fields_desc = [ShortEnumField("type", 11, _tls_ext),
+                   ShortField("len", None),
+                   FieldLenField("ecpllen", None, fmt="B", length_of="ecpl"),
+                   FieldListField("ecpl", [0],
+                                    ByteEnumField("nc", None,
+                                                  _tls_ecpoint_format),
+                                    length_from=lambda pkt: pkt.ecpllen) ]
+
+
+class TLS_Ext_SignatureAlgorithms(TLS_Ext_Unknown):                 # RFC 5246
+    name = "TLS Extension - Signature Algorithms"
+    fields_desc = [ShortEnumField("type", 13, _tls_ext),
+                   ShortField("len", None),
+                   SigAndHashAlgsLenField("sig_algs_len", None,
+                                          length_of="sig_algs"),
+                   SigAndHashAlgsField("sig_algs", [],
+                                       EnumField("hash_sig", None,
+                                                    _tls_hash_sig),
+                                       length_from=
+                                           lambda pkt: pkt.sig_algs_len) ]
+
+
+class TLS_Ext_Heartbeat(TLS_Ext_Unknown):                           # RFC 6520
+    name = "TLS Extension - Heartbeat"
+    fields_desc = [ShortEnumField("type", 0x0f, _tls_ext),
+                   ShortField("len", None),
+                   ByteEnumField("heartbeat_mode", 2,
+                       { 1: "peer_allowed_to_send",
+                         2: "peer_not_allowed_to_send" }) ]
+
+
+class ProtocolName(Packet):
+    name = "Protocol Name"
+    fields_desc = [ FieldLenField("len", None, fmt='B', length_of="protocol"),
+                    StrLenField("protocol", "",
+                                length_from=lambda pkt: pkt.len)]
+    def guess_payload_class(self, p):
+        return Padding
+
+class ProtocolListField(PacketListField):
+    def i2repr(self, pkt, x):
+        res = [p.protocol for p in x]
+        return "[%s]" % ", ".join(res)
+
+class TLS_Ext_ALPN(TLS_Ext_PrettyPacketList):                       # RFC 7301
+    name = "TLS Extension - Application Layer Protocol Negotiation"
+    fields_desc = [ShortEnumField("type", 0x10, _tls_ext),
+                   ShortField("len", None),
+                   FieldLenField("protocolslen", None, length_of="protocols"),
+                   ProtocolListField("protocols", [], ProtocolName,
+                                     length_from=lambda pkt:pkt.protocolslen) ]
+
+
+class TLS_Ext_Padding(TLS_Ext_Unknown):                             # RFC 7685
+    name = "TLS Extension - Padding"
+    fields_desc = [ShortEnumField("type", 0x15, _tls_ext),
+                   FieldLenField("len", None, length_of="padding"),
+                   StrLenField("padding", "",
+                               length_from=lambda pkt: pkt.len) ]
+
+
+class TLS_Ext_EncryptThenMAC(TLS_Ext_Unknown):                      # RFC 7366
+    name = "TLS Extension - Encrypt-then-MAC"
+    fields_desc = [ShortEnumField("type", 0x16, _tls_ext),
+                   ShortField("len", None) ]
+
+
+class TLS_Ext_ExtendedMasterSecret(TLS_Ext_Unknown):                # RFC 7627
+    name = "TLS Extension - Extended Master Secret"
+    fields_desc = [ShortEnumField("type", 0x17, _tls_ext),
+                   ShortField("len", None) ]
+
+
+class TLS_Ext_SessionTicket(TLS_Ext_Unknown):                       # RFC 5077
+    """
+    RFC 5077 updates RFC 4507 according to most implementations, which do not
+    use another (useless) 'ticketlen' field after the global 'len' field.
+    """
+    name = "TLS Extension - Session Ticket"
+    fields_desc = [ShortEnumField("type", 0x23, _tls_ext),
+                   FieldLenField("len", None, length_of="ticket"),
+                   StrLenField("ticket", "",
+                               length_from=lambda pkt: pkt.len) ]
+
+
+class TLS_Ext_KeyShare(TLS_Ext_Unknown):
+    name = "TLS Extension - Key Share (dummy class)"
+    fields_desc = [ShortEnumField("type", 0x28, _tls_ext),
+                   ShortField("len", None) ]
+
+
+class TLS_Ext_PreSharedKey(TLS_Ext_Unknown):
+    name = "TLS Extension - Pre Shared Key (dummy class)"
+    fields_desc = [ShortEnumField("type", 0x29, _tls_ext),
+                   ShortField("len", None) ]
+
+
+class TLS_Ext_EarlyData(TLS_Ext_Unknown):
+    name = "TLS Extension - Early Data"
+    fields_desc = [ShortEnumField("type", 0x2a, _tls_ext),
+                   ShortField("len", None) ]
+
+
+class TLS_Ext_SupportedVersions(TLS_Ext_Unknown):
+    name = "TLS Extension - Supported Versions"
+    fields_desc = [ShortEnumField("type", 0x2b, _tls_ext),
+                   ShortField("len", None),
+                   FieldLenField("versionslen", None, fmt='B',
+                                 length_of="versions"),
+                   FieldListField("versions", [],
+                                  ShortEnumField("version", None,
+                                                 _tls_version),
+                                  length_from=lambda pkt: pkt.versionslen) ]
+
+
+class TLS_Ext_Cookie(TLS_Ext_Unknown):
+    name = "TLS Extension - Cookie"
+    fields_desc = [ShortEnumField("type", 0x2c, _tls_ext),
+                   ShortField("len", None),
+                   FieldLenField("cookielen", None, length_of="cookie"),
+                   XStrLenField("cookie", "",
+                                length_from=lambda pkt: pkt.cookielen) ]
+
+
+_tls_psk_kx_modes = { 0: "psk_ke", 1: "psk_dhe_ke" }
+
+class TLS_Ext_PSKKeyExchangeModes(TLS_Ext_Unknown):
+    name = "TLS Extension - PSK Key Exchange Modes"
+    fields_desc = [ShortEnumField("type", 0x2d, _tls_ext),
+                   ShortField("len", None),
+                   FieldLenField("kxmodeslen", None, fmt='B',
+                                 length_of="kxmodes"),
+                   FieldListField("kxmodes", [],
+                                  ByteEnumField("kxmode", None,
+                                                 _tls_psk_kx_modes),
+                                  length_from=lambda pkt: pkt.kxmodeslen) ]
+
+
+class TLS_Ext_TicketEarlyDataInfo(TLS_Ext_Unknown):
+    name = "TLS Extension - Ticket Early Data Info"
+    fields_desc = [ShortEnumField("type", 0x2e, _tls_ext),
+                   ShortField("len", None),
+                   IntField("max_early_data_size", 0) ]
+
+
+class TLS_Ext_NPN(TLS_Ext_PrettyPacketList):
+    """
+    Defined in RFC-draft-agl-tls-nextprotoneg-03. Deprecated in favour of ALPN.
+    """
+    name = "TLS Extension - Next Protocol Negotiation"
+    fields_desc = [ShortEnumField("type", 0x3374, _tls_ext),
+                   FieldLenField("len", None, length_of="protocols"),
+                   ProtocolListField("protocols", [], ProtocolName,
+                                     length_from=lambda pkt:pkt.len) ]
+
+
+class TLS_Ext_RenegotiationInfo(TLS_Ext_Unknown):                   # RFC 5746
+    name = "TLS Extension - Renegotiation Indication"
+    fields_desc = [ShortEnumField("type", 0xff01, _tls_ext),
+                   ShortField("len", None),
+                   FieldLenField("reneg_conn_len", None, fmt='B',
+                                 length_of="renegotiated_connection"),
+                   StrLenField("renegotiated_connection", "",
+                               length_from=lambda pkt: pkt.reneg_conn_len) ]
+
+
+_tls_ext_cls = { 0: TLS_Ext_ServerName,
+                 1: TLS_Ext_MaxFragLen,
+                 2: TLS_Ext_ClientCertURL,
+                 3: TLS_Ext_TrustedCAInd,
+                 4: TLS_Ext_TruncatedHMAC,
+                 5: TLS_Ext_CSR,
+                 6: TLS_Ext_UserMapping,
+                 7: TLS_Ext_ClientAuthz,
+                 8: TLS_Ext_ServerAuthz,
+                 9: _TLS_Ext_CertTypeDispatcher,
+               #10: TLS_Ext_SupportedEllipticCurves,
+                10: TLS_Ext_SupportedGroups,
+                11: TLS_Ext_SupportedPointFormat,
+                13: TLS_Ext_SignatureAlgorithms,
+                0x0f: TLS_Ext_Heartbeat,
+                0x10: TLS_Ext_ALPN,
+                0x15: TLS_Ext_Padding,
+                0x16: TLS_Ext_EncryptThenMAC,
+                0x17: TLS_Ext_ExtendedMasterSecret,
+                0x23: TLS_Ext_SessionTicket,
+                0x28: TLS_Ext_KeyShare,
+                0x29: TLS_Ext_PreSharedKey,
+                0x2a: TLS_Ext_EarlyData,
+                0x2b: TLS_Ext_SupportedVersions,
+                0x2c: TLS_Ext_Cookie,
+                0x2d: TLS_Ext_PSKKeyExchangeModes,
+                0x2e: TLS_Ext_TicketEarlyDataInfo,
+               #0x2f: TLS_Ext_CertificateAuthorities,       #XXX
+               #0x30: TLS_Ext_OIDFilters,                   #XXX
+                0x3374: TLS_Ext_NPN,
+                0xff01: TLS_Ext_RenegotiationInfo
+                }
+
+
+class _ExtensionsLenField(FieldLenField):
+    def getfield(self, pkt, s):
+        """
+        We try to compute a length, usually from a msglen parsed earlier.
+        If this length is 0, we consider 'selection_present' (from RFC 5246)
+        to be False. This means that there should not be any length field.
+        However, with TLS 1.3, zero lengths are always explicit.
+        """
+        ext = pkt.get_field(self.length_of)
+        l = ext.length_from(pkt)
+        if l is None or l <= 0:
+            v = pkt.tls_session.tls_version
+            if v < 0x0304:
+                return s, None
+        return super(_ExtensionsLenField, self).getfield(pkt, s)
+
+    def addfield(self, pkt, s, i):
+        """
+        There is a hack with the _ExtensionsField.i2len. It works only because
+        we expect _ExtensionsField.i2m to return a string of the same size (if
+        not of the same value) upon successive calls (e.g. through i2len here,
+        then i2m when directly building the _ExtensionsField).
+
+        XXX A proper way to do this would be to keep the extensions built from
+        the i2len call here, instead of rebuilding them later on.
+        """
+        if i is None:
+            if self.length_of is not None:
+                fld,fval = pkt.getfield_and_val(self.length_of)
+
+                tmp = pkt.tls_session.frozen
+                pkt.tls_session.frozen = True
+                f = fld.i2len(pkt, fval)
+                pkt.tls_session.frozen = tmp
+
+                i = self.adjust(pkt, f)
+                if i == 0: # for correct build if no ext and not explicitly 0
+                    return s
+        return s + struct.pack(self.fmt, i)
+
+class _ExtensionsField(StrLenField):
+    islist=1
+    holds_packets=1
+
+    def i2len(self, pkt, i):
+        if i is None:
+            return 0
+        return len(self.i2m(pkt, i))
+
+    def getfield(self, pkt, s):
+        l = self.length_from(pkt)
+        if l is None:
+            return s, []
+        return s[l:], self.m2i(pkt, s[:l])
+
+    def i2m(self, pkt, i):
+        if i is None:
+            return ""
+        if isinstance(pkt, _GenericTLSSessionInheritance):
+            if not pkt.tls_session.frozen:
+                s = ""
+                for ext in i:
+                    if isinstance(ext, _GenericTLSSessionInheritance):
+                        ext.tls_session = pkt.tls_session
+                        s += ext.str_stateful()
+                    else:
+                        s += str(ext)
+                return s
+        return "".join(map(str, i))
+
+    def m2i(self, pkt, m):
+        res = []
+        while m:
+            t = struct.unpack("!H", m[:2])[0]
+            l = struct.unpack("!H", m[2:4])[0]
+            cls = _tls_ext_cls.get(t, TLS_Ext_Unknown)
+            if cls is TLS_Ext_KeyShare:
+                from scapy.layers.tls.keyexchange_tls13 import _tls_ext_keyshare_cls
+                cls = _tls_ext_keyshare_cls.get(pkt.msgtype, TLS_Ext_Unknown)
+            elif cls is TLS_Ext_PreSharedKey:
+                from scapy.layers.tls.keyexchange_tls13 import _tls_ext_presharedkey_cls
+                cls = _tls_ext_presharedkey_cls.get(pkt.msgtype, TLS_Ext_Unknown)
+            res.append(cls(m[:l+4], tls_session=pkt.tls_session))
+            m = m[l+4:]
+        return res
+
+
diff --git a/scapy/layers/tls/handshake.py b/scapy/layers/tls/handshake.py
index cb872515589425df17d5f44567fe826f9901c43c..8e6120411c8b153d39ee0b82f5ebf8f6944efe71 100644
--- a/scapy/layers/tls/handshake.py
+++ b/scapy/layers/tls/handshake.py
@@ -1,6 +1,6 @@
 ## This file is part of Scapy
 ## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
-##                     2015, 2016 Maxence Tury
+##               2015, 2016, 2017 Maxence Tury
 ## This program is published under a GPLv2 license
 
 """
@@ -10,34 +10,35 @@ This module covers the handshake TLS subprotocol, except for the key exchange
 mechanisms which are addressed with keyexchange.py.
 """
 
-from __future__ import absolute_import
-from __future__ import print_function
+from __future__ import absolute_import, print_function
 import math
 
 from scapy.error import warning
 from scapy.fields import *
 from scapy.packet import Packet, Raw, Padding
 from scapy.utils import repr_hex
-from scapy.layers.x509 import X509_Extensions, OCSP_Response
+from scapy.layers.x509 import OCSP_Response
 from scapy.layers.tls.cert import Cert, PrivKey, PubKey
-from scapy.layers.tls.basefields import _tls_version, _TLSVersionField
-from scapy.layers.tls.keyexchange import (_tls_named_curves, _tls_hash_sig,
-                                          _TLSSignature, _TLSServerParamsField,
+from scapy.layers.tls.basefields import (_tls_version, _TLSVersionField,
+                                         _TLSClientVersionField)
+from scapy.layers.tls.extensions import (_ExtensionsLenField, _ExtensionsField,
+                                         _cert_status_type, TLS_Ext_SupportedVersions)
+from scapy.layers.tls.keyexchange import (_TLSSignature, _TLSServerParamsField,
                                           _TLSSignatureField, ServerRSAParams,
-                                          SigAndHashAlgsField,
+                                          SigAndHashAlgsField, _tls_hash_sig,
                                           SigAndHashAlgsLenField)
+from scapy.layers.tls.keyexchange_tls13 import TicketField
 from scapy.layers.tls.session import (_GenericTLSSessionInheritance,
-                                      writeConnState,
-                                      readConnState)
+                                      readConnState, writeConnState)
 from scapy.layers.tls.crypto.compression import (_tls_compression_algs,
                                                  _tls_compression_algs_cls,
-                                                 _GenericComp,
+                                                 Comp_NULL, _GenericComp,
                                                  _GenericCompMetaclass)
 from scapy.layers.tls.crypto.suites import (_tls_cipher_suites,
                                             _tls_cipher_suites_cls,
                                             _GenericCipherSuite,
                                             _GenericCipherSuiteMetaclass,
-                                            TLS_DHE_RSA_WITH_AES_128_CBC_SHA)
+                                            TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256)
 
 
 ###############################################################################
@@ -46,12 +47,13 @@ from scapy.layers.tls.crypto.suites import (_tls_cipher_suites,
 
 _tls_handshake_type = { 0: "hello_request",         1: "client_hello",
                         2: "server_hello",          3: "hello_verify_request",
-                        4: "session_ticket",        11: "certificate",
+                        4: "session_ticket",        6: "hello_retry_request",
+                        8: "encrypted_extensions",  11: "certificate",
                         12: "server_key_exchange",  13: "certificate_request",
                         14: "server_hello_done",    15: "certificate_verify",
                         16: "client_key_exchange",  20: "finished",
                         21: "certificate_url",      22: "certificate_status",
-                        23: "supplemental_data" }
+                        23: "supplemental_data",    24: "key_update" }
 
 
 class _TLSHandshake(_GenericTLSSessionInheritance):
@@ -161,7 +163,7 @@ class _CipherSuitesField(StrLenField):
     def i2repr(self, pkt, x):
         if x is None:
             return "None"
-        l = [self.i2repr_one(pkt, z) for x in x]
+        l = [self.i2repr_one(pkt, z) for z in x]
         if len(l) == 1:
             l = l[0]
         else:
@@ -196,542 +198,6 @@ class _CompressionMethodsField(_CipherSuitesField):
         return x
 
 
-###############################################################################
-### ClientHello/ServerHello extensions                                      ###
-###############################################################################
-
-# We provide these extensions mostly for packet manipulation purposes.
-# For now, most of them are not considered by our automaton.
-
-_tls_ext = {  0: "server_name",             # RFC 4366
-              1: "max_fragment_length",     # RFC 4366
-              2: "client_certificate_url",  # RFC 4366
-              3: "trusted_ca_keys",         # RFC 4366
-              4: "truncated_hmac",          # RFC 4366
-              5: "status_request",          # RFC 4366
-              6: "user_mapping",            # RFC 4681
-              7: "client_authz",            # RFC 5878
-              8: "server_authz",            # RFC 5878
-              9: "cert_type",               # RFC 6091
-             10: "elliptic_curves",         # RFC 4492
-             11: "ec_point_formats",        # RFC 4492
-             13: "signature_algorithms",    # RFC 5246
-             0x0f: "heartbeat",             # RFC 6520
-             0x10: "alpn",                  # RFC 7301
-             0x15: "padding",               # RFC 7685
-             0x23: "session_ticket",        # RFC 5077
-             0x3374: "next_protocol_negotiation",
-                                            # RFC-draft-agl-tls-nextprotoneg-03
-             0xff01: "renegotiation_info"   # RFC 5746
-             }
-
-
-class TLS_Ext_Unknown(_GenericTLSSessionInheritance):
-    name = "TLS Extension - Scapy Unknown"
-    fields_desc = [ShortEnumField("type", None, _tls_ext),
-                   FieldLenField("len", None, fmt="!H", length_of="val"),
-                   StrLenField("val", "",
-                               length_from=lambda pkt: pkt.len) ]
-
-    def post_build(self, p, pay):
-        if self.len is None:
-            l = len(p) - 4
-            p = p[:2] + struct.pack("!H", l) + p[4:]
-        return p+pay
-
-class TLS_Ext_PrettyPacketList(TLS_Ext_Unknown):
-    """
-    Dummy extension used for server_name/ALPN/NPN for a lighter representation:
-    the final field is showed as a 1-line list rather than as lots of packets.
-    XXX Define a new condition for packet lists in Packet._show_or_dump?
-    """
-    def _show_or_dump(self, dump=False, indent=3,
-                      lvl="", label_lvl="", first_call=True):
-        """ Reproduced from packet.py """
-        ct = AnsiColorTheme() if dump else conf.color_theme
-        s = "%s%s %s %s \n" % (label_lvl, ct.punct("###["),
-                               ct.layer_name(self.name), ct.punct("]###"))
-        for f in self.fields_desc[:-1]:
-            ncol = ct.field_name
-            vcol = ct.field_value
-            fvalue = self.getfieldval(f.name)
-            begn = "%s  %-10s%s " % (label_lvl+lvl, ncol(f.name),
-                                     ct.punct("="),)
-            reprval = f.i2repr(self,fvalue)
-            if isinstance(reprval, str):
-                reprval = reprval.replace("\n", "\n"+" "*(len(label_lvl)
-                                                          +len(lvl)
-                                                          +len(f.name)
-                                                          +4))
-            s += "%s%s\n" % (begn,vcol(reprval))
-        f = self.fields_desc[-1]
-        ncol = ct.field_name
-        vcol = ct.field_value
-        fvalue = self.getfieldval(f.name)
-        begn = "%s  %-10s%s " % (label_lvl+lvl, ncol(f.name), ct.punct("="),)
-        reprval = f.i2repr(self,fvalue)
-        if isinstance(reprval, str):
-            reprval = reprval.replace("\n", "\n"+" "*(len(label_lvl)
-                                                      +len(lvl)
-                                                      +len(f.name)
-                                                      +4))
-        s += "%s%s\n" % (begn,vcol(reprval))
-        if self.payload:
-            s += self.payload._show_or_dump(dump=dump, indent=indent,
-                                lvl=lvl+(" "*indent*self.show_indent),
-                                label_lvl=label_lvl, first_call=False)
-
-        if first_call and not dump:
-            print(s)
-        else:
-            return s
-
-
-_tls_server_name_types = { 0: "host_name" }
-
-class ServerName(Packet):
-    name = "HostName"
-    fields_desc = [ ByteEnumField("nametype", 0, _tls_server_name_types),
-                    FieldLenField("namelen", None, length_of="servername"),
-                    StrLenField("servername", "",
-                                length_from=lambda pkt: pkt.namelen) ]
-    def guess_payload_class(self, p):
-        return Padding
-
-class ServerListField(PacketListField):
-    def i2repr(self, pkt, x):
-        res = [p.servername for p in x]
-        return "[%s]" % ", ".join(res)
-
-class TLS_Ext_ServerName(TLS_Ext_PrettyPacketList):                 # RFC 4366
-    name = "TLS Extension - Server Name"
-    fields_desc = [ShortEnumField("type", 0, _tls_ext),
-                   FieldLenField("len", None, length_of="servernames",
-                                 adjust=lambda pkt,x: x+2),
-                   FieldLenField("servernameslen", None,
-                                 length_of="servernames"),
-                   ServerListField("servernames", [], ServerName,
-                                   length_from=lambda pkt: pkt.servernameslen)]
-
-
-class TLS_Ext_MaxFragLen(TLS_Ext_Unknown):                          # RFC 4366
-    name = "TLS Extension - Server Name"
-    fields_desc = [ShortEnumField("type", 1, _tls_ext),
-                   ShortField("len", None),
-                   ByteEnumField("maxfraglen", 4, { 1: "2^9",
-                                                    2: "2^10",
-                                                    3: "2^11",
-                                                    4: "2^12" }) ]
-
-
-class TLS_Ext_ClientCertURL(TLS_Ext_Unknown):                       # RFC 4366
-    name = "TLS Extension - Server Name"
-    fields_desc = [ShortEnumField("type", 2, _tls_ext),
-                   ShortField("len", None) ]
-
-
-_tls_trusted_authority_types = {0: "pre_agreed",
-                                1: "key_sha1_hash",
-                                2: "x509_name",
-                                3: "cert_sha1_hash" }
-
-class TAPreAgreed(Packet):
-    name = "Trusted authority - pre_agreed"
-    fields_desc = [ ByteEnumField("idtype", 0, _tls_trusted_authority_types) ]
-    def guess_payload_class(self, p):
-        return Padding
-
-class TAKeySHA1Hash(Packet):
-    name = "Trusted authority - key_sha1_hash"
-    fields_desc = [ ByteEnumField("idtype", 1, _tls_trusted_authority_types),
-                    StrFixedLenField("id", None, 20) ]
-    def guess_payload_class(self, p):
-        return Padding
-
-class TAX509Name(Packet):
-    """
-    XXX Section 3.4 of RFC 4366. Implement a more specific DNField
-    rather than current StrLenField.
-    """
-    name = "Trusted authority - x509_name"
-    fields_desc = [ ByteEnumField("idtype", 2, _tls_trusted_authority_types),
-                    FieldLenField("dnlen", None, length_of="dn"),
-                    StrLenField("dn", "", length_from=lambda pkt: pkt.dnlen) ]
-    def guess_payload_class(self, p):
-        return Padding
-
-class TACertSHA1Hash(Packet):
-    name = "Trusted authority - cert_sha1_hash"
-    fields_desc = [ ByteEnumField("idtype", 3, _tls_trusted_authority_types),
-                    StrFixedLenField("id", None, 20) ]
-    def guess_payload_class(self, p):
-        return Padding
-
-_tls_trusted_authority_cls = {0: TAPreAgreed,
-                              1: TAKeySHA1Hash,
-                              2: TAX509Name,
-                              3: TACertSHA1Hash }
-
-class _TAListField(PacketListField):
-    """
-    Specific version that selects the right Trusted Authority (previous TA*)
-    class to be used for dissection based on idtype.
-    """
-    def m2i(self, pkt, m):
-        idtype = ord(m[0])
-        cls = self.cls
-        if idtype in _tls_trusted_authority_cls:
-            cls = _tls_trusted_authority_cls[idtype]
-        return cls(m)
-
-class TLS_Ext_TrustedCAInd(TLS_Ext_Unknown):                        # RFC 4366
-    name = "TLS Extension - Trusted CA Indication"
-    fields_desc = [ShortEnumField("type", 3, _tls_ext),
-                   ShortField("len", None),
-                   FieldLenField("talen", None, length_of="ta"),
-                   _TAListField("ta", [], Raw,
-                                length_from=lambda pkt: pkt.talen) ]
-
-
-class TLS_Ext_TruncatedHMAC(TLS_Ext_Unknown):                       # RFC 4366
-    name = "TLS Extension - Truncated HMAC"
-    fields_desc = [ShortEnumField("type", 4, _tls_ext),
-                   ShortField("len", None) ]
-
-
-class ResponderID(Packet):
-    name = "Responder ID structure"
-    fields_desc = [ FieldLenField("respidlen", None, length_of="respid"),
-                    StrLenField("respid", "",
-                                length_from=lambda pkt: pkt.respidlen)]
-    def guess_payload_class(self, p):
-        return Padding
-
-class OCSPStatusRequest(Packet):
-    """
-    This is the structure defined in RFC 6066, not in RFC 6960!
-    """
-    name = "OCSPStatusRequest structure"
-    fields_desc = [ FieldLenField("respidlen", None, length_of="respid"),
-                    PacketListField("respid", [], ResponderID,
-                                    length_from=lambda pkt: pkt.respidlen),
-                    FieldLenField("reqextlen", None, length_of="reqext"),
-                    PacketField("reqext", "", X509_Extensions) ]
-    def guess_payload_class(self, p):
-        return Padding
-
-_cert_status_type = { 1: "ocsp" }
-_cert_status_req_cls  = { 1: OCSPStatusRequest }
-
-class _StatusReqField(PacketListField):
-    def m2i(self, pkt, m):
-        idtype = pkt.stype
-        cls = self.cls
-        if idtype in _cert_status_req_cls:
-            cls = _cert_status_req_cls[idtype]
-        return cls(m)
-
-class TLS_Ext_CSR(TLS_Ext_Unknown):                                 # RFC 4366
-    name = "TLS Extension - Certificate Status Request"
-    fields_desc = [ShortEnumField("type", 5, _tls_ext),
-                   ShortField("len", None),
-                   ByteEnumField("stype", None, _cert_status_type),
-                   _StatusReqField("req", [], Raw,
-                                  length_from=lambda pkt: pkt.len - 1) ]
-
-
-class TLS_Ext_UserMapping(TLS_Ext_Unknown):                         # RFC 4681
-    name = "TLS Extension - User Mapping"
-    fields_desc = [ShortEnumField("type", 6, _tls_ext),
-                   ShortField("len", None),
-                   FieldLenField("umlen", None, fmt="B", length_of="um"),
-                   FieldListField("um", [],
-                                  ByteField("umtype", 0),
-                                  length_from=lambda pkt: pkt.umlen) ]
-
-
-class TLS_Ext_ClientAuthz(TLS_Ext_Unknown):                         # RFC 5878
-    """ XXX Unsupported """
-    name = "TLS Extension - Client Authz"
-    fields_desc = [ShortEnumField("type", 7, _tls_ext),
-                   ShortField("len", None),
-                   ]
-
-class TLS_Ext_ServerAuthz(TLS_Ext_Unknown):                         # RFC 5878
-    """ XXX Unsupported """
-    name = "TLS Extension - Server Authz"
-    fields_desc = [ShortEnumField("type", 8, _tls_ext),
-                   ShortField("len", None),
-                   ]
-
-
-_tls_cert_types = { 0: "X.509", 1: "OpenPGP" }
-
-class TLS_Ext_ClientCertType(TLS_Ext_Unknown):                      # RFC 5081
-    name = "TLS Extension - Certificate Type (client version)"
-    fields_desc = [ShortEnumField("type", 9, _tls_ext),
-                   ShortField("len", None),
-                   FieldLenField("ctypeslen", None, length_of="ctypes"),
-                   FieldListField("ctypes", [0, 1],
-                                  ByteEnumField("certtypes", None,
-                                                _tls_cert_types),
-                                  length_from=lambda pkt: pkt.ctypeslen) ]
-
-class TLS_Ext_ServerCertType(TLS_Ext_Unknown):                      # RFC 5081
-    name = "TLS Extension - Certificate Type (server version)"
-    fields_desc = [ShortEnumField("type", 9, _tls_ext),
-                   ShortField("len", None),
-                   ByteEnumField("ctype", None, _tls_cert_types) ]
-
-def _TLS_Ext_CertTypeDispatcher(m, *args, **kargs):
-    """
-    We need to select the correct one on dissection. We use the length for
-    that, as 1 for client version would emply an empty list.
-    """
-    l = struct.unpack("!H", m[2:4])[0]
-    if l == 1:
-        cls = TLS_Ext_ServerCertType
-    else:
-        cls = TLS_Ext_ClientCertType
-    return cls(m, *args, **kargs)
-
-
-class TLS_Ext_SupportedEllipticCurves(TLS_Ext_Unknown):             # RFC 4492
-    name = "TLS Extension - Supported Elliptic Curves"
-    fields_desc = [ShortEnumField("type", 10, _tls_ext),
-                   ShortField("len", None),
-                   FieldLenField("ecllen", None, length_of="ecl"),
-                   FieldListField("ecl", [],
-                                    ShortEnumField("nc", None,
-                                                   _tls_named_curves),
-                                    length_from=lambda pkt: pkt.ecllen) ]
-
-
-_tls_ecpoint_format = { 0: "uncompressed",
-                        1: "ansiX962_compressed_prime",
-                        2: "ansiX962_compressed_char2" }
-
-class TLS_Ext_SupportedPointFormat(TLS_Ext_Unknown):                # RFC 4492
-    name = "TLS Extension - Supported Point Format"
-    fields_desc = [ShortEnumField("type", 11, _tls_ext),
-                   ShortField("len", None),
-                   FieldLenField("ecpllen", None, fmt="B", length_of="ecpl"),
-                   FieldListField("ecpl", [0],
-                                    ByteEnumField("nc", None,
-                                                  _tls_ecpoint_format),
-                                    length_from=lambda pkt: pkt.ecpllen) ]
-
-
-class TLS_Ext_SignatureAlgorithms(TLS_Ext_Unknown):                 # RFC 5246
-    name = "TLS Extension - Signature Algorithms"
-    fields_desc = [ShortEnumField("type", 13, _tls_ext),
-                   ShortField("len", None),
-                   SigAndHashAlgsLenField("sig_algs_len", None,
-                                          length_of="sig_algs"),
-                   SigAndHashAlgsField("sig_algs", [],
-                                       EnumField("hash_sig", None,
-                                                    _tls_hash_sig),
-                                       length_from=
-                                           lambda pkt: pkt.sig_algs_len) ]
-
-
-class TLS_Ext_Heartbeat(TLS_Ext_Unknown):                           # RFC 6520
-    name = "TLS Extension - Heartbeat"
-    fields_desc = [ShortEnumField("type", 0x0f, _tls_ext),
-                   ShortField("len", None),
-                   ByteEnumField("heartbeat_mode", 2,
-                       { 1: "peer_allowed_to_send",
-                         2: "peer_not_allowed_to_send" }) ]
-
-
-class ProtocolName(Packet):
-    name = "Protocol Name"
-    fields_desc = [ FieldLenField("len", None, fmt='B', length_of="protocol"),
-                    StrLenField("protocol", "",
-                                length_from=lambda pkt: pkt.len)]
-    def guess_payload_class(self, p):
-        return Padding
-
-class ProtocolListField(PacketListField):
-    def i2repr(self, pkt, x):
-        res = [p.protocol for p in x]
-        return "[%s]" % ", ".join(res)
-
-class TLS_Ext_ALPN(TLS_Ext_PrettyPacketList):                       # RFC 7301
-    name = "TLS Extension - Application Layer Protocol Negotiation"
-    fields_desc = [ShortEnumField("type", 0x10, _tls_ext),
-                   ShortField("len", None),
-                   FieldLenField("protocolslen", None, length_of="protocols"),
-                   ProtocolListField("protocols", [], ProtocolName,
-                                     length_from=lambda pkt:pkt.protocolslen) ]
-
-
-class TLS_Ext_Padding(TLS_Ext_Unknown):                             # RFC 7685
-    name = "TLS Extension - Padding"
-    fields_desc = [ShortEnumField("type", 0x15, _tls_ext),
-                   FieldLenField("len", None, length_of="padding"),
-                   StrLenField("padding", "",
-                               length_from=lambda pkt: pkt.len) ]
-
-
-class TLS_Ext_SessionTicket(TLS_Ext_Unknown):                       # RFC 5077
-    """
-    RFC 5077 updates RFC 4507 according to most implementations, which do not
-    use another (useless) 'ticketlen' field after the global 'len' field.
-    """
-    name = "TLS Extension - Session Ticket"
-    fields_desc = [ShortEnumField("type", 0x23, _tls_ext),
-                   FieldLenField("len", None, length_of="ticket"),
-                   StrLenField("ticket", "",
-                               length_from=lambda pkt: pkt.len) ]
-
-
-class TLS_Ext_NPN(TLS_Ext_PrettyPacketList):
-    """
-    Defined in RFC-draft-agl-tls-nextprotoneg-03. Deprecated in favour of ALPN.
-    """
-    name = "TLS Extension - Next Protocol Negotiation"
-    fields_desc = [ShortEnumField("type", 0x3374, _tls_ext),
-                   FieldLenField("len", None, length_of="protocols"),
-                   ProtocolListField("protocols", [], ProtocolName,
-                                     length_from=lambda pkt:pkt.len) ]
-
-
-class TLS_Ext_RenegotiationInfo(TLS_Ext_Unknown):                   # RFC 5746
-    name = "TLS Extension - Renegotiation Indication"
-    fields_desc = [ShortEnumField("type", 0xff01, _tls_ext),
-                   ShortField("len", None),
-                   FieldLenField("reneg_conn_len", None, fmt='B',
-                                 length_of="renegotiated_connection"),
-                   StrLenField("renegotiated_connection", "",
-                               length_from=lambda pkt: pkt.reneg_conn_len) ]
-
-
-_tls_ext_cls = { 0: TLS_Ext_ServerName,
-                 1: TLS_Ext_MaxFragLen,
-                 2: TLS_Ext_ClientCertURL,
-                 3: TLS_Ext_TrustedCAInd,
-                 4: TLS_Ext_TruncatedHMAC,
-                 5: TLS_Ext_CSR,
-                 6: TLS_Ext_UserMapping,
-                 7: TLS_Ext_ClientAuthz,
-                 8: TLS_Ext_ServerAuthz,
-                 9: _TLS_Ext_CertTypeDispatcher,
-                10: TLS_Ext_SupportedEllipticCurves,
-                11: TLS_Ext_SupportedPointFormat,
-                13: TLS_Ext_SignatureAlgorithms,
-                0x0f: TLS_Ext_Heartbeat,
-                0x10: TLS_Ext_ALPN,
-                0x15: TLS_Ext_Padding,
-                0x23: TLS_Ext_SessionTicket,
-                0x3374: TLS_Ext_NPN,
-                0xff01: TLS_Ext_RenegotiationInfo
-                }
-
-
-class _ExtensionsLenField(FieldLenField):
-    """
-    This field provides the first half of extensions support implementation
-    as defined in RFC 3546. The second is provided by _ExtensionsField. Both
-    are used as the last fields at the end of ClientHello messages.
-
-    The idea is quite simple:
-    - dissection : the _ExtensionsLenField will compute the remaining length of
-    the message based on the value of a provided field (for instance 'msglen'
-    in ClientHello) and a list of other fields that are considered "shifters".
-    This shifters are length fields of some vectors. The sum of their value
-    will be substracted to the one of the main field. If the result is
-    positive, this means that extensions are present and the
-    _ExtensionsLenField behaves just like a normal FieldLenField. If the value
-    is null, invalid or not sufficient to grab a length, the getfield method of
-    the field will simply return a 0 value without "eating" bytes from current
-    string. In a sense, the field is always present (which means that its value
-    is available for the _ExtensionsField field) but the behavior during
-    dissection is conditional. Then, the _ExtensionsField uses the length value
-    from the _ExtensionsLenField, to know how much data it should grab (TLS
-    extension is basically a vector). If no extensions are present, the length
-    field will have a null value and nothing will be grabbed.
-
-    - build: during build, if some extensions are provided, the
-    _ExtensionsLenField will automatically access the whole length and use it
-    if the user does not provide a specific value. Now, if no extensions are
-    available and the user does not provide a specific value, nothing is added
-    during the build, i.e. no length field with a null value will appear. As a
-    side note, this is also the case for the rebuild of a dissected packet: if
-    the initial packet had a length field with a null value, one will be built.
-    If no length field was was present, nothing is added, i.e. a rebuilt
-    dissected packet will look like the original. Another side note is that the
-    shifters allow us to decide if there is an extension vector but the length
-    of that vector is grabbed from the value of the 2 first bytes, not from the
-    value computed from shifters and msglen.
-    """
-    __slots__ = ["lfld", "shifters"]
-    def __init__(self, name, default,
-                 lfld, shifters=[],
-                 fmt="!H", length_of=None):
-        FieldLenField.__init__(self, name, default,
-                               fmt=fmt, length_of=length_of)
-        self.lfld = lfld
-        self.shifters = shifters
-
-    def getfield(self, pkt, s):
-        # compute the length of remaining data to see if there are ext
-        l = getattr(pkt, self.lfld)
-        for fname in self.shifters:
-            if isinstance(fname, int):
-                l -= fname
-            else:
-                l -= getattr(pkt, fname)
-
-        if l is None or l <= 0 or l < self.sz:
-            return s, None  # let's consider there's no extensions
-
-        return Field.getfield(self, pkt, s)
-
-    def addfield(self, pkt, s, i):
-        if i is None:
-            if self.length_of is not None:
-                fld,fval = pkt.getfield_and_val(self.length_of)
-                f = fld.i2len(pkt, fval)
-                i = self.adjust(pkt, f)
-                if i == 0: # for correct build if no ext and not explicitly 0
-                    return s
-        return s + struct.pack(self.fmt, i)
-
-class _ExtensionsField(StrLenField):
-    """
-    See ExtensionsLenField documentation.
-    """
-    islist=1
-    holds_packets=1
-
-    def i2len(self, pkt, i):
-        if i is None:
-            return 0
-        return len(self.i2m(pkt, i))
-
-    def getfield(self, pkt, s):
-        l = self.length_from(pkt)
-        if l is None:
-            return s, []
-        return s[l:], self.m2i(pkt, s[:l])
-
-    def i2m(self, pkt, i):
-        if i is None:
-            return ""
-        return "".join(map(str, i))
-
-    def m2i(self, pkt, m):
-        res = []
-        while m:
-            t = struct.unpack("!H", m[:2])[0]
-            l = struct.unpack("!H", m[2:4])[0]
-            cls = _tls_ext_cls.get(t, TLS_Ext_Unknown)
-            res.append(cls(m[:l+4], tls_session=pkt.tls_session))
-            m = m[l+4:]
-        return res
-
-
 ###############################################################################
 ### ClientHello                                                             ###
 ###############################################################################
@@ -749,7 +215,7 @@ class TLSClientHello(_TLSHandshake):
     name = "TLS Handshake - Client Hello"
     fields_desc = [ ByteEnumField("msgtype", 1, _tls_handshake_type),
                     ThreeBytesField("msglen", None),
-                    _TLSVersionField("version", 0x0303, _tls_version),
+                    _TLSClientVersionField("version", None, _tls_version),
 
                     #_TLSRandomBytesField("random_bytes", None, 32),
                     _GMTUnixTimeField("gmt_unix_time", None),
@@ -762,7 +228,7 @@ class TLSClientHello(_TLSHandshake):
                     FieldLenField("cipherslen", None, fmt="!H",
                                   length_of="ciphers"),
                     _CipherSuitesField("ciphers",
-                                       [TLS_DHE_RSA_WITH_AES_128_CBC_SHA],
+                                       [TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256],
                                        _tls_cipher_suites, itemfmt="!H",
                                        length_from=lambda pkt: pkt.cipherslen),
 
@@ -773,12 +239,13 @@ class TLSClientHello(_TLSHandshake):
                                              length_from=
                                                  lambda pkt: pkt.complen),
 
-                    _ExtensionsLenField("extlen", None, "msglen",
-                                       shifters = ["sidlen", "cipherslen",
-                                                   "complen", 38],
-                                       length_of="ext"),
+                    _ExtensionsLenField("extlen", None, length_of="ext"),
                     _ExtensionsField("ext", None,
-                                     length_from=lambda pkt: pkt.extlen) ]
+                                     length_from=lambda pkt: (pkt.msglen -
+                                                              (pkt.sidlen or 0) -
+                                                              (pkt.cipherslen or 0) -
+                                                              (pkt.complen or 0) -
+                                                              40)) ]
 
     def post_build(self, p, pay):
         if self.random_bytes is None:
@@ -790,14 +257,20 @@ class TLSClientHello(_TLSHandshake):
         Either for parsing or building, we store the client_random
         along with the raw string representing this handshake message.
         """
+        super(TLSClientHello, self).tls_session_update(msg_str)
+
         self.tls_session.advertised_tls_version = self.version
         self.random_bytes = msg_str[10:38]
         self.tls_session.client_random = (struct.pack('!I',
                                                       self.gmt_unix_time) +
                                           self.random_bytes)
-        self.tls_session.handshake_messages.append(msg_str)
-        self.tls_session.handshake_messages_parsed.append(self)
-
+        if self.ext:
+            for e in self.ext:
+                if isinstance(e, TLS_Ext_SupportedVersions):
+                    if self.tls_session.tls13_early_secret is None:
+                        # this is not recomputed if there was a TLS 1.3 HRR
+                        self.tls_session.compute_tls13_early_secrets()
+                    break
 
 ###############################################################################
 ### ServerHello                                                             ###
@@ -832,11 +305,20 @@ class TLSServerHello(TLSClientHello):
                                              itemfmt="B",
                                              length_from=lambda pkt: 1),
 
-                    _ExtensionsLenField("extlen", None, "msglen",
-                                        shifters = ["sidlen", 38],
-                                        length_of="ext"),
-                    _ExtensionsField("ext", [],
-                                     length_from=lambda pkt: pkt.extlen) ]
+                    _ExtensionsLenField("extlen", None, length_of="ext"),
+                    _ExtensionsField("ext", None,
+                                     length_from=lambda pkt: (pkt.msglen -
+                                                              (pkt.sidlen or 0) -
+                                                              38)) ]
+                                                              #40)) ]
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        if _pkt and len(_pkt) >= 6:
+            version = struct.unpack("!H", _pkt[4:6])[0]
+            if version == 0x0304 or version > 0x7f00:
+                return TLS13ServerHello
+        return TLSServerHello
 
     def tls_session_update(self, msg_str):
         """
@@ -848,16 +330,16 @@ class TLSServerHello(TLSClientHello):
         negotiation when we learn the session keys, and eventually they
         are committed once a ChangeCipherSpec has been sent/received.
         """
+        super(TLSClientHello, self).tls_session_update(msg_str)
+
         self.tls_session.tls_version = self.version
         self.random_bytes = msg_str[10:38]
         self.tls_session.server_random = (struct.pack('!I',
                                                       self.gmt_unix_time) +
                                           self.random_bytes)
-        self.tls_session.handshake_messages.append(msg_str)
-        self.tls_session.handshake_messages_parsed.append(self)
-
         self.tls_session.sid = self.sid
 
+        cs_cls = None
         if self.cipher:
             cs_val = self.cipher
             if cs_val not in _tls_cipher_suites_cls:
@@ -866,6 +348,7 @@ class TLSServerHello(TLSClientHello):
             else:
                 cs_cls = _tls_cipher_suites_cls[cs_val]
 
+        comp_cls = Comp_NULL
         if self.comp:
             comp_val = self.comp[0]
             if comp_val not in _tls_compression_algs_cls:
@@ -885,10 +368,91 @@ class TLSServerHello(TLSClientHello):
                                               tls_version=self.version)
 
 
+class TLS13ServerHello(TLSClientHello):
+    """ TLS 1.3 ServerHello """
+    name = "TLS 1.3 Handshake - Server Hello"
+    fields_desc = [ ByteEnumField("msgtype", 2, _tls_handshake_type),
+                    ThreeBytesField("msglen", None),
+                    _TLSVersionField("version", None, _tls_version),
+                    _TLSRandomBytesField("random_bytes", None, 32),
+                    EnumField("cipher", None, _tls_cipher_suites),
+                    _ExtensionsLenField("extlen", None, length_of="ext"),
+                    _ExtensionsField("ext", None,
+                                     length_from=lambda pkt: (pkt.msglen -
+                                                              38)) ]
+
+    def tls_session_update(self, msg_str):
+        """
+        Either for parsing or building, we store the server_random along with
+        the raw string representing this handshake message. We also store the
+        cipher suite (if recognized), and finally we instantiate the write and
+        read connection states.
+        """
+        super(TLSClientHello, self).tls_session_update(msg_str)
+
+        s = self.tls_session
+        s.tls_version = self.version
+        s.server_random = self.random_bytes
+
+        cs_cls = None
+        if self.cipher:
+            cs_val = self.cipher
+            if cs_val not in _tls_cipher_suites_cls:
+                warning("Unknown cipher suite %d from ServerHello" % cs_val)
+                # we do not try to set a default nor stop the execution
+            else:
+                cs_cls = _tls_cipher_suites_cls[cs_val]
+
+        connection_end = s.connection_end
+        s.pwcs = writeConnState(ciphersuite=cs_cls,
+                                connection_end=connection_end,
+                                tls_version=self.version)
+        s.triggered_pwcs_commit = True
+        s.prcs = readConnState(ciphersuite=cs_cls,
+                               connection_end=connection_end,
+                               tls_version=self.version)
+        s.triggered_prcs_commit = True
+
+        if self.tls_session.tls13_early_secret is None:
+            # In case the connState was not pre-initialized, we could not
+            # compute the early secrets at the ClientHello, so we do it here.
+            self.tls_session.compute_tls13_early_secrets()
+        s.compute_tls13_handshake_secrets()
+
+
+###############################################################################
+### HelloRetryRequest                                                       ###
+###############################################################################
+
+class TLSHelloRetryRequest(_TLSHandshake):
+    name = "TLS 1.3 Handshake - Hello Retry Request"
+    fields_desc = [ ByteEnumField("msgtype", 6, _tls_handshake_type),
+                    ThreeBytesField("msglen", None),
+                    _TLSVersionField("version", None, _tls_version),
+                    _ExtensionsLenField("extlen", None, length_of="ext"),
+                    _ExtensionsField("ext", None,
+                                     length_from=lambda pkt: pkt.msglen - 4) ]
+
+
+###############################################################################
+### EncryptedExtensions                                                     ###
+###############################################################################
+
+class TLSEncryptedExtensions(_TLSHandshake):
+    name = "TLS 1.3 Handshake - Encrypted Extensions"
+    fields_desc = [ ByteEnumField("msgtype", 8, _tls_handshake_type),
+                    ThreeBytesField("msglen", None),
+                    _ExtensionsLenField("extlen", None, length_of="ext"),
+                    _ExtensionsField("ext", None,
+                                     length_from=lambda pkt: pkt.msglen - 2) ]
+
+
 ###############################################################################
 ### Certificate                                                             ###
 ###############################################################################
 
+#XXX It might be appropriate to rewrite this mess with basic 3-byte FieldLenField.
+
 class _ASN1CertLenField(FieldLenField):
     """
     This is mostly a 3-byte FieldLenField.
@@ -923,7 +487,7 @@ class _ASN1CertListField(StrLenField):
     def getfield(self, pkt, s):
         """
         Extract Certs in a loop.
-        XXX We should providesafeguards when trying to parse a Cert.
+        XXX We should provide safeguards when trying to parse a Cert.
         """
         l = None
         if self.length_from is not None:
@@ -965,6 +529,46 @@ class _ASN1CertListField(StrLenField):
     def any2i(self, pkt, x):
         return x
 
+class _ASN1CertField(StrLenField):
+    def i2len(self, pkt, i):
+        if i is None:
+            return 0
+        return len(self.i2m(pkt, i))
+
+    def getfield(self, pkt, s):
+        l = None
+        if self.length_from is not None:
+            l = self.length_from(pkt)
+        ret = ""
+        m = s
+        if l is not None:
+            m, ret = s[:l], s[l:]
+        clen = struct.unpack("!I", '\x00' + m[:3])[0]
+        len_cert = (clen, Cert(m[3:3 + clen]))
+        m = m[3 + clen:]
+        return m + ret, len_cert
+
+    def i2m(self, pkt, i):
+        def i2m_one(i):
+            if isinstance(i, str):
+                return i
+            if isinstance(i, Cert):
+                s = i.der
+                l = struct.pack("!I", len(s))[1:4]
+                return l + s
+
+            (l, s) = i
+            if isinstance(s, Cert):
+                s = s.der
+            return struct.pack("!I", l)[1:4] + s
+
+        if i is None:
+            return ""
+        return i2m_one(i)
+
+    def any2i(self, pkt, x):
+        return x
+
 
 class TLSCertificate(_TLSHandshake):
     """
@@ -977,14 +581,59 @@ class TLSCertificate(_TLSHandshake):
                     _ASN1CertListField("certs", [],
                                       length_from = lambda pkt: pkt.certslen) ]
 
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        if _pkt:
+            tls_session = kargs.get("tls_session", None)
+            if tls_session and tls_session.tls_version >= 0x0304:
+                return TLS13Certificate
+        return TLSCertificate
+
     def post_dissection_tls_session_update(self, msg_str):
+        self.tls_session_update(msg_str)
         connection_end = self.tls_session.connection_end
         if connection_end == "client":
             self.tls_session.server_certs = [x[1] for x in self.certs]
         else:
             self.tls_session.client_certs = [x[1] for x in self.certs]
-        self.tls_session.handshake_messages.append(msg_str)
-        self.tls_session.handshake_messages_parsed.append(self)
+
+
+class _ASN1CertAndExt(_GenericTLSSessionInheritance):
+    name = "Certificate and Extensions"
+    fields_desc = [ _ASN1CertField("cert", ""),
+                    FieldLenField("extlen", None, length_of="ext"),
+                    _ExtensionsField("ext", [],
+                                     length_from=lambda pkt: pkt.extlen) ]
+    def extract_padding(self, s):
+        return "", s
+
+class _ASN1CertAndExtListField(PacketListField):
+    def m2i(self, pkt, m):
+        return self.cls(m, tls_session=pkt.tls_session)
+
+class TLS13Certificate(_TLSHandshake):
+    name = "TLS 1.3 Handshake - Certificate"
+    fields_desc = [ ByteEnumField("msgtype", 11, _tls_handshake_type),
+                    ThreeBytesField("msglen", None),
+                    FieldLenField("cert_req_ctxt_len", None, fmt="B",
+                                  length_of="cert_req_ctxt"),
+                    StrLenField("cert_req_ctxt", "",
+                                length_from=lambda pkt: pkt.cert_req_ctxt_len),
+                    _ASN1CertLenField("certslen", None, length_of="certs"),
+                    _ASN1CertAndExtListField("certs", [], _ASN1CertAndExt,
+                                      length_from=lambda pkt: pkt.certslen) ]
+
+    def post_dissection_tls_session_update(self, msg_str):
+        self.tls_session_update(msg_str)
+        connection_end = self.tls_session.connection_end
+        if connection_end == "client":
+            if self.certs:
+                sc = [x.cert[1] for x in self.certs]
+                self.tls_session.server_certs = sc
+        else:
+            if self.certs:
+                cc = [x.cert[1] for x in self.certs]
+                self.tls_session.client_certs = cc
 
 
 ###############################################################################
@@ -1019,7 +668,7 @@ class TLSServerKeyExchange(_TLSHandshake):
           corresponds to ServerECDHNamedCurveParams (implicit curves).
 
         When the Server*DHParams are built via .fill_missing(), the session
-        server_kx_params and client_kx_params will be updated accordingly.
+        server_kx_privkey will be updated accordingly.
         """
         fval = self.getfieldval("params")
         if fval is None:
@@ -1060,14 +709,16 @@ class TLSServerKeyExchange(_TLSHandshake):
     def post_dissection(self, pkt):
         """
         While previously dissecting Server*DHParams, the session
-        server_kx_params and client_kx_params should have been updated.
+        server_kx_pubkey should have been updated.
 
         XXX Add a 'fixed_dh' OR condition to the 'anonymous' test.
         """
         s = self.tls_session
-        if s.prcs and s.prcs.key_exchange.anonymous:
+        if s.prcs and s.prcs.key_exchange.no_ske:
             print("USELESS SERVER KEY EXCHANGE")
-        if (s.client_random and s.server_random and
+        if (s.prcs and
+            not s.prcs.key_exchange.anonymous and
+            s.client_random and s.server_random and
             s.server_certs and len(s.server_certs) > 0):
             m = s.client_random + s.server_random + str(self.params)
             sig_test = self.sig._verify_sig(m, s.server_certs[0])
@@ -1135,22 +786,19 @@ class TLSCertificateRequest(_TLSHandshake):
                     ThreeBytesField("msglen", None),
                     FieldLenField("ctypeslen", None, fmt="B",
                                   length_of="ctypes"),
-                    _CertTypesField("ctypes", [],
+                    _CertTypesField("ctypes", [1, 64],
                                     _tls_client_certificate_types,
                                     itemfmt="!B",
                                     length_from=lambda pkt: pkt.ctypeslen),
                     SigAndHashAlgsLenField("sig_algs_len", None,
                                            length_of="sig_algs"),
-                    SigAndHashAlgsField("sig_algs", [],
-                                        EnumField("hash_sig", None,
-                                                     _tls_hash_sig),
-                                        length_from=
-                                            lambda pkt: pkt.sig_algs_len),
+                    SigAndHashAlgsField("sig_algs", [0x0403, 0x0401, 0x0201],
+                                EnumField("hash_sig", None, _tls_hash_sig),
+                                length_from=lambda pkt: pkt.sig_algs_len),
                     FieldLenField("certauthlen", None, fmt="!H",
                                   length_of="certauth"),
                     _CertAuthoritiesField("certauth", [],
-                                          length_from=
-                                              lambda pkt: pkt.certauthlen) ]
+                                length_from=lambda pkt: pkt.certauthlen) ]
 
 
 ###############################################################################
@@ -1172,24 +820,48 @@ class TLSCertificateVerify(_TLSHandshake):
     fields_desc = [ ByteEnumField("msgtype", 15, _tls_handshake_type),
                     ThreeBytesField("msglen", None),
                     _TLSSignatureField("sig", None,
-                                 length_from = lambda pkt: pkt.msglen) ]
+                                 length_from=lambda pkt: pkt.msglen) ]
 
     def build(self, *args, **kargs):
         sig = self.getfieldval("sig")
         if sig is None:
             s = self.tls_session
             m = "".join(s.handshake_messages)
+            if s.tls_version >= 0x0304:
+                if s.connection_end == "client":
+                    context_string = "TLS 1.3, client CertificateVerify"
+                elif s.connection_end == "server":
+                    context_string = "TLS 1.3, server CertificateVerify"
+                m = b"\x20"*64 + context_string + b"\x00" + s.wcs.hash.digest(m)
             self.sig = _TLSSignature(tls_session=s)
-            self.sig._update_sig(m, s.client_key)
+            if s.connection_end == "client":
+                self.sig._update_sig(m, s.client_key)
+            elif s.connection_end == "server":
+                # should be TLS 1.3 only
+                self.sig._update_sig(m, s.server_key)
         return _TLSHandshake.build(self, *args, **kargs)
 
     def post_dissection(self, pkt):
         s = self.tls_session
         m = "".join(s.handshake_messages)
-        if s.client_certs and len(s.client_certs) > 0:
-            sig_test = self.sig._verify_sig(m, s.client_certs[0])
-            if not sig_test:
-                print("INVALID CERTIFICATE VERIFY SIGNATURE")
+        if s.tls_version >= 0x0304:
+            if s.connection_end == "client":
+                context_string = "TLS 1.3, server CertificateVerify"
+            elif s.connection_end == "server":
+                context_string = "TLS 1.3, client CertificateVerify"
+            m = b"\x20"*64 + context_string + b"\x00" + s.rcs.hash.digest(m)
+
+        if s.connection_end == "server":
+            if s.client_certs and len(s.client_certs) > 0:
+                sig_test = self.sig._verify_sig(m, s.client_certs[0])
+                if not sig_test:
+                    print("INVALID CERTIFICATE VERIFY SIGNATURE")
+        elif s.connection_end == "client":
+            # should be TLS 1.3 only
+            if s.server_certs and len(s.server_certs) > 0:
+                sig_test = self.sig._verify_sig(m, s.server_certs[0])
+                if not sig_test:
+                    print("INVALID CERTIFICATE VERIFY SIGNATURE")
 
 
 ###############################################################################
@@ -1254,8 +926,10 @@ class TLSClientKeyExchange(_TLSHandshake):
 
 class _VerifyDataField(StrLenField):
     def getfield(self, pkt, s):
-        if pkt.tls_session.tls_version == 0x300:
+        if pkt.tls_session.tls_version == 0x0300:
             sep = 36
+        elif pkt.tls_session.tls_version >= 0x0304:
+            sep = pkt.tls_session.rcs.hash.hash_len
         else:
             sep = 12
         return s[sep:], s[:sep]
@@ -1271,22 +945,59 @@ class TLSFinished(_TLSHandshake):
         if fval is None:
             s = self.tls_session
             handshake_msg = "".join(s.handshake_messages)
-            ms = s.master_secret
             con_end = s.connection_end
-            self.vdata = s.wcs.prf.compute_verify_data(con_end, "write",
-                                                       handshake_msg, ms)
+            if s.tls_version < 0x0304:
+                ms = s.master_secret
+                self.vdata = s.wcs.prf.compute_verify_data(con_end, "write",
+                                                           handshake_msg, ms)
+            else:
+                self.vdata = s.compute_tls13_verify_data(con_end, "write")
         return _TLSHandshake.build(self, *args, **kargs)
 
     def post_dissection(self, pkt):
         s = self.tls_session
-        handshake_msg = "".join(s.handshake_messages)
-        if s.master_secret is not None:
-            ms = s.master_secret
-            con_end = s.connection_end
-            verify_data = s.rcs.prf.compute_verify_data(con_end, "read",
-                                                        handshake_msg, ms)
-            if self.vdata != verify_data:
-                print("INVALID TLS FINISHED RECEIVED")
+        if not s.frozen:
+            handshake_msg = "".join(s.handshake_messages)
+            if s.tls_version < 0x0304 and s.master_secret is not None:
+                ms = s.master_secret
+                con_end = s.connection_end
+                verify_data = s.rcs.prf.compute_verify_data(con_end, "read",
+                                                            handshake_msg, ms)
+                if self.vdata != verify_data:
+                    print("INVALID TLS FINISHED RECEIVED")
+            elif s.tls_version >= 0x0304:
+                con_end = s.connection_end
+                verify_data = s.compute_tls13_verify_data(con_end, "read")
+                if self.vdata != verify_data:
+                    print("INVALID TLS FINISHED RECEIVED")
+
+    def post_build_tls_session_update(self, msg_str):
+        self.tls_session_update(msg_str)
+        s = self.tls_session
+        if s.tls_version >= 0x0304:
+            s.pwcs = writeConnState(ciphersuite=type(s.wcs.ciphersuite),
+                                    connection_end=s.connection_end,
+                                    tls_version=s.tls_version)
+            s.triggered_pwcs_commit = True
+            if s.connection_end == "server":
+                s.compute_tls13_traffic_secrets()
+            elif s.connection_end == "client":
+                s.compute_tls13_traffic_secrets_end()
+                s.compute_tls13_resumption_secret()
+
+    def post_dissection_tls_session_update(self, msg_str):
+        self.tls_session_update(msg_str)
+        s = self.tls_session
+        if s.tls_version >= 0x0304:
+            s.prcs = readConnState(ciphersuite=type(s.rcs.ciphersuite),
+                                   connection_end=s.connection_end,
+                                   tls_version=s.tls_version)
+            s.triggered_prcs_commit = True
+            if s.connection_end == "client":
+                s.compute_tls13_traffic_secrets()
+            elif s.connection_end == "server":
+                s.compute_tls13_traffic_secrets_end()
+                s.compute_tls13_resumption_secret()
 
 
 ## Additional handshake messages
@@ -1424,15 +1135,6 @@ class TLSSupplementalData(_TLSHandshake):
 ### NewSessionTicket                                                        ###
 ###############################################################################
 
-class Ticket(Packet):
-    name = "Recommended Ticket Construction"
-    fields_desc = [ StrFixedLenField("key_name", None, 16),
-                    StrFixedLenField("iv", None, 16),
-                    FieldLenField("estatelen", None, length_of="estate"),
-                    StrLenField("estate", "",
-                                length_from=lambda pkt: pkt.estatelen),
-                    StrFixedLenField("mac", None, 32) ]
-
 class TLSNewSessionTicket(_TLSHandshake):
     """
     XXX When knowing the right secret, we should be able to read the ticket.
@@ -1443,7 +1145,44 @@ class TLSNewSessionTicket(_TLSHandshake):
                     IntField("lifetime", 0xffffffff),
                     FieldLenField("ticketlen", None, length_of="ticket"),
                     StrLenField("ticket", "",
-                                length_from=lambda pkt: pkt.msglen) ]
+                                length_from=lambda pkt: pkt.ticketlen) ]
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        s = kargs.get("tls_session", None)
+        if s and s.tls_version >= 0x0304:
+            return TLS13NewSessionTicket
+        return TLSNewSessionTicket
+
+    def post_dissection_tls_session_update(self, msg_str):
+        self.tls_session_update(msg_str)
+        if self.tls_session.connection_end == "client":
+            self.tls_session.client_session_ticket = self.ticket
+
+
+class TLS13NewSessionTicket(_TLSHandshake):
+    """
+    Uncomment the TicketField line for parsing a RFC 5077 ticket.
+    """
+    name = "TLS Handshake - New Session Ticket"
+    fields_desc = [ ByteEnumField("msgtype", 4, _tls_handshake_type),
+                    ThreeBytesField("msglen", None),
+                    IntField("ticket_lifetime", 0xffffffff),
+                    IntField("ticket_age_add", 0),
+                    FieldLenField("ticketlen", None, length_of="ticket"),
+                    #TicketField("ticket", "",
+                    StrLenField("ticket", "",
+                                length_from=lambda pkt: pkt.ticketlen),
+                    _ExtensionsLenField("extlen", None, length_of="ext"),
+                    _ExtensionsField("ext", None,
+                                 length_from=lambda pkt: (pkt.msglen -
+                                                          (pkt.ticketlen or 0) -
+                                                          12)) ]
+
+    def post_dissection_tls_session_update(self, msg_str):
+        self.tls_session_update(msg_str)
+        if self.tls_session.connection_end == "client":
+            self.tls_session.client_session_ticket = self.ticket
 
 
 ###############################################################################
@@ -1452,7 +1191,8 @@ class TLSNewSessionTicket(_TLSHandshake):
 
 _tls_handshake_cls = { 0: TLSHelloRequest,          1: TLSClientHello,
                        2: TLSServerHello,           3: TLSHelloVerifyRequest,
-                       4: TLSNewSessionTicket,      11: TLSCertificate,
+                       4: TLSNewSessionTicket,      6: TLSHelloRetryRequest,
+                       8: TLSEncryptedExtensions,   11: TLSCertificate,
                        12: TLSServerKeyExchange,    13: TLSCertificateRequest,
                        14: TLSServerHelloDone,      15: TLSCertificateVerify,
                        16: TLSClientKeyExchange,    20: TLSFinished,
diff --git a/scapy/layers/tls/handshake_sslv2.py b/scapy/layers/tls/handshake_sslv2.py
new file mode 100644
index 0000000000000000000000000000000000000000..43ad745033471ab0772f8421b2f38d80733f7e19
--- /dev/null
+++ b/scapy/layers/tls/handshake_sslv2.py
@@ -0,0 +1,543 @@
+## This file is part of Scapy
+## Copyright (C) 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+SSLv2 handshake fields & logic.
+"""
+
+from __future__ import print_function
+import math
+
+from scapy.error import warning
+from scapy.fields import *
+from scapy.packet import Packet, Raw, Padding
+from scapy.layers.tls.cert import Cert, PrivKey, PubKey
+from scapy.layers.tls.basefields import _tls_version, _TLSVersionField
+from scapy.layers.tls.handshake import _CipherSuitesField
+from scapy.layers.tls.keyexchange import _TLSSignatureField, _TLSSignature
+from scapy.layers.tls.session import (_GenericTLSSessionInheritance,
+                                      readConnState, writeConnState)
+from scapy.layers.tls.crypto.suites import (_tls_cipher_suites,
+                                            _tls_cipher_suites_cls,
+                                            _GenericCipherSuite,
+                                            _GenericCipherSuiteMetaclass,
+                                            get_usable_ciphersuites,
+                                            SSL_CK_DES_192_EDE3_CBC_WITH_MD5)
+
+
+###############################################################################
+### Generic SSLv2 Handshake message                                         ###
+###############################################################################
+
+_sslv2_handshake_type = { 0: "error",                1: "client_hello",
+                          2: "client_master_key",    3: "client_finished",
+                          4: "server_hello",         5: "server_verify",
+                          6: "server_finished",      7: "request_certificate",
+                          8: "client_certificate" }
+
+
+class _SSLv2Handshake(_GenericTLSSessionInheritance):
+    """
+    Inherited by other Handshake classes to get post_build().
+    Also used as a fallback for unknown TLS Handshake packets.
+    """
+    name = "SSLv2 Handshake Generic message"
+    fields_desc = [ ByteEnumField("msgtype", None, _sslv2_handshake_type) ]
+
+    def guess_payload_class(self, p):
+        return Padding
+
+    def tls_session_update(self, msg_str):
+        """
+        Covers both post_build- and post_dissection- context updates.
+        """
+        self.tls_session.handshake_messages.append(msg_str)
+        self.tls_session.handshake_messages_parsed.append(self)
+
+
+###############################################################################
+### Error                                                                   ###
+###############################################################################
+
+_tls_error_code = { 1: "no_cipher",         2: "no_certificate",
+                    4: "bad_certificate",   6: "unsupported_certificate_type" }
+
+class SSLv2Error(_SSLv2Handshake):
+    """
+    SSLv2 Error.
+    """
+    name = "SSLv2 Handshake - Error"
+    fields_desc = [ ByteEnumField("msgtype", 0, _sslv2_handshake_type),
+                    ShortEnumField("code", None, _tls_error_code) ]
+
+
+###############################################################################
+### ClientHello                                                             ###
+###############################################################################
+
+class _SSLv2CipherSuitesField(_CipherSuitesField):
+    def __init__(self, name, default, dico, length_from=None):
+        _CipherSuitesField.__init__(self, name, default, dico,
+                                    length_from=length_from)
+        self.itemfmt = ""
+        self.itemsize = 3
+
+    def i2m(self, pkt, val):
+        if val is None:
+            val2 = []
+        val2 = [(x >> 16, x & 0x00ffff) for x in val]
+        return "".join([struct.pack(">BH", x[0], x[1]) for x in val2])
+
+    def m2i(self, pkt, m):
+        res = []
+        while m:
+            res.append(struct.unpack("!I", "\x00" + m[:3])[0])
+            m = m[3:]
+        return res
+
+
+class SSLv2ClientHello(_SSLv2Handshake):
+    """
+    SSLv2 ClientHello.
+    """
+    name = "SSLv2 Handshake - Client Hello"
+    fields_desc = [ ByteEnumField("msgtype", 1, _sslv2_handshake_type),
+                    _TLSVersionField("version", 0x0002, _tls_version),
+
+                    FieldLenField("cipherslen", None, fmt="!H",
+                                  length_of="ciphers"),
+                    FieldLenField("sidlen", None, fmt="!H",
+                                  length_of="sid"),
+                    FieldLenField("challengelen", None, fmt="!H",
+                                  length_of="challenge"),
+
+                    XStrLenField("sid", "",
+                                 length_from=lambda pkt:pkt.sidlen),
+                    _SSLv2CipherSuitesField("ciphers",
+                                      [SSL_CK_DES_192_EDE3_CBC_WITH_MD5],
+                                      _tls_cipher_suites,
+                                      length_from=lambda pkt: pkt.cipherslen),
+                    XStrLenField("challenge", "",
+                                 length_from=lambda pkt:pkt.challengelen) ]
+
+    def tls_session_update(self, msg_str):
+        super(SSLv2ClientHello, self).tls_session_update(msg_str)
+        self.tls_session.advertised_tls_version = self.version
+        self.tls_session.sslv2_common_cs = self.ciphers
+        self.tls_session.sslv2_challenge = self.challenge
+
+
+###############################################################################
+### ServerHello                                                             ###
+###############################################################################
+
+class _SSLv2CertDataField(StrLenField):
+    def getfield(self, pkt, s):
+        l = 0
+        if self.length_from is not None:
+            l = self.length_from(pkt)
+        try:
+            certdata = Cert(s[:l])
+        except:
+            certdata = s[:l]
+        return s[l:], certdata
+
+    def i2len(self, pkt, i):
+        if isinstance(i, Cert):
+            return len(i.der)
+        return len(i)
+
+    def i2m(self, pkt, i):
+        if isinstance(i, Cert):
+            return i.der
+        return i
+
+
+class SSLv2ServerHello(_SSLv2Handshake):
+    """
+    SSLv2 ServerHello.
+    """
+    name = "SSLv2 Handshake - Server Hello"
+    fields_desc = [ ByteEnumField("msgtype", 4, _sslv2_handshake_type),
+
+                    ByteField("sid_hit", 0),
+                    ByteEnumField("certtype", 1, {1: "x509_cert"}),
+                    _TLSVersionField("version", 0x0002, _tls_version),
+
+                    FieldLenField("certlen", None, fmt="!H",
+                                  length_of="cert"),
+                    FieldLenField("cipherslen", None, fmt="!H",
+                                  length_of="ciphers"),
+                    FieldLenField("connection_idlen", None, fmt="!H",
+                                  length_of="connection_id"),
+
+                    _SSLv2CertDataField("cert", "",
+                                        length_from=lambda pkt: pkt.certlen),
+                    _SSLv2CipherSuitesField("ciphers", [], _tls_cipher_suites,
+                                length_from=lambda pkt: pkt.cipherslen),
+                    XStrLenField("connection_id", "",
+                                length_from=lambda pkt: pkt.connection_idlen) ]
+
+    def tls_session_update(self, msg_str):
+        """
+        XXX Something should be done about the session ID here.
+        """
+        super(SSLv2ServerHello, self).tls_session_update(msg_str)
+
+        s = self.tls_session
+        client_cs = s.sslv2_common_cs
+        css = [cs for cs in client_cs if cs in self.ciphers]
+        s.sslv2_common_cs = css
+        s.sslv2_connection_id = self.connection_id
+        s.tls_version = self.version
+        if self.cert is not None:
+            s.server_certs = [self.cert]
+
+
+###############################################################################
+### ClientMasterKey                                                         ###
+###############################################################################
+
+class _SSLv2CipherSuiteField(EnumField):
+    def __init__(self, name, default, dico):
+        EnumField.__init__(self, name, default, dico)
+
+    def i2m(self, pkt, val):
+        if val is None:
+            return ""
+        val2 = (val >> 16, val & 0x00ffff)
+        return struct.pack(">BH", val2[0], val2[1])
+
+    def addfield(self, pkt, s, val):
+        return s + self.i2m(pkt, val)
+
+    def m2i(self, pkt, m):
+        return struct.unpack("!I", "\x00" + m[:3])[0]
+
+    def getfield(self, pkt, s):
+        return s[3:], self.m2i(pkt, s)
+
+class _SSLv2EncryptedKeyField(XStrLenField):
+    def i2repr(self, pkt, x):
+        s = super(_SSLv2EncryptedKeyField, self).i2repr(pkt, x)
+        if pkt.decryptedkey is not None:
+            dx = pkt.decryptedkey
+            ds = super(_SSLv2EncryptedKeyField, self).i2repr(pkt, dx)
+            s += "    [decryptedkey= %s]" % ds
+        return s
+
+class SSLv2ClientMasterKey(_SSLv2Handshake):
+    """
+    SSLv2 ClientMasterKey.
+    """
+    __slots__ = ["decryptedkey"]
+    name = "SSLv2 Handshake - Client Master Key"
+    fields_desc = [ ByteEnumField("msgtype", 2, _sslv2_handshake_type),
+                    _SSLv2CipherSuiteField("cipher", None, _tls_cipher_suites),
+
+                    FieldLenField("clearkeylen", None, fmt="!H",
+                                  length_of="clearkey"),
+                    FieldLenField("encryptedkeylen", None, fmt="!H",
+                                  length_of="encryptedkey"),
+                    FieldLenField("keyarglen", None, fmt="!H",
+                                  length_of="keyarg"),
+
+                    XStrLenField("clearkey", "",
+                                length_from=lambda pkt: pkt.clearkeylen),
+                    _SSLv2EncryptedKeyField("encryptedkey", "",
+                                length_from=lambda pkt: pkt.encryptedkeylen),
+                    XStrLenField("keyarg", "",
+                                length_from=lambda pkt: pkt.keyarglen) ]
+
+    def __init__(self, *args, **kargs):
+        """
+        When post_building, the packets fields are updated (this is somewhat
+        non-standard). We might need these fields later, but calling __str__
+        on a new packet (i.e. not dissected from a raw string) applies
+        post_build to an object different from the original one... unless
+        we hackishly always set self.explicit to 1.
+        """
+        if "decryptedkey" in kargs:
+            self.decryptedkey = kargs["decryptedkey"]
+            del kargs["decryptedkey"]
+        else:
+            self.decryptedkey = ""
+        super(SSLv2ClientMasterKey, self).__init__(*args, **kargs)
+        self.explicit = 1
+
+    def pre_dissect(self, s):
+        clearkeylen = struct.unpack("!H", s[4:6])[0]
+        encryptedkeylen = struct.unpack("!H", s[6:8])[0]
+        encryptedkeystart = 10 + clearkeylen
+        encryptedkey = s[encryptedkeystart:encryptedkeystart+encryptedkeylen]
+        if self.tls_session.server_rsa_key:
+            self.decryptedkey = \
+                    self.tls_session.server_rsa_key.decrypt(encryptedkey)
+        else:
+            self.decryptedkey = None
+        return s
+
+    def post_build(self, pkt, pay):
+        cs_val = None
+        if self.cipher is None:
+            common_cs = self.tls_session.sslv2_common_cs
+            cs_vals = get_usable_ciphersuites(common_cs, "SSLv2")
+            if len(cs_vals) == 0:
+                warning("No known common cipher suite between SSLv2 Hellos.")
+                cs_val = 0x0700c0
+                cipher = "\x07\x00\xc0"
+            else:
+                cs_val = cs_vals[0]         #XXX choose the best one
+                cipher = struct.pack(">BH", cs_val >> 16, cs_val & 0x00ffff)
+            cs_cls = _tls_cipher_suites_cls[cs_val]
+            self.cipher = cs_val
+        else:
+            cipher = pkt[1:4]
+            cs_val = struct.unpack("!I", "\x00" + cipher)[0]
+            if cs_val not in _tls_cipher_suites_cls:
+                warning("Unknown ciphersuite %d from ClientMasterKey" % cs_val)
+                cs_cls = None
+            else:
+                cs_cls = _tls_cipher_suites_cls[cs_val]
+
+        if cs_cls:
+            if (self.encryptedkey == "" and
+                len(self.tls_session.server_certs) > 0):
+                # else, the user is responsible for export slicing & encryption
+                key = randstring(cs_cls.cipher_alg.key_len)
+
+                if self.clearkey == "" and cs_cls.kx_alg.export:
+                    self.clearkey = key[:-5]
+
+                if self.decryptedkey == "":
+                    if cs_cls.kx_alg.export:
+                        self.decryptedkey = key[-5:]
+                    else:
+                        self.decryptedkey = key
+
+                pubkey = self.tls_session.server_certs[0].pubKey
+                self.encryptedkey = pubkey.encrypt(self.decryptedkey)
+
+            if self.keyarg == "" and cs_cls.cipher_alg.type == "block":
+                self.keyarg = randstring(cs_cls.cipher_alg.block_size)
+
+        clearkey = self.clearkey or ""
+        if self.clearkeylen is None:
+            self.clearkeylen = len(clearkey)
+        clearkeylen = struct.pack("!H", self.clearkeylen)
+
+        encryptedkey = self.encryptedkey or ""
+        if self.encryptedkeylen is None:
+            self.encryptedkeylen = len(encryptedkey)
+        encryptedkeylen = struct.pack("!H", self.encryptedkeylen)
+
+        keyarg = self.keyarg or ""
+        if self.keyarglen is None:
+            self.keyarglen = len(keyarg)
+        keyarglen = struct.pack("!H", self.keyarglen)
+
+        s = (pkt[0] + cipher
+             + clearkeylen + encryptedkeylen + keyarglen
+             + clearkey + encryptedkey + keyarg)
+        return s + pay
+
+    def tls_session_update(self, msg_str):
+        super(SSLv2ClientMasterKey, self).tls_session_update(msg_str)
+
+        s = self.tls_session
+        cs_val = self.cipher
+        if cs_val not in _tls_cipher_suites_cls:
+            warning("Unknown cipher suite %d from ClientMasterKey" % cs_val)
+        else:
+            cs_cls = _tls_cipher_suites_cls[cs_val]
+
+        tls_version = s.tls_version
+        connection_end = s.connection_end
+        wcs_seq_num = s.wcs.seq_num
+        s.pwcs = writeConnState(ciphersuite=cs_cls,
+                                            connection_end=connection_end,
+                                            seq_num=wcs_seq_num,
+                                            tls_version=tls_version)
+        rcs_seq_num = s.rcs.seq_num
+        s.prcs = readConnState(ciphersuite=cs_cls,
+                                           connection_end=connection_end,
+                                           seq_num=rcs_seq_num,
+                                           tls_version=tls_version)
+
+        if self.decryptedkey is not None:
+            s.master_secret = self.clearkey + self.decryptedkey
+            s.compute_sslv2_km_and_derive_keys()
+
+            if s.pwcs.cipher.type == "block":
+                s.pwcs.cipher.iv = self.keyarg
+            if s.prcs.cipher.type == "block":
+                s.prcs.cipher.iv = self.keyarg
+
+            s.triggered_prcs_commit = True
+            s.triggered_pwcs_commit = True
+
+
+###############################################################################
+### ServerVerify                                                            ###
+###############################################################################
+
+class SSLv2ServerVerify(_SSLv2Handshake):
+    """
+    In order to parse a ServerVerify, the exact message string should be
+    fed to the class. This is how SSLv2 defines the challenge length...
+    """
+    name = "SSLv2 Handshake - Server Verify"
+    fields_desc = [ ByteEnumField("msgtype", 5, _sslv2_handshake_type),
+                    XStrField("challenge", "") ]
+
+    def build(self, *args, **kargs):
+        fval = self.getfieldval("challenge")
+        if fval is None:
+            self.challenge = self.tls_session.sslv2_challenge
+        return super(SSLv2ServerVerify, self).build(*args, **kargs)
+
+    def post_dissection(self, pkt):
+        s = self.tls_session
+        if s.sslv2_challenge is not None:
+            if self.challenge != s.sslv2_challenge:
+                print("INVALID TLS SERVER VERIFY RECEIVED")
+
+
+###############################################################################
+### RequestCertificate                                                      ###
+###############################################################################
+
+class SSLv2RequestCertificate(_SSLv2Handshake):
+    """
+    In order to parse a RequestCertificate, the exact message string should be
+    fed to the class. This is how SSLv2 defines the challenge length...
+    """
+    name = "SSLv2 Handshake - Request Certificate"
+    fields_desc = [ ByteEnumField("msgtype", 7, _sslv2_handshake_type),
+                    ByteEnumField("authtype", 1, {1: "md5_with_rsa"}),
+                    XStrField("challenge", "") ]
+
+    def tls_session_update(self, msg_str):
+        super(SSLv2RequestCertificate, self).tls_session_update(msg_str)
+        self.tls_session.sslv2_challenge_clientcert = self.challenge
+
+
+###############################################################################
+### ClientCertificate                                                       ###
+###############################################################################
+
+class SSLv2ClientCertificate(_SSLv2Handshake):
+    """
+    SSLv2 ClientCertificate.
+    """
+    name = "SSLv2 Handshake - Client Certificate"
+    fields_desc = [ ByteEnumField("msgtype", 8, _sslv2_handshake_type),
+
+                    ByteEnumField("certtype", 1, {1: "x509_cert"}),
+                    FieldLenField("certlen", None, fmt="!H",
+                                  length_of="certdata"),
+                    FieldLenField("responselen", None, fmt="!H",
+                                  length_of="responsedata"),
+
+                    _SSLv2CertDataField("certdata", "",
+                                      length_from=lambda pkt: pkt.certlen),
+                    _TLSSignatureField("responsedata", None,
+                                length_from=lambda pkt: pkt.responselen) ]
+
+    def build(self, *args, **kargs):
+        s = self.tls_session
+        sig = self.getfieldval("responsedata")
+        test = (sig is None and
+                s.sslv2_key_material is not None and
+                s.sslv2_challenge_clientcert is not None and
+                len(s.server_certs) > 0)
+        if test:
+            s = self.tls_session
+            m = (s.sslv2_key_material +
+                 s.sslv2_challenge_clientcert +
+                 s.server_certs[0].der)
+            self.responsedata = _TLSSignature(tls_session=s)
+            self.responsedata._update_sig(m, s.client_key)
+        else:
+            self.responsedata = ""
+        return super(SSLv2ClientCertificate, self).build(*args, **kargs)
+
+    def post_dissection_tls_session_update(self, msg_str):
+        self.tls_session_update(msg_str)
+
+        s = self.tls_session
+        test = (len(s.client_certs) > 0 and
+                s.sslv2_key_material is not None and
+                s.sslv2_challenge_clientcert is not None and
+                len(s.server_certs) > 0)
+        if test:
+            m = (s.sslv2_key_material +
+                 s.sslv2_challenge_clientcert +
+                 s.server_certs[0].der)
+            sig_test = self.responsedata._verify_sig(m, s.client_certs[0])
+            if not sig_test:
+                print("INVALID CLIENT CERTIFICATE VERIFY SIGNATURE")
+
+    def tls_session_update(self, msg_str):
+        super(SSLv2ClientCertificate, self).tls_session_update(msg_str)
+        if self.certdata:
+            self.tls_session.client_certs = [self.certdata]
+
+
+###############################################################################
+### Finished                                                                ###
+###############################################################################
+
+class SSLv2ClientFinished(_SSLv2Handshake):
+    """
+    In order to parse a ClientFinished, the exact message string should be fed
+    to the class. SSLv2 does not offer any other way to know the c_id length.
+    """
+    name = "SSLv2 Handshake - Client Finished"
+    fields_desc = [ ByteEnumField("msgtype", 3, _sslv2_handshake_type),
+                    XStrField("connection_id", "") ]
+
+    def build(self, *args, **kargs):
+        fval = self.getfieldval("connection_id")
+        if fval == "":
+            self.connection_id = self.tls_session.sslv2_connection_id
+        return super(SSLv2ClientFinished, self).build(*args, **kargs)
+
+    def post_dissection(self, pkt):
+        s = self.tls_session
+        if s.sslv2_connection_id is not None:
+            if self.connection_id != s.sslv2_connection_id:
+                print("INVALID TLS CLIENT FINISHED RECEIVED")
+
+
+class SSLv2ServerFinished(_SSLv2Handshake):
+    """
+    In order to parse a ServerFinished, the exact message string should be fed
+    to the class. SSLv2 does not offer any other way to know the sid length.
+    """
+    name = "SSLv2 Handshake - Server Finished"
+    fields_desc = [ ByteEnumField("msgtype", 6, _sslv2_handshake_type),
+                    XStrField("sid", "") ]
+
+    def build(self, *args, **kargs):
+        fval = self.getfieldval("sid")
+        if fval == "":
+            self.sid = self.tls_session.sid
+        return super(SSLv2ServerFinished, self).build(*args, **kargs)
+
+    def post_dissection_tls_session_update(self, msg_str):
+        self.tls_session_update(msg_str)
+        self.tls_session.sid = self.sid
+
+
+###############################################################################
+### All handshake messages defined in this module                           ###
+###############################################################################
+
+_sslv2_handshake_cls = { 0: SSLv2Error,             1: SSLv2ClientHello,
+                         2: SSLv2ClientMasterKey,   3: SSLv2ClientFinished,
+                         4: SSLv2ServerHello,       5: SSLv2ServerVerify,
+                         6: SSLv2ServerFinished,    7: SSLv2RequestCertificate,
+                         8: SSLv2ClientCertificate }
+
diff --git a/scapy/layers/tls/keyexchange.py b/scapy/layers/tls/keyexchange.py
index 1b2c4aaec52be3146ce083e7879d1ce7d44fce63..05e97391f6a2acbca7ea7bd81219001bef200e7e 100644
--- a/scapy/layers/tls/keyexchange.py
+++ b/scapy/layers/tls/keyexchange.py
@@ -1,6 +1,6 @@
 ## This file is part of Scapy
 ## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
-##                     2015, 2016 Maxence Tury
+##               2015, 2016, 2017 Maxence Tury
 ## This program is published under a GPLv2 license
 
 """
@@ -10,11 +10,7 @@ TLS key exchange logic.
 from __future__ import absolute_import
 import math
 
-from cryptography.hazmat.backends import default_backend
-from cryptography.hazmat.primitives import serialization
-from cryptography.hazmat.primitives.asymmetric import dh, ec, rsa
-
-from scapy.config import conf
+from scapy.config import conf, crypto_validator
 from scapy.error import warning
 from scapy.fields import *
 from scapy.packet import Packet, Raw, Padding
@@ -22,9 +18,13 @@ from scapy.layers.tls.cert import PubKeyRSA, PrivKeyRSA
 from scapy.layers.tls.session import _GenericTLSSessionInheritance
 from scapy.layers.tls.basefields import _tls_version, _TLSClientVersionField
 from scapy.layers.tls.crypto.pkcs1 import pkcs_i2osp, pkcs_os2ip
-from scapy.layers.tls.crypto.ffdh import FFDH_GROUPS
+from scapy.layers.tls.crypto.groups import _ffdh_groups, _tls_named_curves
 import scapy.modules.six as six
 
+if conf.crypto_valid:
+    from cryptography.hazmat.backends import default_backend
+    from cryptography.hazmat.primitives.asymmetric import dh, ec
+
 
 ###############################################################################
 ### Common Fields                                                           ###
@@ -43,7 +43,12 @@ _tls_hash_sig = { 0x0000: "none+anon",    0x0001: "none+rsa",
                   0x0500: "sha384+anon",  0x0501: "sha384+rsa",
                   0x0502: "sha384+dsa",   0x0503: "sha384+ecdsa",
                   0x0600: "sha512+anon",  0x0601: "sha512+rsa",
-                  0x0602: "sha512+dsa",   0x0603: "sha512+ecdsa" }
+                  0x0602: "sha512+dsa",   0x0603: "sha512+ecdsa",
+                  0x0804: "sha256+rsapss",
+                  0x0805: "sha384+rsapss",
+                  0x0806: "sha512+rsapss",
+                  0x0807: "ed25519",
+                  0x0808: "ed448" }
 
 
 def phantom_mode(pkt):
@@ -52,7 +57,6 @@ def phantom_mode(pkt):
     any complete ClientHello, so we're most probably reading/building a
     signature_algorithms extension, hence we cannot be in phantom_mode.
     However, if the tls_version has been set, we test for TLS 1.2.
-    XXX Make this more generic. Also, factorize the classes below (metaclass?).
     """
     if not pkt.tls_session:
         return False
@@ -76,55 +80,80 @@ def phantom_decorate(f, get_or_add):
     return wrapper
 
 class SigAndHashAlgField(EnumField):
-    """
-    Used in _TLSSignature.
-    """
+    """Used in _TLSSignature."""
     phantom_value = None
     getfield = phantom_decorate(EnumField.getfield, True)
     addfield = phantom_decorate(EnumField.addfield, False)
 
 class SigAndHashAlgsLenField(FieldLenField):
-    """
-    Used in TLS_Ext_SignatureAlgorithms and TLSCertificateResquest.
-    """
+    """Used in TLS_Ext_SignatureAlgorithms and TLSCertificateResquest."""
     phantom_value = 0
     getfield = phantom_decorate(FieldLenField.getfield, True)
     addfield = phantom_decorate(FieldLenField.addfield, False)
 
 class SigAndHashAlgsField(FieldListField):
-    """
-    Used in TLS_Ext_SignatureAlgorithms and TLSCertificateResquest.
-    """
+    """Used in TLS_Ext_SignatureAlgorithms and TLSCertificateResquest."""
     phantom_value = []
     getfield = phantom_decorate(FieldListField.getfield, True)
     addfield = phantom_decorate(FieldListField.addfield, False)
 
 
+class SigLenField(FieldLenField):
+    """There is a trick for SSLv2, which uses implicit lengths..."""
+    def getfield(self, pkt, s):
+        v = pkt.tls_session.tls_version
+        if v and v < 0x0300:
+            return s, None
+        return super(SigLenField, self).getfield(pkt, s)
+
+    def addfield(self, pkt, s, val):
+        """With SSLv2 you will never be able to add a sig_len."""
+        v = pkt.tls_session.tls_version
+        if v and v < 0x0300:
+            return s
+        return super(SigLenField, self).addfield(pkt, s, val)
+
+class SigValField(StrLenField):
+    """There is a trick for SSLv2, which uses implicit lengths..."""
+    def getfield(self, pkt, m):
+        s = pkt.tls_session
+        if s.tls_version and s.tls_version < 0x0300:
+            if len(s.client_certs) > 0:
+                sig_len = s.client_certs[0].pubKey.pubkey.key_size / 8
+            else:
+                warning("No client certificate provided. "
+                        "We're making a wild guess about the signature size.")
+                sig_len = 256
+            return m[sig_len:], self.m2i(pkt, m[:sig_len])
+        return super(SigValField, self).getfield(pkt, m)
+
+
 class _TLSSignature(_GenericTLSSessionInheritance):
     """
-    Prior to TLS 1.2, digitally-signed structure implictly used the
-    concatenation of a SHA-1 hash and a MD5 hash (this is the 'tls' mode
-    of key signing). TLS 1.2 introduced explicit SignatureAndHashAlgorithms,
+    Prior to TLS 1.2, digitally-signed structure implicitly used the
+    concatenation of a MD5 hash and a SHA-1 hash.
+    Then TLS 1.2 introduced explicit SignatureAndHashAlgorithms,
     i.e. couples of (hash_alg, sig_alg). See RFC 5246, section 7.4.1.4.1.
 
     By default, the _TLSSignature implements the TLS 1.2 scheme,
     but if it is provided a TLS context with a tls_version < 0x0303
     at initialization, it will fall back to the implicit signature.
+    Even more, the 'sig_len' field won't be used with SSLv2.
 
     #XXX 'sig_alg' should be set in __init__ depending on the context.
     """
     name = "TLS Digital Signature"
     fields_desc = [ SigAndHashAlgField("sig_alg", 0x0401, _tls_hash_sig),
-                    FieldLenField("sig_len", None, fmt="!H",
-                                  length_of="sig_val"),
-                    StrLenField("sig_val", None,
-                                length_from = lambda pkt: pkt.sig_len) ]
+                    SigLenField("sig_len", None, fmt="!H",
+                                length_of="sig_val"),
+                    SigValField("sig_val", None,
+                                length_from=lambda pkt: pkt.sig_len) ]
 
     def __init__(self, *args, **kargs):
-        _GenericTLSSessionInheritance.__init__(self, *args, **kargs)
-        if ("tls_session" in kargs and
-            kargs["tls_session"].tls_version and
-            kargs["tls_session"].tls_version < 0x0303):
+        super(_TLSSignature, self).__init__(*args, **kargs)
+        if (self.tls_session and
+            self.tls_session.tls_version and
+            self.tls_session.tls_version < 0x0303):
             self.sig_alg = None
 
     def _update_sig(self, m, key):
@@ -134,10 +163,17 @@ class _TLSSignature(_GenericTLSSessionInheritance):
         of the PrivKey (neither do we care to compare the both of them).
         """
         if self.sig_alg is None:
-            self.sig_val = key.sign(m, t='pkcs', h='tls')
+            if self.tls_session.tls_version >= 0x0300:
+                self.sig_val = key.sign(m, t='pkcs', h='md5-sha1')
+            else:
+                self.sig_val = key.sign(m, t='pkcs', h='md5')
         else:
-            h = _tls_hash_sig[self.sig_alg].split('+')[0]
-            self.sig_val = key.sign(m, t='pkcs', h=h)
+            h, sig = _tls_hash_sig[self.sig_alg].split('+')
+            if sig.endswith('pss'):
+                t = "pss"
+            else:
+                t = "pkcs"
+            self.sig_val = key.sign(m, t=t, h=h)
 
     def _verify_sig(self, m, cert):
         """
@@ -146,10 +182,17 @@ class _TLSSignature(_GenericTLSSessionInheritance):
         """
         if self.sig_val:
             if self.sig_alg:
-                h = _tls_hash_sig[self.sig_alg].split('+')[0]
-                return cert.verify(m, self.sig_val, t='pkcs', h=h)
+                h, sig = _tls_hash_sig[self.sig_alg].split('+')
+                if sig.endswith('pss'):
+                    t = "pss"
+                else:
+                    t = "pkcs"
+                return cert.verify(m, self.sig_val, t=t, h=h)
             else:
-                return cert.verify(m, self.sig_val, t='pkcs', h='tls')
+                if self.tls_session.tls_version >= 0x0300:
+                    return cert.verify(m, self.sig_val, t='pkcs', h='md5-sha1')
+                else:
+                    return cert.verify(m, self.sig_val, t='pkcs', h='md5')
         return False
 
     def guess_payload_class(self, p):
@@ -171,6 +214,17 @@ class _TLSSignatureField(PacketField):
            return None
         return _TLSSignature(m, tls_session=pkt.tls_session)
 
+    def getfield(self, pkt, s):
+        i = self.m2i(pkt, s)
+        if i is None:
+            return s, None
+        remain = ""
+        if conf.padding_layer in i:
+            r = i[conf.padding_layer]
+            del(r.underlayer.payload)
+            remain = r.load
+        return remain, i
+
 
 class _TLSServerParamsField(PacketField):
     """
@@ -218,11 +272,10 @@ class _TLSServerParamsField(PacketField):
 
 class ServerDHParams(_GenericTLSSessionInheritance):
     """
-    ServerDHParams for FFDH-based key exchanges,
-    as it is defined in RFC 5246, section 7.4.3.
+    ServerDHParams for FFDH-based key exchanges, as defined in RFC 5246/7.4.3.
 
-    Either with .fill_missing() or .post_dissection(), the server_kx_params and
-    client_kx_params of the TLS context are updated according to the
+    Either with .fill_missing() or .post_dissection(), the server_kx_privkey or
+    server_kx_pubkey of the TLS context are updated according to the
     parsed/assembled values. It is the user's responsibility to store and
     restore the original values if he wants to keep them. For instance, this
     could be done between the writing of a ServerKeyExchange and the receiving
@@ -239,18 +292,19 @@ class ServerDHParams(_GenericTLSSessionInheritance):
                     StrLenField("dh_Ys", "",
                                 length_from=lambda pkt: pkt.dh_Yslen) ]
 
+    @crypto_validator
     def fill_missing(self):
         """
         We do not want TLSServerKeyExchange.build() to overload and recompute
         things everytime it is called. This method can be called specifically
         to have things filled in a smart fashion.
 
-        Note that we do not expect dh_params_def.g to be more than 0xff.
+        Note that we do not expect default_params.g to be more than 0xff.
         """
         s = self.tls_session
 
-        default_params = FFDH_GROUPS['modp2048'][0].parameter_numbers()
-        default_mLen = FFDH_GROUPS['modp2048'][1]
+        default_params = _ffdh_groups['modp2048'][0].parameter_numbers()
+        default_mLen = _ffdh_groups['modp2048'][1]
 
         if self.dh_p is "":
             self.dh_p = pkcs_i2osp(default_params.p, default_mLen/8)
@@ -278,11 +332,11 @@ class ServerDHParams(_GenericTLSSessionInheritance):
         if not s.client_kx_ffdh_params:
             s.client_kx_ffdh_params = real_params
 
-    def post_dissection(self, r):
+    @crypto_validator
+    def register_pubkey(self):
         """
         XXX Check that the pubkey received is in the group.
         """
-        #if self.dh_g and self.dh_p and self.dh_Ys: #XXX remove this, probably
         p = pkcs_os2ip(self.dh_p)
         g = pkcs_os2ip(self.dh_g)
         pn = dh.DHParameterNumbers(p, g)
@@ -296,6 +350,12 @@ class ServerDHParams(_GenericTLSSessionInheritance):
         if not s.client_kx_ffdh_params:
             s.client_kx_ffdh_params = pn.parameters(default_backend())
 
+    def post_dissection(self, r):
+        try:
+            self.register_pubkey()
+        except ImportError:
+            pass
+
     def guess_payload_class(self, p):
         """
         The signature after the params gets saved as Padding.
@@ -311,19 +371,6 @@ _tls_ec_curve_types = { 1: "explicit_prime",
                         2: "explicit_char2",
                         3: "named_curve" }
 
-_tls_named_curves = {  1: "sect163k1",  2: "sect163r1",  3: "sect163r2",
-                       4: "sect193r1",  5: "sect193r2",  6: "sect233k1",
-                       7: "sect233r1",  8: "sect239k1",  9: "sect283k1",
-                      10: "sect283r1", 11: "sect409k1", 12: "sect409r1",
-                      13: "sect571k1", 14: "sect571r1", 15: "secp160k1",
-                      16: "secp160r1", 17: "secp160r2", 18: "secp192k1",
-                      19: "secp192r1", 20: "secp224k1", 21: "secp224r1",
-                      22: "secp256k1", 23: "secp256r1", 24: "secp384r1",
-                      25: "secp521r1", 26: "brainpoolP256r1",
-                      27: "brainpoolP384r1", 28: "brainpoolP512r1",
-                      0xff01: "arbitrary_explicit_prime_curves",
-                      0xff02: "arbitrary_explicit_char2_curves"}
-
 _tls_ec_basis_types = { 0: "ec_basis_trinomial", 1: "ec_basis_pentanomial"}
 
 class ECCurvePkt(Packet):
@@ -402,8 +449,8 @@ class _ECBasisField(PacketField):
 
 class ServerECDHExplicitPrimeParams(_GenericTLSSessionInheritance):
     """
-    XXX We provide parsing abilities for ExplicitPrimeParams, but there is no
-    'cryptography' support, hence no context operations.
+    We provide parsing abilities for ExplicitPrimeParams, but there is no
+    support from the cryptography library, hence no context operations.
     """
     name = "Server ECDH parameters - Explicit Prime"
     fields_desc = [ ByteEnumField("curve_type", 1, _tls_ec_curve_types),
@@ -428,33 +475,20 @@ class ServerECDHExplicitPrimeParams(_GenericTLSSessionInheritance):
 
     def fill_missing(self):
         """
-        We do not want TLSServerKeyExchange.build() to overload and recompute
-        things everytime it is called. This method can be called specifically
-        to have things filled in a smart fashion.
-
-        XXX Note that if it is not set by the user, the cofactor will always
+        Note that if it is not set by the user, the cofactor will always
         be 1. It is true for most, but not all, TLS elliptic curves.
-
-        XXX Try and create a curve with the 'cryptography' lib somehow,
-        extract the missing fields for filling, then set s.server_kx_privkey.
         """
         if self.curve_type is None:
             self.curve_type = _tls_ec_curve_types["explicit_prime"]
 
-    def post_dissection(self, pkt):
-        """
-        XXX Store the server_kx_pubkey.
-        XXX Check that the pubkey received is on the curve.
-        """
-
     def guess_payload_class(self, p):
         return Padding
 
 
 class ServerECDHExplicitChar2Params(_GenericTLSSessionInheritance):
     """
-    XXX We provide parsing abilities for Char2Params, but there is no
-    'cryptography' support, hence no context operations.
+    We provide parsing abilities for Char2Params, but there is no
+    support from the cryptography library, hence no context operations.
     """
     name = "Server ECDH parameters - Explicit Char2"
     fields_desc = [ ByteEnumField("curve_type", 2, _tls_ec_curve_types),
@@ -476,21 +510,9 @@ class ServerECDHExplicitChar2Params(_GenericTLSSessionInheritance):
                                 length_from = lambda pkt: pkt.pointlen) ]
 
     def fill_missing(self):
-        """
-        We do not want TLSServerKeyExchange.build() to overload and recompute
-        things everytime it is called. This method can be called specifically
-        to have things filled in a smart fashion.
-        """
         if self.curve_type is None:
             self.curve_type = _tls_ec_curve_types["explicit_char2"]
 
-    def post_dissection(self, pkt):
-        """
-        XXX Store the server_kx_pubkey.
-        XXX Check that the pubkey received is in the group.
-        """
-        pass
-
     def guess_payload_class(self, p):
         return Padding
 
@@ -504,11 +526,13 @@ class ServerECDHNamedCurveParams(_GenericTLSSessionInheritance):
                     StrLenField("point", None,
                                 length_from = lambda pkt: pkt.pointlen) ]
 
+    @crypto_validator
     def fill_missing(self):
         """
         We do not want TLSServerKeyExchange.build() to overload and recompute
         things everytime it is called. This method can be called specifically
         to have things filled in a smart fashion.
+
         XXX We should account for the point_format (before 'point' filling).
         """
         s = self.tls_session
@@ -551,7 +575,8 @@ class ServerECDHNamedCurveParams(_GenericTLSSessionInheritance):
         if not s.client_kx_ecdh_params:
             s.client_kx_ecdh_params = curve
 
-    def post_dissection(self, r):
+    @crypto_validator
+    def register_pubkey(self):
         """
         XXX Support compressed point format.
         XXX Check that the pubkey received is on the curve.
@@ -560,7 +585,6 @@ class ServerECDHNamedCurveParams(_GenericTLSSessionInheritance):
         #if self.point[0] in [b'\x02', b'\x03']:
         #    point_format = 1
 
-        #if self.named_curve and self.point: #XXX remove this, probably
         curve_name = _tls_named_curves[self.named_curve]
         curve = ec._CURVE_TYPES[curve_name]()
         import_point = ec.EllipticCurvePublicNumbers.from_encoded_point
@@ -571,6 +595,12 @@ class ServerECDHNamedCurveParams(_GenericTLSSessionInheritance):
         if not s.client_kx_ecdh_params:
             s.client_kx_ecdh_params = curve
 
+    def post_dissection(self, r):
+        try:
+            self.register_pubkey()
+        except ImportError:
+            pass
+
     def guess_payload_class(self, p):
         return Padding
 
@@ -604,15 +634,10 @@ class ServerRSAParams(_GenericTLSSessionInheritance):
                     StrLenField("rsaexp", "",
                                 length_from = lambda pkt: pkt.rsaexplen) ]
 
+    @crypto_validator
     def fill_missing(self):
-        ext_k = rsa.generate_private_key(public_exponent=0x10001,
-                                         key_size=512,
-                                         backend=default_backend())
-        pem_k = ext_k.private_bytes(
-                        encoding=serialization.Encoding.PEM,
-                        format=serialization.PrivateFormat.TraditionalOpenSSL,
-                        encryption_algorithm=serialization.NoEncryption())
-        k = PrivKeyRSA(pem_k)
+        k = PrivKeyRSA()
+        k.fill_and_store(modulusLen=512)
         self.tls_session.server_tmp_rsa_key = k
         pubNum = k.pubkey.public_numbers()
 
@@ -627,12 +652,19 @@ class ServerRSAParams(_GenericTLSSessionInheritance):
         if self.rsaexplen is None:
             self.rsaexplen = len(self.rsaexp)
 
-    def post_dissection(self, pkt):
+    @crypto_validator
+    def register_pubkey(self):
         mLen = self.rsamodlen
         m    = self.rsamod
         e    = self.rsaexp
         self.tls_session.server_tmp_rsa_key = PubKeyRSA((e, m, mLen))
 
+    def post_dissection(self, pkt):
+        try:
+            self.register_pubkey()
+        except ImportError:
+            pass
+
     def guess_payload_class(self, p):
         return Padding
 
@@ -653,11 +685,6 @@ class ServerPSKParams(Packet):
                         length_from=lambda pkt: pkt.psk_identity_hint_len) ]
 
     def fill_missing(self):
-        """
-        We do not want TLSServerKeyExchange.build() to overload and recompute
-        things everytime it is called. This method can be called specifically
-        to have things filled in a smart fashion.
-        """
         pass
 
     def post_dissection(self, pkt):
@@ -675,8 +702,8 @@ class ServerPSKParams(Packet):
 
 class ClientDiffieHellmanPublic(_GenericTLSSessionInheritance):
     """
-    If the user provides a value for dh_Yc attribute,
-    the pms and ms are set accordingly when .post_build() is called.
+    If the user provides a value for dh_Yc attribute, we assume he will set
+    the pms and ms accordingly and trigger the key derivation on his own.
 
     XXX As specified in 7.4.7.2. of RFC 4346, we should distinguish the needs
     for implicit or explicit value depending on availability of DH parameters
@@ -687,24 +714,28 @@ class ClientDiffieHellmanPublic(_GenericTLSSessionInheritance):
                     StrLenField("dh_Yc", "",
                                 length_from=lambda pkt: pkt.dh_Yclen) ]
 
-    def post_build(self, pkt, pay):
+    @crypto_validator
+    def fill_missing(self):
         s = self.tls_session
-
-        if self.dh_Yc == "":
-            params = s.client_kx_ffdh_params
-            s.client_kx_privkey = params.generate_private_key()
-            pubkey = s.client_kx_privkey.public_key()
-            y = pubkey.public_numbers().y
-            self.dh_Yc = pkcs_i2osp(y, pubkey.key_size/8)
-        # else, we assume that the user wrote the client_kx_privkey by himself
-        if self.dh_Yclen is None:
-            self.dh_Yclen = len(self.dh_Yc)
+        params = s.client_kx_ffdh_params
+        s.client_kx_privkey = params.generate_private_key()
+        pubkey = s.client_kx_privkey.public_key()
+        y = pubkey.public_numbers().y
+        self.dh_Yc = pkcs_i2osp(y, pubkey.key_size/8)
 
         if s.client_kx_privkey and s.server_kx_pubkey:
             pms = s.client_kx_privkey.exchange(s.server_kx_pubkey)
             s.pre_master_secret = pms
             s.compute_ms_and_derive_keys()
 
+    def post_build(self, pkt, pay):
+        if self.dh_Yc == "":
+            try:
+                self.fill_missing()
+            except ImportError:
+                pass
+        if self.dh_Yclen is None:
+            self.dh_Yclen = len(self.dh_Yc)
         return pkcs_i2osp(self.dh_Yclen, 2) + self.dh_Yc + pay
 
     def post_dissection(self, m):
@@ -715,6 +746,7 @@ class ClientDiffieHellmanPublic(_GenericTLSSessionInheritance):
         """
         s = self.tls_session
 
+        # if there are kx params and keys, we assume the crypto library is ok
         if s.client_kx_ffdh_params:
             y = pkcs_os2ip(self.dh_Yc)
             param_numbers = s.client_kx_ffdh_params.parameter_numbers()
@@ -739,33 +771,38 @@ class ClientECDiffieHellmanPublic(_GenericTLSSessionInheritance):
                     StrLenField("ecdh_Yc", "",
                                 length_from=lambda pkt: pkt.ecdh_Yclen)]
 
-    def post_build(self, pkt, pay):
+    @crypto_validator
+    def fill_missing(self):
         s = self.tls_session
-
-        if self.ecdh_Yc == "":
-            params = s.client_kx_ecdh_params
-            s.client_kx_privkey = ec.generate_private_key(params,
-                                                          default_backend())
-            pubkey = s.client_kx_privkey.public_key()
-            x = pubkey.public_numbers().x
-            y = pubkey.public_numbers().y
-            self.ecdh_Yc = (b"\x04" +
-                            pkcs_i2osp(x, params.key_size/8) +
-                            pkcs_i2osp(y, params.key_size/8))
-        # else, we assume that the user wrote the client_kx_privkey by himself
-        if self.ecdh_Yclen is None:
-            self.ecdh_Yclen = len(self.ecdh_Yc)
+        params = s.client_kx_ecdh_params
+        s.client_kx_privkey = ec.generate_private_key(params,
+                                                      default_backend())
+        pubkey = s.client_kx_privkey.public_key()
+        x = pubkey.public_numbers().x
+        y = pubkey.public_numbers().y
+        self.ecdh_Yc = (b"\x04" +
+                        pkcs_i2osp(x, params.key_size/8) +
+                        pkcs_i2osp(y, params.key_size/8))
 
         if s.client_kx_privkey and s.server_kx_pubkey:
             pms = s.client_kx_privkey.exchange(ec.ECDH(), s.server_kx_pubkey)
             s.pre_master_secret = pms
             s.compute_ms_and_derive_keys()
 
+    def post_build(self, pkt, pay):
+        if self.ecdh_Yc == "":
+            try:
+                self.fill_missing()
+            except ImportError:
+                pass
+        if self.ecdh_Yclen is None:
+            self.ecdh_Yclen = len(self.ecdh_Yc)
         return pkcs_i2osp(self.ecdh_Yclen, 1) + self.ecdh_Yc + pay
 
     def post_dissection(self, m):
         s = self.tls_session
 
+        # if there are kx params and keys, we assume the crypto library is ok
         if s.client_kx_ecdh_params:
             import_point = ec.EllipticCurvePublicNumbers.from_encoded_point
             pub_num = import_point(s.client_kx_ecdh_params, self.ecdh_Yc)
@@ -779,6 +816,17 @@ class ClientECDiffieHellmanPublic(_GenericTLSSessionInheritance):
 
 ### RSA Encryption (standard & export)
 
+class _UnEncryptedPreMasterSecret(Raw):
+    """
+    When the content of an EncryptedPreMasterSecret could not be deciphered,
+    we use this class to represent the encrypted data.
+    """
+    name = "RSA Encrypted PreMaster Secret (protected)"
+    def __init__(self, *args, **kargs):
+        if 'tls_session' in kargs:
+            del(kargs['tls_session'])
+        return super(_UnEncryptedPreMasterSecret, self).__init__(*args, **kargs)
+
 class EncryptedPreMasterSecret(_GenericTLSSessionInheritance):
     """
     Pay attention to implementation notes in section 7.4.7.1 of RFC 5246.
@@ -788,6 +836,14 @@ class EncryptedPreMasterSecret(_GenericTLSSessionInheritance):
                                            _tls_version),
                     StrFixedLenField("random", None, 46) ]
 
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        if 'tls_session' in kargs:
+            s = kargs['tls_session']
+            if s.server_tmp_rsa_key is None and s.server_rsa_key is None:
+                return _UnEncryptedPreMasterSecret
+        return EncryptedPreMasterSecret
+
     def pre_dissect(self, m):
         s = self.tls_session
         tbd = m
@@ -808,7 +864,8 @@ class EncryptedPreMasterSecret(_GenericTLSSessionInheritance):
             decrypted = s.server_rsa_key.decrypt(tbd)
             pms = decrypted[-48:]
         else:
-            pms = b"\x00"*48     # Hack but we should not be there anyway
+            # the dispatch_hook is supposed to prevent this case
+            pms = b"\x00"*48
             err = "No server RSA key to decrypt Pre Master Secret. Skipping."
             warning(err)
 
@@ -831,9 +888,9 @@ class EncryptedPreMasterSecret(_GenericTLSSessionInheritance):
         s.compute_ms_and_derive_keys()
 
         if s.server_tmp_rsa_key is not None:
-            enc = s.server_tmp_rsa_key.encrypt(pkt, "pkcs")
+            enc = s.server_tmp_rsa_key.encrypt(pkt, t="pkcs")
         elif s.server_certs is not None and len(s.server_certs) > 0:
-            enc = s.server_certs[0].encrypt(pkt, "pkcs")
+            enc = s.server_certs[0].encrypt(pkt, t="pkcs")
         else:
             warning("No material to encrypt Pre Master Secret")
 
diff --git a/scapy/layers/tls/keyexchange_tls13.py b/scapy/layers/tls/keyexchange_tls13.py
new file mode 100644
index 0000000000000000000000000000000000000000..07202a51a682cc0220cc094e7fef8c8f784c6a3d
--- /dev/null
+++ b/scapy/layers/tls/keyexchange_tls13.py
@@ -0,0 +1,283 @@
+## This file is part of Scapy
+## Copyright (C) 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+TLS 1.3 key exchange logic.
+"""
+
+from __future__ import print_function
+import math
+
+from scapy.config import conf, crypto_validator
+from scapy.error import warning
+from scapy.fields import *
+from scapy.packet import Packet, Raw, Padding
+from scapy.layers.tls.cert import PubKeyRSA, PrivKeyRSA
+from scapy.layers.tls.session import _GenericTLSSessionInheritance
+from scapy.layers.tls.basefields import _tls_version, _TLSClientVersionField
+from scapy.layers.tls.extensions import TLS_Ext_Unknown, _tls_ext
+from scapy.layers.tls.crypto.pkcs1 import pkcs_i2osp, pkcs_os2ip
+from scapy.layers.tls.crypto.groups import (_tls_named_ffdh_groups,
+                                            _tls_named_curves, _ffdh_groups,
+                                            _tls_named_groups)
+
+if conf.crypto_valid:
+    from cryptography.hazmat.backends import default_backend
+    from cryptography.hazmat.primitives.asymmetric import dh, ec
+if conf.crypto_valid_advanced:
+    from cryptography.hazmat.primitives.asymmetric import x25519
+
+
+class KeyShareEntry(Packet):
+    """
+    When building from scratch, we create a DH private key, and when
+    dissecting, we create a DH public key. Default group is secp256r1.
+    """
+    __slots__ = ["privkey", "pubkey"]
+    name = "Key Share Entry"
+    fields_desc = [ShortEnumField("group", None, _tls_named_groups),
+                   FieldLenField("kxlen", None, length_of="key_exchange"),
+                   StrLenField("key_exchange", "",
+                               length_from=lambda pkt: pkt.kxlen) ]
+
+    def __init__(self, *args, **kargs):
+        self.privkey = None
+        self.pubkey = None
+        super(KeyShareEntry, self).__init__(*args, **kargs)
+
+    def do_build(self):
+        """
+        We need this hack, else 'self' would be replaced by __iter__.next().
+        """
+        tmp = self.explicit
+        self.explicit = True
+        b = super(KeyShareEntry, self).do_build()
+        self.explicit = tmp
+        return b
+
+    @crypto_validator
+    def create_privkey(self):
+        """
+        This is called by post_build() for key creation.
+        """
+        if self.group in _tls_named_ffdh_groups:
+            params = _ffdh_groups[_tls_named_ffdh_groups[self.group]][0]
+            privkey = params.generate_private_key()
+            self.privkey = privkey
+            pubkey = privkey.public_key()
+            self.key_exchange = pubkey.public_numbers().y
+        elif self.group in _tls_named_curves:
+            if _tls_named_curves[self.group] == "x25519":
+                if conf.crypto_valid_advanced:
+                    privkey = x25519.X25519PrivateKey.generate()
+                    self.privkey = privkey
+                    pubkey = privkey.public_key()
+                    self.key_exchange = pubkey.public_bytes()
+            elif _tls_named_curves[self.group] != "x448":
+                curve = ec._CURVE_TYPES[_tls_named_curves[self.group]]()
+                privkey = ec.generate_private_key(curve, default_backend())
+                self.privkey = privkey
+                pubkey = privkey.public_key()
+                self.key_exchange = pubkey.public_numbers().encode_point()
+
+    def post_build(self, pkt, pay):
+        if self.group is None:
+            self.group = 23     # secp256r1
+
+        if self.key_exchange == "":
+            try:
+                self.create_privkey()
+            except ImportError:
+                pass
+
+        if self.kxlen is None:
+            self.kxlen = len(self.key_exchange)
+
+        group = struct.pack("!H", self.group)
+        kxlen = struct.pack("!H", self.kxlen)
+        return group + kxlen + self.key_exchange + pay
+
+    @crypto_validator
+    def register_pubkey(self):
+        if self.group in _tls_named_ffdh_groups:
+            params = _ffdh_groups[_tls_named_ffdh_groups[self.group]][0]
+            pn = params.parameter_numbers()
+            public_numbers = dh.DHPublicNumbers(self.key_exchange, pn)
+            self.pubkey = public_numbers.public_key(default_backend())
+        elif self.group in _tls_named_curves:
+            if _tls_named_curves[self.group] == "x25519":
+                if conf.crypto_valid_advanced:
+                    import_point = x25519.X25519PublicKey.from_public_bytes
+                    self.pubkey = import_point(self.key_exchange)
+            elif _tls_named_curves[self.group] != "x448":
+                curve = ec._CURVE_TYPES[_tls_named_curves[self.group]]()
+                import_point = ec.EllipticCurvePublicNumbers.from_encoded_point
+                public_numbers = import_point(curve, self.key_exchange)
+                self.pubkey = public_numbers.public_key(default_backend())
+
+    def post_dissection(self, r):
+        try:
+            self.register_pubkey()
+        except ImportError:
+            pass
+
+    def extract_padding(self, s):
+        return "", s
+
+
+class TLS_Ext_KeyShare_CH(TLS_Ext_Unknown):
+    name = "TLS Extension - Key Share (for ClientHello)"
+    fields_desc = [ShortEnumField("type", 0x28, _tls_ext),
+                   ShortField("len", None),
+                   FieldLenField("client_shares_len", None,
+                                 length_of="client_shares"),
+                   PacketListField("client_shares", [], KeyShareEntry,
+                            length_from=lambda pkt: pkt.client_shares_len) ]
+
+    def post_build(self, pkt, pay):
+        if not self.tls_session.frozen:
+            privshares = self.tls_session.tls13_client_privshares
+            for kse in self.client_shares:
+                if kse.privkey:
+                    if _tls_named_curves[kse.group] in privshares:
+                        print("Group %s used twice in the same ClientHello!" % kse.group)
+                        break
+                    privshares[_tls_named_groups[kse.group]] = kse.privkey
+        return super(TLS_Ext_KeyShare_CH, self).post_build(pkt, pay)
+
+    def post_dissection(self, r):
+        if not self.tls_session.frozen:
+            for kse in self.client_shares:
+                if kse.pubkey:
+                    pubshares = self.tls_session.tls13_client_pubshares
+                    if _tls_named_curves[kse.group] in pubshares:
+                        print("Group %s used twice in the same ClientHello!" % kse.group)
+                        break
+                    pubshares[_tls_named_curves[kse.group]] = kse.pubkey
+        return super(TLS_Ext_KeyShare_CH, self).post_dissection(r)
+
+
+class TLS_Ext_KeyShare_HRR(TLS_Ext_Unknown):
+    name = "TLS Extension - Key Share (for HelloRetryRequest)"
+    fields_desc = [ShortEnumField("type", 0x28, _tls_ext),
+                   ShortField("len", None),
+                   ShortEnumField("selected_group", None, _tls_named_groups) ]
+
+
+class TLS_Ext_KeyShare_SH(TLS_Ext_Unknown):
+    name = "TLS Extension - Key Share (for ServerHello)"
+    fields_desc = [ShortEnumField("type", 0x28, _tls_ext),
+                   ShortField("len", None),
+                   PacketField("server_share", None, KeyShareEntry) ]
+
+    def post_build(self, pkt, pay):
+        if not self.tls_session.frozen and self.server_share.privkey:
+            # if there is a privkey, we assume the crypto library is ok
+            privshare = self.tls_session.tls13_server_privshare
+            if len(privshare) > 0:
+                print("Server key share was already stored...?")
+            group_name = _tls_named_groups[self.server_share.group]
+            privshare[group_name] = self.server_share.privkey
+
+            if group_name in self.tls_session.tls13_client_pubshares:
+                privkey = self.server_share.privkey
+                pubkey = self.tls_session.tls13_client_pubshares[group_name]
+                if group_name in _tls_named_ffdh_groups.itervalues():
+                    pms = privkey.exchange(pubkey)
+                elif group_name in _tls_named_curves.itervalues():
+                    if group_name == "x25519":
+                        pms = privkey.exchange(pubkey)
+                    else:
+                        pms = privkey.exchange(ec.ECDH(), pubkey)
+                self.tls_session.tls13_dhe_secret = pms
+        return super(TLS_Ext_KeyShare_SH, self).post_build(pkt, pay)
+
+    def post_dissection(self, r):
+        if not self.tls_session.frozen and self.server_share.pubkey:
+            # if there is a pubkey, we assume the crypto library is ok
+            pubshare = self.tls_session.tls13_server_pubshare
+            if len(pubshare) > 0:
+                print("Server key share was already stored...?")
+            group_name = _tls_named_groups[self.server_share.group]
+            pubshare[group_name] = self.server_share.pubkey
+
+            if group_name in self.tls_session.tls13_client_privshares:
+                pubkey = self.server_share.pubkey
+                privkey = self.tls_session.tls13_client_privshares[group_name]
+                if group_name in _tls_named_ffdh_groups.itervalues():
+                    pms = privkey.exchange(pubkey)
+                elif group_name in _tls_named_curves.itervalues():
+                    if group_name == "x25519":
+                        pms = privkey.exchange(pubkey)
+                    else:
+                        pms = privkey.exchange(ec.ECDH(), pubkey)
+                self.tls_session.tls13_dhe_secret = pms
+        return super(TLS_Ext_KeyShare_SH, self).post_dissection(r)
+
+
+_tls_ext_keyshare_cls  = { 1: TLS_Ext_KeyShare_CH,
+                           2: TLS_Ext_KeyShare_SH,
+                           6: TLS_Ext_KeyShare_HRR }
+
+
+class Ticket(Packet):
+    name = "Recommended Ticket Construction (from RFC 5077)"
+    fields_desc = [ StrFixedLenField("key_name", None, 16),
+                    StrFixedLenField("iv", None, 16),
+                    FieldLenField("encstatelen", None, length_of="encstate"),
+                    StrLenField("encstate", "",
+                                length_from=lambda pkt: pkt.encstatelen),
+                    StrFixedLenField("mac", None, 32) ]
+
+class TicketField(PacketField):
+    __slots__ = ["length_from"]
+    def __init__(self, name, default, length_from=None, **kargs):
+        self.length_from = length_from
+        PacketField.__init__(self, name, default, Ticket, **kargs)
+
+    def m2i(self, pkt, m):
+        l = self.length_from(pkt)
+        tbd, rem = m[:l], m[l:]
+        return self.cls(tbd)/Padding(rem)
+
+class PSKIdentity(Packet):
+    name = "PSK Identity"
+    fields_desc = [FieldLenField("identity_len", None,
+                                 length_of="identity"),
+                   TicketField("identity", "",
+                               length_from=lambda pkt: pkt.identity_len),
+                   IntField("obfuscated_ticket_age", 0) ]
+
+class PSKBinderEntry(Packet):
+    name = "PSK Binder Entry"
+    fields_desc = [FieldLenField("binder_len", None, fmt="B",
+                                 length_of="binder"),
+                   StrLenField("binder", "",
+                               length_from=lambda pkt: pkt.binder_len) ]
+
+class TLS_Ext_PreSharedKey_CH(TLS_Ext_Unknown):
+    #XXX define post_build and post_dissection methods
+    name = "TLS Extension - Pre Shared Key (for ClientHello)"
+    fields_desc = [ShortEnumField("type", 0x28, _tls_ext),
+                   ShortField("len", None),
+                   FieldLenField("identities_len", None,
+                                 length_of="identities"),
+                   PacketListField("identities", [], PSKIdentity,
+                            length_from=lambda pkt: pkt.identities_len),
+                   FieldLenField("binders_len", None,
+                                 length_of="binders"),
+                   PacketListField("binders", [], PSKBinderEntry,
+                            length_from=lambda pkt: pkt.binders_len) ]
+
+
+class TLS_Ext_PreSharedKey_SH(TLS_Ext_Unknown):
+    name = "TLS Extension - Pre Shared Key (for ServerHello)"
+    fields_desc = [ShortEnumField("type", 0x29, _tls_ext),
+                   ShortField("len", None),
+                   ShortField("selected_identity", None) ]
+
+
+_tls_ext_presharedkey_cls  = { 1: TLS_Ext_PreSharedKey_CH,
+                               2: TLS_Ext_PreSharedKey_SH }
+
diff --git a/scapy/layers/tls/record.py b/scapy/layers/tls/record.py
index 0705d72a118eeec469c0d3eed50f9dd4702cac70..4526b637a9a18fcc841d50e60abcc34060ef7117 100644
--- a/scapy/layers/tls/record.py
+++ b/scapy/layers/tls/record.py
@@ -1,6 +1,6 @@
 ## This file is part of Scapy
 ## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
-##                     2015, 2016 Maxence Tury
+##               2015, 2016, 2017 Maxence Tury
 ## This program is published under a GPLv2 license
 
 """
@@ -20,7 +20,8 @@ from scapy.fields import *
 from scapy.packet import *
 from scapy.layers.inet import TCP
 from scapy.layers.tls.session import _GenericTLSSessionInheritance
-from scapy.layers.tls.handshake import _tls_handshake_cls, _TLSHandshake
+from scapy.layers.tls.handshake import (_tls_handshake_cls, _TLSHandshake,
+                                        TLS13ServerHello)
 from scapy.layers.tls.basefields import (_TLSVersionField, _tls_version,
                                          _TLSIVField, _TLSMACField,
                                          _TLSPadField, _TLSPadLenField,
@@ -28,6 +29,8 @@ from scapy.layers.tls.basefields import (_TLSVersionField, _tls_version,
 from scapy.layers.tls.crypto.pkcs1 import randstring, pkcs_i2osp
 from scapy.layers.tls.crypto.compression import Comp_NULL
 from scapy.layers.tls.crypto.cipher_aead import AEADTagError
+if conf.crypto_valid_advanced:
+    from scapy.layers.tls.crypto.cipher_aead import Cipher_CHACHA20_POLY1305
 from scapy.layers.tls.crypto.cipher_stream import Cipher_NULL
 from scapy.layers.tls.crypto.ciphers import CipherError
 from scapy.layers.tls.crypto.h_mac import HMACError
@@ -55,8 +58,15 @@ class _TLSMsgListField(PacketListField):
     we inherit from PacketListField.
     """
     def __init__(self, name, default, length_from=None):
-        PacketListField.__init__(self, name, default, cls=None,
-                                 length_from=length_from)
+        if not length_from:
+            length_from = self._get_length
+        super(_TLSMsgListField, self).__init__(name, default, cls=None,
+                                               length_from=length_from)
+
+    def _get_length(self, pkt):
+        if pkt.deciphered_len is None:
+            return pkt.len
+        return pkt.deciphered_len
 
     def m2i(self, pkt, m):
         """
@@ -78,7 +88,10 @@ class _TLSMsgListField(PacketListField):
         if cls is Raw:
             return Raw(m)
         else:
-            return cls(m, tls_session=pkt.tls_session)
+            try:
+                return cls(m, tls_session=pkt.tls_session)
+            except:
+                return Raw(m)
 
     def getfield(self, pkt, s):
         """
@@ -102,9 +115,15 @@ class _TLSMsgListField(PacketListField):
         if l is not None:
             remain, ret = s[:l], s[l:]
 
-        if pkt.decipherable:
-            if remain == "":
+        if remain == "":
+            if pkt.tls_session.tls_version > 0x0200 and pkt.type == 23:
                 return ret, [TLSApplicationData(data="")]
+            else:
+                return ret, [Raw(load="")]
+
+        if False in pkt.tls_session.rcs.cipher.ready.itervalues():
+            return ret, _TLSEncryptedContent(remain)
+        else:
             while remain:
                 raw_msg = remain
                 p = self.m2i(pkt, remain)
@@ -117,13 +136,12 @@ class _TLSMsgListField(PacketListField):
                 else:
                     remain = ""
 
-                if not isinstance(p, Raw):
-                    p.post_dissection_tls_session_update(raw_msg)
+                if isinstance(p, _GenericTLSSessionInheritance):
+                    if not p.tls_session.frozen:
+                        p.post_dissection_tls_session_update(raw_msg)
 
                 lst.append(p)
             return remain + ret, lst
-        else:
-            return ret, _TLSEncryptedContent(remain)
 
     def i2m(self, pkt, p):
        """
@@ -142,9 +160,13 @@ class _TLSMsgListField(PacketListField):
                elif isinstance(p, TLSApplicationData):
                    pkt.type = 23
            p.tls_session = pkt.tls_session
-           cur = str(p)
-           p.post_build_tls_session_update(cur)
+           if not pkt.tls_session.frozen:
+               cur = p.str_stateful()
+               p.post_build_tls_session_update(cur)
+           else:
+               cur = str(p)
        else:
+           pkt.type = 23
            cur = str(p)
        return cur
 
@@ -156,6 +178,10 @@ class _TLSMsgListField(PacketListField):
         res = ""
         for p in val:
             res += self.i2m(pkt, p)
+        if (isinstance(pkt, _GenericTLSSessionInheritance) and
+            pkt.tls_session.tls_version >= 0x0304 and
+            not isinstance(pkt, TLS13ServerHello)):
+                return s + res
         if not pkt.type:
             pkt.type = 0
         hdr = struct.pack("!B", pkt.type) + s[1:5]
@@ -170,7 +196,13 @@ class TLS(_GenericTLSSessionInheritance):
     In .pre_dissect(), according to the type of the current cipher algorithm
     (self.tls_session.rcs.cipher.type), we extract the 'iv', 'mac', 'pad' and
     'padlen'. Some of these fields may remain blank: for instance, when using
-    a stream cipher, there is no IV nor any padding.
+    a stream cipher, there is no IV nor any padding. The 'len' should always
+    hold the length of the ciphered message; for the plaintext version, you
+    should rely on the additional 'deciphered_len' attribute.
+
+    XXX Fix 'deciphered_len' which should not be defined when failing with
+    AEAD decryption. This is related to the 'decryption_success' below.
+    Also, follow this behaviour in record_sslv2.py and record_tls13.py
 
     Once we have isolated the ciphered message aggregate (which should be one
     or several TLS messages of the same type), we try to decipher it. Either we
@@ -187,47 +219,66 @@ class TLS(_GenericTLSSessionInheritance):
         t2 = TLS(<server_hello | certificate | server_key_exchange>)
         t3 = TLS(<server_hello | certificate | server_key_exchange>,
                  tls_session=t1.tls_session)
-    As no context was passed to t2, neither was any client_random. Hence scapy
+    (Note that to do things properly, here 't1.tls_session' should actually be
+    't1.tls_session.mirror()'. See session.py for explanations.)
+
+    As no context was passed to t2, neither was any client_random. Hence Scapy
     will not be able to verify the signature of the server_key_exchange inside
     t2. However, it should be able to do so for t3, thanks to the tls_session.
     The consequence of not having a complete TLS context is even more obvious
     when trying to parse ciphered content, as we decribed before.
 
-    Thus, in order to parse TLS-protected communications with scapy:
-    _either scapy reads every message from one side of the TLS connection and
+    Thus, in order to parse TLS-protected communications with Scapy:
+    _either Scapy reads every message from one side of the TLS connection and
     builds every message from the other side (as such, it should know the
     secrets needed for the generation of the pre_master_secret), while passing
     the same tls_session context (this is how our automaton.py mostly works);
-    _or, if scapy did not build any TLS message, it has to create a TLS context
+    _or, if Scapy did not build any TLS message, it has to create a TLS context
     and feed it with secrets retrieved by whatever technique. Note that the
     knowing the private key of the server certificate will not be sufficient
     if a PFS ciphersuite was used. However, if you got a master_secret somehow,
-    use it with tls_session.(w|r)cs.derive_keys() and leave the rest to scapy.
+    use it with tls_session.(w|r)cs.derive_keys() and leave the rest to Scapy.
 
-    When building a TLS message, we expect the tls_session to have the right
-    parameters for ciphering. Else, .post_build() might fail.
+    When building a TLS message with str_stateful, we expect the tls_session to
+    have the right parameters for ciphering. Else, .post_build() might fail.
     """
-    __slots__ = ["decipherable"]
+    __slots__ = ["deciphered_len"]
     name = "TLS"
     fields_desc = [ ByteEnumField("type", None, _tls_type),
                     _TLSVersionField("version", None, _tls_version),
                     _TLSLengthField("len", None),
                     _TLSIVField("iv", None),
-                    _TLSMsgListField("msg", None,
-                                     length_from=lambda pkt: pkt.len),
+                    _TLSMsgListField("msg", []),
                     _TLSMACField("mac", None),
                     _TLSPadField("pad", None),
                     _TLSPadLenField("padlen", None) ]
 
     def __init__(self, *args, **kargs):
-        """
-        As long as 'decipherable' is True, _TLSMsgListField will try to
-        decipher the content of the TLS message. Else, it will simply
-        store/deliver the ciphered version.
-        """
-        self.decipherable = True
-        _GenericTLSSessionInheritance.__init__(self, *args, **kargs)
-
+        self.deciphered_len = kargs.get("deciphered_len", None)
+        super(TLS, self).__init__(*args, **kargs)
+
+    @classmethod
+    def dispatch_hook(cls, _pkt=None, *args, **kargs):
+        """
+        If the TLS class was called on raw SSLv2 data, we want to return an
+        SSLv2 record instance. We acknowledge the risk of SSLv2 packets with a
+        msglen of 0x1403, 0x1503, 0x1603 or 0x1703 which will never be casted
+        as SSLv2 records but TLS ones instead, but hey, we can't be held
+        responsible for low-minded extensibility choices.
+        """
+        if _pkt and len(_pkt) >= 2:
+            byte0 = struct.unpack("B", _pkt[0])[0]
+            byte1 = struct.unpack("B", _pkt[1])[0]
+            if (byte0 not in _tls_type) or (byte1 != 3):
+                from scapy.layers.tls.record_sslv2 import SSLv2
+                return SSLv2
+            else:
+                s = kargs.get("tls_session", None)
+                if s and s.tls_version >= 0x0304:
+                    if s.rcs and not isinstance(s.rcs.cipher, Cipher_NULL):
+                        from scapy.layers.tls.record_tls13 import TLS13
+                        return TLS13
+        return TLS
 
     ### Parsing methods
 
@@ -246,9 +297,9 @@ class TLS(_GenericTLSSessionInheritance):
             # this is why we need to look into the provided hdr.
             add_data = read_seq_num + hdr[0] + hdr[1:3]
             # Last two bytes of add_data are appended by the return function
-            return self.tls_session.rcs.cipher.auth_decrypt(add_data, s)
+            return self.tls_session.rcs.cipher.auth_decrypt(add_data, s,
+                                                            read_seq_num)
         except CipherError as e:
-            self.decipherable = False
             return e.args
         except AEADTagError as e:
             print("INTEGRITY CHECK FAILED")
@@ -259,14 +310,10 @@ class TLS(_GenericTLSSessionInheritance):
         Provided with stream- or block-ciphered data, return the clear version.
         The cipher should have been updated with the right IV early on,
         which should not be at the beginning of the input.
-        Note that we still return the slicing of the original input
-        in case of decryption failure.
+        In case of decryption failure, a CipherError will be raised with
+        the slicing of the original input as first argument.
         """
-        try:
-            return self.tls_session.rcs.cipher.decrypt(s)
-        except CipherError as e:
-            self.decipherable = False
-            return e.args
+        return self.tls_session.rcs.cipher.decrypt(s)
 
     def _tls_hmac_verify(self, hdr, msg, mac):
         """
@@ -279,16 +326,16 @@ class TLS(_GenericTLSSessionInheritance):
         It would fail with an AEAD cipher, because rcs.hmac would be None.
         See RFC 5246, section 6.2.3.
         """
+        read_seq_num = struct.pack("!Q", self.tls_session.rcs.seq_num)
+        self.tls_session.rcs.seq_num += 1
+
         mac_len = self.tls_session.rcs.mac_len
         if mac_len == 0:            # should be TLS_NULL_WITH_NULL_NULL
             return True
         if len(mac) != mac_len:
             return False
 
-        read_seq_num = struct.pack("!Q", self.tls_session.rcs.seq_num)
-        self.tls_session.rcs.seq_num += 1
         alg = self.tls_session.rcs.hmac
-
         version = struct.unpack("!H", hdr[1:3])[0]
         try:
             if version > 0x300:
@@ -324,6 +371,8 @@ class TLS(_GenericTLSSessionInheritance):
         hdr, efrag, r = s[:5], s[5:5+msglen], s[msglen+5:]
 
         iv = mac = pad = ""
+        self.padlen = None
+        decryption_success = False
 
         cipher_type = self.tls_session.rcs.cipher.type
 
@@ -331,93 +380,115 @@ class TLS(_GenericTLSSessionInheritance):
             version = struct.unpack("!H", s[1:3])[0]
 
             # Decrypt
-            if version >= 0x0302:
-                # Explicit IV for TLS 1.1 and 1.2
-                block_size = self.tls_session.rcs.cipher.block_size
-                iv, efrag = efrag[:block_size], efrag[block_size:]
-                self.tls_session.rcs.cipher.iv = iv
-                pfrag = self._tls_decrypt(efrag)
-                hdr = hdr[:3] + struct.pack("!H", len(pfrag))
-            else:
-                # Implicit IV for SSLv3 and TLS 1.0
-                pfrag = self._tls_decrypt(efrag)
-
-            # Excerpt below better corresponds to TLS 1.1 IV definition,
-            # but the result is the same as with TLS 1.2 anyway.
-            # This leading *IV* has been decrypted by _tls_decrypt with a
-            # random IV, hence it does not correspond to anything.
-            # What actually matters is that we got the first encrypted block
-            # in order to decrypt the second block (first data block).
-            #if version >= 0x0302:
-            #    block_size = self.tls_session.rcs.cipher.block_size
-            #    iv, pfrag = pfrag[:block_size], pfrag[block_size:]
-            #    l = struct.unpack('!H', hdr[3:5])[0]
-            #    hdr = hdr[:3] + struct.pack('!H', l-block_size)
-
-            # Extract padding ('pad' actually includes the trailing padlen)
-            padlen = ord(pfrag[-1]) + 1
-            mfrag, pad = pfrag[:-padlen], pfrag[-padlen:]
-
-            # Extract MAC
-            l = self.tls_session.rcs.mac_len
-            if l != 0:
-                cfrag, mac = mfrag[:-l], mfrag[-l:]
+            try:
+                if version >= 0x0302:
+                    # Explicit IV for TLS 1.1 and 1.2
+                    block_size = self.tls_session.rcs.cipher.block_size
+                    iv, efrag = efrag[:block_size], efrag[block_size:]
+                    self.tls_session.rcs.cipher.iv = iv
+                    pfrag = self._tls_decrypt(efrag)
+                else:
+                    # Implicit IV for SSLv3 and TLS 1.0
+                    pfrag = self._tls_decrypt(efrag)
+            except CipherError as e:
+                # This will end up dissected as _TLSEncryptedContent.
+                cfrag = e.args[0]
             else:
-                cfrag, mac = mfrag, ""
+                decryption_success = True
+                # Excerpt below better corresponds to TLS 1.1 IV definition,
+                # but the result is the same as with TLS 1.2 anyway.
+                # This leading *IV* has been decrypted by _tls_decrypt with a
+                # random IV, hence it does not correspond to anything.
+                # What actually matters is that we got the first encrypted block
+                # in order to decrypt the second block (first data block).
+                #if version >= 0x0302:
+                #    block_size = self.tls_session.rcs.cipher.block_size
+                #    iv, pfrag = pfrag[:block_size], pfrag[block_size:]
+                #    l = struct.unpack('!H', hdr[3:5])[0]
+                #    hdr = hdr[:3] + struct.pack('!H', l-block_size)
+
+                # Extract padding ('pad' actually includes the trailing padlen)
+                padlen = ord(pfrag[-1]) + 1
+                mfrag, pad = pfrag[:-padlen], pfrag[-padlen:]
+                self.padlen = padlen
+
+                # Extract MAC
+                l = self.tls_session.rcs.mac_len
+                if l != 0:
+                    cfrag, mac = mfrag[:-l], mfrag[-l:]
+                else:
+                    cfrag, mac = mfrag, ""
 
-            # Verify integrity
-            hdr = hdr[:3] + struct.pack('!H', len(cfrag))
-            is_mac_ok = self._tls_hmac_verify(hdr, cfrag, mac)
-            if not is_mac_ok:
-                print("INTEGRITY CHECK FAILED")
+                # Verify integrity
+                chdr = hdr[:3] + struct.pack('!H', len(cfrag))
+                is_mac_ok = self._tls_hmac_verify(chdr, cfrag, mac)
+                if not is_mac_ok:
+                    print("INTEGRITY CHECK FAILED")
 
         elif cipher_type == 'stream':
             # Decrypt
-            pfrag = self._tls_decrypt(efrag)
-            mfrag = pfrag
-
-            # Extract MAC
-            l = self.tls_session.rcs.mac_len
-            if l != 0:
-                cfrag, mac = mfrag[:-l], mfrag[-l:]
+            try:
+                pfrag = self._tls_decrypt(efrag)
+            except CipherError as e:
+                # This will end up dissected as _TLSEncryptedContent.
+                cfrag = e.args[0]
             else:
-                cfrag, mac = mfrag, ""
+                decryption_success = True
+                mfrag = pfrag
 
-            # Verify integrity
-            hdr = hdr[:3] + struct.pack('!H', len(cfrag))
-            is_mac_ok = self._tls_hmac_verify(hdr, cfrag, mac)
-            if not is_mac_ok:
-                print("INTEGRITY CHECK FAILED")
+                # Extract MAC
+                l = self.tls_session.rcs.mac_len
+                if l != 0:
+                    cfrag, mac = mfrag[:-l], mfrag[-l:]
+                else:
+                    cfrag, mac = mfrag, ""
+
+                # Verify integrity
+                chdr = hdr[:3] + struct.pack('!H', len(cfrag))
+                is_mac_ok = self._tls_hmac_verify(chdr, cfrag, mac)
+                if not is_mac_ok:
+                    print("INTEGRITY CHECK FAILED")
 
         elif cipher_type == 'aead':
             # Authenticated encryption
             # crypto/cipher_aead.py prints a warning for integrity failure
-            iv, cfrag, mac = self._tls_auth_decrypt(hdr, efrag)
+            if (conf.crypto_valid_advanced and
+                isinstance(self.tls_session.rcs.cipher, Cipher_CHACHA20_POLY1305)):
+                iv = ""
+                cfrag, mac = self._tls_auth_decrypt(hdr, efrag)
+            else:
+                iv, cfrag, mac = self._tls_auth_decrypt(hdr, efrag)
+            decryption_success = True       # see XXX above
+
+        frag = self._tls_decompress(cfrag)
 
-        if self.decipherable:
-            frag = self._tls_decompress(cfrag)
+        if (decryption_success and
+            not isinstance(self.tls_session.rcs.cipher, Cipher_NULL)):
+            self.deciphered_len = len(frag)
         else:
-            frag = cfrag
+            self.deciphered_len = None
 
         reconstructed_body = iv + frag + mac + pad
 
-        l = len(frag)
-        # note that we do not include the MAC, only the content
-        hdr = hdr[:3] + struct.pack("!H", l)
-
         return hdr + reconstructed_body + r
 
     def post_dissect(self, s):
         """
-        Commit the pending read state if it has been triggered.
-        We update nothing if the prcs was not set, as this probably means that
-        we're working out-of-context (and we need to keep the default rcs).
+        Commit the pending r/w state if it has been triggered (e.g. by an
+        underlying TLSChangeCipherSpec or a SSLv2ClientMasterKey). We update
+        nothing if the prcs was not set, as this probably means that we're
+        working out-of-context (and we need to keep the default rcs).
         """
         if self.tls_session.triggered_prcs_commit:
             if self.tls_session.prcs is not None:
                 self.tls_session.rcs = self.tls_session.prcs
                 self.tls_session.prcs = None
             self.tls_session.triggered_prcs_commit = False
+        if self.tls_session.triggered_pwcs_commit:
+            if self.tls_session.pwcs is not None:
+                self.tls_session.wcs = self.tls_session.pwcs
+                self.tls_session.pwcs = None
+            self.tls_session.triggered_pwcs_commit = False
         return s
 
     def do_dissect_payload(self, s):
@@ -458,7 +529,8 @@ class TLS(_GenericTLSSessionInheritance):
                     pkcs_i2osp(self.type, 1) +
                     pkcs_i2osp(self.version, 2) +
                     pkcs_i2osp(len(s), 2))
-        return self.tls_session.wcs.cipher.auth_encrypt(s, add_data)
+        return self.tls_session.wcs.cipher.auth_encrypt(s, add_data,
+                                                        write_seq_num)
 
     def _tls_hmac_add(self, hdr, msg):
         """
@@ -472,8 +544,8 @@ class TLS(_GenericTLSSessionInheritance):
         """
         write_seq_num = struct.pack("!Q", self.tls_session.wcs.seq_num)
         self.tls_session.wcs.seq_num += 1
-        alg = self.tls_session.wcs.hmac
 
+        alg = self.tls_session.wcs.hmac
         version = struct.unpack("!H", hdr[1:3])[0]
         if version > 0x300:
             h = alg.digest(write_seq_num + hdr + msg)
@@ -562,15 +634,6 @@ class TLS(_GenericTLSSessionInheritance):
             # Authenticated encryption (with nonce_explicit as header)
             efrag = self._tls_auth_encrypt(cfrag)
 
-        # Now, we can commit pending write state if needed
-        # We update nothing if the pwcs was not set. This probably means that
-        # we're working out-of-context (and we need to keep the default wcs).
-        if self.tls_session.triggered_pwcs_commit:
-            if self.tls_session.pwcs is not None:
-                self.tls_session.wcs = self.tls_session.pwcs
-                self.tls_session.pwcs = None
-            self.tls_session.triggered_pwcs_commit = False
-
         if self.len is not None:
             # The user gave us a 'len', let's respect this ultimately
             hdr = hdr[:3] + struct.pack("!H", self.len)
@@ -578,6 +641,16 @@ class TLS(_GenericTLSSessionInheritance):
             # Update header with the length of TLSCiphertext.fragment
             hdr = hdr[:3] + struct.pack("!H", len(efrag))
 
+        # Now we commit the pending write state if it has been triggered (e.g.
+        # by an underlying TLSChangeCipherSpec or a SSLv2ClientMasterKey). We
+        # update nothing if the pwcs was not set. This probably means that
+        # we're working out-of-context (and we need to keep the default wcs).
+        if self.tls_session.triggered_pwcs_commit:
+            if self.tls_session.pwcs is not None:
+                self.tls_session.wcs = self.tls_session.pwcs
+                self.tls_session.pwcs = None
+            self.tls_session.triggered_pwcs_commit = False
+
         return hdr + efrag + pay
 
 
diff --git a/scapy/layers/tls/record_sslv2.py b/scapy/layers/tls/record_sslv2.py
new file mode 100644
index 0000000000000000000000000000000000000000..b348b92447e8df3932ba843855b59c53e8ead0ad
--- /dev/null
+++ b/scapy/layers/tls/record_sslv2.py
@@ -0,0 +1,271 @@
+## This file is part of Scapy
+## Copyright (C) 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+SSLv2 Record.
+"""
+
+from __future__ import print_function
+import struct
+
+from scapy.config import conf
+from scapy.fields import *
+from scapy.packet import *
+from scapy.layers.tls.session import _GenericTLSSessionInheritance
+from scapy.layers.tls.record import _TLSMsgListField, TLS
+from scapy.layers.tls.handshake_sslv2 import _sslv2_handshake_cls
+from scapy.layers.tls.basefields import (_SSLv2LengthField, _SSLv2PadField,
+                                         _SSLv2PadLenField, _TLSMACField)
+
+
+###############################################################################
+### SSLv2 Record Protocol                                                   ###
+###############################################################################
+
+class _SSLv2MsgListField(_TLSMsgListField):
+    def __init__(self, name, default, length_from=None):
+        if not length_from:
+            length_from=lambda pkt: ((pkt.len & 0x7fff) -
+                                     (pkt.padlen or 0) -
+                                     len(pkt.mac))
+        super(_SSLv2MsgListField, self).__init__(name, default, length_from)
+
+    def m2i(self, pkt, m):
+        cls = Raw
+        if len(m) >= 1:
+            msgtype = ord(m[0])
+            cls = _sslv2_handshake_cls.get(msgtype, Raw)
+
+        if cls is Raw:
+            return Raw(m)
+        else:
+            return cls(m, tls_session=pkt.tls_session)
+
+    def i2m(self, pkt, p):
+       cur = ""
+       if isinstance(p, _GenericTLSSessionInheritance):
+           p.tls_session = pkt.tls_session
+           if not pkt.tls_session.frozen:
+               cur = p.str_stateful()
+               p.post_build_tls_session_update(cur)
+           else:
+               cur = str(p)
+       else:
+           cur = str(p)
+       return cur
+
+    def addfield(self, pkt, s, val):
+        res = ""
+        for p in val:
+            res += self.i2m(pkt, p)
+        return s + res
+
+
+class SSLv2(TLS):
+    """
+    The encrypted_data is the encrypted version of mac+msg+pad.
+    """
+    __slots__ = ["with_padding", "protected_record"]
+    name = "SSLv2"
+    fields_desc = [ _SSLv2LengthField("len", None),
+                    _SSLv2PadLenField("padlen", None),
+                    _TLSMACField("mac", ""),
+                    _SSLv2MsgListField("msg", []),
+                    _SSLv2PadField("pad", "") ]
+
+    def __init__(self, *args, **kargs):
+        self.with_padding = kargs.get("with_padding", False)
+        self.protected_record = kargs.get("protected_record", None)
+        super(SSLv2, self).__init__(*args, **kargs)
+
+    ### Parsing methods
+
+    def _sslv2_mac_verify(self, msg, mac):
+        secret = self.tls_session.rcs.cipher.key
+        if secret is None:
+            return True
+
+        mac_len = self.tls_session.rcs.mac_len
+        if mac_len == 0:            # should be TLS_NULL_WITH_NULL_NULL
+            return True
+        if len(mac) != mac_len:
+            return False
+
+        read_seq_num = struct.pack("!I", self.tls_session.rcs.seq_num)
+        alg = self.tls_session.rcs.hash
+        h = alg.digest(secret + msg + read_seq_num)
+        return h == mac
+
+    def pre_dissect(self, s):
+        if len(s) < 2:
+            raise Exception("Invalid record: header is too short.")
+
+        msglen = struct.unpack("!H", s[:2])[0]
+        if msglen & 0x8000:
+            hdrlen = 2
+            msglen_clean = msglen & 0x7fff
+        else:
+            hdrlen = 3
+            msglen_clean = msglen & 0x3fff
+
+        hdr = s[:hdrlen]
+        efrag = s[hdrlen:hdrlen+msglen_clean]
+        self.protected_record = s[:hdrlen+msglen_clean]
+        r = s[hdrlen+msglen_clean:]
+
+        mac = pad = ""
+
+        cipher_type = self.tls_session.rcs.cipher.type
+
+        # Decrypt (with implicit IV if block cipher)
+        mfrag = self._tls_decrypt(efrag)
+
+        # Extract MAC
+        maclen = self.tls_session.rcs.mac_len
+        if maclen == 0:
+            mac, pfrag = "", mfrag
+        else:
+            mac, pfrag = mfrag[:maclen], mfrag[maclen:]
+
+        # Extract padding
+        padlen = 0
+        if hdrlen == 3:
+            padlen = struct.unpack("B", s[2])[0]
+        if padlen == 0:
+            cfrag, pad = pfrag, ""
+        else:
+            cfrag, pad = pfrag[:-padlen], pfrag[-padlen:]
+
+        # Verify integrity
+        is_mac_ok = self._sslv2_mac_verify(cfrag + pad, mac)
+        if not is_mac_ok:
+            print("INTEGRITY CHECK FAILED")
+
+        reconstructed_body = mac + cfrag + pad
+        return hdr + reconstructed_body + r
+
+    def post_dissect(self, s):
+        """
+        SSLv2 may force us to commit the write connState here.
+        """
+        if self.tls_session.triggered_prcs_commit:
+            if self.tls_session.prcs is not None:
+                self.tls_session.rcs = self.tls_session.prcs
+                self.tls_session.prcs = None
+            self.tls_session.triggered_prcs_commit = False
+        if self.tls_session.triggered_pwcs_commit:
+            if self.tls_session.pwcs is not None:
+                self.tls_session.wcs = self.tls_session.pwcs
+                self.tls_session.pwcs = None
+            self.tls_session.triggered_pwcs_commit = False
+
+        if self.tls_session.prcs is not None:
+            self.tls_session.prcs.seq_num += 1
+        self.tls_session.rcs.seq_num += 1
+        return s
+
+    def do_dissect_payload(self, s):
+        if s:
+            try:
+                p = SSLv2(s, _internal=1, _underlayer=self,
+                          tls_session = self.tls_session)
+            except KeyboardInterrupt:
+                raise
+            except:
+                p = conf.raw_layer(s, _internal=1, _underlayer=self)
+            self.add_payload(p)
+
+
+    ### Building methods
+
+    def _sslv2_mac_add(self, msg):
+        secret = self.tls_session.wcs.cipher.key
+        if secret is None:
+            return msg
+
+        write_seq_num = struct.pack("!I", self.tls_session.wcs.seq_num)
+        alg = self.tls_session.wcs.hash
+        h = alg.digest(secret + msg + write_seq_num)
+        return h + msg
+
+    def _sslv2_pad(self, s):
+        padding = ""
+        block_size = self.tls_session.wcs.cipher.block_size
+        padlen = block_size - (len(s) % block_size)
+        if padlen == block_size:
+            padlen = 0
+        padding = "\x00" * padlen
+        return s + padding
+
+    def post_build(self, pkt, pay):
+        if self.protected_record is not None:
+            # we do not update the tls_session
+            return self.protected_record + pay
+
+        if self.padlen is None:
+            cfrag = pkt[2:]
+        else:
+            cfrag = pkt[3:]
+
+        if self.pad == "" and self.tls_session.wcs.cipher.type == 'block':
+            pfrag = self._sslv2_pad(cfrag)
+        else:
+            pad = self.pad or ""
+            pfrag = cfrag + pad
+
+        padlen = self.padlen
+        if padlen is None:
+            padlen = len(pfrag) - len(cfrag)
+        hdr = pkt[:2]
+        if padlen > 0:
+            hdr += struct.pack("B", padlen)
+
+        # Integrity
+        if self.mac == "":
+            mfrag = self._sslv2_mac_add(pfrag)
+        else:
+            mfrag = self.mac + pfrag
+
+        # Encryption
+        efrag = self._tls_encrypt(mfrag)
+
+        if self.len is not None:
+            l = self.len
+            if not self.with_padding:
+                l |= 0x8000
+            hdr = struct.pack("!H", l) + hdr[2:]
+        else:
+            # Update header with the length of TLSCiphertext.fragment
+            msglen_new = len(efrag)
+            if padlen:
+                if msglen_new > 0x3fff:
+                    raise Exception("Invalid record: encrypted data too long.")
+            else:
+                if msglen_new > 0x7fff:
+                    raise Exception("Invalid record: encrypted data too long.")
+                msglen_new |= 0x8000
+            hdr = struct.pack("!H", msglen_new) + hdr[2:]
+
+        # Now we commit the pending write state if it has been triggered (e.g.
+        # by an underlying TLSChangeCipherSpec or a SSLv2ClientMasterKey). We
+        # update nothing if the pwcs was not set. This probably means that
+        # we're working out-of-context (and we need to keep the default wcs).
+        # SSLv2 may force us to commit the reading connState here.
+        if self.tls_session.triggered_pwcs_commit:
+            if self.tls_session.pwcs is not None:
+                self.tls_session.wcs = self.tls_session.pwcs
+                self.tls_session.pwcs = None
+            self.tls_session.triggered_pwcs_commit = False
+        if self.tls_session.triggered_prcs_commit:
+            if self.tls_session.prcs is not None:
+                self.tls_session.rcs = self.tls_session.prcs
+                self.tls_session.prcs = None
+            self.tls_session.triggered_prcs_commit = False
+
+        if self.tls_session.pwcs is not None:
+            self.tls_session.pwcs.seq_num += 1
+        self.tls_session.wcs.seq_num += 1
+
+        return hdr + efrag + pay
+
diff --git a/scapy/layers/tls/record_tls13.py b/scapy/layers/tls/record_tls13.py
new file mode 100644
index 0000000000000000000000000000000000000000..45015dd8fead2236c1f1bbbda95328f8c6d20bc0
--- /dev/null
+++ b/scapy/layers/tls/record_tls13.py
@@ -0,0 +1,207 @@
+## This file is part of Scapy
+## Copyright (C) 2017 Maxence Tury
+## This program is published under a GPLv2 license
+
+"""
+Common TLS 1.3 fields & bindings.
+
+This module covers the record layer, along with the ChangeCipherSpec, Alert and
+ApplicationData submessages. For the Handshake type, see tls_handshake.py.
+
+See the TLS class documentation for more information.
+"""
+
+from __future__ import print_function
+import struct
+
+from scapy.config import conf
+from scapy.fields import *
+from scapy.packet import *
+from scapy.layers.tls.session import _GenericTLSSessionInheritance
+from scapy.layers.tls.basefields import (_TLSVersionField, _tls_version,
+                                         _TLSMACField, _TLSLengthField, _tls_type)
+from scapy.layers.tls.record import _TLSMsgListField
+from scapy.layers.tls.crypto.cipher_aead import AEADTagError
+from scapy.layers.tls.crypto.cipher_stream import Cipher_NULL
+from scapy.layers.tls.crypto.ciphers import CipherError
+
+
+###############################################################################
+### TLS Record Protocol                                                     ###
+###############################################################################
+
+class TLSInnerPlaintext(_GenericTLSSessionInheritance):
+    name = "TLS Inner Plaintext"
+    fields_desc = [ _TLSMsgListField("msg", []),
+                    ByteEnumField("type", None, _tls_type),
+                    XStrField("pad", "") ]
+
+    def pre_dissect(self, s):
+        """
+        We need to parse the padding and type as soon as possible,
+        else we won't be able to parse the message list...
+        """
+        if len(s) < 1:
+            raise Exception("Invalid InnerPlaintext (too short).")
+
+        l = len(s) - 1
+        if s[-1] != "\x00":
+            msg_len = l
+        else:
+            n = 1
+            while s[-n] != "\x00" and n < l:
+                n += 1
+            msg_len = l - n
+        self.fields_desc[0].length_from = lambda pkt: msg_len
+
+        self.type = struct.unpack("B", s[msg_len:msg_len+1])[0]
+
+        return s
+
+class _TLSInnerPlaintextField(PacketField):
+    def __init__(self, name, default, *args, **kargs):
+        super(_TLSInnerPlaintextField, self).__init__(name,
+                                                      default,
+                                                      TLSInnerPlaintext)
+
+    def m2i(self, pkt, m):
+        return self.cls(m, tls_session=pkt.tls_session)
+
+    def getfield(self, pkt, s):
+        tag_len = pkt.tls_session.rcs.mac_len
+        frag_len = pkt.len - tag_len
+        if frag_len < 1:
+            warning("InnerPlaintext should at least contain a byte type!")
+            return s, None
+        remain, i = super(_TLSInnerPlaintextField, self).getfield(pkt, s[:frag_len])
+        # remain should be empty here
+        return remain + s[frag_len:], i
+
+    def i2m(self, pkt, p):
+        if isinstance(p, _GenericTLSSessionInheritance):
+            p.tls_session = pkt.tls_session
+            if not pkt.tls_session.frozen:
+                return p.str_stateful()
+        return str(p)
+
+
+class TLS13(_GenericTLSSessionInheritance):
+    __slots__ = ["deciphered_len"]
+    name = "TLS 1.3"
+    fields_desc = [ ByteEnumField("type", 0x17, _tls_type),
+                    _TLSVersionField("version", 0x0301, _tls_version),
+                    _TLSLengthField("len", None),
+                    _TLSInnerPlaintextField("inner", TLSInnerPlaintext()),
+                    _TLSMACField("auth_tag", None) ]
+
+    def __init__(self, *args, **kargs):
+        self.deciphered_len = kargs.get("deciphered_len", None)
+        super(TLS13, self).__init__(*args, **kargs)
+
+
+    ### Parsing methods
+
+    def _tls_auth_decrypt(self, s):
+        """
+        Provided with the record header and AEAD-ciphered data, return the
+        sliced and clear tuple (TLSInnerPlaintext, tag). Note that
+        we still return the slicing of the original input in case of decryption
+        failure. Also, if the integrity check fails, a warning will be issued,
+        but we still return the sliced (unauthenticated) plaintext.
+        """
+        rcs = self.tls_session.rcs
+        read_seq_num = struct.pack("!Q", rcs.seq_num)
+        rcs.seq_num += 1
+        try:
+            return rcs.cipher.auth_decrypt("", s, read_seq_num)
+        except CipherError as e:
+            return e.args
+        except AEADTagError as e:
+            print("INTEGRITY CHECK FAILED")
+            return e.args
+
+    def pre_dissect(self, s):
+        """
+        Decrypt, verify and decompress the message.
+        """
+        if len(s) < 5:
+            raise Exception("Invalid record: header is too short.")
+
+        if isinstance(self.tls_session.rcs.cipher, Cipher_NULL):
+            self.deciphered_len = None
+            return s
+        else:
+            msglen = struct.unpack('!H', s[3:5])[0]
+            hdr, efrag, r = s[:5], s[5:5+msglen], s[msglen+5:]
+            frag, auth_tag = self._tls_auth_decrypt(efrag)
+            self.deciphered_len = len(frag)
+            return hdr + frag + auth_tag + r
+
+    def post_dissect(self, s):
+        """
+        Commit the pending read state if it has been triggered. We update
+        nothing if the prcs was not set, as this probably means that we're
+        working out-of-context (and we need to keep the default rcs).
+        """
+        if self.tls_session.triggered_prcs_commit:
+            if self.tls_session.prcs is not None:
+                self.tls_session.rcs = self.tls_session.prcs
+                self.tls_session.prcs = None
+            self.tls_session.triggered_prcs_commit = False
+        return s
+
+    def do_dissect_payload(self, s):
+        """
+        Try to dissect the following data as a TLS message.
+        Note that overloading .guess_payload_class() would not be enough,
+        as the TLS session to be used would get lost.
+        """
+        if s:
+            try:
+                p = TLS(s, _internal=1, _underlayer=self,
+                        tls_session = self.tls_session)
+            except KeyboardInterrupt:
+                raise
+            except:
+                p = conf.raw_layer(s, _internal=1, _underlayer=self)
+            self.add_payload(p)
+
+
+    ### Building methods
+
+    def _tls_auth_encrypt(self, s):
+        """
+        Return the TLSCiphertext.encrypted_record for AEAD ciphers.
+        """
+        wcs = self.tls_session.wcs
+        write_seq_num = struct.pack("!Q", wcs.seq_num)
+        wcs.seq_num += 1
+        return wcs.cipher.auth_encrypt(s, "", write_seq_num)
+
+    def post_build(self, pkt, pay):
+        """
+        Apply the previous methods according to the writing cipher type.
+        """
+        # Compute the length of TLSPlaintext fragment
+        hdr, frag = pkt[:5], pkt[5:]
+        if not isinstance(self.tls_session.rcs.cipher, Cipher_NULL):
+            frag = self._tls_auth_encrypt(frag)
+
+        if self.len is not None:
+            # The user gave us a 'len', let's respect this ultimately
+            hdr = hdr[:3] + struct.pack("!H", self.len)
+        else:
+            # Update header with the length of TLSCiphertext.inner
+            hdr = hdr[:3] + struct.pack("!H", len(frag))
+
+        # Now we commit the pending write state if it has been triggered. We
+        # update nothing if the pwcs was not set. This probably means that
+        # we're working out-of-context (and we need to keep the default wcs).
+        if self.tls_session.triggered_pwcs_commit:
+            if self.tls_session.pwcs is not None:
+                self.tls_session.wcs = self.tls_session.pwcs
+                self.tls_session.pwcs = None
+            self.tls_session.triggered_pwcs_commit = False
+
+        return hdr + frag + pay
+
diff --git a/scapy/layers/tls/session.py b/scapy/layers/tls/session.py
index 04b7882a826eb0d45c2a2ff2a051e96b86cdea8e..0dda8aa36c1eaa3f38a9fa1ac49094dcad54f48a 100644
--- a/scapy/layers/tls/session.py
+++ b/scapy/layers/tls/session.py
@@ -1,6 +1,6 @@
 ## This file is part of Scapy
 ## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
-##                     2015, 2016 Maxence Tury
+##               2015, 2016, 2017 Maxence Tury
 ## This program is published under a GPLv2 license
 
 """
@@ -15,8 +15,9 @@ import struct
 from scapy.config import conf
 from scapy.error import warning
 from scapy.packet import Packet
-from scapy.utils import repr_hex
+from scapy.utils import repr_hex, strxor
 from scapy.layers.tls.crypto.compression import Comp_NULL
+from scapy.layers.tls.crypto.hkdf import TLS13_HKDF
 from scapy.layers.tls.crypto.prf import PRF
 
 # Note the following import may happen inside connState.__init__()
@@ -48,28 +49,33 @@ class connState(object):
     parameters a current state.  The initial current state always
     specifies that no encryption, compression, or MAC will be used.
 
+    (For practical reasons, Scapy scraps these two last lines, through the
+    implementation of dummy ciphers and MAC with TLS_NULL_WITH_NULL_NULL.)
+
     These attributes and behaviours are mostly mapped in this class.
-    Also, note that scapy may make a current state out of a pending state
+    Also, note that Scapy may make a current state out of a pending state
     which has been initialized with dummy security parameters. We need
     this in order to know when the content of a TLS message is encrypted,
     whether we possess the right keys to decipher/verify it or not.
-    For instance, when scapy parses a CKE without knowledge of any secret,
+    For instance, when Scapy parses a CKE without knowledge of any secret,
     and then a CCS, it needs to know that the following Finished
     is encrypted and signed according to a new cipher suite, even though
     it cannot decipher the message nor verify its integrity.
     """
-
     def __init__(self,
-                 connection_end="client",
+                 connection_end="server",
                  read_or_write="read",
+                 seq_num=0,
                  compression_alg=Comp_NULL,
                  ciphersuite=None,
                  tls_version=0x0303):
 
+        self.tls_version = tls_version
+
         # It is the user's responsibility to keep the record seq_num
         # under 2**64-1. If this value gets maxed out, the TLS class in
         # record.py will crash when trying to encode it with struct.pack().
-        self.seq_num = 0
+        self.seq_num = seq_num
 
         self.connection_end = connection_end
         self.row = read_or_write
@@ -77,28 +83,29 @@ class connState(object):
         if ciphersuite is None:
             from scapy.layers.tls.crypto.suites import TLS_NULL_WITH_NULL_NULL
             ciphersuite = TLS_NULL_WITH_NULL_NULL
-
         self.ciphersuite = ciphersuite(tls_version=tls_version)
 
         self.compression = compression_alg()
         self.key_exchange = ciphersuite.kx_alg()
-        self.prf = PRF(ciphersuite.hash_alg.name, tls_version)
-
-        # The attributes below usually get updated by .derive_keys()
-        # As discussed, we need to initialize cipher and mac with dummy values.
-
-        self.master_secret = None       # 48-byte shared secret
-        self.cipher_secret = None       # key for the symmetric cipher
-        self.mac_secret = None          # key for the MAC (stays None for AEAD)
-
         self.cipher = ciphersuite.cipher_alg()
+        self.hash = ciphersuite.hash_alg()
 
-        if ciphersuite.hmac_alg is None:        # AEAD
-            self.hmac = None
-            self.mac_len = self.cipher.tag_len
+        if tls_version > 0x0200:
+            if ciphersuite.cipher_alg.type == "aead":
+                self.hmac = None
+                self.mac_len = self.cipher.tag_len
+            else:
+                self.hmac = ciphersuite.hmac_alg()
+                self.mac_len = self.hmac.hmac_len
         else:
-            self.hmac = ciphersuite.hmac_alg()
-            self.mac_len = self.hmac.hmac_len
+            self.hmac = ciphersuite.hmac_alg()          # should be Hmac_NULL
+            self.mac_len = self.hash.hash_len
+
+        if tls_version >= 0x0304:
+            self.hkdf = TLS13_HKDF(self.hash.name.lower())
+        else:
+            self.prf = PRF(ciphersuite.hash_alg.name, tls_version)
+
 
     def debug_repr(self, name, secret):
         if conf.debug_tls and secret:
@@ -111,9 +118,9 @@ class connState(object):
                     client_random="",
                     server_random="",
                     master_secret=""):
+        #XXX Can this be called over a non-usable suite? What happens then?
 
         cs = self.ciphersuite
-        self.master_secret = master_secret
 
         # Derive the keys according to the cipher type and protocol version
         key_block = self.prf.derive_key_block(master_secret,
@@ -136,28 +143,27 @@ class connState(object):
             if skip_first:
                 start += cs.hmac_alg.key_len
             end = start + cs.hmac_alg.key_len
-            self.mac_secret = key_block[start:end]
-            self.debug_repr("mac_secret", self.mac_secret)
+            mac_secret = key_block[start:end]
+            self.debug_repr("mac_secret", mac_secret)
             pos += 2*cs.hmac_alg.key_len
         else:
-            self.mac_secret = None
+            mac_secret = None
 
         ### Cipher secret
         start = pos
         if skip_first:
             start += cipher_alg.key_len
         end = start + cipher_alg.key_len
-        key = key_block[start:end]
+        cipher_secret = key_block[start:end]
         if cs.kx_alg.export:
             reqLen = cipher_alg.expanded_key_len
-            key = self.prf.postprocess_key_for_export(key,
+            cipher_secret = self.prf.postprocess_key_for_export(cipher_secret,
                                                       client_random,
                                                       server_random,
                                                       self.connection_end,
                                                       self.row,
                                                       reqLen)
-        self.cipher_secret = key
-        self.debug_repr("cipher_secret", self.cipher_secret)
+        self.debug_repr("cipher_secret", cipher_secret)
         pos += 2*cipher_alg.key_len
 
         ### Implicit IV (for block and AEAD ciphers)
@@ -168,19 +174,19 @@ class connState(object):
             end = start + cipher_alg.block_size
         elif cipher_alg.type == "aead":
             if skip_first:
-                start += cipher_alg.salt_len
-            end = start + cipher_alg.salt_len
+                start += cipher_alg.fixed_iv_len
+            end = start + cipher_alg.fixed_iv_len
 
         ### Now we have the secrets, we can instantiate the algorithms
         if cs.hmac_alg is None:         # AEAD
             self.hmac = None
             self.mac_len = cipher_alg.tag_len
         else:
-            self.hmac = cs.hmac_alg(self.mac_secret)
+            self.hmac = cs.hmac_alg(mac_secret)
             self.mac_len = self.hmac.hmac_len
 
         if cipher_alg.type == "stream":
-            cipher = cipher_alg(self.cipher_secret)
+            cipher = cipher_alg(cipher_secret)
         elif cipher_alg.type == "block":
             # We set an IV every time, even though it does not matter for
             # TLS 1.1+ as it requires an explicit IV. Indeed the cipher.iv
@@ -193,18 +199,64 @@ class connState(object):
                                                      self.connection_end,
                                                      self.row,
                                                      reqLen)
-            cipher = cipher_alg(self.cipher_secret, iv)
+            cipher = cipher_alg(cipher_secret, iv)
             self.debug_repr("block iv", iv)
         elif cipher_alg.type == "aead":
-            salt = key_block[start:end]
+            fixed_iv = key_block[start:end]
             nonce_explicit_init = 0
             # If you ever wanted to set a random nonce_explicit, use this:
             #exp_bit_len = cipher_alg.nonce_explicit_len * 8
             #nonce_explicit_init = random.randint(0, 2**exp_bit_len - 1)
-            cipher = cipher_alg(self.cipher_secret, salt, nonce_explicit_init)
-            self.debug_repr("aead salt", salt)
+            cipher = cipher_alg(cipher_secret, fixed_iv, nonce_explicit_init)
+            self.debug_repr("aead fixed iv", fixed_iv)
         self.cipher = cipher
 
+    def sslv2_derive_keys(self, key_material):
+        """
+        There is actually only one key, the CLIENT-READ-KEY or -WRITE-KEY.
+
+        Note that skip_first is opposite from the one with SSLv3 derivation.
+
+        Also, if needed, the IV should be set elsewhere.
+        """
+        skip_first = True
+        if ((self.connection_end == "client" and self.row == "read") or
+            (self.connection_end == "server" and self.row == "write")):
+            skip_first = False
+
+        cipher_alg = self.ciphersuite.cipher_alg
+
+        start = 0
+        if skip_first:
+            start += cipher_alg.key_len
+        end = start + cipher_alg.key_len
+        cipher_secret = key_material[start:end]
+        self.cipher = cipher_alg(cipher_secret)
+        self.debug_repr("cipher_secret", cipher_secret)
+
+    def tls13_derive_keys(self, key_material):
+        cipher_alg = self.ciphersuite.cipher_alg
+        key_len = cipher_alg.key_len
+        iv_len = cipher_alg.fixed_iv_len
+        write_key = self.hkdf.expand_label(key_material, "key", "", key_len)
+        write_iv = self.hkdf.expand_label(key_material, "iv", "", iv_len)
+        self.cipher = cipher_alg(write_key, write_iv)
+
+    def snapshot(self):
+        """
+        This is used mostly as a way to keep the cipher state and the seq_num.
+        """
+        snap = connState(connection_end=self.connection_end,
+                         read_or_write=self.row,
+                         seq_num=self.seq_num,
+                         compression_alg=type(self.compression),
+                         ciphersuite=type(self.ciphersuite),
+                         tls_version=self.tls_version)
+        snap.cipher = self.cipher.snapshot()
+        if self.hmac:
+            snap.hmac.key = self.hmac.key
+        return snap
+
     def __repr__(self):
         def indent(s):
             if s and s[-1] == '\n':
@@ -215,11 +267,12 @@ class connState(object):
         res =  "Connection end : %s\n" % self.connection_end.upper()
         res += "Cipher suite   : %s (0x%04x)\n" % (self.ciphersuite.name,
                                                    self.ciphersuite.val)
-        res += "Compression Alg: %s (0x%02x)\n" % (self.compression.name,
+        res += "Compression    : %s (0x%02x)\n" % (self.compression.name,
                                                    self.compression.val)
         tabsize = 4
         return res.expandtabs(tabsize)
 
+
 class readConnState(connState):
     def __init__(self, **kargs):
         connState.__init__(self, read_or_write="read", **kargs)
@@ -240,15 +293,20 @@ class tlsSession(object):
     a writeConnState instance. Along with overarching network attributes, a
     tlsSession object also holds negotiated, shared information, such as the
     key exchange parameters and the master secret (when available).
+
+    The default connection_end is "server". This corresponds to the expected
+    behaviour for static exchange analysis (with a ClientHello parsed first).
     """
     def __init__(self,
                  ipsrc=None, ipdst=None,
                  sport=None, dport=None, sid=None,
-                 connection_end="client",
+                 connection_end="server",
                  wcs=None, rcs=None):
 
-        ### Network settings
+        # Use this switch to prevent additions to the 'handshake_messages'.
+        self.frozen = False
 
+        ### Network settings
         self.ipsrc = ipsrc
         self.ipdst = ipdst
         self.sport = sport
@@ -258,25 +316,27 @@ class tlsSession(object):
         # Our TCP socket. None until we send (or receive) a packet.
         self.sock = None
 
-
         ### Connection states
-
         self.connection_end = connection_end
 
         if wcs is None:
+            # Instantiate wcs with dummy values.
             self.wcs = writeConnState(connection_end=connection_end)
-            self.wcs.derive_keys(client_random="",
-                                 server_random="",
-                                 master_secret="")
+            self.wcs.derive_keys()
+        else:
+            self.wcs = wcs
+
         if rcs is None:
+            # Instantiate rcs with dummy values.
             self.rcs = readConnState(connection_end=connection_end)
-            self.rcs.derive_keys(client_random="",
-                                 server_random="",
-                                 master_secret="")
+            self.rcs.derive_keys()
+        else:
+            self.rcs = rcs
 
         # The pending write/read states are updated by the building/parsing
         # of various TLS packets. They get committed to self.wcs/self.rcs
-        # once scapy builds/parses a ChangeCipherSpec message.
+        # once Scapy builds/parses a ChangeCipherSpec message, or for certain
+        # other messages in case of TLS 1.3.
         self.pwcs = None
         self.triggered_pwcs_commit = False
         self.prcs = None
@@ -293,8 +353,8 @@ class tlsSession(object):
 
         # The server private key, as a PrivKey instance, when acting as server.
         # XXX It would be nice to be able to provide both an RSA and an ECDSA
-        # key in order for the same scapy server to support both families of
-        # cipher suites. See INIT_TLS_SESSION() in automaton.py.
+        # key in order for the same Scapy server to support both families of
+        # cipher suites. See INIT_TLS_SESSION() in automaton_srv.py.
         # (For now server_key holds either one of both types for DHE
         # authentication, while server_rsa_key is used only for RSAkx.)
         self.server_key = None
@@ -305,7 +365,7 @@ class tlsSession(object):
         # RSA keys longer than 512 bits for RSAkx. When their usual RSA key
         # was longer than this, they had to create a new key and send it via
         # a ServerRSAParams message. When receiving such a message,
-        # scapy stores this key in server_tmp_rsa_key as a PubKey instance.
+        # Scapy stores this key in server_tmp_rsa_key as a PubKey instance.
         self.server_tmp_rsa_key = None
 
         # When client authentication is performed, we need at least a
@@ -317,24 +377,30 @@ class tlsSession(object):
 
         ### Ephemeral key exchange parameters
 
-        ## XXX Explain why we need pubkey (which should be contained in privkey)
-        # also, params is used to hold params between the SKE and the CKE
-        self.server_kx_privkey = None
-        self.server_kx_pubkey = None
-        self.client_kx_privkey = None
-        self.client_kx_pubkey = None
-
+        # These are the group/curve parameters, needed to hold the information
+        # e.g. from receiving an SKE to sending a CKE. Usually, only one of
+        # these attributes will be different from None.
         self.client_kx_ffdh_params = None
         self.client_kx_ecdh_params = None
 
-        ## Either an instance of FFDHParams or ECDHParams.
-        ## Depending on which side of the connection we operate,
-        ## one of these params will not hold 'priv' and 'secret' attributes.
-        ## We did not use these intermediaries for RSAkx, as the 'priv' would
-        ## equate the PrivKey, and the 'secret' the pre_master_secret.
-        ## (It could have been useful for RSAkx export, though...)
-        #self.server_kx_params = None
-        #self.client_kx_params = None
+        # These are PrivateKeys and PublicKeys from the appropriate FFDH/ECDH
+        # cryptography module, i.e. these are not raw bytes. Usually, only one
+        # in two will be different from None, e.g. when being a TLS client you
+        # will need the client_kx_privkey (the serialized public key is not
+        # actually registered) and you will receive a server_kx_pubkey.
+        self.client_kx_privkey = None
+        self.client_kx_pubkey = None
+        self.server_kx_privkey = None
+        self.server_kx_pubkey = None
+
+        # When using TLS 1.3, the tls13_client_pubshares will contain every
+        # potential key share (equate the 'client_kx_pubkey' before) the client
+        # offered, indexed by the id of the FFDH/ECDH group. These dicts
+        # effectively replace the four previous attributes.
+        self.tls13_client_privshares = {}
+        self.tls13_client_pubshares = {}
+        self.tls13_server_privshare = {}
+        self.tls13_server_pubshare = {}
 
 
         ### Negotiated session parameters
@@ -342,37 +408,118 @@ class tlsSession(object):
         # The advertised TLS version found in the ClientHello (and
         # EncryptedPreMasterSecret if used). If acting as server, it is set to
         # the value advertised by the client in its ClientHello.
-        #XXX See what needs to be changed in automaton.py in order to keep
-        # this to None. For now it is necessary for running the client.
-        self.advertised_tls_version = 0x303
+        # The default value corresponds to TLS 1.2 (and TLS 1.3, incidentally).
+        self.advertised_tls_version = 0x0303
 
         # The agreed-upon TLS version found in the ServerHello.
         self.tls_version = None
 
-        # These attributes should eventually be known to both sides.
+        # These attributes should eventually be known to both sides (SSLv3-TLS 1.2).
         self.client_random = None
         self.server_random = None
         self.pre_master_secret = None
         self.master_secret = None
 
+        # A session ticket received by the client.
+        self.client_session_ticket = None
+
+        # These attributes should only be used with SSLv2 connections.
+        # We need to keep the KEY-MATERIAL here because it may be reused.
+        self.sslv2_common_cs = []
+        self.sslv2_connection_id = None
+        self.sslv2_challenge = None
+        self.sslv2_challenge_clientcert = None
+        self.sslv2_key_material = None
+
+        # These attributes should only be used with TLS 1.3 connections.
+        self.tls13_psk_secret = None
+        self.tls13_early_secret = None
+        self.tls13_dhe_secret = None
+        self.tls13_handshake_secret = None
+        self.tls13_master_secret = None
+        self.tls13_derived_secrets = {}
+
         # Handshake messages needed for Finished computation/validation.
         # No record layer headers, no HelloRequests, no ChangeCipherSpecs.
         self.handshake_messages = []
         self.handshake_messages_parsed = []
 
         # All exchanged TLS packets.
-        self.exchanged_pkts = []
+        #XXX no support for now
+        #self.exchanged_pkts = []
+
+
+    def __setattr__(self, name, val):
+        if name == "connection_end":
+            if hasattr(self, "rcs") and self.rcs:
+                self.rcs.connection_end = val
+            if hasattr(self, "wcs") and self.wcs:
+                self.wcs.connection_end = val
+            if hasattr(self, "prcs") and self.prcs:
+                self.prcs.connection_end = val
+            if hasattr(self, "pwcs") and self.pwcs:
+                self.pwcs.connection_end = val
+        super(tlsSession, self).__setattr__(name, val)
+
+
+    ### Mirroring
+
+    def mirror(self):
+        """
+        This function takes a tlsSession object and swaps the IP addresses,
+        ports, connection ends and connection states. The triggered_commit are
+        also swapped (though it is probably overkill, it is cleaner this way).
+
+        It is useful for static analysis of a series of messages from both the
+        client and the server. In such a situation, it should be used every
+        time the message being read comes from a different side than the one
+        read right before, as the reading state becomes the writing state, and
+        vice versa. For instance you could do:
+
+        client_hello = open('client_hello.raw').read()
+        <read other messages>
+
+        m1 = TLS(client_hello)
+        m2 = TLS(server_hello, tls_session=m1.tls_session.mirror())
+        m3 = TLS(server_cert, tls_session=m2.tls_session)
+        m4 = TLS(client_keyexchange, tls_session=m3.tls_session.mirror())
+        """
+
+        self.ipdst, self.ipsrc = self.ipsrc, self.ipdst
+        self.dport, self.sport = self.sport, self.dport
 
+        self.rcs, self.wcs = self.wcs, self.rcs
+        if self.rcs:
+            self.rcs.row = "read"
+        if self.wcs:
+            self.wcs.row = "write"
 
-    ### Master secret management
+        self.prcs, self.pwcs = self.pwcs, self.prcs
+        if self.prcs:
+            self.prcs.row = "read"
+        if self.pwcs:
+            self.pwcs.row = "write"
+
+        self.triggered_prcs_commit, self.triggered_pwcs_commit = \
+                self.triggered_pwcs_commit, self.triggered_prcs_commit
+
+        if self.connection_end == "client":
+            self.connection_end = "server"
+        elif self.connection_end == "server":
+            self.connection_end = "client"
+
+        return self
+
+
+    ### Secrets management for SSLv3 to TLS 1.2
 
     def compute_master_secret(self):
         if self.pre_master_secret is None:
-            warning("Missing pre_master_secret while computing master_secret")
+            warning("Missing pre_master_secret while computing master_secret!")
         if self.client_random is None:
-            warning("Missing client_random while computing master_secret")
+            warning("Missing client_random while computing master_secret!")
         if self.server_random is None:
-            warning("Missing server_random while computing master_secret")
+            warning("Missing server_random while computing master_secret!")
 
         ms = self.pwcs.prf.compute_master_secret(self.pre_master_secret,
                                                  self.client_random,
@@ -391,11 +538,208 @@ class tlsSession(object):
                               master_secret=self.master_secret)
 
 
+    ### Secrets management for SSLv2
+
+    def compute_sslv2_key_material(self):
+        if self.master_secret is None:
+            warning("Missing master_secret while computing key_material!")
+        if self.sslv2_challenge is None:
+            warning("Missing challenge while computing key_material!")
+        if self.sslv2_connection_id is None:
+            warning("Missing connection_id while computing key_material!")
+
+        km = self.pwcs.prf.derive_key_block(self.master_secret,
+                                            self.sslv2_challenge,
+                                            self.sslv2_connection_id,
+                                            2*self.pwcs.cipher.key_len)
+        self.sslv2_key_material = km
+        if conf.debug_tls:
+            print("master secret: %s" % repr_hex(self.master_secret))
+            print("key material: %s" % repr_hex(km))
+
+    def compute_sslv2_km_and_derive_keys(self):
+        self.compute_sslv2_key_material()
+        self.prcs.sslv2_derive_keys(key_material=self.sslv2_key_material)
+        self.pwcs.sslv2_derive_keys(key_material=self.sslv2_key_material)
+
+
+    ### Secrets management for TLS 1.3
+
+    def compute_tls13_early_secrets(self):
+        """
+        Ciphers key and IV are updated accordingly for 0-RTT data.
+        self.handshake_messages should be ClientHello only.
+        """
+        # we use the prcs rather than the pwcs in a totally arbitrary way
+        if self.prcs is None:
+            # too soon
+            return
+
+        hkdf = self.prcs.hkdf
+
+        self.tls13_early_secret = hkdf.extract(None,
+                                               self.tls13_psk_secret)
+
+        bk = hkdf.derive_secret(self.tls13_early_secret,
+                                "external psk binder key",
+                               #"resumption psk binder key",
+                                "")
+        self.tls13_derived_secrets["binder_key"] = bk
+
+        if len(self.handshake_messages) > 1:
+            # these secrets are not defined in case of HRR
+            return
+
+        cets = hkdf.derive_secret(self.tls13_early_secret,
+                                  "client early traffic secret",
+                                  "".join(self.handshake_messages))
+        self.tls13_derived_secrets["client_early_traffic_secret"] = cets
+
+        ees = hkdf.derive_secret(self.tls13_early_secret,
+                                 "early exporter master secret",
+                                 "".join(self.handshake_messages))
+        self.tls13_derived_secrets["early_exporter_secret"] = ees
+
+        if self.connection_end == "server":
+            self.prcs.tls13_derive_keys(cets)
+        elif self.connection_end == "client":
+            self.pwcs.tls13_derive_keys(cets)
+
+    def compute_tls13_handshake_secrets(self):
+        """
+        Ciphers key and IV are updated accordingly for Handshake data.
+        self.handshake_messages should be ClientHello...ServerHello.
+        """
+        if self.tls13_early_secret is None:
+            warning("No early secret. This is abnormal.")
+
+        hkdf = self.prcs.hkdf
+
+        self.tls13_handshake_secret = hkdf.extract(self.tls13_early_secret,
+                                                   self.tls13_dhe_secret)
+
+        chts = hkdf.derive_secret(self.tls13_handshake_secret,
+                                  "client handshake traffic secret",
+                                  "".join(self.handshake_messages))
+        self.tls13_derived_secrets["client_handshake_traffic_secret"] = chts
+
+        shts = hkdf.derive_secret(self.tls13_handshake_secret,
+                                  "server handshake traffic secret",
+                                  "".join(self.handshake_messages))
+        self.tls13_derived_secrets["server_handshake_traffic_secret"] = shts
+
+        if self.connection_end == "server":
+            self.prcs.tls13_derive_keys(chts)
+            self.pwcs.tls13_derive_keys(shts)
+        elif self.connection_end == "client":
+            self.pwcs.tls13_derive_keys(chts)
+            self.prcs.tls13_derive_keys(shts)
+
+    def compute_tls13_traffic_secrets(self):
+        """
+        Ciphers key and IV are updated accordingly for Application data.
+        self.handshake_messages should be ClientHello...ServerFinished.
+        """
+        hkdf = self.prcs.hkdf
+
+        self.tls13_master_secret = hkdf.extract(self.tls13_handshake_secret,
+                                                None)
+
+        cts0 = hkdf.derive_secret(self.tls13_master_secret,
+                                  "client application traffic secret",
+                                  "".join(self.handshake_messages))
+        self.tls13_derived_secrets["client_traffic_secrets"] = [cts0]
+
+        sts0 = hkdf.derive_secret(self.tls13_master_secret,
+                                  "server application traffic secret",
+                                  "".join(self.handshake_messages))
+        self.tls13_derived_secrets["server_traffic_secrets"] = [sts0]
+
+        es = hkdf.derive_secret(self.tls13_master_secret,
+                                "exporter master secret",
+                                "".join(self.handshake_messages))
+        self.tls13_derived_secrets["exporter_secret"] = es
+
+        if self.connection_end == "server":
+            #self.prcs.tls13_derive_keys(cts0)
+            self.pwcs.tls13_derive_keys(sts0)
+        elif self.connection_end == "client":
+            #self.pwcs.tls13_derive_keys(cts0)
+            self.prcs.tls13_derive_keys(sts0)
+
+    def compute_tls13_traffic_secrets_end(self):
+        cts0 = self.tls13_derived_secrets["client_traffic_secrets"][0]
+        if self.connection_end == "server":
+            self.prcs.tls13_derive_keys(cts0)
+        elif self.connection_end == "client":
+            self.pwcs.tls13_derive_keys(cts0)
+
+    def compute_tls13_verify_data(self, connection_end, read_or_write):
+        shts = "server_handshake_traffic_secret"
+        chts = "client_handshake_traffic_secret"
+        if read_or_write == "read":
+            hkdf = self.rcs.hkdf
+            if connection_end == "client":
+                basekey = self.tls13_derived_secrets[shts]
+            elif connection_end == "server":
+                basekey = self.tls13_derived_secrets[chts]
+        elif read_or_write == "write":
+            hkdf = self.wcs.hkdf
+            if connection_end == "client":
+                basekey = self.tls13_derived_secrets[chts]
+            elif connection_end == "server":
+                basekey = self.tls13_derived_secrets[shts]
+
+        if not hkdf or not basekey:
+            warning("Missing arguments for verify_data computation!")
+            return None
+        #XXX this join() works in standard cases, but does it in all of them?
+        handshake_context = "".join(self.handshake_messages)
+        return hkdf.compute_verify_data(basekey, handshake_context)
+
+    def compute_tls13_resumption_secret(self):
+        """
+        self.handshake_messages should be ClientHello...ClientFinished.
+        """
+        if self.connection_end == "server":
+            hkdf = self.prcs.hkdf
+        elif self.connection_end == "client":
+            hkdf = self.pwcs.hkdf
+        rs = hkdf.derive_secret(self.tls13_master_secret,
+                                "resumption master secret",
+                                "".join(self.handshake_messages))
+        self.tls13_derived_secrets["resumption_secret"] = rs
+
+    def compute_tls13_next_traffic_secrets(self):
+        """
+        Ciphers key and IV are updated accordingly.
+        """
+        hkdf = self.prcs.hkdf
+        hl = hkdf.hash.digest_size
+
+        cts = self.tls13_derived_secrets["client_traffic_secrets"]
+        ctsN = cts[-1]
+        ctsN_1 = hkdf.expand_label(ctsN, "application traffic secret", "", hl)
+        cts.append(ctsN_1)
+
+        sts = self.tls13_derived_secrets["server_traffic_secrets"]
+        stsN = sts[-1]
+        stsN_1 = hkdf.expand_label(ctsN, "application traffic secret", "", hl)
+        cts.append(stsN_1)
+
+        if self.connection_end == "server":
+            self.prcs.tls13_derive_keys(ctsN_1)
+            self.pwcs.tls13_derive_keys(stsN_1)
+        elif self.connection_end == "client":
+            self.pwcs.tls13_derive_keys(ctsN_1)
+            self.prcs.tls13_derive_keys(stsN_1)
+
     ### Tests for record building/parsing
 
     def consider_read_padding(self):
         # Return True if padding is needed. Used by TLSPadField.
-        return self.rcs.cipher.type == "block"
+        return (self.rcs.cipher.type == "block" and
+                not (False in self.rcs.cipher.ready.itervalues()))
 
     def consider_write_padding(self):
         # Return True if padding is needed. Used by TLSPadField.
@@ -446,7 +790,6 @@ class tlsSession(object):
         return "%s:%s > %s:%s" % (self.ipsrc, str(self.sport),
                                   self.ipdst, str(self.dport))
 
-
 ###############################################################################
 ### Session singleton                                                       ###
 ###############################################################################
@@ -459,7 +802,7 @@ class _GenericTLSSessionInheritance(Packet):
     been negotiated. Passing information is also essential to the handshake.
     To this end, various TLS objects inherit from the present class.
     """
-    __slots__ = ["tls_session"]
+    __slots__ = ["tls_session", "rcs_snap_init", "wcs_snap_init"]
     name = "Dummy Generic TLS Packet"
     fields_desc = []
 
@@ -476,6 +819,9 @@ class _GenericTLSSessionInheritance(Packet):
             else:
                 self.tls_session = tls_session
 
+        self.rcs_snap_init = self.tls_session.rcs.snapshot()
+        self.wcs_snap_init = self.tls_session.wcs.snapshot()
+
         Packet.__init__(self, _pkt=_pkt, post_transform=post_transform,
                         _internal=_internal, _underlayer=_underlayer,
                         **fields)
@@ -504,12 +850,62 @@ class _GenericTLSSessionInheritance(Packet):
         pkt.tls_session = self.tls_session
         return pkt
 
+    def str_stateful(self):
+        return super(_GenericTLSSessionInheritance, self).__str__()
+
+    def __str__(self):
+        """
+        The __str__ call has to leave the connection states unchanged.
+        We also have to delete raw_packet_cache in order to access post_build.
+
+        For performance, the pending connStates are not snapshotted.
+        This should not be an issue, but maybe pay attention to this.
+
+        The previous_freeze_state prevents issues with calling a str() calling
+        in turn another str(), which would unfreeze the session too soon.
+        """
+        s = self.tls_session
+        rcs_snap = s.rcs.snapshot()
+        wcs_snap = s.wcs.snapshot()
+        rpc_snap = self.raw_packet_cache
+
+        s.wcs = self.rcs_snap_init
+
+        self.raw_packet_cache = None
+        previous_freeze_state = s.frozen
+        s.frozen = True
+        built_packet = super(_GenericTLSSessionInheritance, self).__str__()
+        s.frozen = previous_freeze_state
+
+        s.rcs = rcs_snap
+        s.wcs = wcs_snap
+        self.raw_packet_cache = rpc_snap
+
+        return built_packet
+
     def show2(self):
         """
         Rebuild the TLS packet with the same context, and then .show() it.
         We need self.__class__ to call the subclass in a dynamic way.
+
+        Howether we do not want the tls_session.{r,w}cs.seq_num to be updated.
+        We have to bring back the init states (it's possible the cipher context
+        has been updated because of parsing) but also to keep the current state
+        and restore it afterwards (the str() call may also update the states).
         """
-        self.__class__(str(self), tls_session=self.tls_session).show()
+        s = self.tls_session
+        rcs_snap = s.rcs.snapshot()
+        wcs_snap = s.wcs.snapshot()
+
+        s.rcs = self.rcs_snap_init
+
+        built_packet = str(self)
+        s.frozen = True
+        self.__class__(built_packet, tls_session=s).show()
+        s.frozen = False
+
+        s.rcs = rcs_snap
+        s.wcs = wcs_snap
 
     # Uncomment this when the automata update IPs and ports properly
     #def mysummary(self):
diff --git a/scapy/layers/tls/tools.py b/scapy/layers/tls/tools.py
index cdc5145ecdc95f2e69c0ca3137bc467ff16f0bbb..5cb5b1a0089772b9e3e8a7e7362c69762825a170 100644
--- a/scapy/layers/tls/tools.py
+++ b/scapy/layers/tls/tools.py
@@ -1,6 +1,6 @@
 ## This file is part of Scapy
 ## Copyright (C) 2007, 2008, 2009 Arnaud Ebalard
-##                     2015, 2016 Maxence Tury
+##               2015, 2016, 2017 Maxence Tury
 ## This program is published under a GPLv2 license
 
 """
diff --git a/test/cert.uts b/test/cert.uts
index 474ae927bc899a52dc265bf7c273b10797785305..a605b061daf495eb059b8d2ae0b49a0c18d5564a 100644
--- a/test/cert.uts
+++ b/test/cert.uts
@@ -35,7 +35,7 @@ AwIDAQAB
 x_pubNum = x.pubkey.public_numbers()
 type(x) is PubKeyRSA
 
-= PubKey class : key format is PEM
+= PubKey class : Verifying PEM key format
 x.frmt == "PEM"
 
 = PubKey class : Importing DER-encoded RSA Key
@@ -43,13 +43,13 @@ y = PubKey(b'0\x82\x01\"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x0
 y_pubNum = y.pubkey.public_numbers()
 type(y) is PubKeyRSA
 
-= PubKey class : key format is DER
+= PubKey class : Verifying DER key format
 y.frmt == "DER"
 
-= PubKey class : checking modulus value
+= PubKey class : Checking modulus value
 x_pubNum.n == y_pubNum.n and x_pubNum.n == 19231328316532061413420367242571475005688288081144416166988378525696075445024135424022026378563116068168327239354659928492979285632474448448624869172454076124150405352043642781483254546569202103296262513098482624188672299255268092629150366527784294463900039290024710152521604731213565912934889752122898104556895316819303096201441834849255370122572613047779766933573375974464479123135292080801384304131606933504677232323037116557327478512106367095125103346134248056463878553619525193565824925835325216545121044922690971718737998420984924512388011040969150550056783451476150234324593710633552558175109683813482739004163L
 
-= PubKey class : checking public exponent value
+= PubKey class : Checking public exponent value
 x_pubNum.e == y_pubNum.e and x_pubNum.e == 65537L
 
 = PubKey class : Importing PEM-encoded ECDSA public key
@@ -61,10 +61,10 @@ Jd5qtmDF2Zu+xrwrBRT0HBnPweDU+RsFxcyU/QxD9WYORzYarqxbcA==
 """)
 type(z) is PubKeyECDSA
 
-= PubKey class : checking curve
+= PubKey class : Checking curve
 z.pubkey.curve.name == "secp256k1"
 
-= PubKey class : checking point value
+= PubKey class : Checking point value
 z.pubkey.public_numbers().x == 104748656174769496952370005421566518252704263000192720134585149244759951661467L
 
 
@@ -106,11 +106,11 @@ x_privNum = x.key.private_numbers()
 x_pubNum = x.pubkey.public_numbers()
 type(x) is PrivKeyRSA
 
-= PrivKey class : checking public attributes
+= PrivKey class : Checking public attributes
 assert(x_pubNum.n == 19231328316532061413420367242571475005688288081144416166988378525696075445024135424022026378563116068168327239354659928492979285632474448448624869172454076124150405352043642781483254546569202103296262513098482624188672299255268092629150366527784294463900039290024710152521604731213565912934889752122898104556895316819303096201441834849255370122572613047779766933573375974464479123135292080801384304131606933504677232323037116557327478512106367095125103346134248056463878553619525193565824925835325216545121044922690971718737998420984924512388011040969150550056783451476150234324593710633552558175109683813482739004163L)
 x_pubNum.e == 65537L
 
-= PrivKey class : checking private attributes
+= PrivKey class : Checking private attributes
 assert(x_privNum.p == 140977881300857803928857666115326329496639762170623218602431133528876162476487960230341078724702018316260690172014674492782486113504117653531825010840338251572887403113276393351318549036549656895326851872473595350667293402676143426484331639796163189182788306480699144107905869179435145810212051656274284113969L)
 assert(x_privNum.q == 136413798668820291889092636919077529673097927884427227010121877374504825870002258140616512268521246045642663981036167305976907058413796938050224182519965099316625879807962173794483933183111515251808827349718943344770056106787713032506379905031673992574818291891535689493330517205396872699985860522390496583027L)
 assert(x_privNum.dmp1 == 46171616708754015342920807261537213121074749458020000367465429453038710215532257783908950878847126373502288079285334594398328912526548076894076506899568491565992572446455658740752572386903609191774044411412991906964352741123956581870694330173563737928488765282233340389888026245745090096745219902501964298369L)
@@ -127,25 +127,31 @@ weDU+RsFxcyU/QxD9WYORzYarqxbcA==
 """)
 type(y) is PrivKeyECDSA
 
-= PrivKey class : checking public attributes
+= PrivKey class : Checking public attributes
 assert(y.key.curve.name == "secp256k1")
 y.key.public_key().public_numbers().y == 86290575637772818452062569410092503179882738810918951913926481113065456425840L
 
-= PrivKey class : checking private attributes
+= PrivKey class : Checking private attributes
 y.key.private_numbers().private_value == 90719786431263082134670936670180839782031078050773732489701961692235185651857L
 
 
-########### PKCS crypto #############################################
-# these are legacy tests which should be removed eventually (see our pkcs1.py)
+########### Keys crypto tests #######################################
 
-+ PKCS 1 legacy tests
++ PubKey/PrivKey classes crypto tests
 
-= PKCS 1 legacy : RSA signature & verification
+= PrivKey/PubKey classes : Signing/Verifying with MD5_SHA1 hash
+m = "Testing our PKCS #1 legacy methods"    # ignore this string
+s = x.sign(m, t="pkcs", h="md5-sha1")
+assert(s == b"\x0cm\x8a\x8f\xae`o\xcdC=\xfea\xf4\xff\xf0i\xfe\xa3!\xfd\xa5=*\x99?\x08!\x03A~\xa3-B\xe8\xca\xaf\xb4H|\xa3\x98\xe9\xd5U\xfdL\xb1\x9c\xd8\xb2{\xa1/\xfcr\x8c\xa7\xd3\xa9%\xde\x13\xa8\xf6\xc6<\xc7\xdb\xe3\xa62\xeb\xe9?\xe5by\xc2\x9e\xad\xec\x92:\x14\xd96\xa8\xc0+\xea8'{=\x91$\xdf\xed\xe1+eF8\x9fI\x1f\xa1\xcb4s\xd1#\xdf\xa11\x88o\x050i Hg\x0690\xe6\xe8?\\<:k\x94\x82\x91\x0f\x06\xc7>ZQ\xc2\xcdn\xdb\xf4\x9d\x7f!\xa9>\xe8\xea\xb3\xd83]\x8d\x90\xd4\xa0b\xe6\xe6$d[\xe4\xb4 |W\xb2t\x8c\xb2\xd5>>+\xf1\xa6W'\xaf\xc2CU\x82\x13\xc4\x0b\xc4vD*\xc3\xef\xa6s\nQ\xe6\rS@B\xd2\xa4V\xdc\xd1D\x7f\x00\xaa\xac\xac\x96i\xf1kg*\xe9*\x90a@\xc8uDy\x16\xe2\x03\xd1\x9fa\xe2s\xdb\xees\xa4\x8cna\xba\xdaE\x006&\xa4")
+x_pub = PubKey((x._pubExp, x._modulus, x._modulusLen))
+x_pub.verify(m, s, t="pkcs", h="md5-sha1")
+
+= PrivKey/PubKey classes : Signing/Verifying with MD5_SHA1 hash with legacy support
 m = "Testing our PKCS #1 legacy methods"
-s = x.sign_legacy(m, t="pkcs", h="tls")
+s = x._legacy_sign_md5_sha1(m)
 assert(s == b"\x0cm\x8a\x8f\xae`o\xcdC=\xfea\xf4\xff\xf0i\xfe\xa3!\xfd\xa5=*\x99?\x08!\x03A~\xa3-B\xe8\xca\xaf\xb4H|\xa3\x98\xe9\xd5U\xfdL\xb1\x9c\xd8\xb2{\xa1/\xfcr\x8c\xa7\xd3\xa9%\xde\x13\xa8\xf6\xc6<\xc7\xdb\xe3\xa62\xeb\xe9?\xe5by\xc2\x9e\xad\xec\x92:\x14\xd96\xa8\xc0+\xea8\'{=\x91$\xdf\xed\xe1+eF8\x9fI\x1f\xa1\xcb4s\xd1#\xdf\xa11\x88o\x050i Hg\x0690\xe6\xe8?\\<:k\x94\x82\x91\x0f\x06\xc7>ZQ\xc2\xcdn\xdb\xf4\x9d\x7f!\xa9>\xe8\xea\xb3\xd83]\x8d\x90\xd4\xa0b\xe6\xe6$d[\xe4\xb4 |W\xb2t\x8c\xb2\xd5>>+\xf1\xa6W\'\xaf\xc2CU\x82\x13\xc4\x0b\xc4vD*\xc3\xef\xa6s\nQ\xe6\rS@B\xd2\xa4V\xdc\xd1D\x7f\x00\xaa\xac\xac\x96i\xf1kg*\xe9*\x90a@\xc8uDy\x16\xe2\x03\xd1\x9fa\xe2s\xdb\xees\xa4\x8cna\xba\xdaE\x006&\xa4")
 x_pub = PubKey((x._pubExp, x._modulus, x._modulusLen))
-x_pub.verify_legacy(m, s, t="pkcs", h="tls")
+x_pub._legacy_verify_md5_sha1(m, s)
 
 
 ########### Cert class ##############################################
diff --git a/test/configs/windows.utsc b/test/configs/windows.utsc
index 253ddebd0a016691b50033dae67785680e46edf2..462da81c53b0965a7a65ea4a36c7f6ab54c25d86 100644
--- a/test/configs/windows.utsc
+++ b/test/configs/windows.utsc
@@ -9,6 +9,6 @@
   },
   "format": "text",
   "kw_ko": [
-    "combined_modes_ccm"
+    "crypto_advanced"
   ]
 }
diff --git a/test/configs/windows2.utsc b/test/configs/windows2.utsc
index 1cf7a6c54a6d1f63bd525509d307d304f63ce3a1..d5eed103ac9eb5d3f3cf20dcad7efddb723a924f 100644
--- a/test/configs/windows2.utsc
+++ b/test/configs/windows2.utsc
@@ -9,8 +9,8 @@
   },
   "format": "html",
   "kw_ko": [
-    "combined_modes_ccm",
-    "mock_read_routes6_bsd",
+    "crypto_advanced",
+    "mock_read_routes6_bsd"
     "appveyor_only"
   ]
 }
diff --git a/test/ipsec.uts b/test/ipsec.uts
index 8a71d37f28145c8f56d6579e52d7c7a2e1d12d61..d30ce4a354e0380ae081b44f52e018895c04159c 100644
--- a/test/ipsec.uts
+++ b/test/ipsec.uts
@@ -1596,7 +1596,7 @@ except IPSecIntegrityError, err:
 
 #######################################
 = IPv4 / ESP - Transport - AES-CCM - NULL
-~ combined_modes_ccm
+~ crypto_advanced
 
 p = IP(src='1.1.1.1', dst='2.2.2.2')
 p /= TCP(sport=45012, dport=80)
@@ -1650,7 +1650,7 @@ assert(d_ref.haslayer(ICMP))
 
 #######################################
 = IPv4 / ESP - Transport - AES-CCM - NULL - altered packet
-~ combined_modes_ccm
+~ crypto_advanced
 
 p = IP(src='1.1.1.1', dst='2.2.2.2')
 p /= TCP(sport=45012, dport=80)
@@ -1832,7 +1832,7 @@ except IPSecIntegrityError, err:
 
 #######################################
 = IPv4 / ESP - Tunnel - AES-CCM - NULL
-~ combined_modes_ccm
+~ crypto_advanced
 
 p = IP(src='1.1.1.1', dst='2.2.2.2')
 p /= TCP(sport=45012, dport=80)
@@ -1867,7 +1867,7 @@ assert(d == p)
 
 #######################################
 = IPv4 / ESP - Tunnel - AES-CCM - NULL
-~ combined_modes_ccm
+~ crypto_advanced
 
 p = IP(src='1.1.1.1', dst='2.2.2.2')
 p /= TCP(sport=45012, dport=80)
@@ -2989,7 +2989,7 @@ except IPSecIntegrityError, err:
 
 #######################################
 = IPv6 / ESP - Transport - AES-CCM - NULL
-~ combined_modes_ccm
+~ crypto_advanced
 
 p = IPv6(src='11::22', dst='22::11')
 p /= TCP(sport=45012, dport=80)
@@ -3021,7 +3021,7 @@ assert(d[TCP] == p[TCP])
 
 #######################################
 = IPv6 / ESP - Transport - AES-CCM - NULL - altered packet
-~ combined_modes_ccm
+~ crypto_advanced
 
 p = IPv6(src='11::22', dst='22::11')
 p /= TCP(sport=45012, dport=80)
@@ -3331,7 +3331,7 @@ except IPSecIntegrityError, err:
 
 #######################################
 = IPv6 / ESP - Tunnel - AES-CCM - NULL
-~ combined_modes_ccm
+~ crypto_advanced
 
 p = IPv6(src='11::22', dst='22::11')
 p /= TCP(sport=45012, dport=80)
@@ -3364,7 +3364,7 @@ assert(d[TCP] == p[TCP])
 
 #######################################
 = IPv6 / ESP - Tunnel - AES-CCM - NULL - altered packet
-~ combined_modes_ccm
+~ crypto_advanced
 
 p = IPv6(src='11::22', dst='22::11')
 p /= TCP(sport=45012, dport=80)
diff --git a/test/sslv2.uts b/test/sslv2.uts
new file mode 100644
index 0000000000000000000000000000000000000000..e5fd8bf427071abde803296259b17716b542c2ad
--- /dev/null
+++ b/test/sslv2.uts
@@ -0,0 +1,280 @@
+% Tests for TLS module
+#
+# Try me with :
+# bash test/run_tests -t test/tls.uts -F
+
+~ crypto
+
+###############################################################################
+################# Reading SSLv2 vulnerable test session #######################
+###############################################################################
+
+# These packets come from a session between an s_server and an s_client.
+# We assume the server's private key has been retrieved. Because the cipher
+# suite does not provide PFS, we are able to break the data confidentiality.
+# With openssl version being 0.9.8v, these are exactly the commands used:
+# openssl s_server -cert test/tls/pki/srv_cert.pem -key test/tls/pki/srv_key.pem -Verify 2 -cipher EXP-RC4-MD5
+# openssl s_client -ssl2 -cert test/tls/pki/cli_cert.pem -key test/tls/pki/cli_key.pem
+
++ Read a vulnerable SSLv2 session
+
+= Reading SSLv2 session - Loading unparsed SSLv2 records
+ch = b'\x80.\x01\x00\x02\x00\x15\x00\x00\x00\x10\x07\x00\xc0\x05\x00\x80\x03\x00\x80\x01\x00\x80\x06\x00@\x04\x00\x80\x02\x00\x80\x1a\xfb/\x9c\xa3\xd1)4T\xa3\x15(!\xff\xd1\x0c'
+sh = b'\x83\xc0\x04\x00\x01\x00\x02\x03\xa2\x00\x03\x00\x100\x82\x03\x9e0\x82\x02\x86\xa0\x03\x02\x01\x02\x02\t\x00\xfe\x04W\r\xc7\'\xe9\xf60\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x000T1\x0b0\t\x06\x03U\x04\x06\x13\x02MN1\x140\x12\x06\x03U\x04\x07\x0c\x0bUlaanbaatar1\x170\x15\x06\x03U\x04\x0b\x0c\x0eScapy Test PKI1\x160\x14\x06\x03U\x04\x03\x0c\rScapy Test CA0\x1e\x17\r160916102811Z\x17\r260915102811Z0X1\x0b0\t\x06\x03U\x04\x06\x13\x02MN1\x140\x12\x06\x03U\x04\x07\x0c\x0bUlaanbaatar1\x170\x15\x06\x03U\x04\x0b\x0c\x0eScapy Test PKI1\x1a0\x18\x06\x03U\x04\x03\x0c\x11Scapy Test Server0\x82\x01"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x000\x82\x01\n\x02\x82\x01\x01\x00\xcc\xf1\xf1\x9b`-`\xae\xf2\x98\r\')\xd9\xc0\tYL\x0fJ0\xa8R\xdf\xe5\xb1!\x9fO\xc3=V\x93\xdd_\xc6\xf7\xb3\xf6U\x8b\xe7\x92\xe2\xde\xf2\x85I\xb4\xa1,\xf4\xfdv\xa8g\xca\x04 `\x11\x18\xa6\xf2\xa9\xb6\xa6\x1d\xd9\xaa\xe5\xd9\xdb\xaf\xe6\xafUW\x9f\xffR\x89e\xe6\x80b\x80!\x94\xbc\xcf\x81\x1b\xcbg\xc2\x9d\xb5\x05w\x04\xa6\xc7\x88\x18\x80xh\x956\xde\x97\x1b\xb6a\x87B\x1au\x98E\x82\xeb>2\x11\xc8\x9b\x86B9\x8dM\x12\xb7X\x1b\x19\xf3\x9d+\xa1\x98\x82\xca\xd7;$\xfb\t9\xb0\xbc\xc2\x95\xcf\x82)u\x16)?B \x17+M@\x8cVl\xad\xba\x0f4\x85\xb1\x7f@yqx\xb7\xa5\x04\xbb\x94\xf7\xb5A\x95\xee|\xeb\x8d\x0cyhY\xef\xcb\xb3\xfa>x\x1e\xeegLz\xdd\xe0\x99\xef\xda\xe7\xef\xb2\t]\xbe\x80 !\x05\x83,D\xdb]*v)\xa5\xb0#\x88t\x07T"\xd6)z\x92\xf5o-\x9e\xe7\xf8&+\x9cXe\x02\x03\x01\x00\x01\xa3o0m0\t\x06\x03U\x1d\x13\x04\x020\x000\x0b\x06\x03U\x1d\x0f\x04\x04\x03\x02\x05\xe00\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14\xa1+ p\xd2k\x80\xe5e\xbc\xeb\x03\x0f\x88\x9ft\xad\xdd\xf6\x130\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14fS\x94\xf4\x15\xd1\xbdgh\xb0Q725\xe1\xa4\xaa\xde\x07|0\x13\x06\x03U\x1d%\x04\x0c0\n\x06\x08+\x06\x01\x05\x05\x07\x03\x010\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x81\x88\x92sk\x93\xe7\x95\xd6\xddA\xee\x8e\x1e\xbd\xa3HX\xa7A5?{}\xd07\x98\x0e\xb8,\x94w\xc8Q6@\xadY\t(\xc8V\xd6\xea[\xac\xb4\xd8?h\xb7f\xca\xe1V7\xa9\x00e\xeaQ\xc9\xec\xb2iI]\xf9\xe3\xc0\xedaT\xc9\x12\x9f\xc6\xb0\nsU\xe8U5`\xef\x1c6\xf0\xda\xd1\x90wV\x04\xb8\xab8\xee\xf7\t\xc5\xa5\x98\x90#\xea\x1f\xdb\x15\x7f2(\x81\xab\x9b\x85\x02K\x95\xe77Q{\x1bH.\xfb>R\xa3\r\xb4F\xa9\x92:\x1c\x1f\xd7\n\x1eXJ\xfa.Q\x8f)\xc6\x1e\xb8\x0e1\x0es\xf1\'\x88\x17\xca\xc8i\x0c\xfa\x83\xcd\xb3y\x0e\x14\xb0\xb8\x9b/:-\t\xe3\xfc\x06\xf0:n\xfd6;+\x1a\t*\xe8\xab_\x8c@\xe4\x81\xb2\xbc\xf7\x83g\x11nN\x93\xea"\xaf\xff\xa3\x9awWv\xd0\x0b8\xac\xf8\x8a\x945\x8e\xd7\xd4a\xcc\x01\xff$\xb4\x8fa#\xba\x88\xd7Y\xe4\xe9\xba*N\xb5\x15\x0f\x9c\xd0\xea\x06\x91\xd9\xde\xab\x02\x00\x809\x19R\xa4\xab)\x93\x94\xcd\x8a,^\x03\xb9\xf1\x80'
+mk = b"\x81\x15\x02\x02\x00\x80\x00\x0b\x01\x00\x00\x00~\xc2\xf9\x9e\x94i\x02\xfe\xed\xc4\xdc\x9b\xe0\xe7 \xa8\xcct'\xf7\xc3\x05\xfc\xa5^\x13\xdc\xaa\xf6\xfa\x88\x9f\xf9\x89\xc5@\x86\xfe\xe3\xe0\x88\xd8\x02B%5\x8e\xeaV\xd9\x08\xd2In\x9aY\xca\x87\x86k\xdf\xc3W\x13x\xff\x98\xb9!bU9\x07R.i\xce\x19\xf0\xa0\xc0\x1af\x87\\M&4\x8d\xa8G\xc1\xcd\x1d\x8a\x95F\t\xba\x07\x193\xb44\x8f\xbd+\xbdmz\xd0\x11\xa3\xbd9\xaaU\xbd\xfcX\xbb\xf0%V\x1e\xae\xd1\xf3\xe9\xa0\xd7\x19E\r\xac\xad\xde/mc\xa4\xb0\xb0\x12No\xd9\xf5\xd9\xb4\xcb\xa7\xa5\t\xa6\xb9L,\x07\xfc\x9f>m4\x96\xadlS\xf1b\xdeo\xa2\xb6Hh\x85\xc5P\xec\x89i \xf7\xd6\xa0h\xa7\xa4\x95\x8dL\x16\xde_\x14\xe3\x18\xb2\x05\x1a1g\xdd\x9f\xd0\x06\x16\x06\x8c\xd4\xcc\x8a\x89\xbc\x9c6\xa9\xa1\xa8+5\x19g\xd6\x83\x1f\xe0\xd8j\x1a\x98!\x95Y\xbb\x1et\x1e2-\xab\xf8\xe3\xb7d\x92\xbe\xb0\x1a\xcf\x84G\xcc\xf4}\x01\x9eq\x14`q*z\xeaW."
+sv = b'\x80!\xc5\x84A`t1\xc3\xeaZ\xea\xdf\xd9\x87e_\xb9j`Yb\xbc\xbc\x08\xf5\x9c\x9b\xe6\xfaF\xa0\x87\x02\x07'
+cf = b'\x80!w\xa2\x88\x83uv\xd5|\xde\xbdoz\xba&^O\xda\x82k\x01L_xSx\x08\xe0\x1a\xaf\xa0\x07\x93\xa5'
+rc = b'\x80"\xfe\x03\xe0$\xec{\x08-\xe9h\xf7\xc9(i\xa6N\xd8\xaa\xe3\xb2;\xf1\xfd\xf5+\x80\xa9\x1a*\xb3Z\xa7\xbe\xde'
+cc = b'\x84\xb8\xe3j:\xc9\xa9OL\x9d\x08\xb7"\xf4)<\xf7\x0c\x92\x10{u\xd1\t\xccR(R\xc2\x02\xe0\n\x85i\xffJ\xb7\xc7\xf09\x98\x99\xde\x06\xd1\xe2\x1a\xeff[.`\x85\xf0_gs\x91\'\x0e\x82\x1b\xf6\x93\xf34m\x9d\xdc=\xf9\xeas\xac;\xe3\xcbB\xcf`\x899S"\xa8\xf9\x9b-\x07<\xfa\xf9|j\x11Z{\xa1\x1d\xd6\xf6\xdbgv\t\xa8\xa3[\x85\x82\x02^\x17\xd6\xcb\x8e\x08\xae\x87\xa1\x84\xec\x17\x0fuX,\xd4\x95\x98\x91\xea\xb3o4\x8a\xbc\x14\xfc"\x97\xfa_\xf9D\x0cB+\x07\x16K\x18&\x05x\x97.\xbc\xe1\xc4e\xb8S\xadwh\x8b\xeb\xe0\x10\x01\xd7\x08-\x81\xac\xff\xb2\x10\xcf\x14\x99VNw\xb618\xbd\xff\x18\x9c\xfb\x08\x07\xce{\x03b\x12\x81\x1d!t\xf9l\x84^d\x0eA\xdbj\xb7\xc6\x7f3\xf9t\x15\xa7)1\x95ko\xe6\x95\xd0\xbc\xe6S!"\xcaO>\x80\xad\xe0|\xb8+\xc4\x88me.\xe3O\xaf\xe2\x14k\xdc\x89\xe9\xc0O4\xa7\xc9\xb9\xe9a\xf7i\xb0\x1eH\xc7\x90\xe5ep\xa4\x8d2\x9d)MD\xb5\xc3\xc6G\xdd\xf3\x8f\x0e\xe0\xef\x17\x7f\x9f\x02\x02\xe7\xd7U\xc5\xfc+\x9d_\xf4\x1e#\x0e\x19\x9cX\xd4\xe6\x85\xe5\x1bR\xd2\xb1\xdf\x93K\xb9\xecD\xbbx\xda\x8f\x08u\xee\xad\xb6a\xc7\xb1\xed\xd7\xf9!/O\xb4\x17kg/D\xd7/\x9f\x1e\t\xf2\x9d\xc3i\xfb\xa9V\x19yme\xe8\xaa\x0e]\x7f\xff\xbf\xdc\xa5\xd8b\\\x14\x11f\xcdI\xe5\xb4\xc4\x0cl\x87z\xfb\xec\xbe\x06G\x05\xf5\xf7D.w[\xcf)}T\x13\x99]L\xeeg\xf1\x1f\xcc\xfc\xed\\\xf7Xh\xc5\x9f>}\xc0\xfb\xce=Ngr\x99\xcb6^\xdc\x86a\xb8w\xd8\xd5\x0e\xd7\x1f\xd7\x9d\xc52\x10 \xc4{\xb0\xb2\xc6\xdaJ3\xd1R\xd7\xe5\xc8\xe4e\xa6g[\xa5\xecVL\xf4\x15\x0b\x94\x81\xe2\x7fU\xff\xf9\x8c\x01\xf2\xc1\xae\xc7>\xe2U\x89\x92\xc4\t\x95@\x83}\x83\xca\xd2\xca\x02-.\xf9N&\xbb\xb3i\xba\xfe\xcf\x7f\xd6t\x03\xb8\x05~\xf89[@\xa0\xc5u\xf5\xe9\x89`jE9G1\xad\x18\xccQc(T\x1cc\xa98\x1e\xf4\xec\xac"\xed$3K\x1fM\xa1\xbc`3\x81k\x81\xc6|\xaeh\x86\xca\xde\x18h\n\x95\xb6M*MNNTugX\xfbC\xfe\xb9K\xf4\x01\xa0:S\x10\x9b\xf7\x1aW\x91\x86\xc7[\xf7r\xb8\xc2^P\xdf\x14\x90\xc3\x8d\x1f\xb0^\xe8\xd2\xd9\xd7i\x0e\xa1\x0b>\nr\xdcl\xce\x8a\x11\xd6(\xabq\xd6\x05\x1f9\x9c\x7f5\xacw\xb0L\x97J\n\x94\xac\x00\xda(-@\x0c\xc5\xd8\x86\x82\x91\xe2\t\xd7\xc9\xc0\xb0\x1fs4etn{\xfaE\xd4\xce\x9b\xc9\xd6B\xe9\xbd\xf1.\xd4\xf65\xb8[a\x80\x80?3\xa7\x0b\x05\xe3)\xd3r\xbdd\xe9\xd5+\x99\xcc\x0f,xi(\xbd@\xb2\x8b\xe4\xf8\x12\xebt\xd5\xdfg\xe1\xd4\x16+,\xa8e\xf3z\\\x15\xfc\xd0D\xfc\xad\xbc\xa5\xad\xa5\xad\xde\xb7"\x18?\x84\r\xe6\xb1\xd7io\xea\xf0\xaf\xe6;\xaf\xdc\xa5@\x7f7\x99\xe0\xd2\x00\x0f_\xcd\x12\xe5\xf7\x9b\xbd\x8b\xa6_\xf0\xe5B\xf5\x7f\x96\xa8B\xeff{,V\x83b\xc7Y\xe5Z|QE\xe3\x8e\xb9\xed=\x16\x9e\x9e\xdeW\xa7X\x10\x02:\xd2\x8bl~$\xc2\xd6\xec\xc5\xfa=)\x93\xe9gJ\x8f\xc9\xb5\xb5A\xd0\x11]\xb9;ks\xfba\x84\xa0\x94,W\x1e\x07\x1e\xc2j\xa1\x9f\x8d\xbb\xe2\xb9 \x0f\xac\x9b\xbb\xe1\x12\t7\x8eJ;\x9d\xd4\x15\x197\x97\xf7xo\xcdJ\x15(\x88`z\x00\xff\xd0R.:\xc9\x92\xcbY~\xc3\x8ex\xd7\xab\xf6q\x98x\x99\xf2R;# \x0e\x16F\x9b\x15\xff\x90\x08\x06F\x01\xb7\xcd\xa0\xbaM\xf8xy\x99W\xaa\x82\xcc70\x02\x97\x0e\xd8\xeb\xdeLk\xa4\xe8\xbb\x7f\x0fN\xc6>hTN\n\xe2\xb1\xcc\xb0\xbcH\x99\x83]\x0f\x84\x07\xf5\x1e\x079\xb0[\xd8\xc9I\x93\xa0-\x0c\xc2\xd8W\x13\xed;\r\xe0S\xe6\x82m<\x8b\x0c\xfd\xce\xd6\xecA\xe7Zm\xeb\x03\x9b%p\xfa\xb0\x8c^\x7f\xb5e\xa8\xb0\x7f\xbf\xcd\xbf\x1f3\xa3\xb3<Qk\xfc\xa7>\xddZ\xb3\'\x1arO?\x16\x8a\x90\xb3n$\xd5\xa5\x91\xe2\x91\x00Qy\xdb\xcf\xc8\xd9xP\x92\xd3 \xfd\xb2R\xb7\xe0\x8f\x02\xcf\x15\xad3\xda\xa8\xe0}\xa0\x8c\xfb\xfe-\x14\xb3\x85E\xe6\x91@1\xb6\n\x90;\xb6\xbf\xbb\x16e{\xce\xaf\xd7\xdf\xac\xc9\xe7-,\xd0<\xf8L[f~\x13R\xf7\xaf\xff\xa3\xe1\x98\x93\x03V9\x04\x13y\xaa\x17=\xef\xe6`f\xb49\x8e3\xc8\xac\x81+}\x9a\xaf\xfbe\xa0J\xd2\xcb?\x870\xd9\x0e\x87\xa2\xe1YS\x06v\xc51\x18\x9a\x8b\xd5\xc8\xdd\x01y\xee\xab3\x16\xfd\x93\xc7\x1a8\xe9'
+sf = b'\x80!\xc9\x18i\x80\xfb\xe9\xea\xa7F\x83n\xaaP\xc0\x88\x8a\x03\xce"9p\xbcW\xb1r\xac\xcc\xb1\xaa\x08\x85\xb4\xe2'
+d1 = b'\x80C\x99\x83z\xc0\xdc\xe7\xf0I\x80\x8c\x8e\x1c\xc7bx\x98\xd3\x84\xd6\x06\xc8r\x9b\x197\xd2\xe9\x08\xc53s\x88 y\x8e)\x9f\xdcE*e\xb2r\xa2$<h\x1c/\x89\x9e\xc0\xc2D9KWB7\xddSi\xb6\xaeE\x13\xe2'
+d2 = b"\x80%\xee'\xc9\xb5(\xe3`\xcf\xaf\x1b\xa3 \x81\xad\x8b0\xc2Y\x8eg\xaaV\x90\x92\x02\xfe\xd1\xd0\t\xa3fE\x88\x97\x85\x08\xf5"
+import binascii
+
+= Reading SSLv2 session - SSLv2 parsing does not raise any error
+t = SSLv2(ch)
+
+= Reading SSLv2 session - Record with cleartext
+assert(t.len == 46)
+assert(not t.padlen)
+assert(not t.mac)
+assert(not t.pad)
+len(t.msg) == 1
+
+= Reading SSLv2 session - Record __getitem__
+SSLv2ClientHello in t
+
+= Reading SSLv2 session - ClientHello
+ch = t.msg[0]
+assert(isinstance(ch, SSLv2ClientHello))
+assert(ch.msgtype == 1)
+assert(ch.version == 0x0002)
+assert(ch.cipherslen == 21)
+assert(not ch.sid)
+assert(ch.challengelen == 16)
+assert(ch.ciphers == [0x0700c0, 0x050080, 0x030080, 0x010080, 0x060040, 0x040080, 0x020080])
+ch.challenge == binascii.unhexlify('1afb2f9ca3d1293454a3152821ffd10c')
+
+= Reading SSLv2 session - ServerHello
+t = SSLv2(sh, tls_session=t.tls_session.mirror())
+sh = t.msg[0]
+assert(isinstance(sh, SSLv2ServerHello))
+assert(sh.msgtype == 4)
+assert(sh.sid_hit == 0)
+assert(sh.certtype == 1)
+assert(sh.version == 0x0002)
+assert(sh.certlen == 930)
+assert(sh.cipherslen == 3)
+assert(sh.connection_idlen == 16)
+assert(isinstance(sh.cert, Cert))
+assert(len(sh.cert.der) == 930)
+assert(sh.cert.subject_str == '/C=MN/L=Ulaanbaatar/OU=Scapy Test PKI/CN=Scapy Test Server')
+assert(sh.ciphers == [0x020080])
+sh.connection_id == binascii.unhexlify('391952a4ab299394cd8a2c5e03b9f180')
+
+= Reading SSLv2 session - ClientMasterKey with unknown server key
+t_enc = SSLv2(mk)
+mk_enc = t_enc.msg[0]
+assert(mk_enc.clearkeylen == 11)
+assert(mk_enc.encryptedkeylen == 256)
+assert(mk_enc.clearkey == binascii.unhexlify('7ec2f99e946902feedc4dc'))
+assert(mk_enc.encryptedkey[:3] == b"\x9b\xe0\xe7" and mk_enc.encryptedkey[-3:] == b"\xea\x57\x2e")
+mk_enc.decryptedkey is None
+
+= Reading SSLv2 session - Importing server compromised key
+import os
+basedir = os.path.abspath(os.path.join(os.path.dirname(__file__),"../"))
+rsa_key = PrivKeyRSA(basedir + '/test/tls/pki/srv_key.pem')
+t.tls_session.server_rsa_key = rsa_key
+
+= Reading SSLv2 session - ClientMasterKey with compromised server key
+t = SSLv2(mk, tls_session=t.tls_session.mirror())
+assert(t.len == 277 and not t.padlen and not t.mac and not t.pad)
+mk = t.msg[0]
+assert(isinstance(mk, SSLv2ClientMasterKey))
+assert(mk.msgtype == 2)
+assert(mk.cipher == 0x020080)
+assert(mk.clearkeylen == 11)
+assert(mk.encryptedkeylen == 256)
+assert(mk.keyarglen == 0)
+assert(mk.clearkey == binascii.unhexlify('7ec2f99e946902feedc4dc'))
+assert(mk.decryptedkey == binascii.unhexlify('e2d430fc04'))
+not mk.keyarg
+
+= Reading SSLv2 session - Checking session secrets
+s = t.tls_session
+assert(s.sslv2_common_cs == [0x020080])
+assert(s.sslv2_challenge == ch.challenge)
+assert(not s.pre_master_secret)
+assert(s.master_secret == b'~\xc2\xf9\x9e\x94i\x02\xfe\xed\xc4\xdc\xe2\xd40\xfc\x04')
+assert(s.sslv2_key_material == b'\xf4\xae\x00\x03kB>\x06\xba[\xd7\xea,\x08\xc2\xae\xba\xf3\x10\xbf\xea\x08\x8flV\x11D\xc5L\xad3\xf9')
+assert(s.rcs.cipher.key == b'\xba\xf3\x10\xbf\xea\x08\x8flV\x11D\xc5L\xad3\xf9')
+s.wcs.cipher.key == b'\xf4\xae\x00\x03kB>\x06\xba[\xd7\xea,\x08\xc2\xae'
+
+= Reading SSLv2 session - Record with ciphertext
+t = SSLv2(sv, tls_session=t.tls_session.mirror())
+assert(t.len == 33)
+assert(not t.padlen)
+assert(t.mac == b'?:\xf3vE\xf3\xe83\x1a\xd0\xab\xba\xb6\x86\xe6\x89')
+not t.pad
+
+= Reading SSLv2 session - ServerVerify
+sv = t.msg[0]
+assert(isinstance(sv, SSLv2ServerVerify))
+assert(sv.msgtype == 5)
+sv.challenge == ch.challenge
+
+= Reading SSLv2 session - ClientFinished
+t = SSLv2(cf, tls_session=t.tls_session.mirror())
+cf = t.msg[0]
+assert(isinstance(cf, SSLv2ClientFinished))
+assert(cf.msgtype == 3)
+cf.connection_id == sh.connection_id
+
+= Reading SSLv2 session - RequestCertificate
+t = SSLv2(rc, tls_session=t.tls_session.mirror())
+rc = t.msg[0]
+assert(isinstance(rc, SSLv2RequestCertificate))
+assert(rc.msgtype == 7)
+assert(rc.authtype == 1)
+rc.challenge == binascii.unhexlify('19619ddf7384d68e7a614ae1989ab41e')
+
+= Reading SSLv2 session - ClientCertificate
+t = SSLv2(cc, tls_session=t.tls_session.mirror())
+cc = t.msg[0]
+assert(isinstance(cc, SSLv2ClientCertificate))
+assert(cc.msgtype == 8)
+assert(cc.certtype == 1)
+assert(cc.certlen == 930)
+assert(cc.responselen == 256)
+assert(isinstance(cc.certdata, Cert))
+assert(len(cc.certdata.der) == 930)
+assert(cc.certdata.subject_str == '/C=MN/L=Ulaanbaatar/OU=Scapy Test PKI/CN=Scapy Test Client')
+assert(len(cc.responsedata.sig_val) == 256)
+cc.responsedata.sig_val[:4] == b"\x81#\x95\xb5" and cc.responsedata.sig_val[-4:] == b"RM6\xd3"
+
+= Reading SSLv2 session - ServerFinished
+t = SSLv2(sf, tls_session=t.tls_session.mirror())
+sf = t.msg[0]
+assert(isinstance(sf, SSLv2ServerFinished))
+assert(sf.msgtype == 6)
+sf.sid == binascii.unhexlify('11c1e8070b2cf249ad3d85caf8854bc8')
+
+= Reading SSLv2 session - Application data
+t1 = SSLv2(d1, tls_session=t.tls_session.mirror())
+data1 = t1.msg[0]
+assert(isinstance(data1, Raw))
+assert(data1.load == 'These are horrendous parameters for a TLS session.\n')
+t2 = SSLv2(d2, tls_session=t1.tls_session)
+data2 = t2.msg[0]
+assert(isinstance(data2, Raw))
+data2.load ==  '*nothing to do here*\n'
+
+= Reading SSLv2 session - Checking final sequence numbers
+t2.tls_session.rcs.seq_num == 6 and t2.tls_session.wcs.seq_num == 4
+
+
+###############################################################################
+####################### TLS-related scapy internals ###########################
+###############################################################################
+
++ Check TLS-related scapy internals
+
+= Check TLS-related scapy internals - Checking str() harmlessness (with RC4)
+t1.show()
+assert(t1.msg[0].load == 'These are horrendous parameters for a TLS session.\n')
+assert(t1.tls_session.rcs.seq_num == 6 and t1.tls_session.wcs.seq_num == 4)
+d1 == str(t1)
+
+= Check TLS-related scapy internals - Checking show2() harmlessness (with RC4)
+t2.show2()
+assert(t2.msg[0].load == '*nothing to do here*\n')
+assert(t2.tls_session.rcs.seq_num == 6 and t2.tls_session.wcs.seq_num == 4)
+d2 == str(t2)
+
+= Check TLS-related scapy internals - Checking show2() harmlessness (with 3DES)
+# These are also taken from a s_client/s_server session.
+ch = b'\x80.\x01\x00\x02\x00\x15\x00\x00\x00\x10\x07\x00\xc0\x05\x00\x80\x03\x00\x80\x01\x00\x80\x06\x00@\x04\x00\x80\x02\x00\x80!2bc\x97^\xa3\r9\x14*\xe4\xd6\xd4\n\x1e'
+sh = b'\x83\xd2\x04\x00\x01\x00\x02\x03\xa2\x00\x15\x00\x100\x82\x03\x9e0\x82\x02\x86\xa0\x03\x02\x01\x02\x02\t\x00\xfe\x04W\r\xc7\'\xe9\xf60\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x000T1\x0b0\t\x06\x03U\x04\x06\x13\x02MN1\x140\x12\x06\x03U\x04\x07\x0c\x0bUlaanbaatar1\x170\x15\x06\x03U\x04\x0b\x0c\x0eScapy Test PKI1\x160\x14\x06\x03U\x04\x03\x0c\rScapy Test CA0\x1e\x17\r160916102811Z\x17\r260915102811Z0X1\x0b0\t\x06\x03U\x04\x06\x13\x02MN1\x140\x12\x06\x03U\x04\x07\x0c\x0bUlaanbaatar1\x170\x15\x06\x03U\x04\x0b\x0c\x0eScapy Test PKI1\x1a0\x18\x06\x03U\x04\x03\x0c\x11Scapy Test Server0\x82\x01"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x000\x82\x01\n\x02\x82\x01\x01\x00\xcc\xf1\xf1\x9b`-`\xae\xf2\x98\r\')\xd9\xc0\tYL\x0fJ0\xa8R\xdf\xe5\xb1!\x9fO\xc3=V\x93\xdd_\xc6\xf7\xb3\xf6U\x8b\xe7\x92\xe2\xde\xf2\x85I\xb4\xa1,\xf4\xfdv\xa8g\xca\x04 `\x11\x18\xa6\xf2\xa9\xb6\xa6\x1d\xd9\xaa\xe5\xd9\xdb\xaf\xe6\xafUW\x9f\xffR\x89e\xe6\x80b\x80!\x94\xbc\xcf\x81\x1b\xcbg\xc2\x9d\xb5\x05w\x04\xa6\xc7\x88\x18\x80xh\x956\xde\x97\x1b\xb6a\x87B\x1au\x98E\x82\xeb>2\x11\xc8\x9b\x86B9\x8dM\x12\xb7X\x1b\x19\xf3\x9d+\xa1\x98\x82\xca\xd7;$\xfb\t9\xb0\xbc\xc2\x95\xcf\x82)u\x16)?B \x17+M@\x8cVl\xad\xba\x0f4\x85\xb1\x7f@yqx\xb7\xa5\x04\xbb\x94\xf7\xb5A\x95\xee|\xeb\x8d\x0cyhY\xef\xcb\xb3\xfa>x\x1e\xeegLz\xdd\xe0\x99\xef\xda\xe7\xef\xb2\t]\xbe\x80 !\x05\x83,D\xdb]*v)\xa5\xb0#\x88t\x07T"\xd6)z\x92\xf5o-\x9e\xe7\xf8&+\x9cXe\x02\x03\x01\x00\x01\xa3o0m0\t\x06\x03U\x1d\x13\x04\x020\x000\x0b\x06\x03U\x1d\x0f\x04\x04\x03\x02\x05\xe00\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14\xa1+ p\xd2k\x80\xe5e\xbc\xeb\x03\x0f\x88\x9ft\xad\xdd\xf6\x130\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14fS\x94\xf4\x15\xd1\xbdgh\xb0Q725\xe1\xa4\xaa\xde\x07|0\x13\x06\x03U\x1d%\x04\x0c0\n\x06\x08+\x06\x01\x05\x05\x07\x03\x010\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x81\x88\x92sk\x93\xe7\x95\xd6\xddA\xee\x8e\x1e\xbd\xa3HX\xa7A5?{}\xd07\x98\x0e\xb8,\x94w\xc8Q6@\xadY\t(\xc8V\xd6\xea[\xac\xb4\xd8?h\xb7f\xca\xe1V7\xa9\x00e\xeaQ\xc9\xec\xb2iI]\xf9\xe3\xc0\xedaT\xc9\x12\x9f\xc6\xb0\nsU\xe8U5`\xef\x1c6\xf0\xda\xd1\x90wV\x04\xb8\xab8\xee\xf7\t\xc5\xa5\x98\x90#\xea\x1f\xdb\x15\x7f2(\x81\xab\x9b\x85\x02K\x95\xe77Q{\x1bH.\xfb>R\xa3\r\xb4F\xa9\x92:\x1c\x1f\xd7\n\x1eXJ\xfa.Q\x8f)\xc6\x1e\xb8\x0e1\x0es\xf1\'\x88\x17\xca\xc8i\x0c\xfa\x83\xcd\xb3y\x0e\x14\xb0\xb8\x9b/:-\t\xe3\xfc\x06\xf0:n\xfd6;+\x1a\t*\xe8\xab_\x8c@\xe4\x81\xb2\xbc\xf7\x83g\x11nN\x93\xea"\xaf\xff\xa3\x9awWv\xd0\x0b8\xac\xf8\x8a\x945\x8e\xd7\xd4a\xcc\x01\xff$\xb4\x8fa#\xba\x88\xd7Y\xe4\xe9\xba*N\xb5\x15\x0f\x9c\xd0\xea\x06\x91\xd9\xde\xab\x07\x00\xc0\x05\x00\x80\x03\x00\x80\x01\x00\x80\x06\x00@\x04\x00\x80\x02\x00\x80\x03\xea\xd5\x88T&\xe7\\\xc9wM\x05\x1fo\xbf\xec'
+mk = b'\x81\x12\x02\x07\x00\xc0\x00\x00\x01\x00\x00\x08(\xc98#b\xc1\x94\x9e\xf1|\xd3\x98/\x84\xa6\xfb\x1e}b7\xf1\xfa9\xc3\xb4A\xe4\xb7\x97\xcd\x0c\xd4\x81\x82\xee\x8b\x80f]_\xbc\xe5\xeb\xe4}b\x82 \xa1S\xd5\xbe\xb3\xf7\xbb\x1c]zm\xe6\xc5\x95\xe3Y\x9d\x84b\xf2\x89\x08i\xf9"\x8d\xf7\xb9\xe3\xb5\xcb\x90\xc2V\xcel\x14\xb0\xd4-\xb3\xd3\xfe\x83\x8a(\x025\xd0Y\x9b4M\xde\xc6\x99{H\x89u.\xfa\x17\x13|\xd39\xf6sc\xaat\x9fy\xf69s9\xbfM\xdc\xcdT\x8d~U"\xdd\xce\xab\xfa\x0e\xa9\x90$s&\x0c8\xe4\x13b\x15\xc7\xc2\r#\xc96\x04{\x14\xd0\x19\xef\x13ql\x07\xbf\'\xfb\xdc\x14t\xf6I\xe6\x0b\xe7\xf2\xd6e\'%2H\x81\x16\x0bT;0\x95G%E\xc559p\x85S\x07\xbf\x03\xb0^\x06\xf0\xdcL\xd4\xf2\x9b\xf7\xc6T\xdb%MS\x8ch\xb5\x91H\xffF\xf0\xafCw\x1a:7\x1f\xf8\x05\x944\xc1p\xb6\x8e\x12.#,a\xd4s\xc7\x9f\xe5\xbf\xcb\xee\x1aS\xa71\x15\xf5pE'
+sv = b'\x00(\x07q\x8c\x08\xdb\xa8\xaa\x8d\x87\x0b\xe8\x01^\xed\x87\x8e\xb2\xc0\xa9\x84\x11v)~\xf8\xd9,\xea\xe2\x05\x86\x1d\x01n\xe1\xe6\xccE[\xcej'
+t = SSLv2(ch)
+t.show2()
+challenge = t.msg[0].challenge
+t = SSLv2(sh, tls_session=t.tls_session.mirror())
+t.show2()
+t.tls_session.server_rsa_key = rsa_key
+t = SSLv2(mk, tls_session=t.tls_session.mirror())
+t.show2()
+t.show2()
+t = SSLv2(sv, tls_session=t.tls_session.mirror())
+t.show2()
+t.show2()
+assert(t.padlen == 7)
+assert(t.mac == b'\xe7\xe4\x08\x0e\x86\xc4\x93\t\x80l/\x80\xdaQ\xa0z')
+assert(t.tls_session.rcs.seq_num == 2)
+assert(t.tls_session.wcs.seq_num == 2)
+t.msg[0].challenge == challenge
+
+= Check TLS-related scapy internals - Checking tls_session freeze during show2()
+l = len(t.tls_session.handshake_messages)
+t.show2()
+l == len(t.tls_session.handshake_messages)
+
+= Check TLS-related scapy internals - Checking SSLv2 cast from TLS class
+t = TLS(ch)
+assert(isinstance(t, SSLv2))
+t.msg[0].challenge == challenge
+
+
+###############################################################################
+######################### Building SSLv2 packets ##############################
+###############################################################################
+
++ Build SSLv2 packets
+
+= Building SSLv2 packets - Various default messages
+str(SSLv2())
+str(SSLv2Error())
+str(SSLv2ClientHello())
+str(SSLv2ServerHello())
+str(SSLv2ClientMasterKey())
+str(SSLv2ServerVerify())
+str(SSLv2ClientFinished())
+str(SSLv2RequestCertificate())
+str(SSLv2ClientCertificate())
+str(SSLv2ServerFinished())
+
+= Building SSLv2 packets - Error within clear record
+t = SSLv2(msg=SSLv2Error(code='no_cipher'))
+str(t) == b'\x80\x03\x00\x00\x01'
+
+= Building SSLv2 packets - ClientHello with automatic length computation
+ch_pkt = SSLv2ClientHello()
+ch_pkt.msgtype = 'client_hello'
+ch_pkt.version = 'SSLv2'
+ch_pkt.ciphers = [SSL_CK_DES_192_EDE3_CBC_WITH_MD5, SSL_CK_IDEA_128_CBC_WITH_MD5, SSL_CK_RC2_128_CBC_WITH_MD5, SSL_CK_RC4_128_WITH_MD5, SSL_CK_DES_64_CBC_WITH_MD5, SSL_CK_RC2_128_CBC_EXPORT40_WITH_MD5, SSL_CK_RC4_128_EXPORT40_WITH_MD5]
+ch_pkt.challenge = binascii.unhexlify('21326263975ea30d39142ae4d6d40a1e')
+t = SSLv2(msg=ch_pkt)
+str(t) == ch
+
+= Building SSLv2 packets - ClientMasterKey context linking
+mk = SSLv2ClientMasterKey(cipher='SSL_CK_DES_192_EDE3_CBC_WITH_MD5', clearkey=b'\xff'*19, encryptedkey=b'\0'*256, decryptedkey=b'\xaa'*5, keyarg=b'\x01'*8)
+t = SSLv2(msg=mk)
+t.tls_session.sslv2_connection_id = b'\xba'*16
+t.tls_session.sslv2_challenge = b'\x42'*16
+t.str_stateful()
+s = t.tls_session
+assert(s.master_secret == b'\xff'*19 + b'\xaa'*5)
+assert(isinstance(s.rcs.ciphersuite, SSL_CK_DES_192_EDE3_CBC_WITH_MD5))
+assert(isinstance(s.wcs.ciphersuite, SSL_CK_DES_192_EDE3_CBC_WITH_MD5))
+s.rcs.cipher.iv == b'\x01'*8
+s.wcs.cipher.iv == b'\x01'*8
+
+
+###############################################################################
+############################ Automaton behaviour ##############################
+###############################################################################
+
+# see test/tls/tests_tls_netaccess.uts
+
diff --git a/test/tls.uts b/test/tls.uts
index 0c08772930ed984bc14ad2a2a535b9d510238e47..8b67228596d0b65eb9f8b15b6feceeb236fcba3d 100644
--- a/test/tls.uts
+++ b/test/tls.uts
@@ -13,7 +13,8 @@
 ### HMAC                                                                    ###
 ###############################################################################
 
-+ Test Hmac_MD5
++ Test HMACs
+
 = Crypto - Hmac_MD5 instantiation, parameter check 
 from scapy.layers.tls.crypto.h_mac import Hmac_MD5
 a = Hmac_MD5("somekey")
@@ -31,7 +32,6 @@ t7 = a(b'\xaa'*80).digest("Test Using Larger Than Block-Size Key and Larger Than
 t1 and t2 and t3 and t4 and t5 and t6 and t7
 
 
-+ Test Hmac_SHA
 = Crypto - Hmac_SHA instantiation, parameter check 
 from scapy.layers.tls.crypto.h_mac import Hmac_SHA
 a = Hmac_SHA("somekey")
@@ -49,7 +49,6 @@ t7 = a(b'\xaa'*80).digest("Test Using Larger Than Block-Size Key and Larger Than
 t1 and t2 and t3 and t4 and t5 and t6 and t7
 
 
-+ Test Hmac_SHA2
 = Crypto - Hmac_SHA2 behavior on test vectors from RFC 4231
 
 class _hmac_test_case_1:
@@ -250,7 +249,8 @@ _all_hmac_sha2_tests()
 ### PRF                                                                     ###
 ###############################################################################
 
-+ Test _tls_P_MD5
++ Test PRFs and associated methods
+
 = Crypto - _tls_P_MD5 behavior on test vectors borrowed from RFC 2202 (+ errata)
 from scapy.layers.tls.crypto.prf import _tls_P_MD5
 t1 = _tls_P_MD5(b'\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b\x0b', "Hi There", 64) == b'8\x99\xc0\xb8!\xd7}RI\xb2\xbb\x8e\xbe\xf8\x97Y\xcc\xffL\xae\xc3I\x8f\x7f .\x81\xe0\xce\x1a\x82\xbd\x19\xa0\x16\x10P}\xf0\xda\xdc\xa0>\xc4,\xa1\xcfS`\x85\xc5\x084+QN31b\xd7%L\x9d\xdc'
@@ -263,7 +263,6 @@ t7 = _tls_P_MD5(b'\xaa'*80, "Test Using Larger Than Block-Size Key and Larger Th
 t1 and t2 and t3 and t4 and t5 and t6 and t7
 
 
-+ Test _tls_P_SHA1
 = Crypto - _tls_P_SHA1 behavior on test vectors borrowed from RFC 2202 (+ errata)
 from scapy.layers.tls.crypto.prf import _tls_P_SHA1
 t1 = _tls_P_SHA1(b'\x0b'*20, "Hi There", 80) == b'\x13\r\x11Q7(\xc1\xad\x7f>%m\xfc\x08\xb6\xb9$\xb1MG\xe4\x9c\xcdY\x0e\\T\xd0\x8f\x1a-O@`\xd2\x9eV_\xfd\xed\x1f\x93V\xfb\x18\xb6\xbclq3A\xa2\x87\xb1u\xfc\xb3RQ\x19;#\n(\xd2o%lB\x8b\x01\x89\x1c6m"\xc3\xe2\xa0\xe7'
@@ -276,7 +275,6 @@ t7 = _tls_P_SHA1(b'\xaa'*80, "Test Using Larger Than Block-Size Key and Larger T
 t1 and t2 and t3 and t4 and t5 and t6 and t7
 
 
-+ Test _tls_PRF()
 = Crypto - _tls_PRF behavior on test vectors borrowed from RFC 2202 (+ errata)
 from scapy.layers.tls.crypto.prf import _tls_PRF
 t1 = _tls_PRF(b'\x0b'*20, "Test Label XXXX", "Hi There", 80) == b'E\xcc\xeb\x12\x0b<\xbfh\x1f\xc3\xd3%J\x85\xdeQ\t\xbc[\xcd.\xbe\x170\xf2\xebm\xe6g\x05x\xad\x86V\x0b\xb3\xb7\xe5i\x7fh}T\xe5$\xe4\xba\xa0\xc6\xf0\xf1\xb1\xe1\x8a\xf5\xcc\x9ab\x1c\xc9\x10\x82\x93\x82Q\xd2\x80\xf0\xf8\x0f\x03\xe2\xbe\xc3\x94T\x05\xben\x9e'
@@ -289,7 +287,6 @@ t7 = _tls_PRF(b'\xaa'*80, "Test Label MMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM", "Test Us
 t1 and t2 and t3 and t4 and t5 and t6 and t7
 
 
-+ Test _ssl_PRF()
 = Crypto - _ssl_PRF behavior on test vectors
 from scapy.layers.tls.crypto.prf import _ssl_PRF
 t1 = _ssl_PRF(b'\x0b'*20, "Hi There", 80) == b'\x0fo\xbe9\x83>~Bc\xaea^\x86\xd2b\x94X\xfd9Be\xe799\xf2\x00\xfcS\xd6\x1c=\xe5\x7fin\x1e\xf9r\xc8\xe6k\x19K\x8a\x85SK\xe5\xb7;A\x19b\x86F3M\x8d=\xcf\x15\xeedo\xd3\xae\xa2\x95\x8e\x80\x13\xabG\x8d\x1c,\x8c\xab\xf7\xd4'
@@ -302,7 +299,6 @@ t7 = _ssl_PRF(b'\xaa'*80, "Test Using Larger Than Block-Size Key and Larger Than
 t1 and t2 and t3 and t4 and t5 and t6 and t7
 
 
-+ Test PRF for TLS 1.2
 = Crypto - _tls12_*_PRF behavior, using SHA-256, SHA-384 and SHA-512
 # https://www.ietf.org/mail-archive/web/tls/current/msg03416.html
 
@@ -341,7 +337,6 @@ def _all_prf_tls12_tests():
 _all_prf_tls12_tests()
 
 
-+ Test compute_master_secret() for SSL
 = Crypto - compute_master_secret() in SSL mode
 f = PRF(tls_version=0x300)
 t1 = f.compute_master_secret("A"*48, "B"*32, "C"*32) == b'\xe8\xb5O68e\x8c\x1e\xd0hD!\xc1Zk\x9e\xc7x3\xfc".\xf9\x17\xd5B\xfc\xef\x8d\xed\x9fP\xcer\x83|6\x02\xe0\x86\xda\xab-G\x8c\xa9H5'
@@ -351,7 +346,6 @@ t4 = f.compute_master_secret("D"*48, "B"*32, "A"*32) == b'\xbe\x9a\xc8)\xb5{.H1\
 t1 and t2 and t3 and t4
 
 
-+ Test derive_key_block() for SSL
 = Crypto - derive_key_block() in SSL mode
 t1 = f.derive_key_block("A"*48, "B"*32, "C"*32, 72) == b'\xe8\xb5O68e\x8c\x1e\xd0hD!\xc1Zk\x9e\xc7x3\xfc".\xf9\x17\xd5B\xfc\xef\x8d\xed\x9fP\xcer\x83|6\x02\xe0\x86\xda\xab-G\x8c\xa9H5\xdf\x14\xa9\xcfV\r\xea}\x98\x04\x8dK,\xb6\xf7;\xaa\xa8\xa5\xad\x7f\x0fCY'
 t2 = f.derive_key_block("A"*48, "C"*32, "B"*32, 72) == b'Ts/q\x83\x88\x10\x9c1Y\xff\xf3vo\xe3\x8aM\x9b\xa3k[J\xeeWXs\xcfTe\x19\xc6\xb1\x0ebj1}\x0c\xca\x97=|\x88W\xd8q\xfb|\x17\x99\nH;\xec\xd2\x15\xabd\xed\xc3\xe0p\xd8\x1eS\xb5\xf4*8\xceE^'
@@ -360,7 +354,6 @@ t4 = f.derive_key_block("D"*48, "B"*32, "A"*32, 72) == b'\xbe\x9a\xc8)\xb5{.H1\x
 t1 and t2 and t3 and t4
 
 
-+ Test compute_master_secret() for TLS 1.0
 = Crypto - compute_master_secret() in TLS 1.0 mode
 from scapy.layers.tls.crypto.prf import PRF
 f = PRF(tls_version=0x301)
@@ -371,7 +364,6 @@ t4 = f.compute_master_secret("D"*48, "B"*32, "A"*32) == b'\xeb3\xf5Ty\x08xqP\x01
 t1 and t2 and t3 and t4
 
 
-+ Test derive_key_block() for TLS 1.0
 = Crypto - derive_key_block() in TLS 1.0 mode
 t1 = f.derive_key_block("A"*48, "B"*32, "C"*32, 72) == b'\x06\xccA\xd5\xf3\x9dT`ZC!/\xa0\xbe\x95\x86m\xdb@\x18\xfb\x95\xad\xcd\xac<(K\x88\xacB\x92s\x8d7AVG\xf04\x0be\x8dv\x02\xd6\x03\x7f\xe4\x8eYe\x88\xb7YI\xc2\xf0!\x1dSx\x86\xdeY\x81\x89\x11\xa6\xd9\xd1\xed'
 t2 = f.derive_key_block("A"*48, "C"*32, "B"*32, 72) == b"\\@d\x1d9V\xae\xe2'\xf6Q\xc9\xd7\x8beu\xe8u\xd9\xe8\r\x18a\x8c|\xde\x95H\xec\xc5}I\xf9s(e\xe4\x87*s\x98=\x96wsj\xfe\x0euo\x1f\\1hh-\x0f\xda9\x9etk\x0fW\x03\xe2k\xb0\x87Pb3"
@@ -384,13 +376,6 @@ t1 and t2 and t3 and t4
 ### Ciphers                                                                 ###
 ###############################################################################
 
-+ Test _CipherSuitesField
-
-a = _CipherSuitesField("test", None, {})
-assert a.i2repr(None, None) == "None"
-assert isinstance(a.randval(), RandBin)
-assert hash(a) == 8890308
-
 + Test RC4
 = Crypto - RC4 stream cipher, encryption/decryption checks from RFC 6229
 
@@ -423,9 +408,32 @@ def _all_rc4_tests():
 _all_rc4_tests()
 
 
-+ Test DES-CBC
-= Crypto - DES cipher in CBC mode, check from FIPS PUB 81
+= Crypto - RC2 block cipher, encryption/decryption checks from RFC 2268
+
 import binascii
+class _rc2_128_cbc_test:
+    k= binascii.unhexlify("88bca90e90875a7f0f79c384627bafb2")
+    p= binascii.unhexlify("0000000000000000")
+    c= binascii.unhexlify("2269552ab0f85ca6")
+    iv=binascii.unhexlify("0000000000000000")
+
+def _all_rc2_tests():
+    try:
+        from scapy.layers.tls.crypto.cipher_block import Cipher_RC2_CBC
+        res = True
+        t = _rc2_128_cbc_test
+        tmp = (Cipher_RC2_CBC(t.k, t.iv).encrypt(t.p) == t.c and
+               Cipher_RC2_CBC(t.k, t.iv).decrypt(t.c) == t.p)
+        res = res and tmp
+        return res
+    except ImportError:
+        return True
+
+_all_rc2_tests()
+
+
+= Crypto - DES cipher in CBC mode, check from FIPS PUB 81
+
 class _descbc_test:
     k= binascii.unhexlify("0123456789abcdef")
     p= binascii.unhexlify("4e6f77206973207468652074696d6520666f7220616c6c20")
@@ -444,7 +452,6 @@ def _all_aes_cbc_tests():
 _all_aes_cbc_tests()
 
 
-+ Test AES-CBC
 = Crypto - AES cipher in CBC mode, checks from RFC 3602
 
 class _aes128cbc_test_1:
@@ -496,7 +503,6 @@ def _all_aes_cbc_tests():
 _all_aes_cbc_tests()
 
 
-+ Test AES-GCM
 = Crypto - AES cipher in GCM mode, auth_encrypt() and auth_decrypt() checks
 #https://tools.ietf.org/html/draft-mcgrew-gcm-test-01
 
@@ -563,7 +569,7 @@ def _all_aes_gcm_tests():
     res = True
     ciphers = []
     for t in [_aes128gcm_test_1, _aes128gcm_test_2]:
-        c = Cipher_AES_128_GCM(key=t.k, salt=t.n[:4],
+        c = Cipher_AES_128_GCM(key=t.k, fixed_iv=t.n[:4],
                                nonce_explicit=pkcs_os2ip(t.n[4:]))
         ne = t.n[-c.nonce_explicit_len:]
         tup = ne, t.p, t.ct[-c.tag_len:]
@@ -571,7 +577,7 @@ def _all_aes_gcm_tests():
         tmp2 = c.auth_encrypt(t.p, t.a) == (ne + t.ct)
         res = res and tmp1 and tmp2
     for t in [_aes256gcm_test_1, _aes256gcm_test_2]:
-        c = Cipher_AES_256_GCM(key=t.k, salt=t.n[:4],
+        c = Cipher_AES_256_GCM(key=t.k, fixed_iv=t.n[:4],
                                nonce_explicit=pkcs_os2ip(t.n[4:]))
         ne = t.n[-c.nonce_explicit_len:]
         tup = ne, t.p, t.ct[-c.tag_len:]
@@ -583,47 +589,45 @@ def _all_aes_gcm_tests():
 _all_aes_gcm_tests()
 
 
-#XXX CCM remains to be added to the cryptography library
-+ Test AES-CCM
 = Crypto - AES cipher in CCM mode, checks from IEEE P1619.1
-~ combined_modes_ccm
+~ crypto_advanced
 
 class _aes256ccm_test_1:
-    k= b"\0"*32
-    n= b"\0"*12
-    p= b"\0"*16
+    k= "\0"*32
+    n= "\0"*12
+    p= "\0"*16
     a= ""
-    ct=(b"\xc1\x94\x40\x44\xc8\xe7\xaa\x95\xd2\xde\x95\x13\xc7\xf3\xdd\x8c" +
-       b"\x4b\x0a\x3e\x5e\x51\xf1\x51\xeb\x0f\xfa\xe7\xc4\x3d\x01\x0f\xdb")
+    ct=("\xc1\x94\x40\x44\xc8\xe7\xaa\x95\xd2\xde\x95\x13\xc7\xf3\xdd\x8c" +
+       "\x4b\x0a\x3e\x5e\x51\xf1\x51\xeb\x0f\xfa\xe7\xc4\x3d\x01\x0f\xdb")
 
 class _aes256ccm_test_2:
-    k=(b"\xfb\x76\x15\xb2\x3d\x80\x89\x1d\xd4\x70\x98\x0b\xc7\x95\x84\xc8" +
-       b"\xb2\xfb\x64\xce\x60\x97\x87\x8d\x17\xfc\xe4\x5a\x49\xe8\x30\xb7")
-    n= b"\xdb\xd1\xa3\x63\x60\x24\xb7\xb4\x02\xda\x7d\x6f"
-    p= b"\xa9"
-    a= b"\x36"
-    ct=b"\x9d\x32\x61\xb1\xcf\x93\x14\x31\xe9\x9a\x32\x80\x67\x38\xec\xbd\x2a"
+    k=("\xfb\x76\x15\xb2\x3d\x80\x89\x1d\xd4\x70\x98\x0b\xc7\x95\x84\xc8" +
+       "\xb2\xfb\x64\xce\x60\x97\x87\x8d\x17\xfc\xe4\x5a\x49\xe8\x30\xb7")
+    n= "\xdb\xd1\xa3\x63\x60\x24\xb7\xb4\x02\xda\x7d\x6f"
+    p= "\xa9"
+    a= "\x36"
+    ct="\x9d\x32\x61\xb1\xcf\x93\x14\x31\xe9\x9a\x32\x80\x67\x38\xec\xbd\x2a"
 
 class _aes256ccm_test_3:
-    k=(b"\xfb\x76\x15\xb2\x3d\x80\x89\x1d\xd4\x70\x98\x0b\xc7\x95\x84\xc8" +
-       b"\xb2\xfb\x64\xce\x60\x97\x8f\x4d\x17\xfc\xe4\x5a\x49\xe8\x30\xb7")
-    n= b"\xdb\xd1\xa3\x63\x60\x24\xb7\xb4\x02\xda\x7d\x6f"
-    p= b"\xa8\x45\x34\x8e\xc8\xc5\xb5\xf1\x26\xf5\x0e\x76\xfe\xfd\x1b\x1e"
+    k=("\xfb\x76\x15\xb2\x3d\x80\x89\x1d\xd4\x70\x98\x0b\xc7\x95\x84\xc8" +
+       "\xb2\xfb\x64\xce\x60\x97\x8f\x4d\x17\xfc\xe4\x5a\x49\xe8\x30\xb7")
+    n= "\xdb\xd1\xa3\x63\x60\x24\xb7\xb4\x02\xda\x7d\x6f"
+    p= "\xa8\x45\x34\x8e\xc8\xc5\xb5\xf1\x26\xf5\x0e\x76\xfe\xfd\x1b\x1e"
     a= ""
-    ct=(b"\xcc\x88\x12\x61\xc6\xa7\xfa\x72\xb9\x6a\x17\x39\x17\x6b\x27\x7f" +
-       b"\x34\x72\xe1\x14\x5f\x2c\x0c\xbe\x14\x63\x49\x06\x2c\xf0\xe4\x23")
+    ct=("\xcc\x88\x12\x61\xc6\xa7\xfa\x72\xb9\x6a\x17\x39\x17\x6b\x27\x7f" +
+       "\x34\x72\xe1\x14\x5f\x2c\x0c\xbe\x14\x63\x49\x06\x2c\xf0\xe4\x23")
 
 class _aes256ccm_test_4:
-    k=(b"\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" +
-       b"\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f")
-    n= b"\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b"
-    p=(b"\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" +
-       b"\x30\x31\x32\x33\x34\x35\x36\x37")
-    a=(b"\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" +
-       b"\x10\x11\x12\x13")
-    ct=(b"\x04\xf8\x83\xae\xb3\xbd\x07\x30\xea\xf5\x0b\xb6\xde\x4f\xa2\x21" +
-       b"\x20\x34\xe4\xe4\x1b\x0e\x75\xe5\x9b\xba\x3f\x3a\x10\x7f\x32\x39" +
-       b"\xbd\x63\x90\x29\x23\xf8\x03\x71")
+    k=("\x40\x41\x42\x43\x44\x45\x46\x47\x48\x49\x4a\x4b\x4c\x4d\x4e\x4f" +
+       "\x50\x51\x52\x53\x54\x55\x56\x57\x58\x59\x5a\x5b\x5c\x5d\x5e\x5f")
+    n= "\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b"
+    p=("\x20\x21\x22\x23\x24\x25\x26\x27\x28\x29\x2a\x2b\x2c\x2d\x2e\x2f" +
+       "\x30\x31\x32\x33\x34\x35\x36\x37")
+    a=("\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x0a\x0b\x0c\x0d\x0e\x0f" +
+       "\x10\x11\x12\x13")
+    ct=("\x04\xf8\x83\xae\xb3\xbd\x07\x30\xea\xf5\x0b\xb6\xde\x4f\xa2\x21" +
+       "\x20\x34\xe4\xe4\x1b\x0e\x75\xe5\x9b\xba\x3f\x3a\x10\x7f\x32\x39" +
+       "\xbd\x63\x90\x29\x23\xf8\x03\x71")
 
 def _all_aes_ccm_tests():
     from scapy.layers.tls.crypto.cipher_aead import Cipher_AES_256_CCM
@@ -631,8 +635,8 @@ def _all_aes_ccm_tests():
     ciphers = []
     for t in [_aes256ccm_test_1, _aes256ccm_test_2,
               _aes256ccm_test_3, _aes256ccm_test_4]:
-        c = Cipher_AES_256_CCM(key=t.k, salt=t.n[:4],
-                               nonce_explicit_init=pkcs_os2ip(t.n[4:]))
+        c = Cipher_AES_256_CCM(key=t.k, fixed_iv=t.n[:4],
+                               nonce_explicit=pkcs_os2ip(t.n[4:]))
         ne = t.n[-c.nonce_explicit_len:]
         tup = ne, t.p, t.ct[-c.tag_len:]
         tmp1 = c.auth_decrypt(t.a, ne + t.ct, add_length=False) == tup
@@ -643,7 +647,80 @@ def _all_aes_ccm_tests():
 _all_aes_ccm_tests()
 
 
-+ Test camellia
+= Crypto - ChaCha20POly1305 test (test vector A.5 from RFC 7539)
+~ crypto_advanced
+
+import binascii
+def clean(s):
+    return binascii.unhexlify(''.join(c for c in s if c.isalnum()))
+
+class _chacha20poly1305_test_1:
+    k= clean("""
+        1c 92 40 a5 eb 55 d3 8a f3 33 88 86 04 f6 b5 f0
+        47 39 17 c1 40 2b 80 09 9d ca 5c bc 20 70 75 c0
+        """)
+    n= clean("""
+        00 00 00 00 01 02 03 04 05 06 07 08
+        """)
+    p= clean("""
+        49 6e 74 65 72 6e 65 74 2d 44 72 61 66 74 73 20
+        61 72 65 20 64 72 61 66 74 20 64 6f 63 75 6d 65
+        6e 74 73 20 76 61 6c 69 64 20 66 6f 72 20 61 20
+        6d 61 78 69 6d 75 6d 20 6f 66 20 73 69 78 20 6d
+        6f 6e 74 68 73 20 61 6e 64 20 6d 61 79 20 62 65
+        20 75 70 64 61 74 65 64 2c 20 72 65 70 6c 61 63
+        65 64 2c 20 6f 72 20 6f 62 73 6f 6c 65 74 65 64
+        20 62 79 20 6f 74 68 65 72 20 64 6f 63 75 6d 65
+        6e 74 73 20 61 74 20 61 6e 79 20 74 69 6d 65 2e
+        20 49 74 20 69 73 20 69 6e 61 70 70 72 6f 70 72
+        69 61 74 65 20 74 6f 20 75 73 65 20 49 6e 74 65
+        72 6e 65 74 2d 44 72 61 66 74 73 20 61 73 20 72
+        65 66 65 72 65 6e 63 65 20 6d 61 74 65 72 69 61
+        6c 20 6f 72 20 74 6f 20 63 69 74 65 20 74 68 65
+        6d 20 6f 74 68 65 72 20 74 68 61 6e 20 61 73 20
+        2f e2 80 9c 77 6f 72 6b 20 69 6e 20 70 72 6f 67
+        72 65 73 73 2e 2f e2 80 9d
+        """)
+    a= clean("""
+        f3 33 88 86 00 00 00 00 00 00 4e 91
+        """)
+    ct=clean("""
+        64 a0 86 15 75 86 1a f4 60 f0 62 c7 9b e6 43 bd
+        5e 80 5c fd 34 5c f3 89 f1 08 67 0a c7 6c 8c b2
+        4c 6c fc 18 75 5d 43 ee a0 9e e9 4e 38 2d 26 b0
+        bd b7 b7 3c 32 1b 01 00 d4 f0 3b 7f 35 58 94 cf
+        33 2f 83 0e 71 0b 97 ce 98 c8 a8 4a bd 0b 94 81
+        14 ad 17 6e 00 8d 33 bd 60 f9 82 b1 ff 37 c8 55
+        97 97 a0 6e f4 f0 ef 61 c1 86 32 4e 2b 35 06 38
+        36 06 90 7b 6a 7c 02 b0 f9 f6 15 7b 53 c8 67 e4
+        b9 16 6c 76 7b 80 4d 46 a5 9b 52 16 cd e7 a4 e9
+        90 40 c5 a4 04 33 22 5e e2 82 a1 b0 a0 6c 52 3e
+        af 45 34 d7 f8 3f a1 15 5b 00 47 71 8c bc 54 6a
+        0d 07 2b 04 b3 56 4e ea 1b 42 22 73 f5 48 27 1a
+        0b b2 31 60 53 fa 76 99 19 55 eb d6 31 59 43 4e
+        ce bb 4e 46 6d ae 5a 10 73 a6 72 76 27 09 7a 10
+        49 e6 17 d9 1d 36 10 94 fa 68 f0 ff 77 98 71 30
+        30 5b ea ba 2e da 04 df 99 7b 71 4d 6c 6f 2c 29
+        a6 ad 5c b4 02 2b 02 70 9b
+        """)
+    tag=clean("""
+        ee ad 9d 67 89 0c bb 22 39 23 36 fe a1 85 1f 38
+        """)
+
+def _all_chacha20poly1305_tests():
+    from scapy.layers.tls.crypto.cipher_aead import Cipher_CHACHA20_POLY1305_TLS13
+    res = True
+    ciphers = []
+    for t in [_chacha20poly1305_test_1]:
+        c = Cipher_CHACHA20_POLY1305_TLS13(key=t.k, fixed_iv=t.n)
+        tmp1 = c.auth_decrypt(t.a, t.ct + t.tag, b"\0"*8) == (t.p, t.tag)
+        tmp2 = c.auth_encrypt(t.p, t.a, b"\0"*8) == t.ct + t.tag
+        res = res and tmp1 and tmp2
+    return res
+
+_all_chacha20poly1305_tests()
+
+
 = Crypto - Camellia cipher, encryption/decryption checks
 
 class _Camellia128_test:
@@ -677,13 +754,14 @@ _all_camellia_tests()
 
 
 ###############################################################################
-############################ Reading test session #############################
+#################### Reading protected test session ###########################
 ###############################################################################
 
 # These packets come from a random TLS thread captured
 # during a github connection from a Mozilla Firefox client.
 
-+ Test strings
++ Read a protected TLS session
+
 = Reading test session - Loading unparsed TLS records
 p1_ch = b'\x16\x03\x01\x00\xd5\x01\x00\x00\xd1\x03\x03\x17\xf2M\xc3|\x19\xdb\xc3<\xb5J\x0b\x8d5\x81\xc5\xce\t 2\x08\xd8\xec\xd1\xf8"B\x9cW\xd0\x16v\x00\x00\x16\xc0+\xc0/\xc0\n\xc0\t\xc0\x13\xc0\x14\x003\x009\x00/\x005\x00\n\x01\x00\x00\x92\x00\x00\x00\x1f\x00\x1d\x00\x00\x1acamo.githubusercontent.com\xff\x01\x00\x01\x00\x00\n\x00\x08\x00\x06\x00\x17\x00\x18\x00\x19\x00\x0b\x00\x02\x01\x00\x00#\x00\x003t\x00\x00\x00\x10\x00)\x00\'\x05h2-16\x05h2-15\x05h2-14\x02h2\x08spdy/3.1\x08http/1.1\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00\r\x00\x16\x00\x14\x04\x01\x05\x01\x06\x01\x02\x01\x04\x03\x05\x03\x06\x03\x02\x03\x04\x02\x02\x02'
 p2_sh = b'\x16\x03\x03\x00T\x02\x00\x00P\x03\x03F\x07n\xe2\x0c\x97g\xb7o\xb6\x9b\x14\x19\xbd\xdd1\x80@\xaaQ+\xc2,\x19\x15"\x82\xe8\xc5,\xe8\x12\x00\xc0/\x00\x00(\x00\x00\x00\x00\xff\x01\x00\x01\x00\x00\x0b\x00\x04\x03\x00\x01\x02\x00#\x00\x00\x00\x05\x00\x00\x00\x10\x00\x0b\x00\t\x08http/1.1'
@@ -694,37 +772,29 @@ p6_tick_ccs_fin = b"\x16\x03\x03\x00\xca\x04\x00\x00\xc6\x00\x00\x04\xb0\x00\xc0
 p7_data = b"\x17\x03\x03\x01\xf6\x00\x00\x00\x00\x00\x00\x00\x01?\x04iy\x00\x04 \\\xd0\xd4\x9eG\x1f\xbf\xa3k\xfe=\xee\xce\x15\xa0%%\x06c}\xf6\xd4\xfb\xa6\xf0\xf6\x0cO\x1c\x9c\x91\xa9\x0b\x88J\xe0z\x94\xcaT\xeb\xc7\xad\x02j\x10\r\xc6\x12\xb9\xb9\x7f<\x84V\xab\x1e\xfc\xe5\x01\xda\xd6G\xf5\xb7\xf2I6\x8b\xc9\xc4a\xd3\x19\xeat\xfc\x9b\xfa\x1e\xe7\x8c\xaa\xb3\xce\xd0\x86G\x9b\x90\xf7\xde\xb1\x8bwM\x93\xa2gS>\xf3\x97\xf1CB\xfb\x8fs\x1e\xff\x83\xf9\x8b\xc0]\xbd\x80Mn3\xff\xa9\xf3)'\xc3S\xc8\xcd:\xbe\xd72B~$\xb2;\xeb+\xa4\xbd\xa9A\xd9 \n\x87\xe9\xe2\xe9\x82\x83M\x19Q\xf2n\x0e\x15\xdf\xb3;0\xdd&R\xb7\x15\x89\xe9O\xd8G7\x7f\xc3\xb8f\xc7\xd3\xc90R\x83\xf3\xd4\x1cd\xe8\xc5\x8d\xe4N(k7\xf0\xb7\xbd\x01\xb3\x9b\x86\xbaC.\x17\x8d\xd0g\xc9\xb1\x01\xfa\x01\xbe\xdbt\xb1u/\x19V\xc6\x08@\xff\xa8n\xe8\xd0\xd6n,\x05\xc9\xc2\xd8g\x19\x03.l\xb4)\xa09\xf9\xe7\x83\x01-\xe8\xf8\xffy\xbf\xf7\xe6\x11\xc5\xf5\x9aG\xb3e \xd85\x0f\x8f\x85H\xea\xc2n\x1eR\xbe\x01\xef\xef\x93\xe7*>\xbd\x84\x8b9HDI\x90\xc4$\x9a\x9aK\x88Ki\n\xa3\xab\xed\x91\xcd\xe8\xb1\xd4\x8e\xbcE\x88\xe8\x05\x16\xd5\xed\x18\x16g>\x04\xd8\x1dB}\x91\x90\xd1\xda\x03\xe1\x972CxtD\x85\xafF|~7D9*U\xad\x0b\xc4#\x06}\xec\xd6\xd3?y\x96\xa4\xb5\xa3\x1d\x1c\xbd\xc9\xc9g\xb12\xc9\x0f\xa1\x03\x12N\x0b\xec\x14\xc9vJ\nM\xa7\xc8h\xd0|(1(\xa3\x98@nH\n\x0b\xa80\x00\x02\xb7\x06Z\xd4M\xdc!AV\xe2\xa7*\xc3\x90U\xee\xd0\xb2\x05\xa3w\xe1\xe2\xbe\x1e\xbe\xd4u\xb1\xa1z\x1e\x1c\x15%7\xdd\xf9\xb9~\x02\xf9s\x0c1\xfb;\xab\xf1\x1e\xaf\x06\x8c\xafe\x00\x15e5\xac\xd7]>\x1dLb5\x8e+\x01n\xcb\x19\xcc\x17Ey\xc8"
 
 
-+ Test TLS
-= Reading test session - TLS parsing (no encryption) does not throw any error
+= Reading TLS test session - TLS parsing (no encryption) does not throw any error
 # We will need to distinguish between connection ends. See next XXX below.
 t1 = TLS(p1_ch)
-t2 = TLS(p2_sh, tls_session=t1.tls_session)
-t3 = TLS(p3_cert, tls_session=t1.tls_session)
-t4 = TLS(p4_certstat_ske_shd, tls_session=t1.tls_session)
+t2 = TLS(p2_sh, tls_session=t1.tls_session.mirror())
+t3 = TLS(p3_cert, tls_session=t2.tls_session)
+t4 = TLS(p4_certstat_ske_shd, tls_session=t3.tls_session)
 
-= Test TLS with bad data
-a = TLS(b'\x00\x03\x03\x00\x03data')
-assert a.haslayer(Raw)
 
-+ Test TLS Record header
-= Reading test session - TLS Record header
+= Reading TLS test session - TLS Record header
 # We leave the possibility for some attributes to be either '' or None.
 assert(t1.type == 0x16)
 assert(t1.version == 0x0301)
 assert(t1.len == 213)
-assert(len(t1.msg) == 1)
 assert(not t1.iv)
 assert(not t1.mac)
 assert(not t1.pad and not t1.padlen)
 len(t1.msg) == 1
 
 
-+ Test TLS Record __getitem__
-= Reading test session - TLS Record __getitem__
+= Reading TLS test session - TLS Record __getitem__
 TLSClientHello in t1
 
-+ Test TLS ClientHello
-= Reading test session - ClientHello
+= Reading TLS test session - ClientHello
 ch = t1.msg[0]
 assert(isinstance(ch, TLSClientHello))
 assert(ch.msgtype == 1)
@@ -740,8 +810,7 @@ assert(ch.complen == 1)
 assert(ch.comp == [0])
 
 
-+ Test TLS ClientHello Extensions
-= Reading test session - ClientHello extensions
+= Reading TLS test session - ClientHello extensions
 assert(ch.extlen == 146)
 ext = ch.ext
 assert(len(ext) == 9)
@@ -755,8 +824,8 @@ assert(ext[0].servernames[0].namelen == 26)
 assert(ext[0].servernames[0].servername == "camo.githubusercontent.com")
 assert(isinstance(ext[1], TLS_Ext_RenegotiationInfo))
 assert(not ext[1].renegotiated_connection)
-assert(isinstance(ext[2], TLS_Ext_SupportedEllipticCurves))
-assert(ext[2].ecl == [0x17, 0x18,  0x19])
+assert(isinstance(ext[2], TLS_Ext_SupportedGroups))
+assert(ext[2].groups == [0x17, 0x18, 0x19])
 assert(isinstance(ext[3], TLS_Ext_SupportedPointFormat))
 assert(ext[3].ecpl == [0])
 assert(isinstance(ext[4], TLS_Ext_SessionTicket))
@@ -773,8 +842,7 @@ assert(len(ext[8].sig_algs) == 10)
 ext[8].sig_algs[-1] == 0x0202
 
 
-+ Test TLS ServerHello
-= Reading test session - ServerHello
+= Reading TLS test session - ServerHello
 assert(TLSServerHello in t2)
 sh = t2.msg[0]
 assert(isinstance(sh, TLSServerHello))
@@ -784,11 +852,8 @@ assert(sh.cipher == 0xc02f)
 assert(len(sh.ext) == 6)
 sh.ext[-1].protocols[-1].protocol == "http/1.1"
 
-= Useless function
-assert not TLSHelloRequest().tls_session_update(None)
 
-+ Test TLS Certificate
-= Reading test session - Certificate
+= Reading TLS test session - Certificate
 cert = t3.msg[0]
 assert(cert.certslen == 2670)
 assert(len(cert.certs) == 2)
@@ -798,24 +863,21 @@ assert(srv_cert.serial == 0x077a5dc3362301f989fe54f7f86f3e64)
 srv_cert.subject['commonName'] == 'www.github.com'
 
 
-+ Test TLS CertStat-SKE-SHD
-= Reading test session - Multiple TLS layers
+= Reading TLS test session - Multiple TLS layers
 cert_stat = t4.msg[0]
 ske = t4.payload.msg[0]
 shd = t4.payload.payload.msg[0]
 isinstance(t4.payload.payload.payload, NoPayload)
 
 
-+ Test TLS CertificateStatus
-= Reading test session - CertificateStatus
+= Reading TLS test session - CertificateStatus
 assert(isinstance(cert_stat, TLSCertificateStatus))
 assert(cert_stat.responselen == 471)
 cert_stat.response[0].responseStatus == 0
 # we leave the remaining OCSP tests to x509.uts
 
 
-+ Test TLS ServerKeyExchange
-= Reading test session - ServerKeyExchange
+= Reading TLS test session - ServerKeyExchange
 assert(isinstance(ske, TLSServerKeyExchange))
 p = ske.params
 assert(isinstance(p, ServerECDHNamedCurveParams))
@@ -825,13 +887,11 @@ assert(ske.sig.sig_alg == 0x0601)
 ske.sig.sig_val[:4] == b'y\x8aQ\x11' and ske.sig.sig_val[-4:] == b'`15\xef'
 
 
-+ Test TLS ServerHelloDone
-= Reading test session - ServerHelloDone
+= Reading TLS test session - ServerHelloDone
 assert(isinstance(shd, TLSServerHelloDone))
 shd.msglen == 0
 
-+ Test tls_session
-= Reading test session - Context checks after 1st RTT
+= Reading TLS test session - Context checks after 1st RTT
 t = shd.tls_session
 assert(len(t.handshake_messages) == 6)
 assert(t.handshake_messages_parsed[-1] is shd)
@@ -847,15 +907,13 @@ assert(t.wcs.row == "write")
 t.rcs.ciphersuite.val == 0
 
 
-+ Test TLS
-= Reading test session - TLS parsing (with encryption) does not throw any error
+= Reading TLS test session - TLS parsing (with encryption) does not throw any error
 # XXX Something should be done, as for instance the reading of the 1st CCS
 # will mess up the reading state of the other side (even before the 2nd CCS).
-t5 = TLS(p5_cke_ccs_fin, tls_session=t1.tls_session)
+t5 = TLS(p5_cke_ccs_fin, tls_session=t4.tls_session.mirror())
 
 
-+ Test TLS ClientKeyExchange
-= Reading test session - ClientKeyExchange
+= Reading TLS test session - ClientKeyExchange
 cke = t5.msg[0]
 ccs = t5.payload.msg[0]
 rec_fin = t5.payload.payload
@@ -867,21 +925,16 @@ assert(isinstance(k, ClientECDiffieHellmanPublic))
 assert(k.ecdh_Yclen == 65)
 assert(k.ecdh_Yc[:4] == b'\x04\xd2\x07\xce' and k.ecdh_Yc[-4:] == b'\xdc\x86[\xe7')
 
-= Bad data in TLSClientKeyExchange
 
-a = TLSClientKeyExchange(str(TLSClientKeyExchange(exchkeys="baddata")))
-assert a.haslayer(Raw)
-
-+ Test TLS ChangeCipherSpec
-= Reading test session - ChangeCipherSpec
+= Reading TLS test session - ChangeCipherSpec
 assert(isinstance(ccs, TLSChangeCipherSpec))
 ccs.msgtype == 1
 
 
-+ Test TLS Finished
-= Reading test session - Finished
+= Reading TLS test session - Finished
 assert(rec_fin.version == 0x0303)
-assert(rec_fin.len == 16)
+assert(rec_fin.deciphered_len == 16)
+assert(rec_fin.len == 40)
 assert(rec_fin.iv == b'\x00\x00\x00\x00\x00\x00\x00\x00')
 assert(rec_fin.mac == b'\xc7^\xc1\x8e\x81M\xff\x00\x0f}G\xf2\x8c\xab\n=')
 assert(not rec_fin.pad and not rec_fin.padlen)
@@ -890,9 +943,8 @@ assert(isinstance(fin, _TLSEncryptedContent))
 fin.load == b'\xd9\xcb,\x8cM\xfd\xbc9\xaa\x05\xf3\xd3\xf3Z\x8a-'
 
 
-+ Test TLS Tick-CCS-Fin
-= Reading test session - Ticket, CCS & Finished
-t6 = TLS(p6_tick_ccs_fin)       # empty tls_session
+= Reading TLS test session - Ticket, CCS & Finished
+t6 = TLS(p6_tick_ccs_fin, tls_session=t5.tls_session.mirror())
 tick = t6.msg[0]
 assert(isinstance(tick, TLSNewSessionTicket))
 assert(tick.msgtype == 4)
@@ -901,16 +953,15 @@ assert(tick.ticketlen == 192)
 assert(tick.ticket[:4] == b'c\xccwJ' and tick.ticket[-4:] == b'\xf3.\xcf\x04')
 ccs = t6.payload.msg[0]
 assert(isinstance(ccs, TLSChangeCipherSpec))
-rec_fin = TLS(str(t6.payload.payload), tls_session=t1.tls_session)
+rec_fin = t6.getlayer(4)
 assert(rec_fin.iv == b'\xd8m\x92\t5YZ:')
 assert(rec_fin.mac == b'\xecguD\xa8\x87$<7+\n\x94\x1e9\x96\xfa')
 assert(isinstance(rec_fin.msg[0], _TLSEncryptedContent))
 rec_fin.msg[0].load == b'7\\)`\xaa`\x7ff\xcd\x10\xa9v\xa3*\x17\x1a'
 
 
-+ Test TLS ApplicationData
-= Reading test session - ApplicationData
-t7 = TLS(p7_data, tls_session=t1.tls_session)
+= Reading TLS test session - ApplicationData
+t7 = TLS(p7_data, tls_session=t6.tls_session.mirror())
 assert(t7.iv == b'\x00\x00\x00\x00\x00\x00\x00\x01')
 assert(t7.mac == b'>\x1dLb5\x8e+\x01n\xcb\x19\xcc\x17Ey\xc8')
 assert(not t7.pad and not t7.padlen)
@@ -918,11 +969,43 @@ assert(isinstance(t7.msg[0], _TLSEncryptedContent))
 len(t7.msg[0].load) == 478
 
 
+###############################################################################
+################## Reading TLS vulnerable test session ########################
+###############################################################################
+
+# These packets come from a session between an s_server and an s_client.
+# We assume the server's private key has been retrieved. Because the cipher
+# suite does not provide PFS, we are able to break the data confidentiality.
+
++ Read a vulnerable TLS session
+
+= Reading TLS vulnerable session - Decrypt data from using a compromised server key
+import os
+basedir = os.path.abspath(os.path.join(os.path.dirname(__file__),"../"))
+key = PrivKeyRSA(basedir + "/test/tls/pki/srv_key.pem")
+ch = b'\x16\x03\x01\x005\x01\x00\x001\x03\x01X\xac\x0e\x8c\xe46\xe9\xedo\xda\x085$M\xae$\x90\xd9\xa93\xb7(\x13J\xf9\xc5?\xef\xf4\x96\xa1\xfa\x00\x00\x04\x00/\x00\xff\x01\x00\x00\x04\x00#\x00\x00'
+sh = b'\x16\x03\x01\x005\x02\x00\x001\x03\x01\x88\xac\xd4\xaf\x93~\xb5\x1b8c\xe7)\xa6\x9b\xa9\xed\xf3\xf3*\xdb\x00\x8bB\xf6\n\xcbz\x8eP\x83`G\x00\x00/\x00\x00\t\xff\x01\x00\x01\x00\x00#\x00\x00\x16\x03\x01\x03\xac\x0b\x00\x03\xa8\x00\x03\xa5\x00\x03\xa20\x82\x03\x9e0\x82\x02\x86\xa0\x03\x02\x01\x02\x02\t\x00\xfe\x04W\r\xc7\'\xe9\xf60\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x000T1\x0b0\t\x06\x03U\x04\x06\x13\x02MN1\x140\x12\x06\x03U\x04\x07\x0c\x0bUlaanbaatar1\x170\x15\x06\x03U\x04\x0b\x0c\x0eScapy Test PKI1\x160\x14\x06\x03U\x04\x03\x0c\rScapy Test CA0\x1e\x17\r160916102811Z\x17\r260915102811Z0X1\x0b0\t\x06\x03U\x04\x06\x13\x02MN1\x140\x12\x06\x03U\x04\x07\x0c\x0bUlaanbaatar1\x170\x15\x06\x03U\x04\x0b\x0c\x0eScapy Test PKI1\x1a0\x18\x06\x03U\x04\x03\x0c\x11Scapy Test Server0\x82\x01"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x01\x0f\x000\x82\x01\n\x02\x82\x01\x01\x00\xcc\xf1\xf1\x9b`-`\xae\xf2\x98\r\')\xd9\xc0\tYL\x0fJ0\xa8R\xdf\xe5\xb1!\x9fO\xc3=V\x93\xdd_\xc6\xf7\xb3\xf6U\x8b\xe7\x92\xe2\xde\xf2\x85I\xb4\xa1,\xf4\xfdv\xa8g\xca\x04 `\x11\x18\xa6\xf2\xa9\xb6\xa6\x1d\xd9\xaa\xe5\xd9\xdb\xaf\xe6\xafUW\x9f\xffR\x89e\xe6\x80b\x80!\x94\xbc\xcf\x81\x1b\xcbg\xc2\x9d\xb5\x05w\x04\xa6\xc7\x88\x18\x80xh\x956\xde\x97\x1b\xb6a\x87B\x1au\x98E\x82\xeb>2\x11\xc8\x9b\x86B9\x8dM\x12\xb7X\x1b\x19\xf3\x9d+\xa1\x98\x82\xca\xd7;$\xfb\t9\xb0\xbc\xc2\x95\xcf\x82)u\x16)?B \x17+M@\x8cVl\xad\xba\x0f4\x85\xb1\x7f@yqx\xb7\xa5\x04\xbb\x94\xf7\xb5A\x95\xee|\xeb\x8d\x0cyhY\xef\xcb\xb3\xfa>x\x1e\xeegLz\xdd\xe0\x99\xef\xda\xe7\xef\xb2\t]\xbe\x80 !\x05\x83,D\xdb]*v)\xa5\xb0#\x88t\x07T"\xd6)z\x92\xf5o-\x9e\xe7\xf8&+\x9cXe\x02\x03\x01\x00\x01\xa3o0m0\t\x06\x03U\x1d\x13\x04\x020\x000\x0b\x06\x03U\x1d\x0f\x04\x04\x03\x02\x05\xe00\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14\xa1+ p\xd2k\x80\xe5e\xbc\xeb\x03\x0f\x88\x9ft\xad\xdd\xf6\x130\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14fS\x94\xf4\x15\xd1\xbdgh\xb0Q725\xe1\xa4\xaa\xde\x07|0\x13\x06\x03U\x1d%\x04\x0c0\n\x06\x08+\x06\x01\x05\x05\x07\x03\x010\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00\x81\x88\x92sk\x93\xe7\x95\xd6\xddA\xee\x8e\x1e\xbd\xa3HX\xa7A5?{}\xd07\x98\x0e\xb8,\x94w\xc8Q6@\xadY\t(\xc8V\xd6\xea[\xac\xb4\xd8?h\xb7f\xca\xe1V7\xa9\x00e\xeaQ\xc9\xec\xb2iI]\xf9\xe3\xc0\xedaT\xc9\x12\x9f\xc6\xb0\nsU\xe8U5`\xef\x1c6\xf0\xda\xd1\x90wV\x04\xb8\xab8\xee\xf7\t\xc5\xa5\x98\x90#\xea\x1f\xdb\x15\x7f2(\x81\xab\x9b\x85\x02K\x95\xe77Q{\x1bH.\xfb>R\xa3\r\xb4F\xa9\x92:\x1c\x1f\xd7\n\x1eXJ\xfa.Q\x8f)\xc6\x1e\xb8\x0e1\x0es\xf1\'\x88\x17\xca\xc8i\x0c\xfa\x83\xcd\xb3y\x0e\x14\xb0\xb8\x9b/:-\t\xe3\xfc\x06\xf0:n\xfd6;+\x1a\t*\xe8\xab_\x8c@\xe4\x81\xb2\xbc\xf7\x83g\x11nN\x93\xea"\xaf\xff\xa3\x9awWv\xd0\x0b8\xac\xf8\x8a\x945\x8e\xd7\xd4a\xcc\x01\xff$\xb4\x8fa#\xba\x88\xd7Y\xe4\xe9\xba*N\xb5\x15\x0f\x9c\xd0\xea\x06\x91\xd9\xde\xab\x16\x03\x01\x00\x04\x0e\x00\x00\x00'
+ck = b"\x16\x03\x01\x01\x06\x10\x00\x01\x02\x01\x00w\x93\xec\xfa\xf3\xdf[\x9a4\xa7\x9e\xcd\x06=\x8dH\xf1\x069\x8c\x06\x01S\xf7\xb5\x16h\xf6\xd5 I\xd7\xf0\xc5Z\xf6\xe0f7\x95\x91\xddNC\xe7$\xf5\xdaZ\xcdG\xd8\x14\xcaV\x98\xc4\xb2\x8cm\xe51@\x9b\x9c\xb8\xadul\xd0\xdf\xf2\xd7@Q\xe4\x05J\xf31[\xdf\xc8'(\x8f#\xf0\xc4\x1c\xc6\x07G\xb327\x85\xad\xa2\xa6\xa2E\x18\x85rP\xb8\x86uL\\7\x82\x18\xceh\xc6\xd1\xf4\xcc\xb9VN\x85\x7f9c\x92\t\x96\x8e\x80\x06\xe4\r\xbfu<\xabgP^z\xc7\xfd\x8e\x12t^\xb7\xc7Lr\xdc5\xf8\xa7\xdb\x9c\xbd\xd5\xad\xabP<\xe7\x9f%f\xb4\xd8\xf4\xf0~\x99\xbeZ\xe9\xbc\x0c9\r\xb2Uq\xfcd\xa4\xda\x89\x90\xd1\x15\x05\xcc\x00\xb1\xcd\xa9c\xb4\xe8\x7fRH\xbd\xe1\xd2\xd8\x9c\xb6\xd2\x8dq9\xe5\t\xeb\xfc\x1b\x06\xac\xab\x96\xa7\xfd{\xdf\xf2\x16\r\xd6'\xb8\xd3\xa5L\xc8\x08 \xb9\xccN\xe5\xf0\xa0S\xf3\xc3\xc9\xdf\xee\xd0\r\xd8[\x14\x03\x01\x00\x01\x01\x16\x03\x01\x000~\x01\xe1!2\x90\xba\xc8 \xb6\x8c\xb7\xd9\xf5\x80\x1d$Z^\xc8\xa3\x9f\xb3\xf1M\x0c\xd1\xedd\xb1'\x0f\xe4ER\xc9\xf7L\xf3;\xc1\xbaz\xfa\xb76\xe3q"
+fin = b"\x16\x03\x01\x00\xaa\x04\x00\x00\xa6\x00\x00\x1c \x00\xa0*\xf5.4:\xe4;t\xf0v\xed\xeaLX\xa5\xce*@\xe7\x83\rWx\xadWkM-\x95\xe7\x98\xcb6x\xeb\xca\xfe8\xf5\x84*\x9bAmZ/o9\xb03\xea\x1e\x99\xfdQ\xbfe\r\xe8W\xd5\xdb\xdd\x83\x90\x14\xc6\xef\x10s\x15\xff\xc2U\xce\xb0\x00\x11\x02|\xed\x99\xbac\xfb\x03M\xce\xd3\x92\xbe\x98\x95\x1c\xef\x9b\xb1\xd6,\x0c6Td\xc9j*\x17\xb9\xde\x13\x8f\xba[\xbcD\x1b\x9a~\xe9\xa2\xf3\xa4V3\xfe\xd6'\xc8i+\xb0m\xf8&\x86\x83\xaa\xe5\x1d\x06\x07lOx\x06 \x02\xbe\xfe\xda\x93-\x9fk\xeaHu\x8a\xec_\x14\x03\x01\x00\x01\x01\x16\x03\x01\x000Pc\xe0T+\x17\\>\xd0\xbc\xe6Xx}\xe5\xa26\xea\x0b\xad\x1bY\x1b\x05,\x7f\xeeQ\xd6\xea!\x9d.\xe0\xf3\x88\xe6'jV\xfdz]M'\xcejJ"
+data = b'\x17\x03\x01\x00 \xe8\x91\'mRT\x17\xa1\xd6}+\x80\x02\xda\xadw.\x82TA\'\xdep\xa4\xe1\xb1H\xa9\xb1\x81gw\x17\x03\x01\x00P\xddD\x18\xdb\x82pz\xb75>\x1c\xd7\xa9=\x18C\xbd\xf0F\xa1k\x0c\xe5&\xf2\xdf\x97\xf0\xab5\xf41W\x85 \xcf\xd9\x98\xa4\xe8\xcc\xff \x1c\xbc\xb3U\xc8\x9c>\xc4$\xa5U\xc6\xd4\x1f"\xce\xf0\x98\xf0D\xd2\x1d\r*\x99*\xdcd4?\xc9\x0b\xa6\xb2\x81%\xfc'
+t = TLS(ch)
+t = TLS(sh, tls_session=t.tls_session.mirror())
+t.tls_session.server_rsa_key = key
+t = TLS(ck, tls_session=t.tls_session.mirror())
+t = TLS(fin, tls_session=t.tls_session.mirror())
+t = TLS(data, tls_session=t.tls_session.mirror())
+assert(len(t.msg) == 1)
+assert(isinstance(t.msg[0], TLSApplicationData))
+assert(t.msg[0].data == "")
+t.getlayer(2).msg[0].data == "To boldly go where no man has gone before...\n"
+
+
 ###############################################################################
 ############################## Building packets ###############################
 ###############################################################################
 
-+ Test Build TLS empty records
++ Build TLS packets
+
 = Building packets - Various default records
 str(TLS())
 str(TLSClientHello())
@@ -935,7 +1018,6 @@ str(TLSChangeCipherSpec())
 str(TLSApplicationData()) == ''
 
 
-+ Test Build TLS ClientHello
 = Building packets - ClientHello with automatic length computation
 ch = TLSClientHello()
 ch.msgtype = 'client_hello'
@@ -946,7 +1028,7 @@ ch.ciphers = [TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256, TLS_ECDHE_RSA_WITH_AES_12
 ch.comp = 'null'
 ext1 = TLS_Ext_ServerName(servernames=ServerName(servername='mn.scapy.wtv'))
 ext2 = TLS_Ext_RenegotiationInfo()
-ext3 = TLS_Ext_SupportedEllipticCurves(ecl=['secp256r1', 'secp384r1', 'secp521r1'])
+ext3 = TLS_Ext_SupportedEllipticCurves(groups=['secp256r1', 'secp384r1', 'secp521r1'])
 ext4 = TLS_Ext_SupportedPointFormat(ecpl='uncompressed')
 ext5 = TLS_Ext_SessionTicket()
 ext6 = TLS_Ext_NPN()
@@ -958,13 +1040,12 @@ t = TLS(type='handshake', version='TLS 1.0', msg=ch)
 str(t) == b'\x16\x03\x01\x00\xc7\x01\x00\x00\xc3\x03\x03&\xee-\xddX\xe1\xb1T\xaa\xb1\x0b\xa0zlg\xf8\xd14]%\xa9\x91d\x08\xc7t\xcd6\xd4"\x9f\xcf\x00\x00\x16\xc0+\xc0/\xc0\n\xc0\t\xc0\x13\xc0\x14\x003\x009\x00/\x005\x00\n\x01\x00\x00\x84\x00\x00\x00\x11\x00\x0f\x00\x00\x0cmn.scapy.wtv\xff\x01\x00\x01\x00\x00\n\x00\x08\x00\x06\x00\x17\x00\x18\x00\x19\x00\x0b\x00\x02\x01\x00\x00#\x00\x003t\x00\x00\x00\x10\x00)\x00\'\x05h2-16\x05h2-15\x05h2-14\x02h2\x08spdy/3.1\x08http/1.1\x00\x05\x00\x05\x01\x00\x00\x00\x00\x00\r\x00\x16\x00\x14\x04\x01\x05\x01\x06\x01\x02\x01\x04\x03\x05\x03\x06\x03\x02\x03\x04\x02\x02\x02'
 
 
-+ Test Build TLS ServerKeyExchange
 = Building packets - ServerHello context linking
 from scapy.layers.tls.crypto.kx_algs import KX_ECDHE_RSA
 from scapy.layers.tls.crypto.cipher_block import Cipher_AES_256_CBC
-sh = TLSServerHello(gmt_unix_time=0x41414141, random_bytes='B'*28, cipher='TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA')
+sh = TLSServerHello(gmt_unix_time=0x41414141, random_bytes='B'*28, cipher=0xc014)
 t = TLS(msg=sh)
-str(t)
+t.str_stateful()
 assert(isinstance(t.tls_session.pwcs.ciphersuite, TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA))
 assert(isinstance(t.tls_session.pwcs.key_exchange, KX_ECDHE_RSA))
 assert(isinstance(t.tls_session.pwcs.cipher, Cipher_AES_256_CBC))
@@ -972,7 +1053,6 @@ assert(isinstance(t.tls_session.pwcs.hmac, Hmac_SHA))
 t.tls_session.server_random == 'A'*4+'B'*28
 
 
-+ Test Build TLS wrong record
 = Building packets - ChangeCipherSpec with forged, forbidden field values
 t = TLS(msg=TLSChangeCipherSpec())
 assert(str(t) == b'\x14\x03\x03\x00\x01\x01')
@@ -984,10 +1064,31 @@ t.len = 0xefff
 str(t) == b'\xde\xad\xbe\xef\xff\x01'
 
 
+= Building packets - TLS record with bad data
+a = TLS(b'\x00\x03\x03\x00\x03data')
+assert a.haslayer(Raw)
+
+
+= Building packets - _CipherSuitesField with no cipher
+from scapy.layers.tls.handshake import _CipherSuitesField
+a = _CipherSuitesField("test", None, {})
+assert a.i2repr(None, None) == "None"
+assert isinstance(a.randval(), RandBin)
+
+
+= Building packets - TLSClientKeyExchange with bad data
+a = TLSClientKeyExchange(str(TLSClientKeyExchange(exchkeys="baddata")))
+assert a.haslayer(Raw)
+
+
+= Building packets - Perform dummy session update
+assert not TLSHelloRequest().tls_session_update(None)
+
+
 ###############################################################################
 ############################ Automaton behaviour ##############################
 ###############################################################################
 
-# see test/run_openssl_tests
+# see test/tls/tests_tls_netaccess.uts
 
 
diff --git a/test/tls/example_client.py b/test/tls/example_client.py
index 3c5f9794c6b5f7183061e150d1e8725f1a3a165f..31a1fcefd2be1b0f16afb19604e5775136ff6997 100755
--- a/test/tls/example_client.py
+++ b/test/tls/example_client.py
@@ -5,6 +5,7 @@
 
 """
 Basic TLS client. A ciphersuite may be commanded via a first argument.
+Default protocol version is TLS 1.2.
 
 For instance, "sudo ./client_simple.py c014" will try to connect to any TLS
 server at 127.0.0.1:4433, with suite TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA.
@@ -16,7 +17,7 @@ import sys
 basedir = os.path.abspath(os.path.join(os.path.dirname(__file__),"../../"))
 sys.path=[basedir]+sys.path
 
-from scapy.layers.tls.automaton import TLSClientAutomaton
+from scapy.layers.tls.automaton_cli import TLSClientAutomaton
 from scapy.layers.tls.handshake import TLSClientHello
 
 
@@ -25,7 +26,9 @@ if len(sys.argv) == 2:
 else:
     ch = None
 
-t = TLSClientAutomaton(client_hello=ch)
+t = TLSClientAutomaton(client_hello=ch,
+                       version="tls13-d18",
+                       mycert=basedir+"/test/tls/pki/cli_cert.pem",
+                       mykey=basedir+"/test/tls/pki/cli_key.pem")
 t.run()
 
-
diff --git a/test/tls/example_server.py b/test/tls/example_server.py
index 7d4d9664c1ba49386032cca6e47f1a1a6c38cfae..ed740aa74e95cd026e4acab00fe0d1ba82acf596 100755
--- a/test/tls/example_server.py
+++ b/test/tls/example_server.py
@@ -17,7 +17,7 @@ import sys
 basedir = os.path.abspath(os.path.join(os.path.dirname(__file__),"../../"))
 sys.path=[basedir]+sys.path
 
-from scapy.layers.tls.automaton import TLSServerAutomaton
+from scapy.layers.tls.automaton_srv import TLSServerAutomaton
 
 
 if len(sys.argv) == 2:
@@ -30,4 +30,3 @@ t = TLSServerAutomaton(mycert=basedir+'/test/tls/pki/srv_cert.pem',
                        preferred_ciphersuite=pcs)
 t.run()
 
-
diff --git a/test/tls/tests_tls_netaccess.uts b/test/tls/tests_tls_netaccess.uts
index 7cf8fb2bc3f8c3d7626ad72288bd7126814a9509..cc311afdb4ad6ce8ef4d41af9501da892f451d15 100644
--- a/test/tls/tests_tls_netaccess.uts
+++ b/test/tls/tests_tls_netaccess.uts
@@ -1,4 +1,4 @@
-% TLS tests with real cases
+% TLS session establishment tests
 
 # More informations at http://www.secdev.org/projects/UTscapy/
 
@@ -30,6 +30,7 @@ def test_tls_server(suite="", version=""):
     CA_f = os.path.abspath("./tls/pki/ca_cert.pem")
     p = subprocess.Popen("openssl s_client -cipher " + suite + " " + version + " -CAfile " + CA_f, stdout=subprocess.PIPE, universal_newlines=True,
                             stdin=subprocess.PIPE, stderr=subprocess.STDOUT, shell=True)
+    msg += b"\nstop_server"
     out, err = p.communicate(input=msg)
     print out
     if p.returncode != 0:
@@ -63,6 +64,11 @@ def test_tls_server(suite="", version=""):
 
 test_tls_server("RC4-SHA", "-tls1")
 
+= Testing TLS server with TLS 1.1 and TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA
+~ open_ssl_client
+
+test_tls_server("EDH-RSA-DES-CBC3-SHA", "-tls1_1")
+
 = Testing TLS server with TLS 1.2 and TLS_DHE_RSA_WITH_AES_128_CBC_SHA256
 ~ open_ssl_client
 
@@ -99,6 +105,14 @@ def perform_tls_client_test(suite, version):
         print "ERROR: Missing one of the return value detected !"
         assert False
 
+= Testing TLS server and client with SSLv2 and SSL_CK_DES_192_EDE3_CBC_WITH_MD5
+
+perform_tls_client_test("0700c0", "0002")
+
+= Testing TLS client with SSLv3 and TLS_RSA_EXPORT_WITH_RC4_40_MD5
+
+perform_tls_client_test("0003", "0300")
+
 = Testing TLS client with TLS 1.0 and TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA
 
 perform_tls_client_test("0088", "0301")
@@ -107,10 +121,11 @@ perform_tls_client_test("0088", "0301")
 
 perform_tls_client_test("c013", "0302")
 
-= Testing TLS server with TLS 1.2 and TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
+= Testing TLS client with TLS 1.2 and TLS_DHE_RSA_WITH_AES_128_GCM_SHA256
 
 perform_tls_client_test("009e", "0303")
 
-= Testing TLS server with SSLv3 and TLS_RSA_EXPORT_WITH_RC4_40_MD5
+= Testing TLS client with TLS 1.2 and TLS_ECDH_anon_WITH_RC4_128_SHA
+
+perform_tls_client_test("c016", "0303")
 
-perform_tls_client_test("0003", "0300")
\ No newline at end of file
diff --git a/test/tls/travis_test_client.py b/test/tls/travis_test_client.py
index 0ff3a5cf4dcf05f416cf647eb857af5031a15be9..efa2d1ca41352175329aa5f613760353905fb834 100755
--- a/test/tls/travis_test_client.py
+++ b/test/tls/travis_test_client.py
@@ -18,15 +18,18 @@ import multiprocessing
 basedir = os.path.abspath(os.path.join(os.path.dirname(__file__),"../../"))
 sys.path=[basedir]+sys.path
 
-from scapy.layers.tls.automaton import TLSClientAutomaton
+from scapy.layers.tls.automaton_cli import TLSClientAutomaton
 from scapy.layers.tls.handshake import TLSClientHello
 
 
 send_data = cipher_suite_code = version = None
 
 def run_tls_test_client(send_data=None, cipher_suite_code=None, version=None):
-    ch = TLSClientHello(version=int(version, 16), ciphers=int(cipher_suite_code, 16))
-    t = TLSClientAutomaton(client_hello=ch, data=send_data)
+    if version == "0002":
+        t = TLSClientAutomaton(data=[send_data, "stop_server", "quit"], version="sslv2")
+    else:
+        ch = TLSClientHello(version=int(version, 16), ciphers=int(cipher_suite_code, 16))
+        t = TLSClientAutomaton(client_hello=ch, data=[send_data, "stop_server", "quit"])
     t.run()
 
 from travis_test_server import run_tls_test_server
diff --git a/test/tls/travis_test_server.py b/test/tls/travis_test_server.py
index e8321cc0773140ab0bafe9e050a72a8aafa5a63e..c56e9abcf5a6c8c48dc7fbc2d2452360648c9828 100755
--- a/test/tls/travis_test_server.py
+++ b/test/tls/travis_test_server.py
@@ -20,7 +20,7 @@ from io import BytesIO
 basedir = os.path.abspath(os.path.join(os.path.dirname(__file__),"../../"))
 sys.path=[basedir]+sys.path
 
-from scapy.layers.tls.automaton import TLSServerAutomaton
+from scapy.layers.tls.automaton_srv import TLSServerAutomaton
 
 
 @contextmanager
@@ -41,7 +41,7 @@ def check_output_for_data(out, err, expected_data):
     if expected_data:
         lines = output.split("\n")
         for l in lines:
-            if l == ("Received '%s'" % expected_data):
+            if l == ("> Received: %s" % expected_data):
                 return (True, output)
         return (False, output)
     else:
@@ -52,7 +52,7 @@ def run_tls_test_server(expected_data, q):
     with captured_output() as (out, err):
         # Prepare automaton
         t = TLSServerAutomaton(mycert=basedir+'/test/tls/pki/srv_cert.pem',
-                           mykey=basedir+'/test/tls/pki/srv_key.pem')
+                               mykey=basedir+'/test/tls/pki/srv_key.pem')
         # Sync threads
         q.put(True)
         # Run server automaton
diff --git a/test/tls13.uts b/test/tls13.uts
new file mode 100644
index 0000000000000000000000000000000000000000..f8aa9b66190dbc90e6650bd4f813ea4c907e9c0a
--- /dev/null
+++ b/test/tls13.uts
@@ -0,0 +1,266 @@
+% Tests for TLS 1.3
+#
+# Try me with :
+# bash test/run_tests -t test/tls13.uts -F
+
+
++ Read a TLS 1.3 session
+# /!\ These tests will not catch our 'INTEGRITY CHECK FAILED's. /!\
+# We deem the knowledge of the plaintext sufficient for passing...
+
+= Reading TLS 1.3 test session (vectors 5 from draft-ietf-tls-tls13-vectors-00)
+~ crypto
+import binascii
+from cryptography.hazmat.primitives.asymmetric.ec import EllipticCurvePrivateNumbers
+from cryptography.hazmat.backends import default_backend
+
+def clean(s):
+    return binascii.unhexlify(''.join(c for c in s if c.isalnum()))
+
+clientHello1 = clean("""
+         16030100ae010000 aa0303d9e9898df6
+         3d43adbe64a2634f 0b63bcdc4019a3e5 26bc013a6042e05b
+         14555c0000061301 130313020100007b 0000000b00090000
+         06736572766572ff 01000100000a0008 0006001d00170018
+         002800260024001d 002005efa94d13f5 adcd14219379d5a3
+         7dbce4721d9294e5 72c6651aeb761838 815b002b0003027f
+         12000d0020001e04 0305030603020308 0408050806040105
+         0106010201040205 0206020202002d00 020101
+         """)
+t = TLS(clientHello1)
+
+helloRetryRequest = clean("""
+         160301000e060000 0a7f120006002800 020017
+         """)
+t = TLS(helloRetryRequest, tls_session=t.tls_session.mirror())
+
+secp256r1_client_privkey = clean("""
+         11fa48d153c917ff d89dff13140760a1
+         36265d399fa9f10e 2d766d42a6c84e90
+         """)
+clientHello2 = clean("""
+         16030100cf010000 cb0303d9e9898df6
+         3d43adbe64a2634f 0b63bcdc4019a3e5 26bc013a6042e05b
+         14555c0000061301 130313020100009c 0000000b00090000
+         06736572766572ff 01000100000a0008 0006001d00170018
+         0028004700450017 0041041e5a785f54 17fb18db42938435
+         34a5c0ba6e744baa 6846d0b32f4e9ea3 922724a08f2adb09
+         f071f81402e7fd8c a33b76abe1cd556f d3e8fe20e0fd2e82
+         02f969002b000302 7f12000d0020001e 0403050306030203
+         0804080508060401 0501060102010402 050206020202002d 00020101
+         """)
+t = TLS(clientHello2, tls_session=t.tls_session.mirror())
+pubnum = t.tls_session.tls13_client_pubshares["secp256r1"].public_numbers()
+privnum = EllipticCurvePrivateNumbers(pkcs_os2ip(secp256r1_client_privkey), pubnum)
+privkey = privnum.private_key(default_backend())
+t.tls_session.tls13_client_privshares["secp256r1"] = privkey
+
+#secp256r1_server_privkey = clean("""
+#         ff265d2062c70725 ca22513e1e6841ff
+#         475e8a00421f0818 186edd1c0080cc6a
+#         """)
+serverHello = clean("""
+         1603010073020000 6f7f1296ff693075
+         d8465651a9c28773 f5496542206ba390 199b9c997545d9a1
+         2666151301004900 2800450017004104 8a4d09cde58dbc04
+         1955b9a41a43c169 6dc5429ffa96f9cd 194a863ac782f181
+         59f072b4f610215d 86407dd7368b754a b2e64f2c1b3f9d45
+         7c264e2b1781a36b
+         """)
+t = TLS(serverHello, tls_session=t.tls_session.mirror())
+
+serverEncHS = clean("""
+         170301029081de4f cfd700da4573d570
+         5942f14a11e569aa 9aacc95260520102 6f74f2b2ad6abe08
+         7b53a4940ff94208 9e02d3159b1c6f11 75d7fcb51abad6fd
+         d4f7ff4af6590b47 16c1d90e1031e1a1 e32079f531108c6b
+         9f79d6120319e0a3 73010e82d780a8f9 c3fdf8474840cdb6
+         7e4943d3808a27cd 5d9375c766a95ef4 8393c235d83ad26a
+         20628671793f75df aa0be78b11fed206 6506d19a769d9d32
+         adc0437784994359 ef5e452609353670 1c46004cf6fc252e
+         546e797238c73b94 b073461158301f78 1498917c32dc0ece
+         658a53790c667397 f7744775c2bef907 b5f7d5677b2e57fe
+         7c4bfd43c7ad1ee4 6fd400c3d3c3c05f e8775f055263e98a
+         692b49a818d0f698 4400c1db2f429fa8 9fb61d523398e1d0
+         2bc5c393027146c0 f326032d18cb8283 473f2b6d554df942
+         c7b1a0050694c7b2 bf31a816f7ff77f1 d7db873dbb6e4646
+         acabfa73c317a34c e6212a3469f549e6 cde71ab229a6f220
+         acda60832b510663 02a23d02c734bd5e 71b04fb248ca47ba
+         0c7b1fd28fee9b5d 86e6b1a6a2a1a43e 3831210519f54134
+         c96486d11ef3125f 74969785690487e0 aa5c0a310ebf9d31
+         95ec5543af8a6ffb 710eb0a90285960d c1ccdc10ecee9669
+         9171e97eae526a17 205012ab6f262e44 31ae9a70ff2ed7bd
+         966ef6bd4563f56a 7a14970dcabf97ae 7e4354db1ea27548
+         c55c11542ad07bcd 6f47a7143b86c4e6 678ce7dc6d51a1b7
+         75687644d6526efa 3c864f592819e7b7 f9f1bbc02ed8821a
+         e66019b240b41f5e ebf9475069700030 7122f7c8a8d6c0da
+         a264c63183238d72 0eacb86879fab9ba 8a673c51a52c8284
+         75e3211223cd2238 bd8b8a934af3e4dd e10e788df23ad6d8
+         51d68b78082ac667 a854356415e7858b e526307332990d8c
+         c38a5dc4cfc22a2c a2bdd9126a2ce13d 7015264921
+         """)
+t = TLS(serverEncHS, tls_session=t.tls_session)
+
+clientFinished = clean("""
+         170301003543adad e592362412fb77d7
+         28b181c01b77cd62 a661e4125e6f9851 826e418f4c292ec6
+         3254e8b0342d65db 8a7f074eed527ea6 98a6
+         """)
+t = TLS(clientFinished, tls_session=t.tls_session.mirror())
+
+clientRecord = clean("""
+         17030100131ef5c9 e7205f31a1edf9b1
+         3600fec1271e4f5d
+         """)
+t = TLS(clientRecord, tls_session=t.tls_session)
+
+serverRecord = clean("""
+         170301001350ff6e 907c508b6b191ff6
+         094faf4c0b32d6a8
+         """)
+t = TLS(serverRecord, tls_session=t.tls_session.mirror())
+
+alert = t.inner.msg[0]
+assert(isinstance(alert, TLSAlert))
+alert.level == 1 and alert.descr == 0
+
+
+= Reading TLS 1.3 test session (vectors 3 from draft-ietf-tls-tls13-vectors-00)
+~ crypto_advanced
+from cryptography.hazmat.primitives.asymmetric.x25519 import X25519PrivateKey
+
+x25519_client_privkey = clean("""
+         00b4198a84ed6a7c 218702891735239d
+         40b7c66505330364 3d3c67f7458ecbc9
+         """)
+clientHello = clean("""
+         1603010200010001 fc03039a464db650
+         dcc81fed6f1fea63 5f15861574c0ed0b fb5778de7724fb92
+         7c5ef100003e1301 13031302c02bc02f cca9cca8c00ac009
+         c013c023c027c014 009eccaa00330032 006700390038006b
+         00160013009c002f 003c0035003d000a 0005000401000195
+         001500fc00000000 0000000000000000 0000000000000000
+         0000000000000000 0000000000000000 0000000000000000
+         0000000000000000 0000000000000000 0000000000000000
+         0000000000000000 0000000000000000 0000000000000000
+         0000000000000000 0000000000000000 0000000000000000
+         0000000000000000 0000000000000000 0000000000000000
+         0000000000000000 0000000000000000 0000000000000000
+         0000000000000000 0000000000000000 0000000000000000
+         0000000000000000 0000000000000000 0000000000000000
+         0000000000000000 0000000000000000 0000000000000000
+         0000000000000000 0000000000000000 0000000b00090000
+         06736572766572ff 01000100000a0014 0012001d00170018
+         0019010001010102 01030104000b0002 0100002300000028
+         00260024001d0020 35e58b160db6124f 01a1d2475a22b72a
+         bd6896701eed4c7e fd6124ee231ba458 002b0007067f1203
+         030302000d002000 1e04030503060302 0308040805080604
+         0105010601020104 0205020602020200 2d00020101
+         """)
+t = TLS(clientHello)
+privkey = X25519PrivateKey._from_private_bytes(x25519_client_privkey)
+t.tls_session.tls13_client_privshares["x25519"] = privkey
+
+x25519_server_privkey = clean("""
+         03d43f48ed52076f 4ce9bab73d1f39ec
+         689cf304075829f5 2b90f9f13bea6f34
+         """)
+serverHello = clean("""
+         1603010052020000 4e7f1298e3436403
+         8683391cbec1039a a0fba2f496d8c8e6 327151cc94bbc5ef
+         7390751301002800 280024001d0020a2 0ed1b7f2d96a7f12
+         568f0e460bb0fc86 dc8d1db6c07d6b10 d4dc74aaac9219
+         """)
+t = TLS(serverHello, tls_session=t.tls_session.mirror())
+
+serverEncHS = clean("""
+         170301029c4e1f34 2dba17a54a09f7a1
+         8ffb2c6a29df17a6 db843044c52861bf 78988527ce366159
+         e6a24871b704d2b9 fade56488921796d 719173a753bdfec8
+         0554c8c15e128695 450ccfdde1204ffd 2fb1ecdcd87b8070
+         644eb5a6b86ec951 aba3ed314754a2f3 14d4d2620b92da1f
+         28f24b9559d76b67 a7b35c17cc231ba5 77a94fb2be59c74f
+         84c8c78bf5faf4cb b2f8a37091580743 3c67d9f4e1b1923a
+         3969b85a2ae9064e 34e84363aae43aa9 f58717836a017b9c
+         33c3ad733c2fd3ce 288ae362764403d0 102a371047d9e49d
+         f9b30596262b1704 f0e9839fff5641ba a7041a4bcf9e4d46
+         7108922fc0ea0bc1 48dab2ebdd155f51 76c632be04a7c610
+         3fbc92754dba7962 4f8a09f8e8d65c17 eee87f98636fbc93
+         bb734674b80d183c da904200a20d8f15 0a214902b6953209
+         aa2431c3973bda3b d92a33878baca7b9 0507f433a55f2fe8
+         f0db81898ebacf31 b68eaabfa27c39b6 a2453a322c005030
+         4e60bf53f0402b38 65b43fe5a7454c13 17a2dc76d1323fb1
+         aa553996876a0dfe 8e789d6adf3dc85b 0636bb58a96e6aad
+         851e7a6fc1dfa796 ec65e33bf9e3c05d 6de35f11e1f32731
+         fb9550a60cb75e90 9345eb0edb81f99f cad883cb41d4a3ef
+         7cbe671b92a8176b 472772be401b83a4 99b06b7ab0a1d9cd
+         795e5ba0b67ce2d6 5c45565028824aa2 08797f405bbcf243
+         27dd69a1d986032f 544b15d110e4d8c4 681cb85c09960adb
+         57fb9723eef0e0bb 275552af25fbdfc1 a4215adf14a9dba2
+         4462dd095f1a78f5 6ed6db3de139936f 14b091ab7f4adc81
+         c277e68bfb6fd925 d92c06c0a4ddd105 9c071073a8a2e987
+         f98948599f27bf6d 1f4369ac6c5a3323 2932fb8aa52ec4e1
+         85790dff0ef5eee0 13b4e90b5bc1cd4a c42b7ce82d856cc0
+         f5d1c80400e68d61 b434cec56d437141 1e31849d4cf88862
+         8ba288548df6a19e c4
+         """)
+t = TLS(serverEncHS, tls_session=t.tls_session)
+
+clientFinished = clean("""
+         1703010035161e94 818226d7bd618063
+         0804644debc52bdd 661034243217ac45 a084228c82086baa
+         4893ecfc969624d6 8e19d88c3e67ccb4 8bdf
+         """)
+t = TLS(clientFinished, tls_session=t.tls_session.mirror())
+
+serverRecord1 = clean("""
+         17030100bbe6b3e9 89df694688f29f5d
+         a42d9f56053fc6d2 f73ee23accad26f9 599ee4dcf4e0cf9e
+         de80128b48156a65 e5e47dee679a8401 1234862b6728fb12
+         be5198d5c023d6f2 0c355fc417a5eade 1aff0bf9ecba14c8
+         7277ea7aeb30055e a4d9b37bc12f7517 27ca7a1efc9285f8
+         ed5e9e3be42ff475 30f2b7347a90618b 6f7f4eba9b8b6564
+         f2159fcfcf09e4b6 2b4b09bb129e7c76 5c877966ca66e5cd
+         a84cdb6087a07fc0 50c97f275568623c 5d0f459d2b1133d1
+         d5d37cd441192da7
+         """)
+t = TLS(serverRecord1, tls_session=t.tls_session.mirror())
+
+clientRecord1 = clean("""
+         170301004341b540 bf5adeaf9d209001
+         9f0733e281964724 526678a1946852cf 6f586dffacf1151d
+         bf7c9262ef6ae960 4a423fff339fd7e4 0cc3e7604ae661f0
+         afa2f775c3668867
+         """)
+t = TLS(clientRecord1, tls_session=t.tls_session.mirror())
+app_data = t.inner.msg[0]
+assert(app_data.data == b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01')
+
+serverRecord2 = clean("""
+         17030100438c3168 1fb21f820ef0603c
+         dc3b9d3deedeb2bb 615aa418fb2590a0 9b0dec00c2299feb
+         17c4206f89ab28d2 7a605e288ac9bd69 657593addd1046be
+         51b23940f8746634
+         """)
+t = TLS(serverRecord2, tls_session=t.tls_session.mirror())
+app_data = t.inner.msg[0]
+assert(app_data.data == b'\x00\x01\x02\x03\x04\x05\x06\x07\x08\t\n\x0b\x0c\r\x0e\x0f\x10\x11\x12\x13\x14\x15\x16\x17\x18\x19\x1a\x1b\x1c\x1d\x1e\x1f !"#$%&\'()*+,-./01')
+
+clientRecord2 = clean("""
+         17030100131ce9b1 f21ba236bca94455
+         ab2aad71c666534a
+         """)
+t = TLS(clientRecord2, tls_session=t.tls_session.mirror())
+alert = t.inner.msg[0]
+assert(isinstance(alert, TLSAlert))
+assert(alert.level == 1 and alert.descr == 0)
+
+serverRecord3 = clean("""
+         1703010013aabcdb 9d293d23fb00deb7
+         11b562afeddffeed
+         """)
+t = TLS(serverRecord3, tls_session=t.tls_session.mirror())
+alert = t.inner.msg[0]
+assert(isinstance(alert, TLSAlert))
+alert.level == 1 and alert.descr == 0
+