diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml
new file mode 100644
index 0000000..42bfcfe
--- /dev/null
+++ b/.github/workflows/python-package.yml
@@ -0,0 +1,32 @@
+name: Run tests
+
+on: push
+
+jobs:
+  build:
+
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        python-version: [3.7, 3.8]
+
+    steps:
+    - uses: actions/checkout@v2
+    - name: Set up Python ${{ matrix.python-version }}
+      uses: actions/setup-python@v2
+      with:
+        python-version: ${{ matrix.python-version }}
+    - name: Install dependencies
+      run: |
+        python -m pip install --upgrade pip pipenv
+    - name: Test module setup
+      run: |
+        python setup.py install
+        cd tests  # Switch folder to avoid conflicts between ./tls_parser and the installed tls_parser module
+        python ../sample.py
+        cd ..
+        pip uninstall -y tls-parser
+    - name: Run tests and linters
+      run: |
+        pipenv install --dev
+        pipenv run invoke test
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/Pipfile b/Pipfile
new file mode 100644
index 0000000..41767c3
--- /dev/null
+++ b/Pipfile
@@ -0,0 +1,17 @@
+[[source]]
+name = "pypi"
+url = "https://pypi.org/simple"
+verify_ssl = true
+
+[dev-packages]
+flake8 = "*"
+mypy = "*"
+black = "==19.10b0"
+pytest = "*"
+invoke = "*"
+pytest-cov = "*"
+
+[packages]
+
+[requires]
+python_version = "3.7"
diff --git a/Pipfile.lock b/Pipfile.lock
new file mode 100644
index 0000000..7698dff
--- /dev/null
+++ b/Pipfile.lock
@@ -0,0 +1,321 @@
+{
+    "_meta": {
+        "hash": {
+            "sha256": "ba77556407095cee54813dc022898092df8f2beeba5f20e4f91d47d14644b7a1"
+        },
+        "pipfile-spec": 6,
+        "requires": {
+            "python_version": "3.7"
+        },
+        "sources": [
+            {
+                "name": "pypi",
+                "url": "https://pypi.org/simple",
+                "verify_ssl": true
+            }
+        ]
+    },
+    "default": {},
+    "develop": {
+        "appdirs": {
+            "hashes": [
+                "sha256:7d5d0167b2b1ba821647616af46a749d1c653740dd0d2415100fe26e27afdf41",
+                "sha256:a841dacd6b99318a741b166adb07e19ee71a274450e68237b4650ca1055ab128"
+            ],
+            "version": "==1.4.4"
+        },
+        "atomicwrites": {
+            "hashes": [
+                "sha256:6d1784dea7c0c8d4a5172b6c620f40b6e4cbfdf96d783691f2e1302a7b88e197",
+                "sha256:ae70396ad1a434f9c7046fd2dd196fc04b12f9e91ffb859164193be8b6168a7a"
+            ],
+            "markers": "sys_platform == 'win32'",
+            "version": "==1.4.0"
+        },
+        "attrs": {
+            "hashes": [
+                "sha256:08a96c641c3a74e44eb59afb61a24f2cb9f4d7188748e76ba4bb5edfa3cb7d1c",
+                "sha256:f7b7ce16570fe9965acd6d30101a28f62fb4a7f9e926b3bbc9b61f8b04247e72"
+            ],
+            "version": "==19.3.0"
+        },
+        "black": {
+            "hashes": [
+                "sha256:1b30e59be925fafc1ee4565e5e08abef6b03fe455102883820fe5ee2e4734e0b",
+                "sha256:c2edb73a08e9e0e6f65a0e6af18b059b8b1cdd5bef997d7a0b181df93dc81539"
+            ],
+            "index": "pypi",
+            "version": "==19.10b0"
+        },
+        "click": {
+            "hashes": [
+                "sha256:d2b5255c7c6349bc1bd1e59e08cd12acbbd63ce649f2588755783aa94dfb6b1a",
+                "sha256:dacca89f4bfadd5de3d7489b7c8a566eee0d3676333fbb50030263894c38c0dc"
+            ],
+            "version": "==7.1.2"
+        },
+        "colorama": {
+            "hashes": [
+                "sha256:7d73d2a99753107a36ac6b455ee49046802e59d9d076ef8e47b61499fa29afff",
+                "sha256:e96da0d330793e2cb9485e9ddfd918d456036c7149416295932478192f4436a1"
+            ],
+            "markers": "sys_platform == 'win32'",
+            "version": "==0.4.3"
+        },
+        "coverage": {
+            "hashes": [
+                "sha256:00f1d23f4336efc3b311ed0d807feb45098fc86dee1ca13b3d6768cdab187c8a",
+                "sha256:01333e1bd22c59713ba8a79f088b3955946e293114479bbfc2e37d522be03355",
+                "sha256:0cb4be7e784dcdc050fc58ef05b71aa8e89b7e6636b99967fadbdba694cf2b65",
+                "sha256:0e61d9803d5851849c24f78227939c701ced6704f337cad0a91e0972c51c1ee7",
+                "sha256:1601e480b9b99697a570cea7ef749e88123c04b92d84cedaa01e117436b4a0a9",
+                "sha256:2742c7515b9eb368718cd091bad1a1b44135cc72468c731302b3d641895b83d1",
+                "sha256:2d27a3f742c98e5c6b461ee6ef7287400a1956c11421eb574d843d9ec1f772f0",
+                "sha256:402e1744733df483b93abbf209283898e9f0d67470707e3c7516d84f48524f55",
+                "sha256:5c542d1e62eece33c306d66fe0a5c4f7f7b3c08fecc46ead86d7916684b36d6c",
+                "sha256:5f2294dbf7875b991c381e3d5af2bcc3494d836affa52b809c91697449d0eda6",
+                "sha256:6402bd2fdedabbdb63a316308142597534ea8e1895f4e7d8bf7476c5e8751fef",
+                "sha256:66460ab1599d3cf894bb6baee8c684788819b71a5dc1e8fa2ecc152e5d752019",
+                "sha256:782caea581a6e9ff75eccda79287daefd1d2631cc09d642b6ee2d6da21fc0a4e",
+                "sha256:79a3cfd6346ce6c13145731d39db47b7a7b859c0272f02cdb89a3bdcbae233a0",
+                "sha256:7a5bdad4edec57b5fb8dae7d3ee58622d626fd3a0be0dfceda162a7035885ecf",
+                "sha256:8fa0cbc7ecad630e5b0f4f35b0f6ad419246b02bc750de7ac66db92667996d24",
+                "sha256:a027ef0492ede1e03a8054e3c37b8def89a1e3c471482e9f046906ba4f2aafd2",
+                "sha256:a3f3654d5734a3ece152636aad89f58afc9213c6520062db3978239db122f03c",
+                "sha256:a82b92b04a23d3c8a581fc049228bafde988abacba397d57ce95fe95e0338ab4",
+                "sha256:acf3763ed01af8410fc36afea23707d4ea58ba7e86a8ee915dfb9ceff9ef69d0",
+                "sha256:adeb4c5b608574a3d647011af36f7586811a2c1197c861aedb548dd2453b41cd",
+                "sha256:b83835506dfc185a319031cf853fa4bb1b3974b1f913f5bb1a0f3d98bdcded04",
+                "sha256:bb28a7245de68bf29f6fb199545d072d1036a1917dca17a1e75bbb919e14ee8e",
+                "sha256:bf9cb9a9fd8891e7efd2d44deb24b86d647394b9705b744ff6f8261e6f29a730",
+                "sha256:c317eaf5ff46a34305b202e73404f55f7389ef834b8dbf4da09b9b9b37f76dd2",
+                "sha256:dbe8c6ae7534b5b024296464f387d57c13caa942f6d8e6e0346f27e509f0f768",
+                "sha256:de807ae933cfb7f0c7d9d981a053772452217df2bf38e7e6267c9cbf9545a796",
+                "sha256:dead2ddede4c7ba6cb3a721870f5141c97dc7d85a079edb4bd8d88c3ad5b20c7",
+                "sha256:dec5202bfe6f672d4511086e125db035a52b00f1648d6407cc8e526912c0353a",
+                "sha256:e1ea316102ea1e1770724db01998d1603ed921c54a86a2efcb03428d5417e489",
+                "sha256:f90bfc4ad18450c80b024036eaf91e4a246ae287701aaa88eaebebf150868052"
+            ],
+            "version": "==5.1"
+        },
+        "flake8": {
+            "hashes": [
+                "sha256:15e351d19611c887e482fb960eae4d44845013cc142d42896e9862f775d8cf5c",
+                "sha256:f04b9fcbac03b0a3e58c0ab3a0ecc462e023a9faf046d57794184028123aa208"
+            ],
+            "index": "pypi",
+            "version": "==3.8.3"
+        },
+        "importlib-metadata": {
+            "hashes": [
+                "sha256:0505dd08068cfec00f53a74a0ad927676d7757da81b7436a6eefe4c7cf75c545",
+                "sha256:15ec6c0fd909e893e3a08b3a7c76ecb149122fb14b7efe1199ddd4c7c57ea958"
+            ],
+            "markers": "python_version < '3.8'",
+            "version": "==1.6.1"
+        },
+        "invoke": {
+            "hashes": [
+                "sha256:87b3ef9d72a1667e104f89b159eaf8a514dbf2f3576885b2bbdefe74c3fb2132",
+                "sha256:93e12876d88130c8e0d7fd6618dd5387d6b36da55ad541481dfa5e001656f134",
+                "sha256:de3f23bfe669e3db1085789fd859eb8ca8e0c5d9c20811e2407fa042e8a5e15d"
+            ],
+            "index": "pypi",
+            "version": "==1.4.1"
+        },
+        "mccabe": {
+            "hashes": [
+                "sha256:ab8a6258860da4b6677da4bd2fe5dc2c659cff31b3ee4f7f5d64e79735b80d42",
+                "sha256:dd8d182285a0fe56bace7f45b5e7d1a6ebcbf524e8f3bd87eb0f125271b8831f"
+            ],
+            "version": "==0.6.1"
+        },
+        "more-itertools": {
+            "hashes": [
+                "sha256:68c70cc7167bdf5c7c9d8f6954a7837089c6a36bf565383919bb595efb8a17e5",
+                "sha256:b78134b2063dd214000685165d81c154522c3ee0a1c0d4d113c80361c234c5a2"
+            ],
+            "version": "==8.4.0"
+        },
+        "mypy": {
+            "hashes": [
+                "sha256:1fe322a51df7ec60e4060c358422732359dda4467c1bd240f2172433b26b39be",
+                "sha256:2bc1fe8f793f1b1809afd6f57c2d9b948e1bf525a78e4c2957392a383c86a4a4",
+                "sha256:43335f3ff3288eb877de6d186ec08e10ea61093405189678756e14c7ccee4a9b",
+                "sha256:5c75603ea44bffad0356df333bd1b411facad321e0692d6b0687d65d94fcd8e1",
+                "sha256:738a8df84da4e9ce1f59cbdebc7d1d03310149405df9a95308d4f2a62a6d7c13",
+                "sha256:845abd8a9537da01e6b96f091e2d6d4ece18e52339f81f9fbbac1b6db2aa8d2d",
+                "sha256:94bb664868b5cf4ca1147d875a4c77883d8c605cf2e916853006e4c6194f1e84",
+                "sha256:b0c6ae0cb991a7a11013d0cc7edf8343184465b6e2dcbb9e44265c7bb3fde3af",
+                "sha256:ca56382e6ebdeb3cb928ae4ce87031cc752a1eca40ff86abc14dc712def41798",
+                "sha256:d6e9611ae026be70604672cd71bee468cb231078d8d9b3d6b43f8dcbd4d9b776",
+                "sha256:d7c9255ba6626e1745bd68e1b85e3d7888844eaf38252fefb8157194e55fd3e9",
+                "sha256:e2f193a2076e4508a88c93c25348a1cea5e6b717ee50b4422a001e9ac819c3d5",
+                "sha256:e60674723cad7b7c7fc4e9075f7a9d5d927d23d290e30cf86aeb987ef135ca1d",
+                "sha256:ec45f2a5935b291d86974a24e09676e467ac108d0c7ce94de44d7650c43a5805"
+            ],
+            "index": "pypi",
+            "version": "==0.781"
+        },
+        "mypy-extensions": {
+            "hashes": [
+                "sha256:090fedd75945a69ae91ce1303b5824f428daf5a028d2f6ab8a299250a846f15d",
+                "sha256:2d82818f5bb3e369420cb3c4060a7970edba416647068eb4c5343488a6c604a8"
+            ],
+            "version": "==0.4.3"
+        },
+        "packaging": {
+            "hashes": [
+                "sha256:4357f74f47b9c12db93624a82154e9b120fa8293699949152b22065d556079f8",
+                "sha256:998416ba6962ae7fbd6596850b80e17859a5753ba17c32284f67bfff33784181"
+            ],
+            "version": "==20.4"
+        },
+        "pathspec": {
+            "hashes": [
+                "sha256:7d91249d21749788d07a2d0f94147accd8f845507400749ea19c1ec9054a12b0",
+                "sha256:da45173eb3a6f2a5a487efba21f050af2b41948be6ab52b6a1e3ff22bb8b7061"
+            ],
+            "version": "==0.8.0"
+        },
+        "pluggy": {
+            "hashes": [
+                "sha256:15b2acde666561e1298d71b523007ed7364de07029219b604cf808bfa1c765b0",
+                "sha256:966c145cd83c96502c3c3868f50408687b38434af77734af1e9ca461a4081d2d"
+            ],
+            "version": "==0.13.1"
+        },
+        "py": {
+            "hashes": [
+                "sha256:a673fa23d7000440cc885c17dbd34fafcb7d7a6e230b29f6766400de36a33c44",
+                "sha256:f3b3a4c36512a4c4f024041ab51866f11761cc169670204b235f6b20523d4e6b"
+            ],
+            "version": "==1.8.2"
+        },
+        "pycodestyle": {
+            "hashes": [
+                "sha256:2295e7b2f6b5bd100585ebcb1f616591b652db8a741695b3d8f5d28bdc934367",
+                "sha256:c58a7d2815e0e8d7972bf1803331fb0152f867bd89adf8a01dfd55085434192e"
+            ],
+            "version": "==2.6.0"
+        },
+        "pyflakes": {
+            "hashes": [
+                "sha256:0d94e0e05a19e57a99444b6ddcf9a6eb2e5c68d3ca1e98e90707af8152c90a92",
+                "sha256:35b2d75ee967ea93b55750aa9edbbf72813e06a66ba54438df2cfac9e3c27fc8"
+            ],
+            "version": "==2.2.0"
+        },
+        "pyparsing": {
+            "hashes": [
+                "sha256:c203ec8783bf771a155b207279b9bccb8dea02d8f0c9e5f8ead507bc3246ecc1",
+                "sha256:ef9d7589ef3c200abe66653d3f1ab1033c3c419ae9b9bdb1240a85b024efc88b"
+            ],
+            "version": "==2.4.7"
+        },
+        "pytest": {
+            "hashes": [
+                "sha256:5c0db86b698e8f170ba4582a492248919255fcd4c79b1ee64ace34301fb589a1",
+                "sha256:7979331bfcba207414f5e1263b5a0f8f521d0f457318836a7355531ed1a4c7d8"
+            ],
+            "index": "pypi",
+            "version": "==5.4.3"
+        },
+        "pytest-cov": {
+            "hashes": [
+                "sha256:1a629dc9f48e53512fcbfda6b07de490c374b0c83c55ff7a1720b3fccff0ac87",
+                "sha256:6e6d18092dce6fad667cd7020deed816f858ad3b49d5b5e2b1cc1c97a4dba65c"
+            ],
+            "index": "pypi",
+            "version": "==2.10.0"
+        },
+        "regex": {
+            "hashes": [
+                "sha256:08997a37b221a3e27d68ffb601e45abfb0093d39ee770e4257bd2f5115e8cb0a",
+                "sha256:112e34adf95e45158c597feea65d06a8124898bdeac975c9087fe71b572bd938",
+                "sha256:1700419d8a18c26ff396b3b06ace315b5f2a6e780dad387e4c48717a12a22c29",
+                "sha256:2f6f211633ee8d3f7706953e9d3edc7ce63a1d6aad0be5dcee1ece127eea13ae",
+                "sha256:52e1b4bef02f4040b2fd547357a170fc1146e60ab310cdbdd098db86e929b387",
+                "sha256:55b4c25cbb3b29f8d5e63aeed27b49fa0f8476b0d4e1b3171d85db891938cc3a",
+                "sha256:5aaa5928b039ae440d775acea11d01e42ff26e1561c0ffcd3d805750973c6baf",
+                "sha256:654cb773b2792e50151f0e22be0f2b6e1c3a04c5328ff1d9d59c0398d37ef610",
+                "sha256:690f858d9a94d903cf5cada62ce069b5d93b313d7d05456dbcd99420856562d9",
+                "sha256:6ad8663c17db4c5ef438141f99e291c4d4edfeaacc0ce28b5bba2b0bf273d9b5",
+                "sha256:89cda1a5d3e33ec9e231ece7307afc101b5217523d55ef4dc7fb2abd6de71ba3",
+                "sha256:92d8a043a4241a710c1cf7593f5577fbb832cf6c3a00ff3fc1ff2052aff5dd89",
+                "sha256:95fa7726d073c87141f7bbfb04c284901f8328e2d430eeb71b8ffdd5742a5ded",
+                "sha256:97712e0d0af05febd8ab63d2ef0ab2d0cd9deddf4476f7aa153f76feef4b2754",
+                "sha256:b2ba0f78b3ef375114856cbdaa30559914d081c416b431f2437f83ce4f8b7f2f",
+                "sha256:bae83f2a56ab30d5353b47f9b2a33e4aac4de9401fb582b55c42b132a8ac3868",
+                "sha256:c78e66a922de1c95a208e4ec02e2e5cf0bb83a36ceececc10a72841e53fbf2bd",
+                "sha256:cf59bbf282b627130f5ba68b7fa3abdb96372b24b66bdf72a4920e8153fc7910",
+                "sha256:e3cdc9423808f7e1bb9c2e0bdb1c9dc37b0607b30d646ff6faf0d4e41ee8fee3",
+                "sha256:e9b64e609d37438f7d6e68c2546d2cb8062f3adb27e6336bc129b51be20773ac",
+                "sha256:fbff901c54c22425a5b809b914a3bfaf4b9570eee0e5ce8186ac71eb2025191c"
+            ],
+            "version": "==2020.6.8"
+        },
+        "six": {
+            "hashes": [
+                "sha256:30639c035cdb23534cd4aa2dd52c3bf48f06e5f4a941509c8bafd8ce11080259",
+                "sha256:8b74bedcbbbaca38ff6d7491d76f2b06b3592611af620f8426e82dddb04a5ced"
+            ],
+            "version": "==1.15.0"
+        },
+        "toml": {
+            "hashes": [
+                "sha256:926b612be1e5ce0634a2ca03470f95169cf16f939018233a670519cb4ac58b0f",
+                "sha256:bda89d5935c2eac546d648028b9901107a595863cb36bae0c73ac804a9b4ce88"
+            ],
+            "version": "==0.10.1"
+        },
+        "typed-ast": {
+            "hashes": [
+                "sha256:0666aa36131496aed8f7be0410ff974562ab7eeac11ef351def9ea6fa28f6355",
+                "sha256:0c2c07682d61a629b68433afb159376e24e5b2fd4641d35424e462169c0a7919",
+                "sha256:249862707802d40f7f29f6e1aad8d84b5aa9e44552d2cc17384b209f091276aa",
+                "sha256:24995c843eb0ad11a4527b026b4dde3da70e1f2d8806c99b7b4a7cf491612652",
+                "sha256:269151951236b0f9a6f04015a9004084a5ab0d5f19b57de779f908621e7d8b75",
+                "sha256:4083861b0aa07990b619bd7ddc365eb7fa4b817e99cf5f8d9cf21a42780f6e01",
+                "sha256:498b0f36cc7054c1fead3d7fc59d2150f4d5c6c56ba7fb150c013fbc683a8d2d",
+                "sha256:4e3e5da80ccbebfff202a67bf900d081906c358ccc3d5e3c8aea42fdfdfd51c1",
+                "sha256:6daac9731f172c2a22ade6ed0c00197ee7cc1221aa84cfdf9c31defeb059a907",
+                "sha256:715ff2f2df46121071622063fc7543d9b1fd19ebfc4f5c8895af64a77a8c852c",
+                "sha256:73d785a950fc82dd2a25897d525d003f6378d1cb23ab305578394694202a58c3",
+                "sha256:8c8aaad94455178e3187ab22c8b01a3837f8ee50e09cf31f1ba129eb293ec30b",
+                "sha256:8ce678dbaf790dbdb3eba24056d5364fb45944f33553dd5869b7580cdbb83614",
+                "sha256:aaee9905aee35ba5905cfb3c62f3e83b3bec7b39413f0a7f19be4e547ea01ebb",
+                "sha256:bcd3b13b56ea479b3650b82cabd6b5343a625b0ced5429e4ccad28a8973f301b",
+                "sha256:c9e348e02e4d2b4a8b2eedb48210430658df6951fa484e59de33ff773fbd4b41",
+                "sha256:d205b1b46085271b4e15f670058ce182bd1199e56b317bf2ec004b6a44f911f6",
+                "sha256:d43943ef777f9a1c42bf4e552ba23ac77a6351de620aa9acf64ad54933ad4d34",
+                "sha256:d5d33e9e7af3b34a40dc05f498939f0ebf187f07c385fd58d591c533ad8562fe",
+                "sha256:fc0fea399acb12edbf8a628ba8d2312f583bdbdb3335635db062fa98cf71fca4",
+                "sha256:fe460b922ec15dd205595c9b5b99e2f056fd98ae8f9f56b888e7a17dc2b757e7"
+            ],
+            "version": "==1.4.1"
+        },
+        "typing-extensions": {
+            "hashes": [
+                "sha256:6e95524d8a547a91e08f404ae485bbb71962de46967e1b71a0cb89af24e761c5",
+                "sha256:79ee589a3caca649a9bfd2a8de4709837400dfa00b6cc81962a1e6a1815969ae",
+                "sha256:f8d2bd89d25bc39dabe7d23df520442fa1d8969b82544370e03d88b5a591c392"
+            ],
+            "version": "==3.7.4.2"
+        },
+        "wcwidth": {
+            "hashes": [
+                "sha256:79375666b9954d4a1a10739315816324c3e73110af9d0e102d906fdb0aec009f",
+                "sha256:8c6b5b6ee1360b842645f336d9e5d68c55817c26d3050f46b235ef2bc650e48f"
+            ],
+            "version": "==0.2.4"
+        },
+        "zipp": {
+            "hashes": [
+                "sha256:aa36550ff0c0b7ef7fa639055d797116ee891440eac1a56f378e2d3179e0320b",
+                "sha256:c599e4d75c98f6798c509911d08a22e6c021d074469042177c8c86fb92eefd96"
+            ],
+            "version": "==3.1.0"
+        }
+    }
+}
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/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/sample.py b/sample.py
new file mode 100644
index 0000000..d80be81
--- /dev/null
+++ b/sample.py
@@ -0,0 +1,16 @@
+from tls_parser.handshake_protocol import TlsHandshakeRecord
+
+
+# Parse a server hello message
+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"
+)
+
+parsed_record, len_consumed = TlsHandshakeRecord.from_bytes(SERVER_HELLO_BYTES)
+print(parsed_record.header.tls_version)
+print(parsed_record.header.type)
+print(parsed_record.header.length)
+print(parsed_record.subprotocol_messages[0].handshake_type)
diff --git a/setup.cfg b/setup.cfg
index b88034e..d7b5075 100644
--- a/setup.cfg
+++ b/setup.cfg
@@ -1,2 +1,14 @@
 [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
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/tasks.py b/tasks.py
new file mode 100644
index 0000000..d108573
--- /dev/null
+++ b/tasks.py
@@ -0,0 +1,13 @@
+from invoke import task, Context
+
+
+@task
+def test(ctx):
+    # type: (Context) -> None
+    # Run the test suite
+    ctx.run("pytest --cov=tls_parser --cov-fail-under 80")
+
+    # Run linters
+    ctx.run("flake8 .")
+    ctx.run("mypy .")
+    ctx.run("black -l 120 tls_parser tests tasks.py --check")
diff --git a/tests/test_alert_protocol.py b/tests/test_alert_protocol.py
index e3801f8..3a533fe 100644
--- a/tests/test_alert_protocol.py
+++ b/tests/test_alert_protocol.py
@@ -1,23 +1,20 @@
-from __future__ import absolute_import
-from __future__ import print_function
+import pytest
 
-import unittest
 from tls_parser.alert_protocol import TlsAlertRecord, TlsAlertSeverityByte
 from tls_parser.exceptions import UnknownTlsVersionByte
 
 
-class TlsAlertRecordTestCase(unittest.TestCase):
-
+class TestTlsAlertRecord:
     def test_from_bytes(self):
-        alert_bytes = b'\x15\x03\x03\x00\x02\x02\x14'
+        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))
+        assert parsed_record.alert_severity == TlsAlertSeverityByte.FATAL
+        assert parsed_record.alert_description == 0x14
+        assert 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):
+        alert_bytes_with_bad_version = b"\x15\x00\x00\x00\x02\x02("
+        with pytest.raises(UnknownTlsVersionByte):
             TlsAlertRecord.from_bytes(alert_bytes_with_bad_version)
diff --git a/tests/test_handshake_protocol.py b/tests/test_handshake_protocol.py
index 7eaf8d6..b58f9c7 100644
--- a/tests/test_handshake_protocol.py
+++ b/tests/test_handshake_protocol.py
@@ -1,78 +1,77 @@
-from __future__ import absolute_import
-from __future__ import print_function
-
-import unittest
-
 import binascii
 import math
 
+from tls_parser.cipher_suites import CipherSuites
 from tls_parser.record_protocol import TlsRecordTlsVersionBytes
 from tls_parser.tls_version import TlsVersionEnum
 
-from tls_parser.handshake_protocol import TlsHandshakeRecord, TlsHandshakeTypeByte,  \
-    TlsRsaClientKeyExchangeRecord
+from tls_parser.handshake_protocol import TlsHandshakeRecord, TlsHandshakeTypeByte, TlsRsaClientKeyExchangeRecord
 
 
-class TlsHandshakeRecordTestCase(unittest.TestCase):
+class TestTlsHandshakeRecord:
 
-    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'
+    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))
+        assert len(parsed_record.subprotocol_messages) == 1
+        assert parsed_record.subprotocol_messages[0].handshake_type == TlsHandshakeTypeByte.SERVER_HELLO
+        assert 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'
+    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'  # noqa: E501
 
     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))
+        assert len(parsed_record.subprotocol_messages) == 1
+        assert parsed_record.subprotocol_messages[0].handshake_type == TlsHandshakeTypeByte.CERTIFICATE
+        assert len_consumed == len(self.SERVER_CERTIFICATE_BYTES)
 
-    SERVER_HELLO_DONE_BYTES = b'\x16\x03\x03\x00\x04\x0e\x00\x00\x00'
+    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))
+        assert len(parsed_record.subprotocol_messages) == 1
+        assert parsed_record.subprotocol_messages[0].handshake_type == TlsHandshakeTypeByte.SERVER_DONE
+        assert 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'
+    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'  # noqa: E501
 
     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'
+        assert len(parsed_record.subprotocol_messages) == 4
+        assert parsed_record.subprotocol_messages[0].handshake_type == TlsHandshakeTypeByte.SERVER_HELLO
+        assert parsed_record.subprotocol_messages[1].handshake_type == TlsHandshakeTypeByte.CERTIFICATE
+        assert parsed_record.subprotocol_messages[2].handshake_type == TlsHandshakeTypeByte.SERVER_KEY_EXCHANGE
+        assert parsed_record.subprotocol_messages[3].handshake_type == TlsHandshakeTypeByte.SERVER_DONE
+        assert 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 = (
+        "0x9944d2fd7dbdc447a1de74002c4746dbf99d0d64e24a0ccea319f04af4b71acb1eeefcfb8ff904f689977006d113365b503e58b7fea2"
+        "cbe985294775c00d5da171d3bf227b71f80223c1f55c141c5a892eefcf8f4d36ddb7b2ed179fb3a48498ea57dd80e09b6c8d64553b15bd"
+        "c92a1465692965d5f1e65d85a679466577fdf8302100c2a07af0b8dd1fc2f63f52e8f220ee0f4ef33a2a6b94e20ac82cf8d1073705f09f"
+        "7a506ead5ba6271e60e80aa32a369a614bd1fa92268f504a6b7da72ad870aca13f543c00d7725d082f796e5f6c6eee02ecd860388360ce"
+        "95a2ba4057cbb0053af32209b88d921d1c2d4569f806c44132214a6178242d2486b643e773"
+    )
+
+    EXPONENT_HEX = "0x10001"
 
     def test_rsa_client_key_exchange_to_bytes(self):
         # Inspired by https://github.com/robotattackorg/robot-detect
@@ -81,14 +80,23 @@ class TlsHandshakeRecordTestCase(unittest.TestCase):
         modulus_bit_size = int(math.ceil(math.log(modulus, 2)))
         modulus_byte_size = (modulus_bit_size + 7) // 8
 
-        pre_master_secret = "aa112233445566778899112233445566778899112233445566778899112233445566778899112233445566778899"
+        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')
+        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
+        record = TlsRsaClientKeyExchangeRecord.from_parameters(
+            TlsVersionEnum.TLSV1_2, exponent, modulus, pms_with_padding
+        )
+        assert record.to_bytes() == self.EXPECTED_CLIENT_KEY_EXCHANGE_BYTES
+
+
+class TestCipherSuites:
+    def test(self):
+        # Ensure the CipherSuites enum is importable and usable
+        assert CipherSuites.TLS_DH_anon_EXPORT_WITH_RC4_40_MD5.value
diff --git a/tests/test_heartbeat_protocol.py b/tests/test_heartbeat_protocol.py
index 49b4dd8..6e67ee0 100644
--- a/tests/test_heartbeat_protocol.py
+++ b/tests/test_heartbeat_protocol.py
@@ -1,14 +1,8 @@
-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):
-
+class TestTlsHeartbeatRequestRecord:
     def test_to_bytes(self):
-        record = TlsHeartbeatRequestRecord.from_parameters(TlsVersionEnum.TLSV1_2, b'123456')
-        self.assertTrue(record.to_bytes())
+        record = TlsHeartbeatRequestRecord.from_parameters(TlsVersionEnum.TLSV1_2, b"123456")
+        assert record.to_bytes()
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