Skip to content

Commit db0c923

Browse files
[TlsParser] Refactored implementation from #748 (#922)
* Refactored TlsParser based upon work done in #748 * Add missing `tls_server_hello.data`, thanks to @JerryKwan * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Pass `check.py` * Run check.py locally * Fix lint errors * Fix indentation issue * Ignore linkcheck for cloudflare links, GHA is getting a 403 reply, while the link actually works * Fix lint * codespell skip Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
1 parent d22d551 commit db0c923

File tree

12 files changed

+1041
-0
lines changed

12 files changed

+1041
-0
lines changed

proxy/http/parser/tls/__init__.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
proxy.py
4+
~~~~~~~~
5+
⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
6+
Network monitoring, controls & Application development, testing, debugging.
7+
8+
:copyright: (c) 2013-present by Abhinav Singh and contributors.
9+
:license: BSD, see LICENSE for more details.
10+
"""
11+
from .tls import TlsParser
12+
from .types import tlsContentType, tlsHandshakeType
13+
14+
__all__ = [
15+
'TlsParser',
16+
'tlsContentType',
17+
'tlsHandshakeType',
18+
]
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
proxy.py
4+
~~~~~~~~
5+
⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
6+
Network monitoring, controls & Application development, testing, debugging.
7+
8+
:copyright: (c) 2013-present by Abhinav Singh and contributors.
9+
:license: BSD, see LICENSE for more details.
10+
"""
11+
from typing import Optional, Tuple
12+
13+
14+
class TlsCertificate:
15+
"""TLS Certificate"""
16+
17+
def __init__(self) -> None:
18+
self.data: Optional[bytes] = None
19+
20+
def parse(self, raw: bytes) -> Tuple[bool, bytes]:
21+
self.data = raw
22+
return True, raw
23+
24+
def build(self) -> bytes:
25+
assert self.data
26+
return self.data
27+
28+
29+
class TlsCertificateRequest:
30+
"""TLS Certificate Request"""
31+
32+
def __init__(self) -> None:
33+
self.data: Optional[bytes] = None
34+
35+
def parse(self, raw: bytes) -> Tuple[bool, bytes]:
36+
return False, raw
37+
38+
def build(self) -> bytes:
39+
assert self.data
40+
return self.data
41+
42+
43+
class TlsCertificateVerify:
44+
"""TLS Certificate Verify"""
45+
46+
def __init__(self) -> None:
47+
self.data: Optional[bytes] = None
48+
49+
def parse(self, raw: bytes) -> Tuple[bool, bytes]:
50+
return False, raw
51+
52+
def build(self) -> bytes:
53+
assert self.data
54+
return self.data

proxy/http/parser/tls/finished.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
proxy.py
4+
~~~~~~~~
5+
⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
6+
Network monitoring, controls & Application development, testing, debugging.
7+
8+
:copyright: (c) 2013-present by Abhinav Singh and contributors.
9+
:license: BSD, see LICENSE for more details.
10+
"""
11+
from typing import Optional, Tuple
12+
13+
14+
class TlsFinished:
15+
"""TLS Finished"""
16+
17+
def __init__(self) -> None:
18+
self.data: Optional[bytes] = None
19+
20+
def parse(self, raw: bytes) -> Tuple[bool, bytes]:
21+
return False, raw
22+
23+
def build(self) -> bytes:
24+
assert self.data
25+
return self.data

proxy/http/parser/tls/handshake.py

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
# -*- coding: utf-8 -*-
2+
"""
3+
proxy.py
4+
~~~~~~~~
5+
⚡⚡⚡ Fast, Lightweight, Pluggable, TLS interception capable proxy server focused on
6+
Network monitoring, controls & Application development, testing, debugging.
7+
8+
:copyright: (c) 2013-present by Abhinav Singh and contributors.
9+
:license: BSD, see LICENSE for more details.
10+
"""
11+
import struct
12+
import logging
13+
14+
from typing import Optional, Tuple
15+
16+
from .types import tlsHandshakeType
17+
from .hello import TlsHelloRequest, TlsClientHello, TlsServerHello, TlsServerHelloDone
18+
from .certificate import TlsCertificate, TlsCertificateRequest, TlsCertificateVerify
19+
from .key_exchange import TlsClientKeyExchange, TlsServerKeyExchange
20+
from .finished import TlsFinished
21+
22+
logger = logging.getLogger(__name__)
23+
24+
25+
class TlsHandshake:
26+
"""TLS Handshake"""
27+
28+
def __init__(self) -> None:
29+
self.msg_type: int = tlsHandshakeType.OTHER
30+
self.length: Optional[bytes] = None
31+
self.hello_request: Optional[TlsHelloRequest] = None
32+
self.client_hello: Optional[TlsClientHello] = None
33+
self.server_hello: Optional[TlsServerHello] = None
34+
self.certificate: Optional[TlsCertificate] = None
35+
self.server_key_exchange: Optional[TlsServerKeyExchange] = None
36+
self.certificate_request: Optional[TlsCertificateRequest] = None
37+
self.server_hello_done: Optional[TlsServerHelloDone] = None
38+
self.certificate_verify: Optional[TlsCertificateVerify] = None
39+
self.client_key_exchange: Optional[TlsClientKeyExchange] = None
40+
self.finished: Optional[TlsFinished] = None
41+
self.data: Optional[bytes] = None
42+
43+
def parse(self, raw: bytes) -> Tuple[bool, bytes]:
44+
length = len(raw)
45+
if length < 4:
46+
logger.debug('invalid data, len(raw) = %s', length)
47+
return False, raw
48+
payload_length, = struct.unpack('!I', b'\x00' + raw[1:4])
49+
self.length = payload_length
50+
if length < 4 + payload_length:
51+
logger.debug(
52+
'incomplete data, len(raw) = %s, len(payload) = %s', length, payload_length,
53+
)
54+
return False, raw
55+
# parse
56+
self.msg_type = raw[0]
57+
self.length = raw[1:4]
58+
self.data = raw[: 4 + payload_length]
59+
payload = raw[4: 4 + payload_length]
60+
if self.msg_type == tlsHandshakeType.HELLO_REQUEST:
61+
# parse hello request
62+
self.hello_request = TlsHelloRequest()
63+
self.hello_request.parse(payload)
64+
elif self.msg_type == tlsHandshakeType.CLIENT_HELLO:
65+
# parse client hello
66+
self.client_hello = TlsClientHello()
67+
self.client_hello.parse(payload)
68+
elif self.msg_type == tlsHandshakeType.SERVER_HELLO:
69+
# parse server hello
70+
self.server_hello = TlsServerHello()
71+
self.server_hello.parse(payload)
72+
elif self.msg_type == tlsHandshakeType.CERTIFICATE:
73+
# parse certificate
74+
self.certificate = TlsCertificate()
75+
self.certificate.parse(payload)
76+
elif self.msg_type == tlsHandshakeType.SERVER_KEY_EXCHANGE:
77+
# parse server key exchange
78+
self.server_key_exchange = TlsServerKeyExchange()
79+
self.server_key_exchange.parse(payload)
80+
elif self.msg_type == tlsHandshakeType.CERTIFICATE_REQUEST:
81+
# parse certificate request
82+
self.certificate_request = TlsCertificateRequest()
83+
self.certificate_request.parse(payload)
84+
elif self.msg_type == tlsHandshakeType.SERVER_HELLO_DONE:
85+
# parse server hello done
86+
self.server_hello_done = TlsServerHelloDone()
87+
self.server_hello_done.parse(payload)
88+
elif self.msg_type == tlsHandshakeType.CERTIFICATE_VERIFY:
89+
# parse certificate verify
90+
self.certificate_verify = TlsCertificateVerify()
91+
self.certificate_verify.parse(payload)
92+
elif self.msg_type == tlsHandshakeType.CLIENT_KEY_EXCHANGE:
93+
# parse client key exchange
94+
self.client_key_exchange = TlsClientKeyExchange()
95+
self.client_key_exchange.parse(payload)
96+
elif self.msg_type == tlsHandshakeType.FINISHED:
97+
# parse finished
98+
self.finished = TlsFinished()
99+
self.finished.parse(payload)
100+
return True, raw[4 + payload_length:]
101+
102+
def build(self) -> bytes:
103+
data = b''
104+
data += bytes([self.msg_type])
105+
payload = b''
106+
if self.msg_type == tlsHandshakeType.CLIENT_HELLO:
107+
assert self.client_hello
108+
payload = self.client_hello.build()
109+
elif self.msg_type == tlsHandshakeType.SERVER_HELLO:
110+
assert self.server_hello
111+
payload = self.server_hello.build()
112+
elif self.msg_type == tlsHandshakeType.CERTIFICATE:
113+
assert self.certificate
114+
payload = self.certificate.build()
115+
elif self.msg_type == tlsHandshakeType.SERVER_KEY_EXCHANGE:
116+
assert self.server_key_exchange
117+
payload = self.server_key_exchange.build()
118+
# calculate length
119+
length = struct.pack('!I', len(payload))[1:]
120+
data += length
121+
data += payload
122+
return data

0 commit comments

Comments
 (0)