diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index 04afd2e..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,104 +0,0 @@
-# PyCharms
-.idea/
-
-# Byte-compiled / optimized / DLL files
-__pycache__/
-*.py[cod]
-*$py.class
-
-# C extensions
-*.so
-
-# Distribution / packaging
-.Python
-env/
-build/
-develop-eggs/
-dist/
-downloads/
-eggs/
-.eggs/
-lib/
-lib64/
-parts/
-sdist/
-var/
-wheels/
-*.egg-info/
-.installed.cfg
-*.egg
-
-# PyInstaller
-#  Usually these files are written by a python script from a template
-#  before PyInstaller builds the exe, so as to inject date/other infos into it.
-*.manifest
-*.spec
-
-# Installer logs
-pip-log.txt
-pip-delete-this-directory.txt
-
-# Unit test / coverage reports
-htmlcov/
-.tox/
-.coverage
-.coverage.*
-.cache
-nosetests.xml
-coverage.xml
-*.cover
-.hypothesis/
-
-# Translations
-*.mo
-*.pot
-
-# Django stuff:
-*.log
-local_settings.py
-
-# Flask stuff:
-instance/
-.webassets-cache
-
-# Scrapy stuff:
-.scrapy
-
-# Sphinx documentation
-docs/_build/
-
-# PyBuilder
-target/
-
-# Jupyter Notebook
-.ipynb_checkpoints
-
-# pyenv
-.python-version
-
-# celery beat schedule file
-celerybeat-schedule
-
-# SageMath parsed files
-*.sage.py
-
-# dotenv
-.env
-
-# virtualenv
-.venv
-venv/
-ENV/
-
-# Spyder project settings
-.spyderproject
-.spyproject
-
-# Rope project settings
-.ropeproject
-
-# mkdocs documentation
-/site
-
-# mypy
-.mypy_cache/
diff --git a/.travis.yml b/.travis.yml
deleted file mode 100644
index 81c2183..0000000
--- a/.travis.yml
+++ /dev/null
@@ -1,18 +0,0 @@
-language: python
-python:
-  - "2.7"
-  - "3.6"
-# Command to install dependencies
-install:
-  - pip install -U pip setuptools
-  - pip install -r requirements.txt
-# Command to run tests
-script: "python run_tests.py"
-
-deploy:
-  provider: pypi
-  user: "nabla"
-  password:
-    secure: "L/pKWyeRCz9Obzl9ihBj9BFJs32A/EsitHKfm2mMDkXy8LI9vDja96TVZpLqDLGLTDx/AwFOhu2Ntc9UGiWokK/ayETv9/e1pM0lFyaOsQ2eznTLuCbpI0WxfpTQ3dbXizjfp+/RdanIbilryEN/nhoCf1x6MgeE3ALH+LmP7J+mNuMLdMI9NR/55V4JT3AC48S5kAI/d3moa38O67UixeW3zizKfpKj63oUSxp2uNSCf27Bwb34QqckizU3gJctzDXJZM2ptG1gECPIU5YOJyI9c8GLNNnWeKsaWf+TPraR9Sw8Los4JQRGO4Mad05JGWfHftdoveBSTpwweLxtRrf5+jDXUtZtkIqkty4KCK4w6s7CC3T8IA3q/uxN2BfuOyKx7pZXAe1kt50EbYa/mqPg9yXOd/vaIMUFQoYLU0mGEc1somvy8WP89oCNU+nVCKN2LcRUtx4IZksTWTbhekKWGg7eHvHx9UeFSsVtgbaG13bdStUT4qaVZLhWyNm9uyakAzQmFIIPTOSJRnW1pVf8kgiwvaRw7K/s+JxN95Xil5nrPuj9C1UClGGN6jjGuJmIPzDazOfwzr1jrBQYBSaEP8tcfXdBWmpmnnx245mXHQw3y5Wtknv1dKlecLnL5+BHt40KSI8whkjvHh40V6ALYAyPc7wPR1OpNXPbZdw="
-  on:
-    tags: true
diff --git a/LICENSE b/LICENSE
deleted file mode 100644
index fd5c403..0000000
--- a/LICENSE
+++ /dev/null
@@ -1,21 +0,0 @@
-The MIT License (MIT)
-
-Copyright (c) 2017 Alban Diquet
-
-Permission is hereby granted, free of charge, to any person obtaining a copy
-of this software and associated documentation files (the "Software"), to deal
-in the Software without restriction, including without limitation the rights
-to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-copies of the Software, and to permit persons to whom the Software is
-furnished to do so, subject to the following conditions:
-
-The above copyright notice and this permission notice shall be included in
-all copies or substantial portions of the Software.
-
-THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
diff --git a/PKG-INFO b/PKG-INFO
new file mode 100644
index 0000000..49d64df
--- /dev/null
+++ b/PKG-INFO
@@ -0,0 +1,11 @@
+Metadata-Version: 1.2
+Name: tls_parser
+Version: 2.0.0
+Summary: Small library to parse TLS records.
+Home-page: https://github.com/nabla-c0d3/tls_parser
+Author: Alban Diquet
+Author-email: nabla.c0d3@gmail.com
+License: UNKNOWN
+Description: UNKNOWN
+Platform: UNKNOWN
+Requires-Python: >=3.7
diff --git a/README.md b/README.md
index f9df29c..6f3fa44 100644
--- a/README.md
+++ b/README.md
@@ -1,7 +1,7 @@
 tls_parser
 ==========
 
-[![Build Status](https://travis-ci.org/nabla-c0d3/tls_parser.svg?branch=master)](https://travis-ci.org/nabla-c0d3/tls_parser)
-[![PyPI version](https://badge.fury.io/py/tls_parser.svg)](https://badge.fury.io/py/tls-parser)
+![Run tests](https://github.com/nabla-c0d3/tls_parser/workflows/Run%20tests/badge.svg)
+[![PyPI version](https://badge.fury.io/py/tls-parser.svg)](https://badge.fury.io/py/tls-parser)
 
 Small library to parse TLS records; used by [SSLyze](https://github.com/nabla-c0d3/sslyze).
diff --git a/debian/changelog b/debian/changelog
index aa9d23e..cc3a0ab 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,6 @@
+tls-parser (1.2.2+git20200621.e3b8997-1) UNRELEASED; urgency=low
+ -- Kali Janitor <janitor@kali.org>  Sun, 28 Mar 2021 02:31:03 -0000
+
 tls-parser (1.2.2-0kali1) kali-dev; urgency=medium
 
   [ Raphaƫl Hertzog ]
diff --git a/requirements.txt b/requirements.txt
deleted file mode 100644
index 5784708..0000000
--- a/requirements.txt
+++ /dev/null
@@ -1,2 +0,0 @@
-typing; python_version < '3.5'
-enum34; python_version < '3.4'
diff --git a/run_tests.py b/run_tests.py
deleted file mode 100644
index 573ba10..0000000
--- a/run_tests.py
+++ /dev/null
@@ -1,19 +0,0 @@
-#!/usr/bin/python
-from __future__ import absolute_import
-from __future__ import print_function
-
-import sys
-import unittest
-import os
-
-
-def main(test_path):
-    sys.path.insert(1, os.path.join(os.path.dirname(__file__), 'lib'))
-    suite = unittest.loader.TestLoader().discover(test_path)
-    result = unittest.TextTestRunner(verbosity=2).run(suite)
-    exit_code = 0 if result.wasSuccessful() else 1
-    sys.exit(exit_code)
-
-if __name__ == '__main__':
-    main('tests')
-
diff --git a/setup.cfg b/setup.cfg
index b88034e..e04a854 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,2 +1,19 @@
 [metadata]
 description-file = README.md
+
+[flake8]
+max-line-length = 120
+
+[mypy]
+python_version = 3.7
+ignore_missing_imports = True
+strict_optional = True
+disallow_untyped_defs = True
+
+[mypy-tests.*]
+disallow_untyped_defs = False
+
+[egg_info]
+tag_build = 
+tag_date = 0
+
diff --git a/setup.py b/setup.py
index 864af02..ac65a47 100644
--- a/setup.py
+++ b/setup.py
@@ -1,21 +1,15 @@
-#!/usr/bin/env python
-
 from setuptools import setup
 from tls_parser import __version__
 from tls_parser import __author__
 from tls_parser import __email__
 
-TLS_PARSER_SETUP = {
-    'name': 'tls_parser',
-    'version': __version__,
-    'description': 'Small library to parse TLS records.',
-    'author': __author__,
-    'author_email': __email__,
-    'url': 'https://github.com/nabla-c0d3/tls_parser',
-    'packages': ['tls_parser'],
-    'extras_require': {':python_version < "3.4"': ['enum34'],
-                       ':python_version < "3.5"': ['typing']},
-}
-
-if __name__ == "__main__":
-    setup(**TLS_PARSER_SETUP)
+setup(
+    name="tls_parser",
+    version=__version__,
+    description="Small library to parse TLS records.",
+    author=__author__,
+    author_email=__email__,
+    url="https://github.com/nabla-c0d3/tls_parser",
+    packages=["tls_parser"],
+    python_requires=">=3.7",
+)
diff --git a/tests/__init__.py b/tests/__init__.py
deleted file mode 100644
index e69de29..0000000
diff --git a/tests/test_alert_protocol.py b/tests/test_alert_protocol.py
deleted file mode 100644
index e3801f8..0000000
--- a/tests/test_alert_protocol.py
+++ /dev/null
@@ -1,23 +0,0 @@
-from __future__ import absolute_import
-from __future__ import print_function
-
-import unittest
-from tls_parser.alert_protocol import TlsAlertRecord, TlsAlertSeverityByte
-from tls_parser.exceptions import UnknownTlsVersionByte
-
-
-class TlsAlertRecordTestCase(unittest.TestCase):
-
-    def test_from_bytes(self):
-        alert_bytes = b'\x15\x03\x03\x00\x02\x02\x14'
-        parsed_record, len_consumed = TlsAlertRecord.from_bytes(alert_bytes)
-        self.assertEqual(parsed_record.alert_severity, TlsAlertSeverityByte.FATAL)
-        self.assertEqual(parsed_record.alert_description, 0x14)
-        self.assertEqual(len_consumed, len(alert_bytes))
-
-    def test_from_bytes_with_invalid_version(self):
-        # Related to https://github.com/nabla-c0d3/sslyze/issues/437
-        # Some servers put invalid TLS version bytes in the TLS alert they send back
-        alert_bytes_with_bad_version = b'\x15\x00\x00\x00\x02\x02('
-        with self.assertRaises(UnknownTlsVersionByte):
-            TlsAlertRecord.from_bytes(alert_bytes_with_bad_version)
diff --git a/tests/test_handshake_protocol.py b/tests/test_handshake_protocol.py
deleted file mode 100644
index 7eaf8d6..0000000
--- a/tests/test_handshake_protocol.py
+++ /dev/null
@@ -1,94 +0,0 @@
-from __future__ import absolute_import
-from __future__ import print_function
-
-import unittest
-
-import binascii
-import math
-
-from tls_parser.record_protocol import TlsRecordTlsVersionBytes
-from tls_parser.tls_version import TlsVersionEnum
-
-from tls_parser.handshake_protocol import TlsHandshakeRecord, TlsHandshakeTypeByte,  \
-    TlsRsaClientKeyExchangeRecord
-
-
-class TlsHandshakeRecordTestCase(unittest.TestCase):
-
-    SERVER_HELLO_BYTES = b'\x16\x03\x03\x00F\x02\x00\x00B\x03\x03\xf2\x00\xfd\x10\xea\x9e\x02\xe5\xc0\x83\x02T7' \
-                         b'\xa7o\xf1\xdb\xd4\x8e\xc8>/\x9c\xeei\xb5\x9fi\xf9\x9e s\x00\xc0/\x00\x00\x1a\x00\x00' \
-                         b'\x00\x00\xff\x01\x00\x01\x00\x00\x0b\x00\x04\x03\x00\x01\x02\x00#\x00\x00\x00\x0f\x00' \
-                         b'\x01\x01'
-
-    def test_server_hello_from_bytes(self):
-        parsed_record, len_consumed = TlsHandshakeRecord.from_bytes(self.SERVER_HELLO_BYTES)
-        self.assertEqual(len(parsed_record.subprotocol_messages), 1)
-        self.assertEqual(parsed_record.subprotocol_messages[0].handshake_type, TlsHandshakeTypeByte.SERVER_HELLO)
-        self.assertEqual(len_consumed, len(self.SERVER_HELLO_BYTES))
-
-    SERVER_CERTIFICATE_BYTES = b'\x16\x03\x03\x14\xe5\x0b\x00\x14\xe1\x00\x14\xde\x00\x05\x000\x82\x04\xfc0\x82\x03\xe4\xa0\x03\x02\x01\x02\x02\x10T\xad\xe7\x86\xd42\xfb\xb5\xdb\xc7\xd4\x1b:&\x07\xba0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x000_1\x0b0\t\x06\x03U\x04\x06\x13\x02FR1\x0e0\x0c\x06\x03U\x04\x08\x13\x05Paris1\x0e0\x0c\x06\x03U\x04\x07\x13\x05Paris1\x0e0\x0c\x06\x03U\x04\n\x13\x05Gandi1 0\x1e\x06\x03U\x04\x03\x13\x17Gandi Standard SSL CA 20\x1e\x17\r151125000000Z\x17\r190224235959Z0b1!0\x1f\x06\x03U\x04\x0b\x13\x18Domain Control Validated1$0"\x06\x03U\x04\x0b\x13\x1bGandi Standard Wildcard SSL1\x170\x15\x06\x03U\x04\x03\x0c\x0e*.mediapart.fr0\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\xc6\xbc\x05\xbbi9o\x07\xc0Y\xcb\x7f\xbd@\x85`q\xb8\x8b3\x99;kE\x14\xe4\xfa\xe9D\x0eG?}\xa8U\xc2\n\xa7k<\xd8\x1f;\xb7$\xd6\x13\xb5+\xa5\xf3\xd8\xb7\xb5\xbf\xdc\xca\x9a\xe8\xd4\xb31{\xe5`Z\xc77\xe3\xe4\x8a\x00\xa1\xcc\xb21\xc7>\xea\xd9d\x0e\xb5)\x1c\xe0s\x0c\xect\x02X\xb4\xd2\xf8\xf500tf\x8bT\x9d\x8d\x97D\xc7\x9e\rP\x9c\x99\'b\xaa\xc5\xe5\xf1\xc1K\x07\xa3-\x93\xb5zz\xab\x8e\xce\xf3\xb4x\x96\x93\x9f)Z\x1f\x02K\xc4\xee\xfd\xa1\x17\xb6\xdc\xb9\xa3\x1c\x84w\r>\x01\x8c\xf8\x10$.\x0b\xf4\x8b\x80\xd6\xfff!\xc0\xb4\x1f \xd5\x16\xd5\x0b\x07\xe3\n\xdcs\xf2n0/\xf8\xf9r\x81\xa6A+\x9a!\xfc\xcdkm\xa4Q\xa9\x87\xd9\xdbR\x12u\x15\xc2\xda\x13\xd7\xba\x1cZ*Q\x93\xc42\xf1\xfb\xcb\xb9pwf\xc3P\x12\x80\xf1\x08\xb0\xd6\x96\xdd\xf7\xb2T\xaaL\x80C|\x17/\x10\x1f\x9e(=2\xcb\x9d\x02\x03\x01\x00\x01\xa3\x82\x01\xaf0\x82\x01\xab0\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14\xb3\x90\xa7\xd8\xc9\xafN\xcda<\x9f|\xad]\x7fA\xfdi0\xea0\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14\xa8c?\x88N,$\xaf\xc7\x8e\xd9\x8d\x80\xf8\xcb\x05N\xd2\x94\x9b0\x0e\x06\x03U\x1d\x0f\x01\x01\xff\x04\x04\x03\x02\x05\xa00\x0c\x06\x03U\x1d\x13\x01\x01\xff\x04\x020\x000\x1d\x06\x03U\x1d%\x04\x160\x14\x06\x08+\x06\x01\x05\x05\x07\x03\x01\x06\x08+\x06\x01\x05\x05\x07\x03\x020K\x06\x03U\x1d \x04D0B06\x06\x0b+\x06\x01\x04\x01\xb21\x01\x02\x02\x1a0\'0%\x06\x08+\x06\x01\x05\x05\x07\x02\x01\x16\x19https://cps.usertrust.com0\x08\x06\x06g\x81\x0c\x01\x02\x010A\x06\x03U\x1d\x1f\x04:0806\xa04\xa02\x860http://crl.usertrust.com/GandiStandardSSLCA2.crl0s\x06\x08+\x06\x01\x05\x05\x07\x01\x01\x04g0e0<\x06\x08+\x06\x01\x05\x05\x070\x02\x860http://crt.usertrust.com/GandiStandardSSLCA2.crt0%\x06\x08+\x06\x01\x05\x05\x070\x01\x86\x19http://ocsp.usertrust.com0\'\x06\x03U\x1d\x11\x04 0\x1e\x82\x0e*.mediapart.fr\x82\x0cmediapart.fr0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00K\xed\xcd\x94"\xef\xba\xb7\xb7\xc5x@b\xcd\xb6\xca\x92\xa9R_\xafV3R_u\x8f\x97\xe1\rU\x08\x12zF\x04\x90h\xd0~\xaaGK\xa2RkE\x90\xb4\xe1T\xdf\xa99\xd4\xce\x04\xc9\x90\xf7\x1d\xfbug\x99\xfc\x9dg\x1bY\x84\xf7k|\x96\xd5g\xb1x+;\x87[\xab\x13\x14_\x9a\xd2\x9e\xb5G\xc7\xf7\xb5\xde7\x00\x89S\xbbT\xb9\xa8X\xfc2\xda\x9e\x01\xb7\xc0T\xc0s\x08\xa7\xbf\xb5\x081\xfc#\x9a[)"n\xdfe\xbbAQ7\x00\xc7\xf5bv\xdb\xc2\xfc\xc1\x83~\xd5\xfbs\x99Gw\xc9\rT\xd2\x03?\xf7\xfb\x1e\xac\xc0\xd6r#2\x1cO\x81\xd0s\xd1\xcd\x88\x7f\xe1\xe5\xff\x91U\xe3\xff\x9f\xcf:\xcc\xb2\xfeR\x02\x07\xa9\xce\x93\r\xb5\x989`Q\xa6\xad\xc9J\x0cv\x12\xa3J\xb4\xde5\x11\xf4\x84\xaf`F\xd4\xd1\xda\xb5K3\t\xb6\xe7\xe8Zx\x95C\xcd\xf2hc\x17\xd1\t\x00[\xdb\x1fmX`\xe4Il\xc8\x18\xdd@;\xc8\xdb\x00\x05\xed0\x82\x05\xe90\x82\x03\xd1\xa0\x03\x02\x01\x02\x02\x10\x05\xe4\xdc;\x948\xab;\x85\x97\xcb\xa6\xa1\x98P\xe30\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0c\x05\x000\x81\x881\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x130\x11\x06\x03U\x04\x08\x13\nNew Jersey1\x140\x12\x06\x03U\x04\x07\x13\x0bJersey City1\x1e0\x1c\x06\x03U\x04\n\x13\x15The USERTRUST Network1.0,\x06\x03U\x04\x03\x13%USERTrust RSA Certification Authority0\x1e\x17\r140912000000Z\x17\r240911235959Z0_1\x0b0\t\x06\x03U\x04\x06\x13\x02FR1\x0e0\x0c\x06\x03U\x04\x08\x13\x05Paris1\x0e0\x0c\x06\x03U\x04\x07\x13\x05Paris1\x0e0\x0c\x06\x03U\x04\n\x13\x05Gandi1 0\x1e\x06\x03U\x04\x03\x13\x17Gandi Standard SSL CA 20\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\x94\x04-\xa6y\x95t\xff\xd5\x00<\xf5\xae\xd8\x94\xb1)|\xc0\x8f\x0b\x0b\x89\xb9\x82\x83\x97n7(\xf5\xa2\x1a\xcf\xd2\x92\x0b\x9b\xa8\xd3\x87\x94s\x84\x10\x9f\xdc5\xcb\xc2-\x92\xac!\xb9\xcb;\xfc@\xc1\xc1\x83!\xf0\xbf\xf8\xf6\x9c\xfa\x9c\x82\x10\xc0\xd0\x8eN\xe5\rL\xb0\x91\\\x90\xb4\xa4@Q\x16\xda\xe4\x84\x12-\x05\\\xa1\x1f\x17\x19$Q\xaaz\xea\xe1\x07\x1b\x86\x8d\x01r\xf2\xe7\xd4\x83#9\x9e\xe0\xe1L\x1fk"\xa3\xb4\x10f\xb0\xed\x82\x96\xd7nj\xb4\xf2?\xb5B\xfc\xdd\x8a\xb5\xab\xba-\x1d:u\x9b1\xdc>\x9d\xac[\xd3A\rl\xb0\x1b\xf5:\xf5y\xea!\xa2\xf8\xf43RK$-\x1e\xa4\x99\xb1mH\xbc\xb8\x12\xferp|\xf7\xfb\x02u\xf4\x8d\xde\xd6\xda\xc0\xa02\x1aR\xdf8k.E8??\x04\x96\x00\xfd\xa1\xf4\xa2\xbb\xd5\x17\xd6\'|\x1bXY\x95^\x8a\x12\xfd\x9c\xab\x81>R(HQ\x85k\xf3\x91\xb2\x86?)\xb5n\x03b\xee\xd6\x05\x02\x03\x01\x00\x01\xa3\x82\x01u0\x82\x01q0\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14Sy\xbfZ\xaa+J\xcfT\x80\xe1\xd8\x9b\xc0\x9d\xf2\xb2\x03f\xcb0\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14\xb3\x90\xa7\xd8\xc9\xafN\xcda<\x9f|\xad]\x7fA\xfdi0\xea0\x0e\x06\x03U\x1d\x0f\x01\x01\xff\x04\x04\x03\x02\x01\x860\x12\x06\x03U\x1d\x13\x01\x01\xff\x04\x080\x06\x01\x01\xff\x02\x01\x000\x1d\x06\x03U\x1d%\x04\x160\x14\x06\x08+\x06\x01\x05\x05\x07\x03\x01\x06\x08+\x06\x01\x05\x05\x07\x03\x020"\x06\x03U\x1d \x04\x1b0\x190\r\x06\x0b+\x06\x01\x04\x01\xb21\x01\x02\x02\x1a0\x08\x06\x06g\x81\x0c\x01\x02\x010P\x06\x03U\x1d\x1f\x04I0G0E\xa0C\xa0A\x86?http://crl.usertrust.com/USERTrustRSACertificationAuthority.crl0v\x06\x08+\x06\x01\x05\x05\x07\x01\x01\x04j0h0?\x06\x08+\x06\x01\x05\x05\x070\x02\x863http://crt.usertrust.com/USERTrustRSAAddTrustCA.crt0%\x06\x08+\x06\x01\x05\x05\x070\x01\x86\x19http://ocsp.usertrust.com0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0c\x05\x00\x03\x82\x02\x01\x00Xg\xfdr\xb2j\xd7|a\x96\x19~\xd9CF\xd1&}\xc8S\xfaf\xb0k-\xa7\xd3\xaaV\xf7:\x88\xd0;r\xc9P\xfd\xf7Y\xb2\xaah\xf5\x8cs\x03\xbb\x95e\x17\xce/\x1c\xdd\x98\x13\xa2\x91\xc9\xee\xa1@n<\x98\xd6\\\xf3\xb2"<-\xee\x1b\xa4\xe1\xde $\x16\xf2\x8c\x11s\x91:\xf6\xfa\xce$\x02\x87\xca\x93\xec\xb4\xb6\xc8\x16\x17\xc5r\xfc\'@\xf6\x13\xfe\x93\xa6\x9dQ\xef<+\xd8wW\x9b\x8ce:5%6\xb7\xb5\x8aco\x07\'\x93\xb1`\x8d\x80\xdb\x96\xd4z\x8f-\xab\x1c\x88\xc9n~\xd6e\x1f\xaf]\xca\x16?(F\xdc\xa05\xe5\xf9\xe9\xe5\xd5\x96\x88\x0cO\xc6\xb7wgH\x84\'\xb6\x1f\xb0h\xdb\xac\xbfw\xb0\x90\xb8\xa2\xc9\x1c2]\x02\xba%C\x81BG\xbb\xd8\xe1\x8f\x0c\x0cF_\xeeF3k\x03\x14\x82\xd3~\xcd\x8f\xaf\x90\xd6\x8e$}@B\xb4jj\x17\xc6\x95\x97\xe1\xf28\xcd\xa7\xed\xb4\'@\x93\xdfr\xa9\xb8\xc6fc78d"0\xa2;\xf1\xb9\xc8{\xc8\xfb):\xab\x1ar\xd2\x06\x12N\xf6\x82\xd4#o>\xc3\x93\xe5\xd8\xb6\xc0\xde\xdc#\x16\xd6\x130\xb7\xa0\x9a\x0e,U\x06\x00p\x01\xcf\xea9\x1d\x80\xdb\x88\xf7\xa5 \xb8[\xfd1&i\x8f-\na\x83:G\xa6\x13T,\x1e\xe3\xedD\xca\xbcj\x1f(\x0eQ\xd9\xde\x0e\x9fu\xcd\x0e\x03\x95\xca\xf9\xc5\xa9*-\xfeA\xa4\xa1G\xae\r\xc2\xf99f3J[\xe1\x84(Yl}\x94\x17v\xe4E\x82\xadp \xfd\xd2oc\xa8\xd7\xfa\xa03\xfa7\xcb\xf7\xb2e\x9e\xdaPo?\xe4\xa7\xf3\x8e]X2\x97p#.\xe7\xfd\xc4\x15\x9b\x9c\'\x8f2\xed\x17\xadX\x811)\x11\x1a\x9b\xd4\xfcl\x95(\xc7N\x05\x07\xa6\xfd\x1d\xbc\x19\xe2\xe8\xb7\xb9\x11\x8a-p\x12R\x85\x8d\x8c3J\x0f\xfc\x99\x92\xe0cp\xda\xa5\x94Gc\x07\xe7X\xc71_\x05=6U\xfe\x83\xb2\xe8\xa6\xad\xd7\xe9\xe6\x02t\x88t\\\xda4\xdb\x90\xd2mQ\n#\xd6#\x00\x05{0\x82\x05w0\x82\x04_\xa0\x03\x02\x01\x02\x02\x10\x13\xea(p[\xf4\xec\xed\x0c6c\t\x80aC60\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0c\x05\x000o1\x0b0\t\x06\x03U\x04\x06\x13\x02SE1\x140\x12\x06\x03U\x04\n\x13\x0bAddTrust AB1&0$\x06\x03U\x04\x0b\x13\x1dAddTrust External TTP Network1"0 \x06\x03U\x04\x03\x13\x19AddTrust External CA Root0\x1e\x17\r000530104838Z\x17\r200530104838Z0\x81\x881\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x130\x11\x06\x03U\x04\x08\x13\nNew Jersey1\x140\x12\x06\x03U\x04\x07\x13\x0bJersey City1\x1e0\x1c\x06\x03U\x04\n\x13\x15The USERTRUST Network1.0,\x06\x03U\x04\x03\x13%USERTrust RSA Certification Authority0\x82\x02"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x02\x0f\x000\x82\x02\n\x02\x82\x02\x01\x00\x80\x12e\x176\x0e\xc3\xdb\x08\xb3\xd0\xacW\rv\xed\xcd\'\xd3L\xadP\x83a\xe2\xaa M\t-d\t\xdc\xce\x89\x9f\xcc=\xa9\xec\xf6\xcf\xc1\xdc\xf1\xd3\xb1\xd6{7(\x11+G\xda9\xc6\xbc:\x19\xb4_\xa6\xbd}\x9d\xa3cB\xb6v\xf2\xa9;+\x91\xf8\xe2o\xd0\xec\x16 \x90\t>\xe2\xe8t\xc9\x18\xb4\x91\xd4bd\xdb\x7f\xa3\x06\xf1\x88\x18j\x90"<\xbc\xfe\x13\xf0\x87\x14{\xf6\xe4\x1f\x8e\xd4\xe4Q\xc6\x11gF\x08Q\xcb\x86\x14T?\xbc3\xfe~l\x9c\xff\x16\x9d\x18\xbdQ\x8e5\xa6\xa7f\xc8rg\xdb!f\xb1\xd4\x9bx\x03\xc0P:\xe8\xcc\xf0\xdc\xbc\x9eL\xfe\xaf\x05\x965\x1fWZ\xb7\xff\xce\xf9=\xb7,\xb6\xf6T\xdd\xc8\xe7\x12:M\xaeL\x8a\xb7\\\x9a\xb4\xb7 =\xca\x7f"4\xae~;hf\x01D\xe7\x01NFS\x9b3`\xf7\x94\xbeS7\x90sC\xf32\xc3S\xef\xdb\xaa\xfetNi\xc7k\x8c`\x93\xde\xc4\xc7\x0c\xdf\xe12\xae\xcc\x93;Qx\x95g\x8b\xee=V\xfe\x0c\xd0i\x0f\x1b\x0f\xf3%&k3m\xf7nG\xfasC\xe5~\x0e\xa5f\xb1)|2\x84cU\x89\xc4\r\xc1\x93T0\x19\x13\xac\xd3}7\xa7\xeb]:l5\\\xdbA\xd7\x12\xda\xa9I\x0b\xdf\xd8\x80\x8a\t\x93b\x8e\xb5f\xcf%\x88\xcd\x84\xb8\xb1?\xa49\x0f\xd9\x02\x9e\xeb\x12L\x95|\xf3k\x05\xa9^\x16\x83\xcc\xb8g\xe2\xe8\x13\x9d\xcc[\x82\xd3L\xb3\xed[\xff\xde\xe5s\xac#;-\x00\xbf5Ut\tI\xd8IX\x1a\x7f\x926\xe6Q\x92\x0e\xf3&}\x1cM\x17\xbc\xc9\xecC&\xd0\xbfA_@\xa9DD\xf4\x99\xe7W\x87\x9eP\x1fWT\xa8>\xfdtc/\xb1Pe\t\xe6XB.C\x1aL\xb4\xf0%GY\xfa\x04\x1e\x93\xd4&FJP\x81\xb2\xde\xbex\xb7\xfcg\x15\xe1\xc9W\x84\x1e\x0fc\xd6\xe9b\xba\xd6_U.\xea\\\xc6(\x08\x04%9\xb8\x0e+\xa9\xf2L\x97\x1c\x07?\rR\xf5\xed\xef/\x82\x0f\x02\x03\x01\x00\x01\xa3\x81\xf40\x81\xf10\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14\xad\xbd\x98z4\xb4&\xf7\xfa\xc4&T\xef\x03\xbd\xe0$\xcbT\x1a0\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14Sy\xbfZ\xaa+J\xcfT\x80\xe1\xd8\x9b\xc0\x9d\xf2\xb2\x03f\xcb0\x0e\x06\x03U\x1d\x0f\x01\x01\xff\x04\x04\x03\x02\x01\x860\x0f\x06\x03U\x1d\x13\x01\x01\xff\x04\x050\x03\x01\x01\xff0\x11\x06\x03U\x1d \x04\n0\x080\x06\x06\x04U\x1d \x000D\x06\x03U\x1d\x1f\x04=0;09\xa07\xa05\x863http://crl.usertrust.com/AddTrustExternalCARoot.crl05\x06\x08+\x06\x01\x05\x05\x07\x01\x01\x04)0\'0%\x06\x08+\x06\x01\x05\x05\x070\x01\x86\x19http://ocsp.usertrust.com0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0c\x05\x00\x03\x82\x01\x01\x00\x93e\xf67\x83\x95\x0f^\xc3\x82\x1c\x1f\xd6w\xe7<\x8a\xc0\xaa\t\xf0\xe9\x0b&\xf1\xe0\xc2ju\xa1\xc7y\xc9\xb9R`\xc8)\x12\x0e\xf0\xad\x03\xd6\t\xc4v\xdf\xe5\xa6\x81\x95\xa7F\xda\x82W\xa9\x95\x92\xc5\xb6\x8f\x03"l3w\xc1{2\x17n\x07\xceZ\x14A:\x05$\x1b\xf6\x14\x06;\xa8%$\x0e\xbb\xcc*u\xdd\xb9pA?|\xd0c6!\x07\x1fF\xff`\xa4\x91\xe1g\xbc\xde\x1f~\x19\x14\xc9cg\x91\xeag\x07k\xb4\x8f\x8b\xc0nC}\xc3\xa1\x80l\xb2\x1e\xbcS\x85}\xdc\x90\xa1\xa4\xbc-\xefFrW5\x05\xbf\xbbF\xbbnm7\x99\xb6\xff#\x92\x91\xc6n@\xf8\x8f)V\xea_\xd5_\x14S\xac\xf0Oa\xea\xf7"\xcc\xa7V\x0b\xe2\xb84\x1f&\xd9{\x19\x05h?\xba<\xd48\x06\xa2\xd3\xe6\x8f\x0e\xe3\xb4qm@B\xc5\x84\xb4@\x95+\xf4e\xa0Hy\xf6\x1d\x81c\x96\x9dOu\xe0\xf8|\xe4\x8e\xa9\xd1\xf2\xad\x8a\xb3\x8c\xc7!\xcd\xc2\xef\x00\x04j0\x82\x04f0\x82\x03N\xa0\x03\x02\x01\x02\x02\x10Q&\n\x93\x1c\xe2\x7f\x9c\xc3\xa5_y\xe0r\xae\x820\r\x06\t*\x86H\x86\xf7\r\x01\x01\x05\x05\x000\x81\x931\x0b0\t\x06\x03U\x04\x06\x13\x02US1\x0b0\t\x06\x03U\x04\x08\x13\x02UT1\x170\x15\x06\x03U\x04\x07\x13\x0eSalt Lake City1\x1e0\x1c\x06\x03U\x04\n\x13\x15The USERTRUST Network1!0\x1f\x06\x03U\x04\x0b\x13\x18http://www.usertrust.com1\x1b0\x19\x06\x03U\x04\x03\x13\x12UTN - DATACorp SGC0\x1e\x17\r050607080910Z\x17\r190624190630Z0o1\x0b0\t\x06\x03U\x04\x06\x13\x02SE1\x140\x12\x06\x03U\x04\n\x13\x0bAddTrust AB1&0$\x06\x03U\x04\x0b\x13\x1dAddTrust External TTP Network1"0 \x06\x03U\x04\x03\x13\x19AddTrust External CA Root0\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\xb7\xf7\x1a3\xe6\xf2\x00\x04-9\xe0N[\xed\x1f\xbcl\x0f\xcd\xb5\xfa#\xb6\xce\xde\x9b\x113\x97\xa4)L}\x93\x9f\xbdJ\xbc\x93\xed\x03\x1a\xe3\x8f\xcf\xe5mPZ\xd6\x97)\x94Z\x80\xb0Iz\xdb.\x95\xfd\xb8\xca\xbf78-\x1e>\x91A\xadpV\xc7\xf0O?\xe82\x9et\xca\xc8\x90T\xe9\xc6_\x0fx\x9d\x9a@<\x0e\xaca\xaa^\x14\x8f\x9e\x87\xa1jP\xdc\xd7\x9aN\xaf\x05\xb3\xa6q\x94\x9cq\xb3P`\n\xc7\x13\x9d8\x07\x86\x02\xa8\xe9\xa8i&\x18\x90\xabL\xb0O#\xab:O\x84\xd8\xdf\xce\x9f\xe1io\xbb\xd7B\xd7kD\xe4\xc7\xad\xeemA_rZq\x087\xb3ye\xa4Y\xa0\x947\xf7\x00/\r\xc2\x92r\xda\xd08r\xdb\x14\xa8E\xc4]*}\xb7\xb4\xd6\xc4\xee\xac\xcd\x13D\xb7\xc9+\xddC\x00%\xfaa\xb9ijX#\x11\xb7\xa73\x8fVuY\xf5\xcd)\xd7F\xb7\n+e\xb6\xd3Bo\x15\xb2\xb8{\xfb\xef\xe9]S\xd54Z\'\x02\x03\x01\x00\x01\xa3\x81\xd80\x81\xd50\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14S2\xd1\xb3\xcf\x7f\xfa\xe0\xf1\xa0]\x85N\x92\xd2\x9eE\x1d\xb4O0\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14\xad\xbd\x98z4\xb4&\xf7\xfa\xc4&T\xef\x03\xbd\xe0$\xcbT\x1a0\x0e\x06\x03U\x1d\x0f\x01\x01\xff\x04\x04\x03\x02\x01\x060\x0f\x06\x03U\x1d\x13\x01\x01\xff\x04\x050\x03\x01\x01\xff0\x11\x06\t`\x86H\x01\x86\xf8B\x01\x01\x04\x04\x03\x02\x01\x020 \x06\x03U\x1d%\x04\x190\x17\x06\n+\x06\x01\x04\x01\x827\n\x03\x03\x06\t`\x86H\x01\x86\xf8B\x04\x010=\x06\x03U\x1d\x1f\x0460402\xa00\xa0.\x86,http://crl.usertrust.com/UTN-DATACorpSGC.crl0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x05\x05\x00\x03\x82\x01\x01\x00\xc6\xeeS\x17h\x14\xb2Q"\x1e\x90X\r\x94\xfd\xbd\xf1p\xe5\x86-\xc361\x8fTHF\xe7-\x087\xbcl\n`\xe1\x0e\xadQ4\xe0\x12\x93\xe9\xbe\xb8\xab\xb8&\xb4\xe9\x96=(\x8f\xaed\x07\xfe\xe0\x01\xec\xc5\xe3\x91\xeb\x18\xa0\xf1u~\xdb\n\xe6\x9f\x91\xdb\xaf\xaeu\xdf#\x91h\xdd\x17\x00ZK\xffdlp\xeb\x01\x1a\xd0\x90\xd9\xc7\xa6\xd6m\xf6\x13\xe4\xff\xb5\xc9\xd2\x1e*\xcb\xb1%C&x\xd90\x9bN\r\x1e\xbei\xef\xdf\xea\xfe-\xb3\xcc\xf9\xb0\xdd\xb5\x14\xca\x91\xd4\xb2\xb5\xa5\xfb\x01\x19\xa3Gy\x9f\x9d\x8c\x95\x874\xf8\x1f8\x92\xda6\xa6\x11\xfak\xebk\xe9\xdcEx\x159\x06\xd7MA\xe4!\xc8\xdc/\x87\xd1\xb7\xbfH`u\xa5b\xcb$\xde;a\xa0) \xa6\xbe\xc5l\x9c\xc4\xe9\ni"\xef\x91:\xfa&\xaf\xd1[A\xa7:\xe2\xf88\x07B\xab\xc1[\xf8\xcem\xba\x0f\x04?24\xac\xdc\x04(\xd7p0\x14&\x06\xc4\xe4\x9b\x98\xd5\xcfx'
-
-    def test_server_certificate_from_bytes(self):
-        parsed_record, len_consumed = TlsHandshakeRecord.from_bytes(self.SERVER_CERTIFICATE_BYTES)
-        self.assertEqual(len(parsed_record.subprotocol_messages), 1)
-        self.assertEqual(parsed_record.subprotocol_messages[0].handshake_type, TlsHandshakeTypeByte.CERTIFICATE)
-        self.assertEqual(len_consumed, len(self.SERVER_CERTIFICATE_BYTES))
-
-    SERVER_HELLO_DONE_BYTES = b'\x16\x03\x03\x00\x04\x0e\x00\x00\x00'
-
-    def test_server_hello_done_from_bytes(self):
-        parsed_record, len_consumed = TlsHandshakeRecord.from_bytes(self.SERVER_HELLO_DONE_BYTES)
-        self.assertEqual(len(parsed_record.subprotocol_messages), 1)
-        self.assertEqual(parsed_record.subprotocol_messages[0].handshake_type, TlsHandshakeTypeByte.SERVER_DONE)
-        self.assertEqual(len_consumed, len(self.SERVER_HELLO_DONE_BYTES))
-
-    # A TLS handshake record with 4 handshake messages packed into the same TLS record
-    MULTIPLE_MESSAGES_BYTES = b'\x16\x03\x03\x12\x81\x02\x00\x00M\x03\x03Y\xa3\x0c\x11\x9e5\xa9\x12\xa9y\xf2A\xdc\xd8\xe7\x12\xdf\xb80\x0b\xec&\xa3\x8d\x02j\xbd\xe0\x14Z0R \x08-\x00\x00\xee5\x1cR\xc3+\x16zY(\xd7d\x8f\t\xc0?c\n\xbe\xfa\x82Rk\x85\xe5\xc5R\x13\xc0\x14\x00\x00\x05\xff\x01\x00\x01\x00\x0b\x00\x10\xdb\x00\x10\xd8\x00\x05K0\x82\x05G0\x82\x04/\xa0\x03\x02\x01\x02\x02\x11\x00\x8cn\x86\n\x12\xd3\xa9[2\xc5i\xc3\xae\xaf}L0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x000\x81\x901\x0b0\t\x06\x03U\x04\x06\x13\x02GB1\x1b0\x19\x06\x03U\x04\x08\x13\x12Greater Manchester1\x100\x0e\x06\x03U\x04\x07\x13\x07Salford1\x1a0\x18\x06\x03U\x04\n\x13\x11COMODO CA Limited1604\x06\x03U\x04\x03\x13-COMODO RSA Domain Validation Secure Server CA0\x1e\x17\r170505000000Z\x17\r190505235959Z0V1!0\x1f\x06\x03U\x04\x0b\x13\x18Domain Control Validated1\x1b0\x19\x06\x03U\x04\x0b\x13\x12GGSSL Wildcard SSL1\x140\x12\x06\x03U\x04\x03\x0c\x0b*.vocons.de0\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\x91\xcc\xb8\xa1\x8cE\xb5`C\x81\\$\xe1Q\xae\xa4o\x80U\x94\x03\x00\x18\x16\xa4J\xd3\xd4\xfe\x0e\x83U\x0f\x1e%\xff\x00\xcbc\t\x1a.\xfbC\xd6\xb0_(\xe2\x9ab\xbb\xce\nhS\xd6\x87\x96@\x9db\xd7|\x9e\xb4\xcc\xdb\x88\xc5\x94oj\x88`\xed>\xbf@R\x94\xf0\xe9\xe4\xc0l\x84\xee\x8c\xcf\x086\xa2\xc6,\xeaK\x86\x14N WOuI\xb4\x9ba\xc9W\xeb\x9f\xc2\xf3\x91\xbd\\\xc7\xa9\xb7\x8f\xba\xa9\x88ah\xdb\xa9\xf1\x88\x02\xb3\xbc\xb5Fqm\x17\xb9\x01\x15\x9e\x8f\xc9m\x14\x80\xb2\xa2z\x1ee\x0c\x80L\xa3\xba\xe6\xeb&\n\x15c\xddP\x7f\xc4\xb1\xdcG\xb4zV\x0fY\x0b5\xbf\xb3\x1a\xb0:F\xc0p\x19\xe4Zh\x07\x15O(\x97\x96\xaf\xf2;\xbd\xfe\xfb\x97\x96\x8ce\x0b\xed\x9e\xc6\x95nBB\xdd\x10\xa2\xc0\x19@6\x90H\xe6=+Z\x9c\xf9\xb5\xd3J<\xb0\xf2\xfb\xd6[\xff\xc3f\x01B\xde\x1di?\xef\x1a)\xc6\xb0\xf6\x02E\xb7\x1f\x02\x03\x01\x00\x01\xa3\x82\x01\xd30\x82\x01\xcf0\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14\x90\xafj:\x94Z\x0b\xd8\x90\xea\x12Vs\xdfC\xb4:(\xda\xe70\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14od2\xf2\xb0\x82\r\r\x16\x95c\xf1\xbe\xaaj_\xe9\xba@\xfc0\x0e\x06\x03U\x1d\x0f\x01\x01\xff\x04\x04\x03\x02\x05\xa00\x0c\x06\x03U\x1d\x13\x01\x01\xff\x04\x020\x000\x1d\x06\x03U\x1d%\x04\x160\x14\x06\x08+\x06\x01\x05\x05\x07\x03\x01\x06\x08+\x06\x01\x05\x05\x07\x03\x020O\x06\x03U\x1d \x04H0F0:\x06\x0b+\x06\x01\x04\x01\xb21\x01\x02\x02\x070+0)\x06\x08+\x06\x01\x05\x05\x07\x02\x01\x16\x1dhttps://secure.comodo.com/CPS0\x08\x06\x06g\x81\x0c\x01\x02\x010T\x06\x03U\x1d\x1f\x04M0K0I\xa0G\xa0E\x86Chttp://crl.comodoca.com/COMODORSADomainValidationSecureServerCA.crl0\x81\x85\x06\x08+\x06\x01\x05\x05\x07\x01\x01\x04y0w0O\x06\x08+\x06\x01\x05\x05\x070\x02\x86Chttp://crt.comodoca.com/COMODORSADomainValidationSecureServerCA.crt0$\x06\x08+\x06\x01\x05\x05\x070\x01\x86\x18http://ocsp.comodoca.com0!\x06\x03U\x1d\x11\x04\x1a0\x18\x82\x0b*.vocons.de\x82\tvocons.de0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0b\x05\x00\x03\x82\x01\x01\x00lE\x88h\x1crm"\x97i\x0f\xe0au\xe6\x8ar\xfc6k\xca9\xd6\x15\x98\xc7\xde\xcf_\x92\xc6\x16F\x16\xc6\xb9tK\xbd\x00@\x026c/\x8c\xce\x97\xc21\x0cP\xb1O\xe1\x05\xca\xba]%\xec\x04l\xd8\xe0\xfb\xd4\xe1J\x07E\x1d\xe4\xaf$"*\xf4\xdc\x89\xfb\xc2\x07-\xc9\x0e\xe0%\xcf\xedgsle\x8f\xfa\x00\xcbJ\xb3\x1a\xbbp\xf0\xe0\xef\x93\xd7\xefud09\xa1\xe6\x92\x00\xf6=5\x11\x1b\x99\xbdu7-\x0f\x16\xc0\xf3\x0eG\x1f\x1b\xfa\xfb\xa9.s[\x06\x0e\x9cDn"\x8a\xbb?\xd8\x0e<\xee\xca\x1c\xb1\xf6\x85\x8dln\x7fG\x9a\x12\xa1V9+c\xd1\xbeDSl\xcef\xeei\xb8\xc8\x9b\xd1\x91J\xcc\xc4eX\xad\xf6\x80\xc0\x11\r\x9c\xe6t\xb1\x1b\xe4\xe2\xb5\xec\xe2\xd0\xefKm\x9b(z(\xf0\xa7\xbd\x93=\x84\xab\xfeG\xcd\x984\xcd`\xa6MQ\x91>\xac\x98\x05\xe0\x1e\x11\xfc\xc5\x962\xa8\xc5\x0c\xb4L4\xbd\xa6\xea%|\xbb\x0c\x00\x06\x0c0\x82\x06\x080\x82\x03\xf0\xa0\x03\x02\x01\x02\x02\x10+.n\xea\xd9u6l\x14\x8an\xdb\xa3|\x8c\x070\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0c\x05\x000\x81\x851\x0b0\t\x06\x03U\x04\x06\x13\x02GB1\x1b0\x19\x06\x03U\x04\x08\x13\x12Greater Manchester1\x100\x0e\x06\x03U\x04\x07\x13\x07Salford1\x1a0\x18\x06\x03U\x04\n\x13\x11COMODO CA Limited1+0)\x06\x03U\x04\x03\x13"COMODO RSA Certification Authority0\x1e\x17\r140212000000Z\x17\r290211235959Z0\x81\x901\x0b0\t\x06\x03U\x04\x06\x13\x02GB1\x1b0\x19\x06\x03U\x04\x08\x13\x12Greater Manchester1\x100\x0e\x06\x03U\x04\x07\x13\x07Salford1\x1a0\x18\x06\x03U\x04\n\x13\x11COMODO CA Limited1604\x06\x03U\x04\x03\x13-COMODO RSA Domain Validation Secure Server CA0\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\x8e\xc2\x02\x19\xe1\xa0Y\xa4\xeb85\x8d,\xfd\x01\xd0\xd3I\xc0d\xc7\x0bb\x05E\x16:\xa8\xa0\xc0\x0c\x02\x7f\x1d\xcc\xdb\xc4\xa1mw\x03\xa3\x0f\x86\xf9\xe3\x06\x9c>\x0b\x81\x8a\x9bI\x1b\xad\x03\xbe\xfaK\xdb\x8c \xed\xd5\xce^e\x8e>\r\xafL\xc2\xb0\xb7E^R/4\xdeH$d\xb4A\xae\x00\x97\xf7\xbeg\xde\x9e\xd0z\xa7S\x80;|\xad\xf5\x96Uo\x97G\n|\x85\x8b"\x97\x8d\xb3\x84\xe0\x96W\xd0p\x18`\x96\x8f\xee-\x07\x93\x9d\xa1\xba\xca\xd1\xcd{\xe9\xc4*\x9a(!\x91Mo\x92O%\xa5\xf2z5\xdd&\xdcF\xa5\xd0\xacY5\x8c\xffN\x91CP?Y\x93\x1elQ!\xeeX\x14\xab\xfeuPx>L\xb0\x1c\x86\x13\xfak\x98\xbc\xe0;\x94\x1e\x85R\xdc\x03\x93$\x18n\xcb\'QE\xe6p\xde%C\xa4\r\xe1J\xa5\xed\xb6~\xc8\xcdm\xee.\x1d\'s]\xdcE0\x80\xaa\xe3\xb2A\x0b\xaf\xbdD\x87\xda\xb9\xe5\x1b\x9d\x7f\xae\xe5\x85\x82\xa5\x02\x03\x01\x00\x01\xa3\x82\x01e0\x82\x01a0\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14\xbb\xaf~\x02=\xfa\xa6\xf1<\x84\x8e\xad\xee8\x98\xec\xd922\xd40\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14\x90\xafj:\x94Z\x0b\xd8\x90\xea\x12Vs\xdfC\xb4:(\xda\xe70\x0e\x06\x03U\x1d\x0f\x01\x01\xff\x04\x04\x03\x02\x01\x860\x12\x06\x03U\x1d\x13\x01\x01\xff\x04\x080\x06\x01\x01\xff\x02\x01\x000\x1d\x06\x03U\x1d%\x04\x160\x14\x06\x08+\x06\x01\x05\x05\x07\x03\x01\x06\x08+\x06\x01\x05\x05\x07\x03\x020\x1b\x06\x03U\x1d \x04\x140\x120\x06\x06\x04U\x1d \x000\x08\x06\x06g\x81\x0c\x01\x02\x010L\x06\x03U\x1d\x1f\x04E0C0A\xa0?\xa0=\x86;http://crl.comodoca.com/COMODORSACertificationAuthority.crl0q\x06\x08+\x06\x01\x05\x05\x07\x01\x01\x04e0c0;\x06\x08+\x06\x01\x05\x05\x070\x02\x86/http://crt.comodoca.com/COMODORSAAddTrustCA.crt0$\x06\x08+\x06\x01\x05\x05\x070\x01\x86\x18http://ocsp.comodoca.com0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0c\x05\x00\x03\x82\x02\x01\x00N+vO\x92\x1cb6\x89\xbaw\xc1\'\x05\xf4\x1c\xd6D\x9d\xa9\x9a>\xaa\xd5ff\x01>\xeaI\xe6\xa25\xbc\xfa\xf6\xdd\x95\x8e\x995\x98\x0e6\x18u\xb1\xdd\xddPr|\xae\xdcw\x88\xce\x0f\xf7\x90 \xca\xa3g.\x1fV\x7f{\xe1D\xeaB\x95\xc4]\r\x01PF\x15\xf2\x81\x89Yl\x8a\xdd\x8c\xf1\x12\xa1\x8d:B\x8a\x98\xf8K4{\';\x08\xb4o$;r\x9dctX<\x1al?O\xc7\x11\x9a\xc8\xa8\xf5\xb57\xef\x10E\xc6l\xd9\xe0^\x95&\xb3\xeb\xad\xa3\xb9\xee\x7f\x0c\x9af5s2`N\xe5\xdd\x8aa,nR\x11wh\x96\xd3\x18uQ\x15\x00\x1bt\x88\xdd\xe1\xc78\x04C(\xe9\x16\xfd\xd9\x05\xd4]G\'`\xd6\xfb8;lr\xa2\x94\xf8B\x1a\xdf\xedo\x06\x8cE\xc2\x06\x00\xaa\xe4\xe8\xdc\xd9\xb5\xe1sx\xec\xf6#\xdc\xd1\xddl\x8e\x1a\x8f\xa5\xeaT|\x96\xb7\xc3\xfeU\x8e\x8dI^\xfcd\xbb\xcf>\xbd\x96\xebi\xcd\xbf\xe0H\xf1b\x82\x10\xe5\x0cFW\xf23\xda\xd0\xc8c\xed\xc6\x1f\x94\x05\x96J\x1a\x91\xd1\xf7\xeb\xcf\x8fR\xae\r\x08\xd9>\xa8\xa0Q\xe9\xc1\x87t\xd5\xc9\xf7t\xab.S\xfb\xbbz\xfb\x97\xe2\xf8\x1f&\x8f\xb3\xd2\xa0\xe07[(;1\xe5\x0eW-Z\xb8\xady\xac^ f\x1a\xa5\xb9\xa6\xb59\xc1\xf5\x98C\xff\xee\xf9\xa7\xa7\xfd\xee\xca$=\x80\x16\xc4\x17\x8f\x8a\xc1`\xa1\x0c\xae[CG\x91K\xd5\x9a\x17_\xf9\xd4\x87\xc1\xc2\x8c\xb7\xe7\xe2\x0f0\x197\x86\xac\xe0\xdcB\x03\xe6\x94\xa8\x9d\xae\xfd\x0f$Q\x94\xce\x92\x08\xd1\xfcP\xf0\x03@{\x88Y\xed\x0e\xdd\xac\xd2w\x824\xdc\x06\x95\x02\xd8\x90\xf9-\xea7\xd5\x1a`\xd0g \xd7\xd8B\x0bE\xaf\x82h\xde\xddf$7\x90)\x94\x19F\x19%\xb8\x80\xd7\xcb\xd4\x86(jDp&#b\xa9\x9f\x86o\xbf\xba\x90p\xd2Vw\x85x\xef\xea%\xa9\x17\xcePr\x8c\x00:\xaa\xe3\xdbc4\x9f\xf8\x06q\x01\xe2\x82 \xd4\xfeo\xbd\xb1\x00\x05x0\x82\x05t0\x82\x04\\\xa0\x03\x02\x01\x02\x02\x10\'f\xeeV\xebI\xf3\x8e\xab\xd7p\xa2\xfc\x84\xde"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0c\x05\x000o1\x0b0\t\x06\x03U\x04\x06\x13\x02SE1\x140\x12\x06\x03U\x04\n\x13\x0bAddTrust AB1&0$\x06\x03U\x04\x0b\x13\x1dAddTrust External TTP Network1"0 \x06\x03U\x04\x03\x13\x19AddTrust External CA Root0\x1e\x17\r000530104838Z\x17\r200530104838Z0\x81\x851\x0b0\t\x06\x03U\x04\x06\x13\x02GB1\x1b0\x19\x06\x03U\x04\x08\x13\x12Greater Manchester1\x100\x0e\x06\x03U\x04\x07\x13\x07Salford1\x1a0\x18\x06\x03U\x04\n\x13\x11COMODO CA Limited1+0)\x06\x03U\x04\x03\x13"COMODO RSA Certification Authority0\x82\x02"0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x01\x05\x00\x03\x82\x02\x0f\x000\x82\x02\n\x02\x82\x02\x01\x00\x91\xe8T\x92\xd2\nV\xb1\xac\r$\xdd\xc5\xcfDgt\x99+7\xa3}#p\x00q\xbcS\xdf\xc4\xfa*\x12\x8fK\x7f\x10V\xbd\x9fpr\xb7a\x7f\xc9K\x0f\x17\xa7=\xe3\xb0\x04a\xee\xff\x11\x97\xc7\xf4\x86>\n\xfa>\\\xf9\x93\xe64z\xd9\x14k\xe7\x9c\xb3\x85\xa0\x82zv\xafq\x90\xd7\xec\xfd\r\xfa\x9cl\xfa\xdf\xb0\x82\xf4\x14~\xf9\xbe\xc4\xa6/O\x7f\x99\x7f\xb5\xfcgCr\xbd\x0c\x00\xd6\x89\xebk,\xd3\xed\x8f\x98\x1c\x14\xab~\xe5\xe3n\xfc\xd8\xa8\xe4\x92$\xdaCkb\xb8U\xfd\xea\xc1\xbcl\xb6\x8b\xf3\x0e\x8d\x9a\xe4\x9bli\x99\xf8xH0E\xd5\xad\xe1\r<E`\xfc2\x96Q\'\xbcg\xc3\xca.\xb6k\xeaF\xc7\xc7 \xa0\xb1\x1fe\xdeH\x08\xba\xa4N\xa9\xf2\x83F7\x84\xeb\xe8\xcc\x81HCgNr*\x9b\\\xbdL\x1b(\x8a\\"{\xb4\xab\x98\xd9\xee\xe0Q\x83\xc3\tFNm>\x99\xfa\x95\x17\xda|3WA<\x8dQ\xed\x0b\xb6\\\xaf,c\x1a\xdfW\xc8?\xbc\xe9]\xc4\x9b\xafE\x99\xe2\xa3Z$\xb4\xba\xa9V=\xcfo\xaa\xffIX\xbe\xf0\xa8\xff\xf4\xb8\xad\xe97\xfb\xba\xb8\xf4\x0b:\xf9\xe8CB\x1e\x89\xd8\x84\xcb\x13\xf1\xd9\xbb\xe1\x89`\xb8\x8c(V\xac\x14\x1d\x9c\n\xe7q\xeb\xcf\x0e\xdd=\xa9\x96\xa1H\xbd<\xf7\xaf\xb5\r"L\xc0\x11\x81\xecV;\xf6\xd3\xa2\xe2[\xb7\xb2\x04"R\x95\x80\x93i\xe8\x8eLe\xf1\x91\x03-pt\x02\xea\x8bg\x15)iR\x02\xbb\xd7\xdfPjUF\xbf\xa0\xa3(a\x7fp\xd0\xc3\xa2\xaa,!\xaaG\xce(\x9c\x06Ev\xbf\x82\x18\'\xb4\xd5\xae\xb4\xcbP\xe6k\xf4L\x86q0\xe9\xa6\xdf\x16\x86\xe0\xd8\xff@\xdd\xfb\xd0B\x88\x7f\xa33:.\\\x1eA\x11\x81c\xce\x18qk+\xec\xa6\x8a\xb71\\:jG\xe0\xc3yY\xd6 \x1a\xaf\xf2j\x98\xaar\xbcWJ\xd2K\x9d\xbb\x10\xfc\xb0LA\xe5\xed\x1d=^(\x9d\x9c\xcc\xbf\xb3Q\xda\xa7G\xe5\x84S\x02\x03\x01\x00\x01\xa3\x81\xf40\x81\xf10\x1f\x06\x03U\x1d#\x04\x180\x16\x80\x14\xad\xbd\x98z4\xb4&\xf7\xfa\xc4&T\xef\x03\xbd\xe0$\xcbT\x1a0\x1d\x06\x03U\x1d\x0e\x04\x16\x04\x14\xbb\xaf~\x02=\xfa\xa6\xf1<\x84\x8e\xad\xee8\x98\xec\xd922\xd40\x0e\x06\x03U\x1d\x0f\x01\x01\xff\x04\x04\x03\x02\x01\x860\x0f\x06\x03U\x1d\x13\x01\x01\xff\x04\x050\x03\x01\x01\xff0\x11\x06\x03U\x1d \x04\n0\x080\x06\x06\x04U\x1d \x000D\x06\x03U\x1d\x1f\x04=0;09\xa07\xa05\x863http://crl.usertrust.com/AddTrustExternalCARoot.crl05\x06\x08+\x06\x01\x05\x05\x07\x01\x01\x04)0\'0%\x06\x08+\x06\x01\x05\x05\x070\x01\x86\x19http://ocsp.usertrust.com0\r\x06\t*\x86H\x86\xf7\r\x01\x01\x0c\x05\x00\x03\x82\x01\x01\x00d\xbf\x83\xf1_\x9a\x85\xd0\xcd\xb8\xa1)W\r\xe8Z\xf7\xd1\xe9>\xf2v\x04n\xf1Rp\xbb\x1e<\xffM\rtj\xcc\x81\x82%\xd3\xc3\xa0*]L\xf5\xba\x8b\xa1m\xc4T\tu\xc7\xe3\'\x0e]\x84y7@\x13w\xf5\xb4\xac\x1c\xd0;\xab\x17\x12\xd6\xef4\x18~+\xe9y\xd3\xabWE\x0c\xaf(\xfa\xd0\xdb\xe5P\x95\x88\xbb\xdf\x85Wi}\x92\xd8R\xcas\x81\xbf\x1c\xf3\xe6\xb8nf\x11\x05\xb3\x1e\x94-\x7f\x91\x95\x92Y\xf1L\xce\xa3\x91qL|G\x0c;\x0b\x19\xf6\xa1\xb1l\x86>\\\xaa\xc4.\x82\xcb\xf9\x07\x96\xbaHM\x90\xf2\x94\xc8\xa9s\xa2\xeb\x06{#\x9d\xde\xa2\xf3MU\x9fzaE\x98\x18h\xc7^@k#\xf5yz\xef\x8c\xb5k\x8b\xb7oF\xf4{\xf1=K\x04\xd8\x93\x80YZ\xe0A$\x1d\xb2\x8f\x15`XG\xdb\xefnF\xfd\x15\xf5\xd9_\x9a\xb3\xdb\xd8\xb8\xe4@\xb3\xcd\x979\xae\x85\xbb\x1d\x8e\xbc\xdc\x87\x9b\xd1\xa6\xef\xf1;o\x108o\x0c\x00\x01I\x03\x00\x17A\x04\xad\xf1\xb9>$\x02\xfc\x96^\xd2L\x9b\x08}a\xe3\x82\x9162\xcb\xd9;\xf1\xbe\xb8Y\xb4\xbb\xf9\x91\x1c\x0c\x80\xad\xf5 \xa7:\xd0\xd5R\xef\\\x03\x9f(2\xd8\x99\xe0\x98\xfc<K\x04TZt\x8f\xf7\xe2\x1aX\x02\x01\x01\x00;\xe7\x98\x0f\xcfA\xc0\x8a\x9bP\xb0\xea\xbb\n\xd4\x89\xe2\xfe7I\x02~!\x1f\xf3\xc3\x07\xf0\xe0\x85c\xd0\x99\nq2\xd2\xb1\x8b\xdca\x84\x95%\xfdNlq+%\xdbN;\x98Z\x88\xad@e\xe5_0\x8fD\x17\xcd\xe9\x1c\xbd\x03\xde\x99\xfe\xfeE\x16p\xc2/\x03?\x9e\xb0\x1b_\x93\x01n\xd9\xc3\x94^\xc2{W\x91\x1d\xd6P\xfbc\xa9\xb0\xea5\x10\x8f_1\xc4`t8:\xe8\xc4N\xc3\x05\xe4S\x0c\xeaH\xfcQ\xeb\xc3\xdf\x1af\x0b_9Ag\xf1QrS\xe0\x9f\x8b\xf5\xeaJ\xf2\x9a\x9a\x18\xf7\xe9\x92\xae!L\xf6\xa2\xac\x0c\xf8\xcaU\xf14]Z\xa2\xad\xc6\x85}VT4=7`?\x84\xe8N\xb44\x93\x04\xa9\xb3\xe6%\xab\x97\x03\xc5Y\x93\xbdt\xb2\x90\x95\xb7-\xde\xf7v\xba\xd0\x95\xc0\x0f\xaca\nud8\x8c\xde\xc3\x95Z\xb6\x1b\xff\xbf9\x9b\x19\xe77D\x83n\xba\x1c\xe5\x17=\xf4\xcaxq\xfav.\x93\xcc\x8a\xfa\xbeA=W\xe5k\x0e\x00\x00\x00'
-
-    def test_multiple_messages_from_bytes(self):
-        parsed_record, len_consumed = TlsHandshakeRecord.from_bytes(self.MULTIPLE_MESSAGES_BYTES)
-        self.assertEqual(len(parsed_record.subprotocol_messages), 4)
-        self.assertEqual(parsed_record.subprotocol_messages[0].handshake_type, TlsHandshakeTypeByte.SERVER_HELLO)
-        self.assertEqual(parsed_record.subprotocol_messages[1].handshake_type, TlsHandshakeTypeByte.CERTIFICATE)
-        self.assertEqual(parsed_record.subprotocol_messages[2].handshake_type, TlsHandshakeTypeByte.SERVER_KEY_EXCHANGE)
-        self.assertEqual(parsed_record.subprotocol_messages[3].handshake_type, TlsHandshakeTypeByte.SERVER_DONE)
-        self.assertEqual(len_consumed, len(self.MULTIPLE_MESSAGES_BYTES))
-
-    EXPECTED_CLIENT_KEY_EXCHANGE_BYTES = \
-        b'\x16\x03\x03\x01\x06\x10\x00\x01\x02\x01\x00\x0b\x0es\xccE\xe9\xee\xca\x9b*\x00]\x80\xb7\x984\x93\xeeh' \
-        b'\xcf\xfc\xe3]\xa3\r\xd1\xb9%\xe3\xbc\xe5\xa4\xc5\x14\xb9~\x92\'\xe4\xbc\xca\xfa\xa2\x0f\xeb c\xab%' \
-        b'\x91\xfc\xa7\x8e\xb5\xa0\xb2\xa3\xd9\x14\x83Z\xfc\x94\x8e2\xaf\xd8\x08\xe4\xe6\xd5\xd2\xb5~\xdf\x1e' \
-        b'\x99\xc5^\x8a\xac8EU\x9brV\xbb\x02\x1eF\xd7\xda\xa2\xeb.\x95\xa5\xb9,4\x98\x1f"#\x07\xfa%\x94\xc0\xe2' \
-        b'\x04\xa3\x04[\x1e\xf9\xacj\xd6\xddo6\x91\x94+\xc1Y5\xe7#\x85e\xbd\x03\'\x10g\xef\xd0\xd7\xc1\x8eF\'\xd9' \
-        b'\x9a\x9f^H;d\xb5\xddv\xceX\xfc\x99\x9f\xbd*\x12nI|\xf4\x109\x9f\x17\x94\xe3T\xd38\xbfj\xff\x881\xb9' \
-        b'\xce\x9b\xe9\x0e\xe6\x9c\x1b\xbd\xcd\x1f\xa0\xf2x\xaf\x01\xb4t\xf4{\x05\x80RC\xc4h\x9e\xbd\xcfcw\xfd' \
-        b'\x02~\x03\xc9\x9a\xfa4\xed\xe8IDA&\xe9\x01\\\xca\xb2\xe3\x08\xeb\xefhV5\xb3\xee%\xae\xceK\x8d\x80N\xdf' \
-        b'\xe8\xb5\xca\xed\xd3\x9a[\xf4'
-
-    MODULUS_HEX =  \
-'0x9944d2fd7dbdc447a1de74002c4746dbf99d0d64e24a0ccea319f04af4b71acb1eeefcfb8ff904f689977006d113365b503e58b7fea2cbe985294775c0' \
-'0d5da171d3bf227b71f80223c1f55c141c5a892eefcf8f4d36ddb7b2ed179fb3a48498ea57dd80e09b6c8d64553b15bdc92a1465692965d5f1e65d85a679' \
-'466577fdf8302100c2a07af0b8dd1fc2f63f52e8f220ee0f4ef33a2a6b94e20ac82cf8d1073705f09f7a506ead5ba6271e60e80aa32a369a614bd1fa9226' \
-'8f504a6b7da72ad870aca13f543c00d7725d082f796e5f6c6eee02ecd860388360ce95a2ba4057cbb0053af32209b88d921d1c2d4569f806c44132214a61' \
-'78242d2486b643e773'
-
-    EXPONENT_HEX = '0x10001'
-
-    def test_rsa_client_key_exchange_to_bytes(self):
-        # Inspired by https://github.com/robotattackorg/robot-detect
-        modulus = int(self.MODULUS_HEX, 16)
-        exponent = int(self.EXPONENT_HEX, 16)
-        modulus_bit_size = int(math.ceil(math.log(modulus, 2)))
-        modulus_byte_size = (modulus_bit_size + 7) // 8
-
-        pre_master_secret = "aa112233445566778899112233445566778899112233445566778899112233445566778899112233445566778899"
-
-        # Generate padding - it should be of the form "00 02 <random> 00 <TLS version> <premaster secret>
-        tls_version_hex = binascii.b2a_hex(TlsRecordTlsVersionBytes[TlsVersionEnum.TLSV1_2.name].value).decode('ascii')
-        pad_len = (modulus_byte_size - 48 - 3) * 2
-        rnd_pad = ("abcd" * (pad_len // 2 + 1))[:pad_len]
-        pms_with_padding = int("0002" + rnd_pad + "00" + tls_version_hex + pre_master_secret, 16)
-
-        record = TlsRsaClientKeyExchangeRecord.from_parameters(TlsVersionEnum.TLSV1_2, exponent, modulus,
-                                                               pms_with_padding)
-        self.assertEqual(record.to_bytes(), self.EXPECTED_CLIENT_KEY_EXCHANGE_BYTES)
\ No newline at end of file
diff --git a/tests/test_heartbeat_protocol.py b/tests/test_heartbeat_protocol.py
deleted file mode 100644
index 49b4dd8..0000000
--- a/tests/test_heartbeat_protocol.py
+++ /dev/null
@@ -1,14 +0,0 @@
-from __future__ import absolute_import
-from __future__ import print_function
-
-import unittest
-
-from tls_parser.heartbeat_protocol import TlsHeartbeatRequestRecord
-from tls_parser.tls_version import TlsVersionEnum
-
-
-class TlsHeartbeatRequestRecordTestCase(unittest.TestCase):
-
-    def test_to_bytes(self):
-        record = TlsHeartbeatRequestRecord.from_parameters(TlsVersionEnum.TLSV1_2, b'123456')
-        self.assertTrue(record.to_bytes())
diff --git a/tls_parser.egg-info/PKG-INFO b/tls_parser.egg-info/PKG-INFO
new file mode 100644
index 0000000..e2b1a01
--- /dev/null
+++ b/tls_parser.egg-info/PKG-INFO
@@ -0,0 +1,11 @@
+Metadata-Version: 1.2
+Name: tls-parser
+Version: 2.0.0
+Summary: Small library to parse TLS records.
+Home-page: https://github.com/nabla-c0d3/tls_parser
+Author: Alban Diquet
+Author-email: nabla.c0d3@gmail.com
+License: UNKNOWN
+Description: UNKNOWN
+Platform: UNKNOWN
+Requires-Python: >=3.7
diff --git a/tls_parser.egg-info/SOURCES.txt b/tls_parser.egg-info/SOURCES.txt
new file mode 100644
index 0000000..6328951
--- /dev/null
+++ b/tls_parser.egg-info/SOURCES.txt
@@ -0,0 +1,18 @@
+README.md
+setup.cfg
+setup.py
+tls_parser/__init__.py
+tls_parser/alert_protocol.py
+tls_parser/application_data_protocol.py
+tls_parser/change_cipher_spec_protocol.py
+tls_parser/cipher_suites.py
+tls_parser/exceptions.py
+tls_parser/handshake_protocol.py
+tls_parser/heartbeat_protocol.py
+tls_parser/parser.py
+tls_parser/record_protocol.py
+tls_parser/tls_version.py
+tls_parser.egg-info/PKG-INFO
+tls_parser.egg-info/SOURCES.txt
+tls_parser.egg-info/dependency_links.txt
+tls_parser.egg-info/top_level.txt
\ No newline at end of file
diff --git a/tls_parser.egg-info/dependency_links.txt b/tls_parser.egg-info/dependency_links.txt
new file mode 100644
index 0000000..8b13789
--- /dev/null
+++ b/tls_parser.egg-info/dependency_links.txt
@@ -0,0 +1 @@
+
diff --git a/tls_parser.egg-info/top_level.txt b/tls_parser.egg-info/top_level.txt
new file mode 100644
index 0000000..ff775b8
--- /dev/null
+++ b/tls_parser.egg-info/top_level.txt
@@ -0,0 +1 @@
+tls_parser
diff --git a/tls_parser/__init__.py b/tls_parser/__init__.py
index 912aa92..dec2633 100644
--- a/tls_parser/__init__.py
+++ b/tls_parser/__init__.py
@@ -1,9 +1,6 @@
-from __future__ import absolute_import
-from __future__ import print_function
-
-__version__ = '1.2.2'
-__author__ = 'Alban Diquet'
-__email__ = 'nabla.c0d3@gmail.com'
+__version__ = "2.0.0"
+__author__ = "Alban Diquet"
+__email__ = "nabla.c0d3@gmail.com"
 
 # A very useful page for working on this module:
 # http://blog.fourthbit.com/2014/12/23/traffic-analysis-of-an-ssl-slash-tls-session
diff --git a/tls_parser/alert_protocol.py b/tls_parser/alert_protocol.py
index 839bf90..e098566 100644
--- a/tls_parser/alert_protocol.py
+++ b/tls_parser/alert_protocol.py
@@ -1,12 +1,9 @@
-from __future__ import absolute_import
-from __future__ import print_function
-
 import struct
 from enum import IntEnum
 from tls_parser.exceptions import NotEnoughData, UnknownTypeByte
 from tls_parser.record_protocol import TlsSubprotocolMessage, TlsRecord, TlsRecordHeader, TlsRecordTypeByte
 from tls_parser.tls_version import TlsVersionEnum
-from typing import Tuple
+from typing import Tuple, List
 
 
 class TlsAlertSeverityByte(IntEnum):
@@ -15,39 +12,34 @@ class TlsAlertSeverityByte(IntEnum):
 
 
 class TlsAlertMessage(TlsSubprotocolMessage):
+    def __init__(self, alert_severity: TlsAlertSeverityByte, alert_description: int) -> None:
+        # Recreate the raw message as bytes
+        full_message_data = b""
+        full_message_data += struct.pack("B", alert_severity.value)
+        full_message_data += struct.pack("B", alert_description)
+        super().__init__(full_message_data)
 
-    def __init__(self, alert_severity, alert_description):
-        # type: (TlsAlertSeverityByte, int) -> None
         self.alert_severity = alert_severity
         # Right now the description is just stored as an int instead of a TlsAlertDescriptionByte
         self.alert_description = alert_description
 
     @classmethod
-    def from_bytes(cls, raw_bytes):
-        # type: (bytes) -> Tuple[TlsAlertMessage, int]
+    def from_bytes(cls, raw_bytes: bytes) -> Tuple["TlsAlertMessage", int]:
         if len(raw_bytes) < 2:
             raise NotEnoughData()
-        
-        alert_severity = TlsAlertSeverityByte(struct.unpack('B', raw_bytes[0:1])[0])
-        alert_description = struct.unpack('B', raw_bytes[1:2])[0]
-        return TlsAlertMessage(alert_severity, alert_description), 2
 
-    def to_bytes(self):
-        # type: () -> bytes
-        bytes = b''
-        bytes += struct.pack('B', self.alert_severity.value)
-        bytes += struct.pack('B', self.alert_description)
-        return bytes
+        alert_severity = TlsAlertSeverityByte(struct.unpack("B", raw_bytes[0:1])[0])
+        alert_description = struct.unpack("B", raw_bytes[1:2])[0]
+        return TlsAlertMessage(alert_severity, alert_description), 2
 
 
 class TlsAlertRecord(TlsRecord):
-    def __init__(self, record_header, alert_message):
-        # type: (TlsRecordHeader, TlsAlertMessage) -> None
-        super(TlsAlertRecord, self).__init__(record_header, [alert_message])
+    def __init__(self, record_header: TlsRecordHeader, alert_message: TlsAlertMessage) -> None:
+        super(TlsAlertRecord, self).__init__(record_header=record_header, subprotocol_messages=[alert_message])
+        self.subprotocol_messages: List[TlsAlertMessage]  # TODO(AD): Fix the interface instead of using an annotation
 
     @property
-    def alert_severity(self):
-        # type: () -> TlsAlertSeverityByte
+    def alert_severity(self) -> TlsAlertSeverityByte:
         """Convenience method to get the severity of the underlying Alert message.
 
         This makes the assumption that an Alert record only contains one Alert message, which seems to be the case in
@@ -56,8 +48,7 @@ class TlsAlertRecord(TlsRecord):
         return self.subprotocol_messages[0].alert_severity
 
     @property
-    def alert_description(self):
-        # type: () -> int
+    def alert_description(self) -> int:
         """Convenience method to get the description of the underlying Alert message.
 
         This makes the assumption that an Alert record only contains one Alert message, which seems to be the case in
@@ -66,15 +57,15 @@ class TlsAlertRecord(TlsRecord):
         return self.subprotocol_messages[0].alert_description
 
     @classmethod
-    def from_parameters(cls, tls_version, alert_severity, alert_description):
-        # type: (TlsVersionEnum, TlsAlertSeverityByte, int) -> TlsAlertRecord
+    def from_parameters(
+        cls, tls_version: TlsVersionEnum, alert_severity: TlsAlertSeverityByte, alert_description: int
+    ) -> "TlsAlertRecord":
         alert_message = TlsAlertMessage(alert_severity, alert_description)
         record_header = TlsRecordHeader(TlsRecordTypeByte.ALERT, tls_version, alert_message.size)
         return TlsAlertRecord(record_header, alert_message)
 
     @classmethod
-    def from_bytes(cls, raw_bytes):
-        # type: (bytes) -> Tuple[TlsAlertRecord, int]
+    def from_bytes(cls, raw_bytes: bytes) -> Tuple["TlsAlertRecord", int]:
         header, len_consumed = TlsRecordHeader.from_bytes(raw_bytes)
         remaining_bytes = raw_bytes[len_consumed::]
 
diff --git a/tls_parser/application_data_protocol.py b/tls_parser/application_data_protocol.py
index 25bc7d0..7a1093c 100644
--- a/tls_parser/application_data_protocol.py
+++ b/tls_parser/application_data_protocol.py
@@ -1,19 +1,9 @@
-from __future__ import absolute_import
-from __future__ import print_function
-
 from tls_parser.record_protocol import TlsRecord, TlsSubprotocolMessage, TlsRecordHeader, TlsRecordTypeByte
 from tls_parser.tls_version import TlsVersionEnum
 
 
 class TlsApplicationDataMessage(TlsSubprotocolMessage):
-
-    def __init__(self, application_data):
-        # type: (bytes) -> None
-        self.data = application_data
-
-    def to_bytes(self):
-        # type: () -> bytes
-        return self.data
+    pass
 
 
 class TlsApplicationDataRecord(TlsRecord):
@@ -21,13 +11,13 @@ class TlsApplicationDataRecord(TlsRecord):
     real world.
     """
 
-    def __init__(self, record_header, application_data):
-        # type: (TlsRecordHeader, TlsApplicationDataMessage) -> None
-        super(TlsApplicationDataRecord, self).__init__(record_header, [application_data])
+    def __init__(self, record_header: TlsRecordHeader, application_data: TlsApplicationDataMessage):
+        super(TlsApplicationDataRecord, self).__init__(
+            record_header=record_header, subprotocol_messages=[application_data]
+        )
 
     @classmethod
-    def from_parameters(cls, tls_version, application_data):
-        # type: (TlsVersionEnum, bytes) -> TlsApplicationDataRecord
+    def from_parameters(cls, tls_version: TlsVersionEnum, application_data: bytes) -> "TlsApplicationDataRecord":
         message = TlsApplicationDataMessage(application_data)
         record_header = TlsRecordHeader(TlsRecordTypeByte.APPLICATION_DATA, tls_version, message.size)
         return TlsApplicationDataRecord(record_header, message)
diff --git a/tls_parser/byte_utils.py b/tls_parser/byte_utils.py
deleted file mode 100644
index 0f814bd..0000000
--- a/tls_parser/byte_utils.py
+++ /dev/null
@@ -1,21 +0,0 @@
-from __future__ import absolute_import
-from __future__ import print_function
-
-import codecs
-from typing import Optional
-
-
-# TODO(AD): Once we drop support for Python 2, use int.to_bytes() instead?
-def int_to_bytes(i, expected_length=None):
-    # type: (int, Optional[int]) -> bytes
-    hex_value = '{0:x}'.format(i)
-    # Make length of hex_value a multiple of two
-    hex_value = '0' * (len(hex_value) % 2) + hex_value
-    bytes_length = int(len(hex_value)/2)
-
-    if expected_length and bytes_length < expected_length:
-        # Pad to the expected length
-        pad_length = expected_length - bytes_length
-        hex_value = '0'*2*pad_length + hex_value
-
-    return codecs.decode(hex_value, 'hex_codec')
diff --git a/tls_parser/change_cipher_spec_protocol.py b/tls_parser/change_cipher_spec_protocol.py
index 8e0370d..fc0f5f1 100644
--- a/tls_parser/change_cipher_spec_protocol.py
+++ b/tls_parser/change_cipher_spec_protocol.py
@@ -4,15 +4,12 @@ from typing import Tuple
 
 
 class TlsChangeCipherSpecRecord(TlsRecord):
-
     @classmethod
-    def from_parameters(cls, tls_version):
-        # type: (TlsVersionEnum) -> TlsChangeCipherSpecRecord
-        ccs_message = TlsSubprotocolMessage(b'\x01')
+    def from_parameters(cls, tls_version: TlsVersionEnum) -> "TlsChangeCipherSpecRecord":
+        ccs_message = TlsSubprotocolMessage(b"\x01")
         record_header = TlsRecordHeader(TlsRecordTypeByte.CHANGE_CIPHER_SPEC, tls_version, ccs_message.size)
         return TlsChangeCipherSpecRecord(record_header, [ccs_message])
 
     @classmethod
-    def from_bytes(cls, raw_bytes):
-        # type: (bytes) -> Tuple[TlsChangeCipherSpecRecord, int]
+    def from_bytes(cls, raw_byte: bytes) -> Tuple["TlsChangeCipherSpecRecord", int]:
         raise NotImplementedError()
diff --git a/tls_parser/cipher_suites.py b/tls_parser/cipher_suites.py
index 6a90c02..b046ea9 100644
--- a/tls_parser/cipher_suites.py
+++ b/tls_parser/cipher_suites.py
@@ -1,6 +1,3 @@
-from __future__ import absolute_import
-from __future__ import print_function
-
 from enum import Enum
 
 
diff --git a/tls_parser/exceptions.py b/tls_parser/exceptions.py
index 589e9b8..e07324a 100644
--- a/tls_parser/exceptions.py
+++ b/tls_parser/exceptions.py
@@ -13,7 +13,6 @@ class UnknownTypeByte(ValueError):
 
 
 class UnknownTlsVersionByte(ValueError):
-    def __init__(self, message, record_type):
-        # type: (str, TlsRecordTypeByte) -> None
+    def __init__(self, message: str, record_type: "TlsRecordTypeByte") -> None:
         super(ValueError, self).__init__(message)
         self.record_type = record_type
diff --git a/tls_parser/handshake_protocol.py b/tls_parser/handshake_protocol.py
index db755c0..854553e 100644
--- a/tls_parser/handshake_protocol.py
+++ b/tls_parser/handshake_protocol.py
@@ -1,74 +1,67 @@
-from __future__ import absolute_import
-from __future__ import print_function
-
 import struct
 from enum import IntEnum
 
-from tls_parser.byte_utils import int_to_bytes
 from tls_parser.tls_version import TlsVersionEnum
 
 from tls_parser.exceptions import NotEnoughData, UnknownTypeByte
 from tls_parser.record_protocol import TlsSubprotocolMessage, TlsRecord, TlsRecordHeader, TlsRecordTypeByte
-from typing import Tuple, List
+from typing import Tuple, Sequence
 
 
 class TlsHandshakeTypeByte(IntEnum):
-   HELLO_REQUEST = 0x00
-   CLIENT_HELLO = 0x01
-   SERVER_HELLO = 0x02
-   CERTIFICATE = 0x0b
-   SERVER_KEY_EXCHANGE = 0x0c
-   CERTIFICATE_REQUEST = 0x0d
-   SERVER_DONE = 0x0e
-   CERTIFICATE_VERIFY = 0x0f
-   CLIENT_KEY_EXCHANGE = 0x10
-   FINISHED = 0x14
+    HELLO_REQUEST = 0x00
+    CLIENT_HELLO = 0x01
+    SERVER_HELLO = 0x02
+    CERTIFICATE = 0x0B
+    SERVER_KEY_EXCHANGE = 0x0C
+    CERTIFICATE_REQUEST = 0x0D
+    SERVER_DONE = 0x0E
+    CERTIFICATE_VERIFY = 0x0F
+    CLIENT_KEY_EXCHANGE = 0x10
+    FINISHED = 0x14
 
 
 class TlsHandshakeMessage(TlsSubprotocolMessage):
     """The payload of a handshake record.
     """
 
-    def __init__(self, handshake_type, handshake_data):
-        # type: (TlsHandshakeTypeByte, bytes) -> None
+    def __init__(self, handshake_type: TlsHandshakeTypeByte, handshake_data: bytes) -> None:
+        # Recreate the raw message as bytes
+        full_message_data = b""
+        # TLS Handshake type - 1 byte
+        full_message_data += struct.pack("B", handshake_type.value)
+        # TLS Handshake length - 3 bytes
+        full_message_data += struct.pack("!I", len(handshake_data))[1:4]  # We only keep the first 3 out of 4 bytes
+        # TLS Handshake message
+        full_message_data += handshake_data
+        super().__init__(full_message_data)
+
         self.handshake_type = handshake_type
         self.handshake_data = handshake_data
 
     @classmethod
-    def from_bytes(cls, raw_bytes):
-        # type: (bytes) -> Tuple[TlsHandshakeMessage, int]
+    def from_bytes(cls, raw_bytes: bytes) -> Tuple["TlsHandshakeMessage", int]:
         if len(raw_bytes) < 4:
             raise NotEnoughData()
 
-        handshake_type = TlsHandshakeTypeByte(struct.unpack('B', raw_bytes[0:1])[0])
-        message_length = struct.unpack('!I', b'\x00' + raw_bytes[1:4])[0]
-        message = raw_bytes[4:message_length+4]
+        handshake_type = TlsHandshakeTypeByte(struct.unpack("B", raw_bytes[0:1])[0])
+        message_length = struct.unpack("!I", b"\x00" + raw_bytes[1:4])[0]
+        message = raw_bytes[4 : message_length + 4]  # noqa: E203
         if len(message) < message_length:
             raise NotEnoughData()
 
         return TlsHandshakeMessage(handshake_type, message), 4 + message_length
 
-    def to_bytes(self):
-        # type: () -> bytes
-        bytes = b''
-        # TLS Handshake type - 1 byte
-        bytes += struct.pack('B', self.handshake_type.value)
-        # TLS Handshake length - 3 bytes
-        bytes += struct.pack('!I', len(self.handshake_data))[1:4]  # We only keep the first 3 out of 4 bytes
-        # TLS Handshake message
-        bytes += self.handshake_data
-        return bytes
-
 
 class TlsHandshakeRecord(TlsRecord):
+    def __init__(self, record_header: TlsRecordHeader, handshake_messages: Sequence[TlsHandshakeMessage]) -> None:
+        super().__init__(record_header=record_header, subprotocol_messages=handshake_messages)
 
-    def __init__(self, record_header, handshake_messages):
-        # type: (TlsRecordHeader, List[TlsHandshakeMessage]) -> None
-        super(TlsHandshakeRecord, self).__init__(record_header, handshake_messages)
+        # TODO(AD): Fix the interface instead of using an annotation
+        self.subprotocol_messages: Sequence[TlsHandshakeMessage]
 
     @classmethod
-    def from_bytes(cls, raw_bytes):
-        # type: (bytes) -> Tuple[TlsHandshakeRecord, int]
+    def from_bytes(cls, raw_bytes: bytes) -> Tuple["TlsHandshakeRecord", int]:
         header, len_consumed_for_header = TlsRecordHeader.from_bytes(raw_bytes)
         remaining_bytes = raw_bytes[len_consumed_for_header::]
 
@@ -89,22 +82,22 @@ class TlsHandshakeRecord(TlsRecord):
 
 
 class TlsRsaClientKeyExchangeRecord(TlsHandshakeRecord):
-
     @classmethod
-    def from_parameters(cls, tls_version, public_exponent, public_modulus, pre_master_secret_with_padding):
-        # type: (TlsVersionEnum, int, int, int) -> TlsHandshakeRecord
-        cke_bytes = b''
+    def from_parameters(
+        cls, tls_version: TlsVersionEnum, public_exponent: int, public_modulus: int, pre_master_secret_with_padding: int
+    ) -> TlsHandshakeRecord:
+        cke_bytes = b""
 
         # Encrypt the pre_master_secret
         encrypted_pms = pow(pre_master_secret_with_padding, public_exponent, public_modulus)
         # Add it to the message but pad it so that its length is the same as the length of the modulus
-        modulus_length = len(int_to_bytes(public_modulus))
-        encrypted_pms_bytes = int_to_bytes(encrypted_pms, expected_length=modulus_length)
+        modulus_length_in_bytes = (public_modulus.bit_length() + 7) // 8
+        encrypted_pms_bytes = encrypted_pms.to_bytes(length=modulus_length_in_bytes, byteorder="big")
 
         # Per RFC 5246: the RSA-encrypted PreMasterSecret in a ClientKeyExchange is preceded by two length bytes
         # These bytes are redundant in the case of RSA because the EncryptedPreMasterSecret is the only data in the
         # ClientKeyExchange
-        msg_size = struct.pack('!I', len(encrypted_pms_bytes))[2:4]  # Length is two bytes
+        msg_size = struct.pack("!I", len(encrypted_pms_bytes))[2:4]  # Length is two bytes
         cke_bytes += msg_size
         cke_bytes += encrypted_pms_bytes
         msg = TlsHandshakeMessage(TlsHandshakeTypeByte.CLIENT_KEY_EXCHANGE, cke_bytes)
diff --git a/tls_parser/heartbeat_protocol.py b/tls_parser/heartbeat_protocol.py
index 687a761..7729fd5 100644
--- a/tls_parser/heartbeat_protocol.py
+++ b/tls_parser/heartbeat_protocol.py
@@ -1,6 +1,3 @@
-from __future__ import absolute_import
-from __future__ import print_function
-
 import struct
 from enum import IntEnum
 from tls_parser.record_protocol import TlsSubprotocolMessage, TlsRecord, TlsRecordHeader, TlsRecordTypeByte
@@ -14,29 +11,26 @@ class TlsHeartbeatTypeByte(IntEnum):
 
 
 class TlsHeartbeatMessage(TlsSubprotocolMessage):
+    def __init__(self, hearbeat_type: TlsHeartbeatTypeByte, heartbeat_data: bytes) -> None:
+        # Recreate the raw message as bytes
+        full_message_data = b""
+        # Heartbeat message type - 1 byte
+        full_message_data += struct.pack("B", hearbeat_type.value)
+        # Heartbeat message length - 2 bytes
+        full_message_data += struct.pack("!H", len(heartbeat_data))
+        # Heartbead message data
+        full_message_data += heartbeat_data
+        # Padding is not handled
+        super().__init__(full_message_data)
 
-    def __init__(self, hearbeat_type, heartbeat_data):
-        # type: (TlsHeartbeatTypeByte, bytes) -> None
+        # TODO(AD): Rename to self.hearbeat_type and self.heartbeat_data to mirror convention in the handshake protocol
         self.type = hearbeat_type
         self.data = heartbeat_data
 
     @classmethod
-    def from_bytes(cls, raw_bytes):
-        # type: (bytes) -> Tuple[TlsHeartbeatMessage, int]
+    def from_bytes(cls, raw_bytes: bytes) -> Tuple["TlsHeartbeatMessage", int]:
         raise NotImplementedError()
 
-    def to_bytes(self):
-        # type: () -> bytes
-        bytes = b''
-        # Heartbeat message type - 1 byte
-        bytes += struct.pack('B', self.type.value)
-        # Heartbeat message length - 2 bytes
-        bytes += struct.pack('!H', len(self.data))
-        # Heartbead message data
-        bytes += self.data
-        # Padding is not handled
-        return bytes
-
 
 class TlsHeartbeatRequestRecord(TlsRecord):
     """https://tools.ietf.org/html/rfc6520.
@@ -48,18 +42,15 @@ class TlsHeartbeatRequestRecord(TlsRecord):
     } HeartbeatMessage;
     """
 
-    def __init__(self, record_header, heartbeat_message):
-        # type: (TlsRecordHeader, TlsHeartbeatMessage) -> None
-        super(TlsHeartbeatRequestRecord, self).__init__(record_header, [heartbeat_message])
+    def __init__(self, record_header: TlsRecordHeader, heartbeat_message: TlsHeartbeatMessage) -> None:
+        super().__init__(record_header=record_header, subprotocol_messages=[heartbeat_message])
 
     @classmethod
-    def from_parameters(cls, tls_version, heartbeat_data):
-        # type: (TlsVersionEnum, bytes) -> TlsHeartbeatRequestRecord
+    def from_parameters(cls, tls_version: TlsVersionEnum, heartbeat_data: bytes) -> "TlsHeartbeatRequestRecord":
         message = TlsHeartbeatMessage(TlsHeartbeatTypeByte.REQUEST, heartbeat_data)
         record_header = TlsRecordHeader(TlsRecordTypeByte.HEARTBEAT, tls_version, message.size)
         return TlsHeartbeatRequestRecord(record_header, message)
 
     @classmethod
-    def from_bytes(cls, raw_bytes):
-        # type: (bytes) -> Tuple[TlsHeartbeatRequestRecord, int]
-        raise NotImplementedError()
\ No newline at end of file
+    def from_bytes(cls, raw_bytes: bytes) -> Tuple["TlsHeartbeatRequestRecord", int]:
+        raise NotImplementedError()
diff --git a/tls_parser/parser.py b/tls_parser/parser.py
index 3fcc734..dc5d4b0 100644
--- a/tls_parser/parser.py
+++ b/tls_parser/parser.py
@@ -5,11 +5,9 @@ from tls_parser.record_protocol import TlsRecord, TlsRecordHeader, TlsRecordType
 from typing import Tuple
 
 
-class TlsRecordParser(object):
-
+class TlsRecordParser:
     @staticmethod
-    def parse_bytes(raw_bytes):
-        # type: (bytes) -> Tuple[TlsRecord, int]
+    def parse_bytes(raw_bytes: bytes) -> Tuple[TlsRecord, int]:
         record_header, len_consumed = TlsRecordHeader.from_bytes(raw_bytes)
 
         # Try to parse the record
diff --git a/tls_parser/record_protocol.py b/tls_parser/record_protocol.py
index 94fe103..3810d43 100644
--- a/tls_parser/record_protocol.py
+++ b/tls_parser/record_protocol.py
@@ -1,19 +1,16 @@
-from __future__ import absolute_import
-from __future__ import print_function
-
 import struct
 from enum import Enum
 from enum import IntEnum
 from tls_parser.exceptions import NotEnoughData, UnknownTypeByte, UnknownTlsVersionByte
 from tls_parser.tls_version import TlsVersionEnum
-from typing import Tuple, List
+from typing import Tuple, Sequence
 
 
 class TlsRecordTlsVersionBytes(Enum):
-    SSLV3 = b'\x03\x00'
-    TLSV1 = b'\x03\x01'
-    TLSV1_1 = b'\x03\x02'
-    TLSV1_2 = b'\x03\x03'
+    SSLV3 = b"\x03\x00"
+    TLSV1 = b"\x03\x01"
+    TLSV1_1 = b"\x03\x02"
+    TLSV1_2 = b"\x03\x03"
 
 
 class TlsRecordTypeByte(IntEnum):
@@ -24,63 +21,72 @@ class TlsRecordTypeByte(IntEnum):
     HEARTBEAT = 0x18
 
 
-class TlsRecordHeader(object):
-    def __init__(self, record_type, tls_version, record_length):
-        # type: (TlsRecordTypeByte, TlsVersionEnum, int) -> None
+class TlsRecordHeader:
+    def __init__(self, record_type: TlsRecordTypeByte, tls_version: TlsVersionEnum, record_length: int) -> None:
         self.type = record_type
         self.tls_version = tls_version
         self.length = record_length
 
     @classmethod
-    def from_bytes(cls, raw_bytes):
-        # type: (bytes) -> Tuple[TlsRecordHeader, int]
+    def from_bytes(cls, raw_bytes: bytes) -> Tuple["TlsRecordHeader", int]:
         if len(raw_bytes) < 5:
             raise NotEnoughData()
 
-        record_type = TlsRecordTypeByte(struct.unpack('B', raw_bytes[0:1])[0])
+        record_type = TlsRecordTypeByte(struct.unpack("B", raw_bytes[0:1])[0])
 
         try:
             tls_version = TlsRecordTlsVersionBytes(raw_bytes[1:3])
         except ValueError as e:
             raise UnknownTlsVersionByte(e.args[0], record_type)
 
-        record_length = struct.unpack('!H', raw_bytes[3:5])[0]
+        record_length = struct.unpack("!H", raw_bytes[3:5])[0]
         return TlsRecordHeader(record_type, TlsVersionEnum[tls_version.name], record_length), 5
 
-    def to_bytes(self):
-        # type: () -> bytes
-        bytes = b''
+    def to_bytes(self) -> bytes:
+        as_bytes = b""
         # TLS Record type - 1 byte
-        bytes += struct.pack('B', self.type.value)
+        as_bytes += struct.pack("B", self.type.value)
         # TLS version - 2 bytes
-        bytes += TlsRecordTlsVersionBytes[self.tls_version.name].value
+        as_bytes += TlsRecordTlsVersionBytes[self.tls_version.name].value
         # Length - 2 bytes
-        bytes += struct.pack('!H', self.length)
-        return bytes
+        as_bytes += struct.pack("!H", self.length)
+        return as_bytes
+
+
+class TlsSubprotocolMessage:
+    # Handshake, Alert, etc.
+
+    def __init__(self, message_data: bytes) -> None:
+        self.message_data = message_data
+
+    def to_bytes(self) -> bytes:
+        return self.message_data
+
+    @property
+    def size(self) -> int:
+        return len(self.to_bytes())
 
 
-class TlsRecord(object):
-    def __init__(self, record_header, subprotocol_messages):
-        # type: (TlsRecordHeader, List[TlsSubprotocolMessage]) -> None
+class TlsRecord:
+    def __init__(self, record_header: TlsRecordHeader, subprotocol_messages: Sequence[TlsSubprotocolMessage]) -> None:
         self.header = record_header
 
         # Several messages can be concatenated into a single record; the messages must belong to the same subprotocol
         # Hence, in practice this only seems to apply to the handshake protocol
         if self.header.type != TlsRecordTypeByte.HANDSHAKE and len(subprotocol_messages) != 1:
-            raise ValueError('Received multiple subprotocol messages for a non-handshake record')
+            raise ValueError("Received multiple subprotocol messages for a non-handshake record")
 
         self.subprotocol_messages = subprotocol_messages
 
     @classmethod
-    def from_bytes(cls, raw_bytes):
-        # type: (bytes) -> Tuple[TlsRecord, int]
+    def from_bytes(cls, raw_bytes: bytes) -> Tuple["TlsRecord", int]:
         record_header, len_consumed = TlsRecordHeader.from_bytes(raw_bytes)
 
         # Try to parse the record
         if record_header.type not in TlsRecordTypeByte:
             raise UnknownTypeByte()
 
-        record_data = raw_bytes[len_consumed:len_consumed+record_header.length]
+        record_data = raw_bytes[len_consumed : len_consumed + record_header.length]  # noqa: E203
         if len(record_data) < record_header.length:
             raise NotEnoughData()
 
@@ -88,27 +94,9 @@ class TlsRecord(object):
         message = TlsSubprotocolMessage(record_data)
         return TlsRecord(record_header, [message]), len_consumed + record_header.length
 
-    def to_bytes(self):
-        # type: () -> bytes
-        bytes = b''
-        bytes += self.header.to_bytes()
+    def to_bytes(self) -> bytes:
+        as_bytes = b""
+        as_bytes += self.header.to_bytes()
         for message in self.subprotocol_messages:
-            bytes += message.to_bytes()
-        return bytes
-
-
-class TlsSubprotocolMessage(object):
-    # Handshake, Alert, etc.
-
-    def __init__(self, message_data):
-        # type: (bytes) -> None
-        self.message_data = message_data
-
-    def to_bytes(self):
-        # type: () -> bytes
-        return self.message_data
-
-    @property
-    def size(self):
-        # type: () -> int
-        return len(self.to_bytes())
+            as_bytes += message.to_bytes()
+        return as_bytes
diff --git a/tls_parser/tls_version.py b/tls_parser/tls_version.py
index fb795c9..d111bb1 100644
--- a/tls_parser/tls_version.py
+++ b/tls_parser/tls_version.py
@@ -1,6 +1,3 @@
-from __future__ import absolute_import
-from __future__ import print_function
-
 from enum import Enum