├── .github └── workflows │ └── python-package.yml ├── .gitignore ├── LICENSE ├── README.md ├── requirements-dev.txt ├── sample.py ├── setup.cfg ├── setup.py ├── tasks.py ├── tests ├── __init__.py ├── test_alert_protocol.py ├── test_handshake_protocol.py └── test_heartbeat_protocol.py └── tls_parser ├── __init__.py ├── alert_protocol.py ├── application_data_protocol.py ├── change_cipher_spec_protocol.py ├── cipher_suites.py ├── exceptions.py ├── handshake_protocol.py ├── heartbeat_protocol.py ├── parser.py ├── record_protocol.py └── tls_version.py /.github/workflows/python-package.yml: -------------------------------------------------------------------------------- 1 | name: Run tests 2 | 3 | on: push 4 | 5 | jobs: 6 | build: 7 | 8 | runs-on: ubuntu-latest 9 | strategy: 10 | matrix: 11 | python-version: [3.8, 3.9, "3.10", "3.11", "3.12", "3.13"] 12 | 13 | steps: 14 | - uses: actions/checkout@v2 15 | - name: Set up Python ${{ matrix.python-version }} 16 | uses: actions/setup-python@v2 17 | with: 18 | python-version: ${{ matrix.python-version }} 19 | - name: Install dependencies 20 | run: | 21 | python -m pip install --upgrade pip setuptools wheel 22 | - name: Test module setup 23 | run: | 24 | python setup.py install 25 | cd tests # Switch folder to avoid conflicts between ./tls_parser and the installed tls_parser module 26 | python ../sample.py 27 | cd .. 28 | python -m pip uninstall -y tls-parser 29 | - name: Run tests and linters 30 | run: | 31 | python -m pip install -r requirements-dev.txt 32 | python -m invoke test 33 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # PyCharms 2 | .idea/ 3 | 4 | # Byte-compiled / optimized / DLL files 5 | __pycache__/ 6 | *.py[cod] 7 | *$py.class 8 | 9 | # C extensions 10 | *.so 11 | 12 | # Distribution / packaging 13 | .Python 14 | env/ 15 | build/ 16 | develop-eggs/ 17 | dist/ 18 | downloads/ 19 | eggs/ 20 | .eggs/ 21 | lib/ 22 | lib64/ 23 | parts/ 24 | sdist/ 25 | var/ 26 | wheels/ 27 | *.egg-info/ 28 | .installed.cfg 29 | *.egg 30 | 31 | # PyInstaller 32 | # Usually these files are written by a python script from a template 33 | # before PyInstaller builds the exe, so as to inject date/other infos into it. 34 | *.manifest 35 | *.spec 36 | 37 | # Installer logs 38 | pip-log.txt 39 | pip-delete-this-directory.txt 40 | 41 | # Unit test / coverage reports 42 | htmlcov/ 43 | .tox/ 44 | .coverage 45 | .coverage.* 46 | .cache 47 | nosetests.xml 48 | coverage.xml 49 | *.cover 50 | .hypothesis/ 51 | 52 | # Translations 53 | *.mo 54 | *.pot 55 | 56 | # Django stuff: 57 | *.log 58 | local_settings.py 59 | 60 | # Flask stuff: 61 | instance/ 62 | .webassets-cache 63 | 64 | # Scrapy stuff: 65 | .scrapy 66 | 67 | # Sphinx documentation 68 | docs/_build/ 69 | 70 | # PyBuilder 71 | target/ 72 | 73 | # Jupyter Notebook 74 | .ipynb_checkpoints 75 | 76 | # pyenv 77 | .python-version 78 | 79 | # celery beat schedule file 80 | celerybeat-schedule 81 | 82 | # SageMath parsed files 83 | *.sage.py 84 | 85 | # dotenv 86 | .env 87 | 88 | # virtualenv 89 | .venv 90 | venv/ 91 | ENV/ 92 | 93 | # Spyder project settings 94 | .spyderproject 95 | .spyproject 96 | 97 | # Rope project settings 98 | .ropeproject 99 | 100 | # mkdocs documentation 101 | /site 102 | 103 | # mypy 104 | .mypy_cache/ 105 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2017 Alban Diquet 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | tls_parser 2 | ========== 3 | 4 | ![Run tests](https://github.com/nabla-c0d3/tls_parser/workflows/Run%20tests/badge.svg) 5 | [![PyPI version](https://badge.fury.io/py/tls-parser.svg)](https://badge.fury.io/py/tls-parser) 6 | 7 | Small library to parse TLS records; used by [SSLyze](https://github.com/nabla-c0d3/sslyze). 8 | 9 | Development environment 10 | ----------------------- 11 | 12 | To setup a development environment: 13 | 14 | ``` 15 | $ pip install --upgrade pip setuptools wheel 16 | $ pip install -e . 17 | $ pip install -r requirements-dev.txt 18 | ``` 19 | 20 | The tests can then be run using: 21 | 22 | ``` 23 | $ invoke test 24 | ``` 25 | -------------------------------------------------------------------------------- /requirements-dev.txt: -------------------------------------------------------------------------------- 1 | mypy==1.0.1 2 | flake8 3 | invoke>=2,<3 4 | black==23.1.0 5 | pytest==7.2.2 6 | pytest-cov 7 | -------------------------------------------------------------------------------- /sample.py: -------------------------------------------------------------------------------- 1 | from tls_parser.handshake_protocol import TlsHandshakeRecord 2 | 3 | 4 | # Parse a server hello message 5 | SERVER_HELLO_BYTES = ( 6 | b"\x16\x03\x03\x00F\x02\x00\x00B\x03\x03\xf2\x00\xfd\x10\xea\x9e\x02\xe5\xc0\x83\x02T7" 7 | b"\xa7o\xf1\xdb\xd4\x8e\xc8>/\x9c\xeei\xb5\x9fi\xf9\x9e s\x00\xc0/\x00\x00\x1a\x00\x00" 8 | b"\x00\x00\xff\x01\x00\x01\x00\x00\x0b\x00\x04\x03\x00\x01\x02\x00#\x00\x00\x00\x0f\x00" 9 | b"\x01\x01" 10 | ) 11 | 12 | parsed_record, len_consumed = TlsHandshakeRecord.from_bytes(SERVER_HELLO_BYTES) 13 | print(parsed_record.header.tls_version) 14 | print(parsed_record.header.type) 15 | print(parsed_record.header.length) 16 | print(parsed_record.subprotocol_messages[0].handshake_type) 17 | -------------------------------------------------------------------------------- /setup.cfg: -------------------------------------------------------------------------------- 1 | [metadata] 2 | description_file = README.md 3 | 4 | [flake8] 5 | max-line-length = 120 6 | 7 | [mypy] 8 | python_version = 3.8 9 | strict_optional = True 10 | disallow_untyped_defs = True 11 | 12 | [mypy-tests.*] 13 | disallow_untyped_defs = False 14 | -------------------------------------------------------------------------------- /setup.py: -------------------------------------------------------------------------------- 1 | from setuptools import setup 2 | from tls_parser import __version__ 3 | from tls_parser import __author__ 4 | from tls_parser import __email__ 5 | 6 | setup( 7 | name="tls_parser", 8 | version=__version__, 9 | description="Small library to parse TLS records.", 10 | author=__author__, 11 | author_email=__email__, 12 | url="https://github.com/nabla-c0d3/tls_parser", 13 | packages=["tls_parser"], 14 | python_requires=">=3.8", 15 | ) 16 | -------------------------------------------------------------------------------- /tasks.py: -------------------------------------------------------------------------------- 1 | from invoke import task, Context 2 | 3 | 4 | @task 5 | def test(ctx: Context) -> None: 6 | # Run linters 7 | ctx.run("flake8 tls_parser tests") 8 | ctx.run("mypy tls_parser tests") 9 | ctx.run("black -l 120 tls_parser tests tasks.py --check") 10 | 11 | # Run the test suite 12 | ctx.run("pytest --cov=tls_parser --cov-fail-under 80") 13 | 14 | 15 | @task 16 | def release(ctx: Context) -> None: 17 | ctx.run("python setup.py sdist bdist_wheel") 18 | ctx.run("python -m twine upload .\dist\*") 19 | -------------------------------------------------------------------------------- /tests/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nabla-c0d3/tls_parser/539d6f5ae68f389882d9eb85055f5bbe89dcc3a8/tests/__init__.py -------------------------------------------------------------------------------- /tests/test_alert_protocol.py: -------------------------------------------------------------------------------- 1 | import pytest 2 | 3 | from tls_parser.alert_protocol import TlsAlertRecord, TlsAlertSeverityByte 4 | from tls_parser.exceptions import UnknownTlsVersionByte 5 | 6 | 7 | class TestTlsAlertRecord: 8 | def test_from_bytes(self): 9 | alert_bytes = b"\x15\x03\x03\x00\x02\x02\x14" 10 | parsed_record, len_consumed = TlsAlertRecord.from_bytes(alert_bytes) 11 | assert parsed_record.alert_severity == TlsAlertSeverityByte.FATAL 12 | assert parsed_record.alert_description == 0x14 13 | assert len_consumed == len(alert_bytes) 14 | 15 | def test_from_bytes_with_invalid_version(self): 16 | # Related to https://github.com/nabla-c0d3/sslyze/issues/437 17 | # Some servers put invalid TLS version bytes in the TLS alert they send back 18 | alert_bytes_with_bad_version = b"\x15\x00\x00\x00\x02\x02(" 19 | with pytest.raises(UnknownTlsVersionByte): 20 | TlsAlertRecord.from_bytes(alert_bytes_with_bad_version) 21 | -------------------------------------------------------------------------------- /tests/test_handshake_protocol.py: -------------------------------------------------------------------------------- 1 | import binascii 2 | import math 3 | 4 | from tls_parser.cipher_suites import CipherSuites 5 | from tls_parser.record_protocol import TlsRecordTlsVersionBytes 6 | from tls_parser.tls_version import TlsVersionEnum 7 | 8 | from tls_parser.handshake_protocol import TlsHandshakeRecord, TlsHandshakeTypeByte, TlsRsaClientKeyExchangeRecord 9 | 10 | 11 | class TestTlsHandshakeRecord: 12 | SERVER_HELLO_BYTES = ( 13 | b"\x16\x03\x03\x00F\x02\x00\x00B\x03\x03\xf2\x00\xfd\x10\xea\x9e\x02\xe5\xc0\x83\x02T7" 14 | b"\xa7o\xf1\xdb\xd4\x8e\xc8>/\x9c\xeei\xb5\x9fi\xf9\x9e s\x00\xc0/\x00\x00\x1a\x00\x00" 15 | b"\x00\x00\xff\x01\x00\x01\x00\x00\x0b\x00\x04\x03\x00\x01\x02\x00#\x00\x00\x00\x0f\x00" 16 | b"\x01\x01" 17 | ) 18 | 19 | def test_server_hello_from_bytes(self): 20 | parsed_record, len_consumed = TlsHandshakeRecord.from_bytes(self.SERVER_HELLO_BYTES) 21 | assert len(parsed_record.subprotocol_messages) == 1 22 | assert parsed_record.subprotocol_messages[0].handshake_type == TlsHandshakeTypeByte.SERVER_HELLO 23 | assert len_consumed == len(self.SERVER_HELLO_BYTES) 24 | 25 | 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 26 | 27 | def test_server_certificate_from_bytes(self): 28 | parsed_record, len_consumed = TlsHandshakeRecord.from_bytes(self.SERVER_CERTIFICATE_BYTES) 29 | assert len(parsed_record.subprotocol_messages) == 1 30 | assert parsed_record.subprotocol_messages[0].handshake_type == TlsHandshakeTypeByte.CERTIFICATE 31 | assert len_consumed == len(self.SERVER_CERTIFICATE_BYTES) 32 | 33 | SERVER_HELLO_DONE_BYTES = b"\x16\x03\x03\x00\x04\x0e\x00\x00\x00" 34 | 35 | def test_server_hello_done_from_bytes(self): 36 | parsed_record, len_consumed = TlsHandshakeRecord.from_bytes(self.SERVER_HELLO_DONE_BYTES) 37 | assert len(parsed_record.subprotocol_messages) == 1 38 | assert parsed_record.subprotocol_messages[0].handshake_type == TlsHandshakeTypeByte.SERVER_DONE 39 | assert len_consumed == len(self.SERVER_HELLO_DONE_BYTES) 40 | 41 | # A TLS handshake record with 4 handshake messages packed into the same TLS record 42 | 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\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 00 87 | tls_version_hex = binascii.b2a_hex(TlsRecordTlsVersionBytes[TlsVersionEnum.TLSV1_2.name].value).decode("ascii") 88 | pad_len = (modulus_byte_size - 48 - 3) * 2 89 | rnd_pad = ("abcd" * (pad_len // 2 + 1))[:pad_len] 90 | pms_with_padding = int("0002" + rnd_pad + "00" + tls_version_hex + pre_master_secret, 16) 91 | 92 | record = TlsRsaClientKeyExchangeRecord.from_parameters( 93 | TlsVersionEnum.TLSV1_2, exponent, modulus, pms_with_padding 94 | ) 95 | assert record.to_bytes() == self.EXPECTED_CLIENT_KEY_EXCHANGE_BYTES 96 | 97 | 98 | class TestCipherSuites: 99 | def test(self): 100 | # Ensure the CipherSuites enum is importable and usable 101 | assert CipherSuites.TLS_DH_anon_EXPORT_WITH_RC4_40_MD5.value 102 | -------------------------------------------------------------------------------- /tests/test_heartbeat_protocol.py: -------------------------------------------------------------------------------- 1 | from tls_parser.heartbeat_protocol import TlsHeartbeatRequestRecord 2 | from tls_parser.tls_version import TlsVersionEnum 3 | 4 | 5 | class TestTlsHeartbeatRequestRecord: 6 | def test_to_bytes(self): 7 | record = TlsHeartbeatRequestRecord.from_parameters(TlsVersionEnum.TLSV1_2, b"123456") 8 | assert record.to_bytes() 9 | -------------------------------------------------------------------------------- /tls_parser/__init__.py: -------------------------------------------------------------------------------- 1 | __version__ = "2.0.2" 2 | __author__ = "Alban Diquet" 3 | __email__ = "nabla.c0d3@gmail.com" 4 | 5 | # A very useful page for working on this module: 6 | # http://blog.fourthbit.com/2014/12/23/traffic-analysis-of-an-ssl-slash-tls-session 7 | -------------------------------------------------------------------------------- /tls_parser/alert_protocol.py: -------------------------------------------------------------------------------- 1 | import struct 2 | from enum import IntEnum 3 | from tls_parser.exceptions import NotEnoughData, UnknownTypeByte 4 | from tls_parser.record_protocol import TlsSubprotocolMessage, TlsRecord, TlsRecordHeader, TlsRecordTypeByte 5 | from tls_parser.tls_version import TlsVersionEnum 6 | from typing import Tuple, List 7 | 8 | 9 | class TlsAlertSeverityByte(IntEnum): 10 | WARNING = 0x01 11 | FATAL = 0x02 12 | 13 | 14 | class TlsAlertMessage(TlsSubprotocolMessage): 15 | def __init__(self, alert_severity: TlsAlertSeverityByte, alert_description: int) -> None: 16 | # Recreate the raw message as bytes 17 | full_message_data = b"" 18 | full_message_data += struct.pack("B", alert_severity.value) 19 | full_message_data += struct.pack("B", alert_description) 20 | super().__init__(full_message_data) 21 | 22 | self.alert_severity = alert_severity 23 | # Right now the description is just stored as an int instead of a TlsAlertDescriptionByte 24 | self.alert_description = alert_description 25 | 26 | @classmethod 27 | def from_bytes(cls, raw_bytes: bytes) -> Tuple["TlsAlertMessage", int]: 28 | if len(raw_bytes) < 2: 29 | raise NotEnoughData() 30 | 31 | alert_severity = TlsAlertSeverityByte(struct.unpack("B", raw_bytes[0:1])[0]) 32 | alert_description = struct.unpack("B", raw_bytes[1:2])[0] 33 | return TlsAlertMessage(alert_severity, alert_description), 2 34 | 35 | 36 | class TlsAlertRecord(TlsRecord): 37 | def __init__(self, record_header: TlsRecordHeader, alert_message: TlsAlertMessage) -> None: 38 | super(TlsAlertRecord, self).__init__(record_header=record_header, subprotocol_messages=[alert_message]) 39 | self.subprotocol_messages: List[TlsAlertMessage] # TODO(AD): Fix the interface instead of using an annotation 40 | 41 | @property 42 | def alert_severity(self) -> TlsAlertSeverityByte: 43 | """Convenience method to get the severity of the underlying Alert message. 44 | 45 | This makes the assumption that an Alert record only contains one Alert message, which seems to be the case in 46 | the real world. 47 | """ 48 | return self.subprotocol_messages[0].alert_severity 49 | 50 | @property 51 | def alert_description(self) -> int: 52 | """Convenience method to get the description of the underlying Alert message. 53 | 54 | This makes the assumption that an Alert record only contains one Alert message, which seems to be the case in 55 | the real world. 56 | """ 57 | return self.subprotocol_messages[0].alert_description 58 | 59 | @classmethod 60 | def from_parameters( 61 | cls, tls_version: TlsVersionEnum, alert_severity: TlsAlertSeverityByte, alert_description: int 62 | ) -> "TlsAlertRecord": 63 | alert_message = TlsAlertMessage(alert_severity, alert_description) 64 | record_header = TlsRecordHeader(TlsRecordTypeByte.ALERT, tls_version, alert_message.size) 65 | return TlsAlertRecord(record_header, alert_message) 66 | 67 | @classmethod 68 | def from_bytes(cls, raw_bytes: bytes) -> Tuple["TlsAlertRecord", int]: 69 | header, len_consumed = TlsRecordHeader.from_bytes(raw_bytes) 70 | remaining_bytes = raw_bytes[len_consumed::] 71 | 72 | if header.type != TlsRecordTypeByte.ALERT: 73 | raise UnknownTypeByte() 74 | 75 | message, len_consumed_for_message = TlsAlertMessage.from_bytes(remaining_bytes) 76 | return TlsAlertRecord(header, message), len_consumed + len_consumed_for_message 77 | -------------------------------------------------------------------------------- /tls_parser/application_data_protocol.py: -------------------------------------------------------------------------------- 1 | from tls_parser.record_protocol import TlsRecord, TlsSubprotocolMessage, TlsRecordHeader, TlsRecordTypeByte 2 | from tls_parser.tls_version import TlsVersionEnum 3 | 4 | 5 | class TlsApplicationDataMessage(TlsSubprotocolMessage): 6 | pass 7 | 8 | 9 | class TlsApplicationDataRecord(TlsRecord): 10 | """We make the assumption that an Application record only contains one message, which seems to be the case in the 11 | real world. 12 | """ 13 | 14 | def __init__(self, record_header: TlsRecordHeader, application_data: TlsApplicationDataMessage): 15 | super(TlsApplicationDataRecord, self).__init__( 16 | record_header=record_header, subprotocol_messages=[application_data] 17 | ) 18 | 19 | @classmethod 20 | def from_parameters(cls, tls_version: TlsVersionEnum, application_data: bytes) -> "TlsApplicationDataRecord": 21 | message = TlsApplicationDataMessage(application_data) 22 | record_header = TlsRecordHeader(TlsRecordTypeByte.APPLICATION_DATA, tls_version, message.size) 23 | return TlsApplicationDataRecord(record_header, message) 24 | -------------------------------------------------------------------------------- /tls_parser/change_cipher_spec_protocol.py: -------------------------------------------------------------------------------- 1 | from tls_parser.record_protocol import TlsRecord, TlsRecordHeader, TlsSubprotocolMessage, TlsRecordTypeByte 2 | from tls_parser.tls_version import TlsVersionEnum 3 | from typing import Tuple 4 | 5 | 6 | class TlsChangeCipherSpecRecord(TlsRecord): 7 | @classmethod 8 | def from_parameters(cls, tls_version: TlsVersionEnum) -> "TlsChangeCipherSpecRecord": 9 | ccs_message = TlsSubprotocolMessage(b"\x01") 10 | record_header = TlsRecordHeader(TlsRecordTypeByte.CHANGE_CIPHER_SPEC, tls_version, ccs_message.size) 11 | return TlsChangeCipherSpecRecord(record_header, [ccs_message]) 12 | 13 | @classmethod 14 | def from_bytes(cls, raw_byte: bytes) -> Tuple["TlsChangeCipherSpecRecord", int]: 15 | raise NotImplementedError() 16 | -------------------------------------------------------------------------------- /tls_parser/cipher_suites.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class CipherSuites(Enum): 5 | TLS_NULL_WITH_NULL_NULL = 0x0000 6 | TLS_RSA_WITH_NULL_MD5 = 0x0001 7 | TLS_RSA_WITH_NULL_SHA = 0x0002 8 | TLS_RSA_EXPORT_WITH_RC4_40_MD5 = 0x0003 9 | TLS_RSA_WITH_RC4_128_MD5 = 0x0004 10 | TLS_RSA_WITH_RC4_128_SHA = 0x0005 11 | TLS_RSA_EXPORT_WITH_RC2_CBC_40_MD5 = 0x0006 12 | TLS_RSA_WITH_IDEA_CBC_SHA = 0x0007 13 | TLS_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0008 14 | TLS_RSA_WITH_DES_CBC_SHA = 0x0009 15 | TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0x000A 16 | TLS_DH_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x000B 17 | TLS_DH_DSS_WITH_DES_CBC_SHA = 0x000C 18 | TLS_DH_DSS_WITH_3DES_EDE_CBC_SHA = 0x000D 19 | TLS_DH_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x000E 20 | TLS_DH_RSA_WITH_DES_CBC_SHA = 0x000F 21 | TLS_DH_RSA_WITH_3DES_EDE_CBC_SHA = 0x0010 22 | TLS_DHE_DSS_EXPORT_WITH_DES40_CBC_SHA = 0x0011 23 | TLS_DHE_DSS_WITH_DES_CBC_SHA = 0x0012 24 | TLS_DHE_DSS_WITH_3DES_EDE_CBC_SHA = 0x0013 25 | TLS_DHE_RSA_EXPORT_WITH_DES40_CBC_SHA = 0x0014 26 | TLS_DHE_RSA_WITH_DES_CBC_SHA = 0x0015 27 | TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0x0016 28 | TLS_DH_anon_EXPORT_WITH_RC4_40_MD5 = 0x0017 29 | TLS_DH_anon_WITH_RC4_128_MD5 = 0x0018 30 | TLS_DH_anon_EXPORT_WITH_DES40_CBC_SHA = 0x0019 31 | TLS_DH_anon_WITH_DES_CBC_SHA = 0x001A 32 | TLS_DH_anon_WITH_3DES_EDE_CBC_SHA = 0x001B 33 | TLS_KRB5_WITH_DES_CBC_SHA = 0x001E 34 | TLS_KRB5_WITH_3DES_EDE_CBC_SHA = 0x001F 35 | TLS_KRB5_WITH_RC4_128_SHA = 0x0020 36 | TLS_KRB5_WITH_IDEA_CBC_SHA = 0x0021 37 | TLS_KRB5_WITH_DES_CBC_MD5 = 0x0022 38 | TLS_KRB5_WITH_3DES_EDE_CBC_MD5 = 0x0023 39 | TLS_KRB5_WITH_RC4_128_MD5 = 0x0024 40 | TLS_KRB5_WITH_IDEA_CBC_MD5 = 0x0025 41 | TLS_KRB5_EXPORT_WITH_DES_CBC_40_SHA = 0x0026 42 | TLS_KRB5_EXPORT_WITH_RC2_CBC_40_SHA = 0x0027 43 | TLS_KRB5_EXPORT_WITH_RC4_40_SHA = 0x0028 44 | TLS_KRB5_EXPORT_WITH_DES_CBC_40_MD5 = 0x0029 45 | TLS_KRB5_EXPORT_WITH_RC2_CBC_40_MD5 = 0x002A 46 | TLS_KRB5_EXPORT_WITH_RC4_40_MD5 = 0x002B 47 | TLS_PSK_WITH_NULL_SHA = 0x002C 48 | TLS_DHE_PSK_WITH_NULL_SHA = 0x002D 49 | TLS_RSA_PSK_WITH_NULL_SHA = 0x002E 50 | TLS_RSA_WITH_AES_128_CBC_SHA = 0x002F 51 | TLS_DH_DSS_WITH_AES_128_CBC_SHA = 0x0030 52 | TLS_DH_RSA_WITH_AES_128_CBC_SHA = 0x0031 53 | TLS_DHE_DSS_WITH_AES_128_CBC_SHA = 0x0032 54 | TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0x0033 55 | TLS_DH_anon_WITH_AES_128_CBC_SHA = 0x0034 56 | TLS_RSA_WITH_AES_256_CBC_SHA = 0x0035 57 | TLS_DH_DSS_WITH_AES_256_CBC_SHA = 0x0036 58 | TLS_DH_RSA_WITH_AES_256_CBC_SHA = 0x0037 59 | TLS_DHE_DSS_WITH_AES_256_CBC_SHA = 0x0038 60 | TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0x0039 61 | TLS_DH_anon_WITH_AES_256_CBC_SHA = 0x003A 62 | TLS_RSA_WITH_NULL_SHA256 = 0x003B 63 | TLS_RSA_WITH_AES_128_CBC_SHA256 = 0x003C 64 | TLS_RSA_WITH_AES_256_CBC_SHA256 = 0x003D 65 | TLS_DH_DSS_WITH_AES_128_CBC_SHA256 = 0x003E 66 | TLS_DH_RSA_WITH_AES_128_CBC_SHA256 = 0x003F 67 | TLS_DHE_DSS_WITH_AES_128_CBC_SHA256 = 0x0040 68 | TLS_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0041 69 | TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0042 70 | TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0043 71 | TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA = 0x0044 72 | TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA = 0x0045 73 | TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA = 0x0046 74 | TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0x0067 75 | TLS_DH_DSS_WITH_AES_256_CBC_SHA256 = 0x0068 76 | TLS_DH_RSA_WITH_AES_256_CBC_SHA256 = 0x0069 77 | TLS_DHE_DSS_WITH_AES_256_CBC_SHA256 = 0x006A 78 | TLS_DHE_RSA_WITH_AES_256_CBC_SHA256 = 0x006B 79 | TLS_DH_anon_WITH_AES_128_CBC_SHA256 = 0x006C 80 | TLS_DH_anon_WITH_AES_256_CBC_SHA256 = 0x006D 81 | TLS_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0084 82 | TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0085 83 | TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0086 84 | TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA = 0x0087 85 | TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA = 0x0088 86 | TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA = 0x0089 87 | TLS_PSK_WITH_RC4_128_SHA = 0x008A 88 | TLS_PSK_WITH_3DES_EDE_CBC_SHA = 0x008B 89 | TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C 90 | TLS_PSK_WITH_AES_256_CBC_SHA = 0x008D 91 | TLS_DHE_PSK_WITH_RC4_128_SHA = 0x008E 92 | TLS_DHE_PSK_WITH_3DES_EDE_CBC_SHA = 0x008F 93 | TLS_DHE_PSK_WITH_AES_128_CBC_SHA = 0x0090 94 | TLS_DHE_PSK_WITH_AES_256_CBC_SHA = 0x0091 95 | TLS_RSA_PSK_WITH_RC4_128_SHA = 0x0092 96 | TLS_RSA_PSK_WITH_3DES_EDE_CBC_SHA = 0x0093 97 | TLS_RSA_PSK_WITH_AES_128_CBC_SHA = 0x0094 98 | TLS_RSA_PSK_WITH_AES_256_CBC_SHA = 0x0095 99 | TLS_RSA_WITH_SEED_CBC_SHA = 0x0096 100 | TLS_DH_DSS_WITH_SEED_CBC_SHA = 0x0097 101 | TLS_DH_RSA_WITH_SEED_CBC_SHA = 0x0098 102 | TLS_DHE_DSS_WITH_SEED_CBC_SHA = 0x0099 103 | TLS_DHE_RSA_WITH_SEED_CBC_SHA = 0x009A 104 | TLS_DH_anon_WITH_SEED_CBC_SHA = 0x009B 105 | TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C 106 | TLS_RSA_WITH_AES_256_GCM_SHA384 = 0x009D 107 | TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E 108 | TLS_DHE_RSA_WITH_AES_256_GCM_SHA384 = 0x009F 109 | TLS_DH_RSA_WITH_AES_128_GCM_SHA256 = 0x00A0 110 | TLS_DH_RSA_WITH_AES_256_GCM_SHA384 = 0x00A1 111 | TLS_DHE_DSS_WITH_AES_128_GCM_SHA256 = 0x00A2 112 | TLS_DHE_DSS_WITH_AES_256_GCM_SHA384 = 0x00A3 113 | TLS_DH_DSS_WITH_AES_128_GCM_SHA256 = 0x00A4 114 | TLS_DH_DSS_WITH_AES_256_GCM_SHA384 = 0x00A5 115 | TLS_DH_anon_WITH_AES_128_GCM_SHA256 = 0x00A6 116 | TLS_DH_anon_WITH_AES_256_GCM_SHA384 = 0x00A7 117 | TLS_PSK_WITH_AES_128_GCM_SHA256 = 0x00A8 118 | TLS_PSK_WITH_AES_256_GCM_SHA384 = 0x00A9 119 | TLS_DHE_PSK_WITH_AES_128_GCM_SHA256 = 0x00AA 120 | TLS_DHE_PSK_WITH_AES_256_GCM_SHA384 = 0x00AB 121 | TLS_RSA_PSK_WITH_AES_128_GCM_SHA256 = 0x00AC 122 | TLS_RSA_PSK_WITH_AES_256_GCM_SHA384 = 0x00AD 123 | TLS_PSK_WITH_AES_128_CBC_SHA256 = 0x00AE 124 | TLS_PSK_WITH_AES_256_CBC_SHA384 = 0x00AF 125 | TLS_PSK_WITH_NULL_SHA256 = 0x00B0 126 | TLS_PSK_WITH_NULL_SHA384 = 0x00B1 127 | TLS_DHE_PSK_WITH_AES_128_CBC_SHA256 = 0x00B2 128 | TLS_DHE_PSK_WITH_AES_256_CBC_SHA384 = 0x00B3 129 | TLS_DHE_PSK_WITH_NULL_SHA256 = 0x00B4 130 | TLS_DHE_PSK_WITH_NULL_SHA384 = 0x00B5 131 | TLS_RSA_PSK_WITH_AES_128_CBC_SHA256 = 0x00B6 132 | TLS_RSA_PSK_WITH_AES_256_CBC_SHA384 = 0x00B7 133 | TLS_RSA_PSK_WITH_NULL_SHA256 = 0x00B8 134 | TLS_RSA_PSK_WITH_NULL_SHA384 = 0x00B9 135 | TLS_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BA 136 | TLS_DH_DSS_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BB 137 | TLS_DH_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BC 138 | TLS_DHE_DSS_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BD 139 | TLS_DHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BE 140 | TLS_DH_anon_WITH_CAMELLIA_128_CBC_SHA256 = 0x00BF 141 | TLS_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C0 142 | TLS_DH_DSS_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C1 143 | TLS_DH_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C2 144 | TLS_DHE_DSS_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C3 145 | TLS_DHE_RSA_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C4 146 | TLS_DH_anon_WITH_CAMELLIA_256_CBC_SHA256 = 0x00C5 147 | TLS_EMPTY_RENEGOTIATION_INFO_SCSV = 0x00FF 148 | TLS_ECDH_ECDSA_WITH_NULL_SHA = 0xC001 149 | TLS_ECDH_ECDSA_WITH_RC4_128_SHA = 0xC002 150 | TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC003 151 | TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA = 0xC004 152 | TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA = 0xC005 153 | TLS_ECDHE_ECDSA_WITH_NULL_SHA = 0xC006 154 | TLS_ECDHE_ECDSA_WITH_RC4_128_SHA = 0xC007 155 | TLS_ECDHE_ECDSA_WITH_3DES_EDE_CBC_SHA = 0xC008 156 | TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA = 0xC009 157 | TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA = 0xC00A 158 | TLS_ECDH_RSA_WITH_NULL_SHA = 0xC00B 159 | TLS_ECDH_RSA_WITH_RC4_128_SHA = 0xC00C 160 | TLS_ECDH_RSA_WITH_3DES_EDE_CBC_SHA = 0xC00D 161 | TLS_ECDH_RSA_WITH_AES_128_CBC_SHA = 0xC00E 162 | TLS_ECDH_RSA_WITH_AES_256_CBC_SHA = 0xC00F 163 | TLS_ECDHE_RSA_WITH_NULL_SHA = 0xC010 164 | TLS_ECDHE_RSA_WITH_RC4_128_SHA = 0xC011 165 | TLS_ECDHE_RSA_WITH_3DES_EDE_CBC_SHA = 0xC012 166 | TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013 167 | TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014 168 | TLS_ECDH_anon_WITH_NULL_SHA = 0xC015 169 | TLS_ECDH_anon_WITH_RC4_128_SHA = 0xC016 170 | TLS_ECDH_anon_WITH_3DES_EDE_CBC_SHA = 0xC017 171 | TLS_ECDH_anon_WITH_AES_128_CBC_SHA = 0xC018 172 | TLS_ECDH_anon_WITH_AES_256_CBC_SHA = 0xC019 173 | TLS_SRP_SHA_WITH_3DES_EDE_CBC_SHA = 0xC01A 174 | TLS_SRP_SHA_RSA_WITH_3DES_EDE_CBC_SHA = 0xC01B 175 | TLS_SRP_SHA_DSS_WITH_3DES_EDE_CBC_SHA = 0xC01C 176 | TLS_SRP_SHA_WITH_AES_128_CBC_SHA = 0xC01D 177 | TLS_SRP_SHA_RSA_WITH_AES_128_CBC_SHA = 0xC01E 178 | TLS_SRP_SHA_DSS_WITH_AES_128_CBC_SHA = 0xC01F 179 | TLS_SRP_SHA_WITH_AES_256_CBC_SHA = 0xC020 180 | TLS_SRP_SHA_RSA_WITH_AES_256_CBC_SHA = 0xC021 181 | TLS_SRP_SHA_DSS_WITH_AES_256_CBC_SHA = 0xC022 182 | TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023 183 | TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC024 184 | TLS_ECDH_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC025 185 | TLS_ECDH_ECDSA_WITH_AES_256_CBC_SHA384 = 0xC026 186 | TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027 187 | TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA384 = 0xC028 188 | TLS_ECDH_RSA_WITH_AES_128_CBC_SHA256 = 0xC029 189 | TLS_ECDH_RSA_WITH_AES_256_CBC_SHA384 = 0xC02A 190 | TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B 191 | TLS_ECDHE_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02C 192 | TLS_ECDH_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02D 193 | TLS_ECDH_ECDSA_WITH_AES_256_GCM_SHA384 = 0xC02E 194 | TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F 195 | TLS_ECDHE_RSA_WITH_AES_256_GCM_SHA384 = 0xC030 196 | TLS_ECDH_RSA_WITH_AES_128_GCM_SHA256 = 0xC031 197 | TLS_ECDH_RSA_WITH_AES_256_GCM_SHA384 = 0xC032 198 | TLS_ECDHE_PSK_WITH_RC4_128_SHA = 0xC033 199 | TLS_ECDHE_PSK_WITH_3DES_EDE_CBC_SHA = 0xC034 200 | TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA = 0xC035 201 | TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA = 0xC036 202 | TLS_ECDHE_PSK_WITH_AES_128_CBC_SHA256 = 0xC037 203 | TLS_ECDHE_PSK_WITH_AES_256_CBC_SHA384 = 0xC038 204 | TLS_ECDHE_PSK_WITH_NULL_SHA = 0xC039 205 | TLS_ECDHE_PSK_WITH_NULL_SHA256 = 0xC03A 206 | TLS_ECDHE_PSK_WITH_NULL_SHA384 = 0xC03B 207 | TLS_RSA_WITH_ARIA_128_CBC_SHA256 = 0xC03C 208 | TLS_RSA_WITH_ARIA_256_CBC_SHA384 = 0xC03D 209 | TLS_DH_DSS_WITH_ARIA_128_CBC_SHA256 = 0xC03E 210 | TLS_DH_DSS_WITH_ARIA_256_CBC_SHA384 = 0xC03F 211 | TLS_DH_RSA_WITH_ARIA_128_CBC_SHA256 = 0xC040 212 | TLS_DH_RSA_WITH_ARIA_256_CBC_SHA384 = 0xC041 213 | TLS_DHE_DSS_WITH_ARIA_128_CBC_SHA256 = 0xC042 214 | TLS_DHE_DSS_WITH_ARIA_256_CBC_SHA384 = 0xC043 215 | TLS_DHE_RSA_WITH_ARIA_128_CBC_SHA256 = 0xC044 216 | TLS_DHE_RSA_WITH_ARIA_256_CBC_SHA384 = 0xC045 217 | TLS_DH_anon_WITH_ARIA_128_CBC_SHA256 = 0xC046 218 | TLS_DH_anon_WITH_ARIA_256_CBC_SHA384 = 0xC047 219 | TLS_ECDHE_ECDSA_WITH_ARIA_128_CBC_SHA256 = 0xC048 220 | TLS_ECDHE_ECDSA_WITH_ARIA_256_CBC_SHA384 = 0xC049 221 | TLS_ECDH_ECDSA_WITH_ARIA_128_CBC_SHA256 = 0xC04A 222 | TLS_ECDH_ECDSA_WITH_ARIA_256_CBC_SHA384 = 0xC04B 223 | TLS_ECDHE_RSA_WITH_ARIA_128_CBC_SHA256 = 0xC04C 224 | TLS_ECDHE_RSA_WITH_ARIA_256_CBC_SHA384 = 0xC04D 225 | TLS_ECDH_RSA_WITH_ARIA_128_CBC_SHA256 = 0xC04E 226 | TLS_ECDH_RSA_WITH_ARIA_256_CBC_SHA384 = 0xC04F 227 | TLS_RSA_WITH_ARIA_128_GCM_SHA256 = 0xC050 228 | TLS_RSA_WITH_ARIA_256_GCM_SHA384 = 0xC051 229 | TLS_DHE_RSA_WITH_ARIA_128_GCM_SHA256 = 0xC052 230 | TLS_DHE_RSA_WITH_ARIA_256_GCM_SHA384 = 0xC053 231 | TLS_DH_RSA_WITH_ARIA_128_GCM_SHA256 = 0xC054 232 | TLS_DH_RSA_WITH_ARIA_256_GCM_SHA384 = 0xC055 233 | TLS_DHE_DSS_WITH_ARIA_128_GCM_SHA256 = 0xC056 234 | TLS_DHE_DSS_WITH_ARIA_256_GCM_SHA384 = 0xC057 235 | TLS_DH_DSS_WITH_ARIA_128_GCM_SHA256 = 0xC058 236 | TLS_DH_DSS_WITH_ARIA_256_GCM_SHA384 = 0xC059 237 | TLS_DH_anon_WITH_ARIA_128_GCM_SHA256 = 0xC05A 238 | TLS_DH_anon_WITH_ARIA_256_GCM_SHA384 = 0xC05B 239 | TLS_ECDHE_ECDSA_WITH_ARIA_128_GCM_SHA256 = 0xC05C 240 | TLS_ECDHE_ECDSA_WITH_ARIA_256_GCM_SHA384 = 0xC05D 241 | TLS_ECDH_ECDSA_WITH_ARIA_128_GCM_SHA256 = 0xC05E 242 | TLS_ECDH_ECDSA_WITH_ARIA_256_GCM_SHA384 = 0xC05F 243 | TLS_ECDHE_RSA_WITH_ARIA_128_GCM_SHA256 = 0xC060 244 | TLS_ECDHE_RSA_WITH_ARIA_256_GCM_SHA384 = 0xC061 245 | TLS_ECDH_RSA_WITH_ARIA_128_GCM_SHA256 = 0xC062 246 | TLS_ECDH_RSA_WITH_ARIA_256_GCM_SHA384 = 0xC063 247 | TLS_PSK_WITH_ARIA_128_CBC_SHA256 = 0xC064 248 | TLS_PSK_WITH_ARIA_256_CBC_SHA384 = 0xC065 249 | TLS_DHE_PSK_WITH_ARIA_128_CBC_SHA256 = 0xC066 250 | TLS_DHE_PSK_WITH_ARIA_256_CBC_SHA384 = 0xC067 251 | TLS_RSA_PSK_WITH_ARIA_128_CBC_SHA256 = 0xC068 252 | TLS_RSA_PSK_WITH_ARIA_256_CBC_SHA384 = 0xC069 253 | TLS_PSK_WITH_ARIA_128_GCM_SHA256 = 0xC06A 254 | TLS_PSK_WITH_ARIA_256_GCM_SHA384 = 0xC06B 255 | TLS_DHE_PSK_WITH_ARIA_128_GCM_SHA256 = 0xC06C 256 | TLS_DHE_PSK_WITH_ARIA_256_GCM_SHA384 = 0xC06D 257 | TLS_RSA_PSK_WITH_ARIA_128_GCM_SHA256 = 0xC06E 258 | TLS_RSA_PSK_WITH_ARIA_256_GCM_SHA384 = 0xC06F 259 | TLS_ECDHE_PSK_WITH_ARIA_128_CBC_SHA256 = 0xC070 260 | TLS_ECDHE_PSK_WITH_ARIA_256_CBC_SHA384 = 0xC071 261 | TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 = 0xC072 262 | TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 = 0xC073 263 | TLS_ECDH_ECDSA_WITH_CAMELLIA_128_CBC_SHA256 = 0xC074 264 | TLS_ECDH_ECDSA_WITH_CAMELLIA_256_CBC_SHA384 = 0xC075 265 | TLS_ECDHE_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0xC076 266 | TLS_ECDHE_RSA_WITH_CAMELLIA_256_CBC_SHA384 = 0xC077 267 | TLS_ECDH_RSA_WITH_CAMELLIA_128_CBC_SHA256 = 0xC078 268 | TLS_ECDH_RSA_WITH_CAMELLIA_256_CBC_SHA384 = 0xC079 269 | TLS_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC07A 270 | TLS_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC07B 271 | TLS_DHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC07C 272 | TLS_DHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC07D 273 | TLS_DH_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC07E 274 | TLS_DH_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC07F 275 | TLS_DHE_DSS_WITH_CAMELLIA_128_GCM_SHA256 = 0xC080 276 | TLS_DHE_DSS_WITH_CAMELLIA_256_GCM_SHA384 = 0xC081 277 | TLS_DH_DSS_WITH_CAMELLIA_128_GCM_SHA256 = 0xC082 278 | TLS_DH_DSS_WITH_CAMELLIA_256_GCM_SHA384 = 0xC083 279 | TLS_DH_anon_WITH_CAMELLIA_128_GCM_SHA256 = 0xC084 280 | TLS_DH_anon_WITH_CAMELLIA_256_GCM_SHA384 = 0xC085 281 | TLS_ECDHE_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC086 282 | TLS_ECDHE_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC087 283 | TLS_ECDH_ECDSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC088 284 | TLS_ECDH_ECDSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC089 285 | TLS_ECDHE_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC08A 286 | TLS_ECDHE_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC08B 287 | TLS_ECDH_RSA_WITH_CAMELLIA_128_GCM_SHA256 = 0xC08C 288 | TLS_ECDH_RSA_WITH_CAMELLIA_256_GCM_SHA384 = 0xC08D 289 | TLS_PSK_WITH_CAMELLIA_128_GCM_SHA256 = 0xC08E 290 | TLS_PSK_WITH_CAMELLIA_256_GCM_SHA384 = 0xC08F 291 | TLS_DHE_PSK_WITH_CAMELLIA_128_GCM_SHA256 = 0xC090 292 | TLS_DHE_PSK_WITH_CAMELLIA_256_GCM_SHA384 = 0xC091 293 | TLS_RSA_PSK_WITH_CAMELLIA_128_GCM_SHA256 = 0xC092 294 | TLS_RSA_PSK_WITH_CAMELLIA_256_GCM_SHA384 = 0xC093 295 | TLS_PSK_WITH_CAMELLIA_128_CBC_SHA256 = 0xC094 296 | TLS_PSK_WITH_CAMELLIA_256_CBC_SHA384 = 0xC095 297 | TLS_DHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 = 0xC096 298 | TLS_DHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 = 0xC097 299 | TLS_RSA_PSK_WITH_CAMELLIA_128_CBC_SHA256 = 0xC098 300 | TLS_RSA_PSK_WITH_CAMELLIA_256_CBC_SHA384 = 0xC099 301 | TLS_ECDHE_PSK_WITH_CAMELLIA_128_CBC_SHA256 = 0xC09A 302 | TLS_ECDHE_PSK_WITH_CAMELLIA_256_CBC_SHA384 = 0xC09B 303 | TLS_RSA_WITH_AES_128_CCM = 0xC09C 304 | TLS_RSA_WITH_AES_256_CCM = 0xC09D 305 | TLS_DHE_RSA_WITH_AES_128_CCM = 0xC09E 306 | TLS_DHE_RSA_WITH_AES_256_CCM = 0xC09F 307 | TLS_RSA_WITH_AES_128_CCM_8 = 0xC0A0 308 | TLS_RSA_WITH_AES_256_CCM_8 = 0xC0A1 309 | TLS_DHE_RSA_WITH_AES_128_CCM_8 = 0xC0A2 310 | TLS_DHE_RSA_WITH_AES_256_CCM_8 = 0xC0A3 311 | TLS_PSK_WITH_AES_128_CCM = 0xC0A4 312 | TLS_PSK_WITH_AES_256_CCM = 0xC0A5 313 | TLS_DHE_PSK_WITH_AES_128_CCM = 0xC0A6 314 | TLS_DHE_PSK_WITH_AES_256_CCM = 0xC0A7 315 | TLS_PSK_WITH_AES_128_CCM_8 = 0xC0A8 316 | TLS_PSK_WITH_AES_256_CCM_8 = 0xC0A9 317 | TLS_PSK_DHE_WITH_AES_128_CCM_8 = 0xC0AA 318 | TLS_PSK_DHE_WITH_AES_256_CCM_8 = 0xC0AB 319 | TLS_ECDHE_ECDSA_WITH_AES_128_CCM = 0xC0AC 320 | TLS_ECDHE_ECDSA_WITH_AES_256_CCM = 0xC0AD 321 | TLS_ECDHE_ECDSA_WITH_AES_128_CCM_8 = 0xC0AE 322 | TLS_ECDHE_ECDSA_WITH_AES_256_CCM_8 = 0xC0AF 323 | OLD_TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCC13 324 | OLD_TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCC14 325 | OLD_TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCC15 326 | TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA8 327 | TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCA9 328 | TLS_DHE_RSA_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAA 329 | TLS_PSK_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAB 330 | TLS_ECDHE_PSK_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAC 331 | TLS_DHE_PSK_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAD 332 | TLS_RSA_PSK_WITH_CHACHA20_POLY1305_SHA256 = 0xCCAE 333 | -------------------------------------------------------------------------------- /tls_parser/exceptions.py: -------------------------------------------------------------------------------- 1 | from typing import TYPE_CHECKING 2 | 3 | if TYPE_CHECKING: 4 | from tls_parser.record_protocol import TlsRecordTypeByte 5 | 6 | 7 | class NotEnoughData(ValueError): 8 | pass 9 | 10 | 11 | class UnknownTypeByte(ValueError): 12 | pass 13 | 14 | 15 | class UnknownTlsVersionByte(ValueError): 16 | def __init__(self, message: str, record_type: "TlsRecordTypeByte") -> None: 17 | super(ValueError, self).__init__(message) 18 | self.record_type = record_type 19 | -------------------------------------------------------------------------------- /tls_parser/handshake_protocol.py: -------------------------------------------------------------------------------- 1 | import struct 2 | from enum import IntEnum 3 | 4 | from tls_parser.tls_version import TlsVersionEnum 5 | 6 | from tls_parser.exceptions import NotEnoughData, UnknownTypeByte 7 | from tls_parser.record_protocol import TlsSubprotocolMessage, TlsRecord, TlsRecordHeader, TlsRecordTypeByte 8 | from typing import Tuple, Sequence 9 | 10 | 11 | class TlsHandshakeTypeByte(IntEnum): 12 | HELLO_REQUEST = 0x00 13 | CLIENT_HELLO = 0x01 14 | SERVER_HELLO = 0x02 15 | HELLO_VERIFY_REQUEST = 0x03 16 | NEW_SESSION_TICKET = 0x04 17 | CERTIFICATE = 0x0B 18 | SERVER_KEY_EXCHANGE = 0x0C 19 | CERTIFICATE_REQUEST = 0x0D 20 | SERVER_DONE = 0x0E 21 | CERTIFICATE_VERIFY = 0x0F 22 | CLIENT_KEY_EXCHANGE = 0x10 23 | FINISHED = 0x14 24 | CERTIFICATE_STATUS = 0x16 25 | 26 | 27 | class TlsHandshakeMessage(TlsSubprotocolMessage): 28 | """The payload of a handshake record.""" 29 | 30 | def __init__(self, handshake_type: TlsHandshakeTypeByte, handshake_data: bytes) -> None: 31 | # Recreate the raw message as bytes 32 | full_message_data = b"" 33 | # TLS Handshake type - 1 byte 34 | full_message_data += struct.pack("B", handshake_type.value) 35 | # TLS Handshake length - 3 bytes 36 | full_message_data += struct.pack("!I", len(handshake_data))[1:4] # We only keep the first 3 out of 4 bytes 37 | # TLS Handshake message 38 | full_message_data += handshake_data 39 | super().__init__(full_message_data) 40 | 41 | self.handshake_type = handshake_type 42 | self.handshake_data = handshake_data 43 | 44 | @classmethod 45 | def from_bytes(cls, raw_bytes: bytes) -> Tuple["TlsHandshakeMessage", int]: 46 | if len(raw_bytes) < 4: 47 | raise NotEnoughData() 48 | 49 | handshake_type = TlsHandshakeTypeByte(struct.unpack("B", raw_bytes[0:1])[0]) 50 | message_length = struct.unpack("!I", b"\x00" + raw_bytes[1:4])[0] 51 | message = raw_bytes[4 : message_length + 4] # noqa: E203 52 | if len(message) < message_length: 53 | raise NotEnoughData() 54 | 55 | return TlsHandshakeMessage(handshake_type, message), 4 + message_length 56 | 57 | 58 | class TlsHandshakeRecord(TlsRecord): 59 | def __init__(self, record_header: TlsRecordHeader, handshake_messages: Sequence[TlsHandshakeMessage]) -> None: 60 | super().__init__(record_header=record_header, subprotocol_messages=handshake_messages) 61 | 62 | # TODO(AD): Fix the interface instead of using an annotation 63 | self.subprotocol_messages: Sequence[TlsHandshakeMessage] 64 | 65 | @classmethod 66 | def from_bytes(cls, raw_bytes: bytes) -> Tuple["TlsHandshakeRecord", int]: 67 | header, len_consumed_for_header = TlsRecordHeader.from_bytes(raw_bytes) 68 | remaining_bytes = raw_bytes[len_consumed_for_header::] 69 | 70 | if header.type != TlsRecordTypeByte.HANDSHAKE: 71 | raise UnknownTypeByte() 72 | 73 | # Try to parse the handshake record - there may be multiple messages packed in the record 74 | messages = [] 75 | total_len_consumed_for_messages = 0 76 | while total_len_consumed_for_messages != header.length: 77 | message, len_consumed_for_message = TlsHandshakeMessage.from_bytes(remaining_bytes) 78 | messages.append(message) 79 | total_len_consumed_for_messages += len_consumed_for_message 80 | remaining_bytes = remaining_bytes[len_consumed_for_message::] 81 | 82 | parsed_record = TlsHandshakeRecord(header, messages) 83 | return parsed_record, len_consumed_for_header + total_len_consumed_for_messages 84 | 85 | 86 | class TlsRsaClientKeyExchangeRecord(TlsHandshakeRecord): 87 | @classmethod 88 | def from_parameters( 89 | cls, tls_version: TlsVersionEnum, public_exponent: int, public_modulus: int, pre_master_secret_with_padding: int 90 | ) -> TlsHandshakeRecord: 91 | cke_bytes = b"" 92 | 93 | # Encrypt the pre_master_secret 94 | encrypted_pms = pow(pre_master_secret_with_padding, public_exponent, public_modulus) 95 | # Add it to the message but pad it so that its length is the same as the length of the modulus 96 | modulus_length_in_bytes = (public_modulus.bit_length() + 7) // 8 97 | encrypted_pms_bytes = encrypted_pms.to_bytes(length=modulus_length_in_bytes, byteorder="big") 98 | 99 | # Per RFC 5246: the RSA-encrypted PreMasterSecret in a ClientKeyExchange is preceded by two length bytes 100 | # These bytes are redundant in the case of RSA because the EncryptedPreMasterSecret is the only data in the 101 | # ClientKeyExchange 102 | msg_size = struct.pack("!I", len(encrypted_pms_bytes))[2:4] # Length is two bytes 103 | cke_bytes += msg_size 104 | cke_bytes += encrypted_pms_bytes 105 | msg = TlsHandshakeMessage(TlsHandshakeTypeByte.CLIENT_KEY_EXCHANGE, cke_bytes) 106 | 107 | # Build the header 108 | header = TlsRecordHeader(TlsRecordTypeByte.HANDSHAKE, tls_version, len(msg.to_bytes())) 109 | return TlsRsaClientKeyExchangeRecord(header, [msg]) 110 | -------------------------------------------------------------------------------- /tls_parser/heartbeat_protocol.py: -------------------------------------------------------------------------------- 1 | import struct 2 | from enum import IntEnum 3 | from tls_parser.record_protocol import TlsSubprotocolMessage, TlsRecord, TlsRecordHeader, TlsRecordTypeByte 4 | from tls_parser.tls_version import TlsVersionEnum 5 | from typing import Tuple 6 | 7 | 8 | class TlsHeartbeatTypeByte(IntEnum): 9 | REQUEST = 0x01 10 | RESPONSE = 0x02 11 | 12 | 13 | class TlsHeartbeatMessage(TlsSubprotocolMessage): 14 | def __init__(self, hearbeat_type: TlsHeartbeatTypeByte, heartbeat_data: bytes) -> None: 15 | # Recreate the raw message as bytes 16 | full_message_data = b"" 17 | # Heartbeat message type - 1 byte 18 | full_message_data += struct.pack("B", hearbeat_type.value) 19 | # Heartbeat message length - 2 bytes 20 | full_message_data += struct.pack("!H", len(heartbeat_data)) 21 | # Heartbead message data 22 | full_message_data += heartbeat_data 23 | # Padding is not handled 24 | super().__init__(full_message_data) 25 | 26 | # TODO(AD): Rename to self.hearbeat_type and self.heartbeat_data to mirror convention in the handshake protocol 27 | self.type = hearbeat_type 28 | self.data = heartbeat_data 29 | 30 | @classmethod 31 | def from_bytes(cls, raw_bytes: bytes) -> Tuple["TlsHeartbeatMessage", int]: 32 | raise NotImplementedError() 33 | 34 | 35 | class TlsHeartbeatRequestRecord(TlsRecord): 36 | """https://tools.ietf.org/html/rfc6520. 37 | struct { 38 | HeartbeatMessageType type; 39 | uint16 payload_length; 40 | opaque payload[HeartbeatMessage.payload_length]; 41 | opaque padding[padding_length]; 42 | } HeartbeatMessage; 43 | """ 44 | 45 | def __init__(self, record_header: TlsRecordHeader, heartbeat_message: TlsHeartbeatMessage) -> None: 46 | super().__init__(record_header=record_header, subprotocol_messages=[heartbeat_message]) 47 | 48 | @classmethod 49 | def from_parameters(cls, tls_version: TlsVersionEnum, heartbeat_data: bytes) -> "TlsHeartbeatRequestRecord": 50 | message = TlsHeartbeatMessage(TlsHeartbeatTypeByte.REQUEST, heartbeat_data) 51 | record_header = TlsRecordHeader(TlsRecordTypeByte.HEARTBEAT, tls_version, message.size) 52 | return TlsHeartbeatRequestRecord(record_header, message) 53 | 54 | @classmethod 55 | def from_bytes(cls, raw_bytes: bytes) -> Tuple["TlsHeartbeatRequestRecord", int]: 56 | raise NotImplementedError() 57 | -------------------------------------------------------------------------------- /tls_parser/parser.py: -------------------------------------------------------------------------------- 1 | from tls_parser.alert_protocol import TlsAlertRecord 2 | from tls_parser.exceptions import UnknownTypeByte 3 | from tls_parser.handshake_protocol import TlsHandshakeRecord 4 | from tls_parser.record_protocol import TlsRecord, TlsRecordHeader, TlsRecordTypeByte 5 | from typing import Tuple 6 | 7 | 8 | class TlsRecordParser: 9 | @staticmethod 10 | def parse_bytes(raw_bytes: bytes) -> Tuple[TlsRecord, int]: 11 | record_header, len_consumed = TlsRecordHeader.from_bytes(raw_bytes) 12 | 13 | # Try to parse the record 14 | if record_header.type == TlsRecordTypeByte.HANDSHAKE: 15 | return TlsHandshakeRecord.from_bytes(raw_bytes) 16 | elif record_header.type == TlsRecordTypeByte.ALERT: 17 | return TlsAlertRecord.from_bytes(raw_bytes) 18 | elif record_header.type in TlsRecordTypeByte: 19 | # Valid record type but we don't have the code to parse it right now 20 | return TlsRecord.from_bytes(raw_bytes) 21 | else: 22 | # Unknown type 23 | raise UnknownTypeByte() 24 | -------------------------------------------------------------------------------- /tls_parser/record_protocol.py: -------------------------------------------------------------------------------- 1 | import struct 2 | from enum import Enum 3 | from enum import IntEnum 4 | from tls_parser.exceptions import NotEnoughData, UnknownTypeByte, UnknownTlsVersionByte 5 | from tls_parser.tls_version import TlsVersionEnum 6 | from typing import Tuple, Sequence 7 | 8 | 9 | class TlsRecordTlsVersionBytes(Enum): 10 | SSLV3 = b"\x03\x00" 11 | TLSV1 = b"\x03\x01" 12 | TLSV1_1 = b"\x03\x02" 13 | TLSV1_2 = b"\x03\x03" 14 | 15 | 16 | class TlsRecordTypeByte(IntEnum): 17 | CHANGE_CIPHER_SPEC = 0x14 18 | ALERT = 0x15 19 | HANDSHAKE = 0x16 20 | APPLICATION_DATA = 0x17 21 | HEARTBEAT = 0x18 22 | 23 | 24 | class TlsRecordHeader: 25 | def __init__(self, record_type: TlsRecordTypeByte, tls_version: TlsVersionEnum, record_length: int) -> None: 26 | self.type = record_type 27 | self.tls_version = tls_version 28 | self.length = record_length 29 | 30 | @classmethod 31 | def from_bytes(cls, raw_bytes: bytes) -> Tuple["TlsRecordHeader", int]: 32 | if len(raw_bytes) < 5: 33 | raise NotEnoughData() 34 | 35 | record_type = TlsRecordTypeByte(struct.unpack("B", raw_bytes[0:1])[0]) 36 | 37 | try: 38 | tls_version = TlsRecordTlsVersionBytes(raw_bytes[1:3]) 39 | except ValueError as e: 40 | raise UnknownTlsVersionByte(e.args[0], record_type) 41 | 42 | record_length = struct.unpack("!H", raw_bytes[3:5])[0] 43 | return TlsRecordHeader(record_type, TlsVersionEnum[tls_version.name], record_length), 5 44 | 45 | def to_bytes(self) -> bytes: 46 | as_bytes = b"" 47 | # TLS Record type - 1 byte 48 | as_bytes += struct.pack("B", self.type.value) 49 | # TLS version - 2 bytes 50 | as_bytes += TlsRecordTlsVersionBytes[self.tls_version.name].value 51 | # Length - 2 bytes 52 | as_bytes += struct.pack("!H", self.length) 53 | return as_bytes 54 | 55 | 56 | class TlsSubprotocolMessage: 57 | # Handshake, Alert, etc. 58 | 59 | def __init__(self, message_data: bytes) -> None: 60 | self.message_data = message_data 61 | 62 | def to_bytes(self) -> bytes: 63 | return self.message_data 64 | 65 | @property 66 | def size(self) -> int: 67 | return len(self.to_bytes()) 68 | 69 | 70 | class TlsRecord: 71 | def __init__(self, record_header: TlsRecordHeader, subprotocol_messages: Sequence[TlsSubprotocolMessage]) -> None: 72 | self.header = record_header 73 | 74 | # Several messages can be concatenated into a single record; the messages must belong to the same subprotocol 75 | # Hence, in practice this only seems to apply to the handshake protocol 76 | if self.header.type != TlsRecordTypeByte.HANDSHAKE and len(subprotocol_messages) != 1: 77 | raise ValueError("Received multiple subprotocol messages for a non-handshake record") 78 | 79 | self.subprotocol_messages = subprotocol_messages 80 | 81 | @classmethod 82 | def from_bytes(cls, raw_bytes: bytes) -> Tuple["TlsRecord", int]: 83 | record_header, len_consumed = TlsRecordHeader.from_bytes(raw_bytes) 84 | 85 | # Try to parse the record 86 | if record_header.type not in TlsRecordTypeByte: 87 | raise UnknownTypeByte() 88 | 89 | record_data = raw_bytes[len_consumed : len_consumed + record_header.length] # noqa: E203 90 | if len(record_data) < record_header.length: 91 | raise NotEnoughData() 92 | 93 | # We do not attempt to parse the message - the data may actually contain multiple messages 94 | message = TlsSubprotocolMessage(record_data) 95 | return TlsRecord(record_header, [message]), len_consumed + record_header.length 96 | 97 | def to_bytes(self) -> bytes: 98 | as_bytes = b"" 99 | as_bytes += self.header.to_bytes() 100 | for message in self.subprotocol_messages: 101 | as_bytes += message.to_bytes() 102 | return as_bytes 103 | -------------------------------------------------------------------------------- /tls_parser/tls_version.py: -------------------------------------------------------------------------------- 1 | from enum import Enum 2 | 3 | 4 | class TlsVersionEnum(Enum): 5 | SSLV3 = 0 6 | TLSV1 = 1 7 | TLSV1_1 = 2 8 | TLSV1_2 = 3 9 | --------------------------------------------------------------------------------