atomicshop 2.15.11__py3-none-any.whl → 3.10.5__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (221) hide show
  1. atomicshop/__init__.py +1 -1
  2. atomicshop/{addons/mains → a_mains}/FACT/update_extract.py +3 -2
  3. atomicshop/a_mains/dns_gateway_setting.py +11 -0
  4. atomicshop/a_mains/get_local_tcp_ports.py +85 -0
  5. atomicshop/a_mains/github_wrapper.py +11 -0
  6. atomicshop/a_mains/install_ca_certificate.py +172 -0
  7. atomicshop/a_mains/process_from_port.py +119 -0
  8. atomicshop/a_mains/set_default_dns_gateway.py +90 -0
  9. atomicshop/a_mains/update_config_toml.py +38 -0
  10. atomicshop/basics/ansi_escape_codes.py +3 -1
  11. atomicshop/basics/argparse_template.py +2 -0
  12. atomicshop/basics/booleans.py +27 -30
  13. atomicshop/basics/bytes_arrays.py +43 -0
  14. atomicshop/basics/classes.py +149 -1
  15. atomicshop/basics/enums.py +2 -2
  16. atomicshop/basics/exceptions.py +5 -1
  17. atomicshop/basics/list_of_classes.py +29 -0
  18. atomicshop/basics/multiprocesses.py +374 -50
  19. atomicshop/basics/strings.py +72 -3
  20. atomicshop/basics/threads.py +14 -0
  21. atomicshop/basics/tracebacks.py +13 -3
  22. atomicshop/certificates.py +153 -52
  23. atomicshop/config_init.py +11 -6
  24. atomicshop/console_user_response.py +7 -14
  25. atomicshop/consoles.py +9 -0
  26. atomicshop/datetimes.py +1 -1
  27. atomicshop/diff_check.py +3 -3
  28. atomicshop/dns.py +128 -3
  29. atomicshop/etws/_pywintrace_fix.py +17 -0
  30. atomicshop/etws/trace.py +40 -42
  31. atomicshop/etws/traces/trace_dns.py +56 -44
  32. atomicshop/etws/traces/trace_tcp.py +130 -0
  33. atomicshop/file_io/csvs.py +27 -5
  34. atomicshop/file_io/docxs.py +34 -17
  35. atomicshop/file_io/file_io.py +31 -17
  36. atomicshop/file_io/jsons.py +49 -0
  37. atomicshop/file_io/tomls.py +139 -0
  38. atomicshop/filesystem.py +616 -291
  39. atomicshop/get_process_list.py +3 -3
  40. atomicshop/http_parse.py +149 -93
  41. atomicshop/ip_addresses.py +6 -1
  42. atomicshop/mitm/centered_settings.py +132 -0
  43. atomicshop/mitm/config_static.py +207 -0
  44. atomicshop/mitm/config_toml_editor.py +55 -0
  45. atomicshop/mitm/connection_thread_worker.py +875 -357
  46. atomicshop/mitm/engines/__parent/parser___parent.py +4 -17
  47. atomicshop/mitm/engines/__parent/recorder___parent.py +108 -51
  48. atomicshop/mitm/engines/__parent/requester___parent.py +116 -0
  49. atomicshop/mitm/engines/__parent/responder___parent.py +75 -114
  50. atomicshop/mitm/engines/__reference_general/parser___reference_general.py +10 -7
  51. atomicshop/mitm/engines/__reference_general/recorder___reference_general.py +5 -5
  52. atomicshop/mitm/engines/__reference_general/requester___reference_general.py +47 -0
  53. atomicshop/mitm/engines/__reference_general/responder___reference_general.py +95 -13
  54. atomicshop/mitm/engines/create_module_template.py +58 -14
  55. atomicshop/mitm/import_config.py +359 -139
  56. atomicshop/mitm/initialize_engines.py +160 -80
  57. atomicshop/mitm/message.py +64 -23
  58. atomicshop/mitm/mitm_main.py +892 -0
  59. atomicshop/mitm/recs_files.py +183 -0
  60. atomicshop/mitm/shared_functions.py +4 -10
  61. atomicshop/mitm/ssh_tester.py +82 -0
  62. atomicshop/mitm/statistic_analyzer.py +136 -40
  63. atomicshop/mitm/statistic_analyzer_helper/moving_average_helper.py +265 -83
  64. atomicshop/monitor/checks/dns.py +1 -1
  65. atomicshop/networks.py +671 -0
  66. atomicshop/on_exit.py +39 -9
  67. atomicshop/package_mains_processor.py +84 -0
  68. atomicshop/permissions/permissions.py +22 -0
  69. atomicshop/permissions/ubuntu_permissions.py +239 -0
  70. atomicshop/permissions/win_permissions.py +33 -0
  71. atomicshop/print_api.py +24 -42
  72. atomicshop/process.py +24 -6
  73. atomicshop/process_poller/process_pool.py +0 -1
  74. atomicshop/process_poller/simple_process_pool.py +204 -5
  75. atomicshop/python_file_patcher.py +1 -1
  76. atomicshop/python_functions.py +27 -75
  77. atomicshop/speech_recognize.py +8 -0
  78. atomicshop/ssh_remote.py +158 -172
  79. atomicshop/system_resource_monitor.py +61 -47
  80. atomicshop/system_resources.py +8 -8
  81. atomicshop/tempfiles.py +1 -2
  82. atomicshop/urls.py +6 -0
  83. atomicshop/venvs.py +28 -0
  84. atomicshop/versioning.py +27 -0
  85. atomicshop/web.py +98 -27
  86. atomicshop/web_apis/google_custom_search.py +44 -0
  87. atomicshop/web_apis/google_llm.py +188 -0
  88. atomicshop/websocket_parse.py +450 -0
  89. atomicshop/wrappers/certauthw/certauth.py +1 -0
  90. atomicshop/wrappers/cryptographyw.py +29 -8
  91. atomicshop/wrappers/ctyping/etw_winapi/const.py +97 -47
  92. atomicshop/wrappers/ctyping/etw_winapi/etw_functions.py +178 -49
  93. atomicshop/wrappers/ctyping/file_details_winapi.py +67 -0
  94. atomicshop/wrappers/ctyping/msi_windows_installer/cabs.py +2 -1
  95. atomicshop/wrappers/ctyping/msi_windows_installer/extract_msi_main.py +2 -2
  96. atomicshop/wrappers/ctyping/setup_device.py +466 -0
  97. atomicshop/wrappers/ctyping/win_console.py +39 -0
  98. atomicshop/wrappers/dockerw/dockerw.py +113 -2
  99. atomicshop/wrappers/elasticsearchw/config_basic.py +0 -12
  100. atomicshop/wrappers/elasticsearchw/elastic_infra.py +75 -0
  101. atomicshop/wrappers/elasticsearchw/elasticsearchw.py +2 -20
  102. atomicshop/wrappers/factw/get_file_data.py +12 -5
  103. atomicshop/wrappers/factw/install/install_after_restart.py +89 -5
  104. atomicshop/wrappers/factw/install/pre_install_and_install_before_restart.py +20 -14
  105. atomicshop/wrappers/githubw.py +537 -54
  106. atomicshop/wrappers/loggingw/consts.py +1 -1
  107. atomicshop/wrappers/loggingw/filters.py +23 -0
  108. atomicshop/wrappers/loggingw/formatters.py +12 -0
  109. atomicshop/wrappers/loggingw/handlers.py +214 -107
  110. atomicshop/wrappers/loggingw/loggers.py +19 -0
  111. atomicshop/wrappers/loggingw/loggingw.py +860 -22
  112. atomicshop/wrappers/loggingw/reading.py +134 -112
  113. atomicshop/wrappers/mongodbw/mongo_infra.py +31 -0
  114. atomicshop/wrappers/mongodbw/mongodbw.py +1324 -36
  115. atomicshop/wrappers/netshw.py +271 -0
  116. atomicshop/wrappers/playwrightw/engine.py +34 -19
  117. atomicshop/wrappers/playwrightw/infra.py +5 -0
  118. atomicshop/wrappers/playwrightw/javascript.py +7 -3
  119. atomicshop/wrappers/playwrightw/keyboard.py +14 -0
  120. atomicshop/wrappers/playwrightw/scenarios.py +172 -5
  121. atomicshop/wrappers/playwrightw/waits.py +9 -7
  122. atomicshop/wrappers/powershell_networking.py +80 -0
  123. atomicshop/wrappers/psutilw/processes.py +37 -1
  124. atomicshop/wrappers/psutilw/psutil_networks.py +85 -0
  125. atomicshop/wrappers/pyopensslw.py +9 -2
  126. atomicshop/wrappers/pywin32w/cert_store.py +116 -0
  127. atomicshop/wrappers/pywin32w/win_event_log/fetch.py +174 -0
  128. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_create.py +3 -105
  129. atomicshop/wrappers/pywin32w/win_event_log/subscribes/process_terminate.py +3 -57
  130. atomicshop/wrappers/pywin32w/wmis/msft_netipaddress.py +113 -0
  131. atomicshop/wrappers/pywin32w/wmis/win32_networkadapterconfiguration.py +259 -0
  132. atomicshop/wrappers/pywin32w/wmis/win32networkadapter.py +112 -0
  133. atomicshop/wrappers/pywin32w/wmis/wmi_helpers.py +236 -0
  134. atomicshop/wrappers/socketw/accepter.py +21 -7
  135. atomicshop/wrappers/socketw/certificator.py +216 -150
  136. atomicshop/wrappers/socketw/creator.py +190 -50
  137. atomicshop/wrappers/socketw/dns_server.py +491 -182
  138. atomicshop/wrappers/socketw/exception_wrapper.py +45 -52
  139. atomicshop/wrappers/socketw/process_getter.py +86 -0
  140. atomicshop/wrappers/socketw/receiver.py +144 -102
  141. atomicshop/wrappers/socketw/sender.py +65 -35
  142. atomicshop/wrappers/socketw/sni.py +334 -165
  143. atomicshop/wrappers/socketw/socket_base.py +134 -0
  144. atomicshop/wrappers/socketw/socket_client.py +137 -95
  145. atomicshop/wrappers/socketw/socket_server_tester.py +11 -7
  146. atomicshop/wrappers/socketw/socket_wrapper.py +717 -116
  147. atomicshop/wrappers/socketw/ssl_base.py +15 -14
  148. atomicshop/wrappers/socketw/statistics_csv.py +148 -17
  149. atomicshop/wrappers/sysmonw.py +1 -1
  150. atomicshop/wrappers/ubuntu_terminal.py +65 -26
  151. atomicshop/wrappers/win_auditw.py +189 -0
  152. atomicshop/wrappers/winregw/__init__.py +0 -0
  153. atomicshop/wrappers/winregw/winreg_installed_software.py +58 -0
  154. atomicshop/wrappers/winregw/winreg_network.py +232 -0
  155. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/METADATA +31 -51
  156. atomicshop-3.10.5.dist-info/RECORD +306 -0
  157. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/WHEEL +1 -1
  158. atomicshop/_basics_temp.py +0 -101
  159. atomicshop/a_installs/win/fibratus.py +0 -9
  160. atomicshop/a_installs/win/mongodb.py +0 -9
  161. atomicshop/a_installs/win/pycharm.py +0 -9
  162. atomicshop/addons/a_setup_scripts/install_psycopg2_ubuntu.sh +0 -3
  163. atomicshop/addons/a_setup_scripts/install_pywintrace_0.3.cmd +0 -2
  164. atomicshop/addons/mains/__pycache__/install_fibratus_windows.cpython-312.pyc +0 -0
  165. atomicshop/addons/mains/__pycache__/msi_unpacker.cpython-312.pyc +0 -0
  166. atomicshop/addons/mains/install_docker_rootless_ubuntu.py +0 -11
  167. atomicshop/addons/mains/install_docker_ubuntu_main_sudo.py +0 -11
  168. atomicshop/addons/mains/install_elastic_search_and_kibana_ubuntu.py +0 -10
  169. atomicshop/addons/mains/install_wsl_ubuntu_lts_admin.py +0 -9
  170. atomicshop/addons/package_setup/CreateWheel.cmd +0 -7
  171. atomicshop/addons/package_setup/Setup in Edit mode.cmd +0 -6
  172. atomicshop/addons/package_setup/Setup.cmd +0 -7
  173. atomicshop/archiver/_search_in_zip.py +0 -189
  174. atomicshop/archiver/archiver.py +0 -34
  175. atomicshop/archiver/search_in_archive.py +0 -250
  176. atomicshop/archiver/sevenz_app_w.py +0 -86
  177. atomicshop/archiver/sevenzs.py +0 -44
  178. atomicshop/archiver/zips.py +0 -293
  179. atomicshop/file_types.py +0 -24
  180. atomicshop/mitm/config_editor.py +0 -37
  181. atomicshop/mitm/engines/create_module_template_example.py +0 -13
  182. atomicshop/mitm/initialize_mitm_server.py +0 -268
  183. atomicshop/pbtkmultifile_argparse.py +0 -88
  184. atomicshop/permissions.py +0 -151
  185. atomicshop/script_as_string_processor.py +0 -38
  186. atomicshop/ssh_scripts/process_from_ipv4.py +0 -37
  187. atomicshop/ssh_scripts/process_from_port.py +0 -27
  188. atomicshop/wrappers/_process_wrapper_curl.py +0 -27
  189. atomicshop/wrappers/_process_wrapper_tar.py +0 -21
  190. atomicshop/wrappers/dockerw/install_docker.py +0 -209
  191. atomicshop/wrappers/elasticsearchw/infrastructure.py +0 -265
  192. atomicshop/wrappers/elasticsearchw/install_elastic.py +0 -232
  193. atomicshop/wrappers/ffmpegw.py +0 -125
  194. atomicshop/wrappers/fibratusw/install.py +0 -81
  195. atomicshop/wrappers/mongodbw/infrastructure.py +0 -53
  196. atomicshop/wrappers/mongodbw/install_mongodb.py +0 -190
  197. atomicshop/wrappers/msiw.py +0 -149
  198. atomicshop/wrappers/nodejsw/install_nodejs.py +0 -139
  199. atomicshop/wrappers/process_wrapper_pbtk.py +0 -16
  200. atomicshop/wrappers/psutilw/networks.py +0 -45
  201. atomicshop/wrappers/pycharmw.py +0 -81
  202. atomicshop/wrappers/socketw/base.py +0 -59
  203. atomicshop/wrappers/socketw/get_process.py +0 -107
  204. atomicshop/wrappers/wslw.py +0 -191
  205. atomicshop-2.15.11.dist-info/RECORD +0 -302
  206. /atomicshop/{addons/mains → a_mains}/FACT/factw_fact_extractor_docker_image_main_sudo.py +0 -0
  207. /atomicshop/{addons → a_mains/addons}/PlayWrightCodegen.cmd +0 -0
  208. /atomicshop/{addons → a_mains/addons}/ScriptExecution.cmd +0 -0
  209. /atomicshop/{addons → a_mains/addons}/inits/init_to_import_all_modules.py +0 -0
  210. /atomicshop/{addons → a_mains/addons}/process_list/ReadMe.txt +0 -0
  211. /atomicshop/{addons → a_mains/addons}/process_list/compile.cmd +0 -0
  212. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.dll +0 -0
  213. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.exp +0 -0
  214. /atomicshop/{addons → a_mains/addons}/process_list/compiled/Win10x64/process_list.lib +0 -0
  215. /atomicshop/{addons → a_mains/addons}/process_list/process_list.cpp +0 -0
  216. /atomicshop/{archiver → permissions}/__init__.py +0 -0
  217. /atomicshop/{wrappers/fibratusw → web_apis}/__init__.py +0 -0
  218. /atomicshop/wrappers/{nodejsw → pywin32w/wmis}/__init__.py +0 -0
  219. /atomicshop/wrappers/pywin32w/{wmi_win32process.py → wmis/win32process.py} +0 -0
  220. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info/licenses}/LICENSE.txt +0 -0
  221. {atomicshop-2.15.11.dist-info → atomicshop-3.10.5.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,450 @@
1
+ from typing import Union, Generator
2
+ import logging
3
+
4
+ from websockets.server import ServerProtocol
5
+ from websockets.client import ClientProtocol
6
+ from websockets.extensions.permessage_deflate import PerMessageDeflate, ServerPerMessageDeflateFactory, ClientPerMessageDeflateFactory
7
+ from websockets.http11 import Request, Response
8
+ from websockets.frames import Frame, Opcode
9
+ from websockets.uri import parse_uri
10
+ from websockets.exceptions import InvalidHeaderValue
11
+ from websockets.protocol import OPEN
12
+ from websockets.streams import StreamReader
13
+ from websockets.exceptions import ProtocolError, PayloadTooBig
14
+
15
+
16
+ class WebsocketParseWrongOpcode(Exception):
17
+ pass
18
+
19
+
20
+ def create_byte_http_response(
21
+ byte_http_request: Union[bytes, bytearray],
22
+ enable_logging: bool = False
23
+ ) -> bytes:
24
+ """
25
+ Create a byte HTTP response from a byte HTTP request.
26
+
27
+ Parameters:
28
+ - byte_http_request (bytes, bytearray): The byte HTTP request.
29
+ - enable_logging (bool): Whether to enable logging.
30
+
31
+ Returns:
32
+ - bytes: The byte HTTP response.
33
+ """
34
+
35
+ # Set up extensions
36
+ permessage_deflate_factory = ServerPerMessageDeflateFactory()
37
+
38
+ # Create the protocol instance
39
+ protocol = ServerProtocol(
40
+ extensions=[permessage_deflate_factory],
41
+ )
42
+ # At this state the protocol.state is State.CONNECTING
43
+
44
+ if enable_logging:
45
+ logging.basicConfig(level=logging.DEBUG)
46
+ protocol.logger.setLevel(logging.DEBUG)
47
+
48
+
49
+ protocol.receive_data(byte_http_request)
50
+ events = protocol.events_received()
51
+ event = events[0]
52
+ if isinstance(event, Request):
53
+ # Accept the handshake.
54
+ # After the response is sent, it means the handshake was successful, the protocol.state is State.OPEN
55
+ # Only after this state we can parse frames.
56
+ response = protocol.accept(event)
57
+ return response.serialize()
58
+ else:
59
+ raise ValueError("The event is not a Request object.")
60
+
61
+
62
+ class WebsocketFrameParser:
63
+ def __init__(self):
64
+ # Instantiate the permessage-deflate extension.
65
+ # If a frame uses 'deflate', then the 'permessage_deflate' should be the same object during parsing of
66
+ # several message on the same socket. Each time 'PerMessageDeflate' is initiated, the context changes
67
+ # and more than one message can't be parsed.
68
+ self.permessage_deflate_masked = PerMessageDeflate(
69
+ remote_no_context_takeover=False,
70
+ local_no_context_takeover=False,
71
+ remote_max_window_bits=15,
72
+ local_max_window_bits=15,
73
+ )
74
+
75
+ # We need separate instances for masked (frames from client) and unmasked (frames from server).
76
+ self.permessage_deflate_unmasked = PerMessageDeflate(
77
+ remote_no_context_takeover=False,
78
+ local_no_context_takeover=False,
79
+ remote_max_window_bits=15,
80
+ local_max_window_bits=15,
81
+ )
82
+
83
+ def parse_frame_bytes(
84
+ self,
85
+ data_bytes: bytes
86
+ ):
87
+ # Define the read_exact function
88
+ def read_exact(n: int) -> Generator[None, None, bytes]:
89
+ return reader.read_exact(n)
90
+
91
+ # Helper function to run generator-based coroutines
92
+ def run_coroutine(coroutine):
93
+ try:
94
+ while True:
95
+ next(coroutine)
96
+ except StopIteration as e:
97
+ return e.value
98
+ except Exception as e:
99
+ raise e # Re-raise exceptions to be handled by the caller
100
+
101
+ # Function to parse frames
102
+ def parse_frame(mask: bool, deflate: bool):
103
+ try:
104
+ if mask:
105
+ # Decide whether to include permessage-deflate extension
106
+ extensions = [self.permessage_deflate_masked] if deflate else []
107
+ else:
108
+ extensions = [self.permessage_deflate_unmasked] if deflate else []
109
+
110
+ # Use Frame.parse to parse the frame
111
+ frame_parser = Frame.parse(
112
+ read_exact,
113
+ mask=mask, # Client frames are masked
114
+ max_size=None,
115
+ extensions=extensions
116
+ )
117
+ current_frame = run_coroutine(frame_parser)
118
+ except EOFError as e:
119
+ # Not enough data to parse a complete frame
120
+ raise e
121
+ except (ProtocolError, PayloadTooBig) as e:
122
+ print("Error parsing frame:", e)
123
+ raise e
124
+ except Exception as e:
125
+ print("Error parsing frame:", e)
126
+ raise e
127
+ return current_frame
128
+
129
+ def process_frame(current_frame):
130
+ if current_frame.opcode == Opcode.TEXT:
131
+ message = current_frame.data.decode('utf-8', errors='replace')
132
+ return message, 'TEXT'
133
+ elif current_frame.opcode == Opcode.BINARY:
134
+ return current_frame.data, 'BINARY'
135
+ elif current_frame.opcode == Opcode.CLOSE:
136
+ return current_frame.data, 'CLOSE'
137
+ elif current_frame.opcode == Opcode.PING:
138
+ return current_frame.data, 'PING'
139
+ elif current_frame.opcode == Opcode.PONG:
140
+ return current_frame.data, 'PONG'
141
+ else:
142
+ raise WebsocketParseWrongOpcode("Received unknown frame with opcode:", current_frame.opcode)
143
+
144
+ # Create the StreamReader instance
145
+ reader = StreamReader()
146
+
147
+ masked = is_frame_masked(data_bytes)
148
+ deflated = is_frame_deflated(data_bytes)
149
+
150
+ # Feed the data into the reader
151
+ reader.feed_data(data_bytes)
152
+
153
+ # Parse and process frames
154
+ frame = parse_frame(masked, deflated)
155
+ parsed_frame, frame_opcode = process_frame(frame)
156
+
157
+ # This is basically not needed since we restart the 'reader = StreamReader()' each function execution.
158
+ # # After processing, reset the reader's buffer
159
+ # reader.buffer = b''
160
+
161
+ result: dict = {
162
+ 'is_deflated': deflated,
163
+ 'is_masked': masked,
164
+ 'frame': parsed_frame,
165
+ 'opcode': frame_opcode
166
+ }
167
+
168
+ return result
169
+
170
+
171
+ def create_websocket_frame(
172
+ data: Union[str, bytes, bytearray],
173
+ deflate: bool = False,
174
+ mask: bool = False,
175
+ opcode: int = None
176
+ ) -> bytes:
177
+ """
178
+ Create a WebSocket frame with the given data, optionally applying
179
+ permessage-deflate compression and masking.
180
+
181
+ Parameters:
182
+ - data (str, bytes, bytearray): The payload data.
183
+ If str, it will be encoded to bytes using UTF-8.
184
+ - deflate (bool): Whether to apply permessage-deflate compression.
185
+ - mask (bool): Whether to apply masking to the frame.
186
+ - opcode (int): The opcode of the frame. If not provided, it will be
187
+ determined based on the type of data.
188
+ Example:
189
+ from websockets.frames import Opcode
190
+ Opcode.TEXT, Opcode.BINARY, Opcode.CLOSE, Opcode.PING, Opcode.PONG.
191
+
192
+ Returns:
193
+ - bytes: The serialized WebSocket frame ready to be sent.
194
+ """
195
+
196
+ # Determine the opcode if not provided
197
+ if opcode is None:
198
+ if isinstance(data, str):
199
+ opcode = Opcode.TEXT
200
+ elif isinstance(data, (bytes, bytearray)):
201
+ opcode = Opcode.BINARY
202
+ else:
203
+ raise TypeError("Data must be of type str, bytes, or bytearray.")
204
+ else:
205
+ if not isinstance(opcode, int):
206
+ raise TypeError("Opcode must be an integer.")
207
+ if not isinstance(data, (str, bytes, bytearray)):
208
+ raise TypeError("Data must be of type str, bytes, or bytearray.")
209
+
210
+ # Encode string data if necessary
211
+ if isinstance(data, str):
212
+ payload = data.encode('utf-8')
213
+ else:
214
+ payload = bytes(data)
215
+
216
+ # Create the Frame instance
217
+ frame = Frame(opcode=opcode, data=payload)
218
+
219
+ # Set up extensions if deflate is True
220
+ extensions = []
221
+ if deflate:
222
+ permessage_deflate = PerMessageDeflate(
223
+ remote_no_context_takeover=False,
224
+ local_no_context_takeover=False,
225
+ remote_max_window_bits=15,
226
+ local_max_window_bits=15,
227
+ )
228
+ extensions.append(permessage_deflate)
229
+
230
+ # Serialize the frame with the specified options
231
+ try:
232
+ frame_bytes = frame.serialize(
233
+ mask=mask,
234
+ extensions=extensions,
235
+ )
236
+ except Exception as e:
237
+ raise RuntimeError(f"Error serializing frame: {e}")
238
+
239
+ return frame_bytes
240
+
241
+
242
+ def is_frame_masked(frame_bytes: bytes):
243
+ """
244
+ Determine whether a WebSocket frame is masked.
245
+
246
+ Parameters:
247
+ - frame_bytes (bytes): The raw bytes of the WebSocket frame.
248
+
249
+ Returns:
250
+ - bool: True if the frame is masked, False otherwise.
251
+ """
252
+ if len(frame_bytes) < 2:
253
+ raise ValueError("Frame is too short to determine masking.")
254
+
255
+ # The second byte of the frame header contains the MASK bit
256
+ second_byte = frame_bytes[1]
257
+
258
+ # The MASK bit is the most significant bit (MSB) of the second byte
259
+ mask_bit = (second_byte & 0x80) != 0 # 0x80 is 1000 0000 in binary
260
+
261
+ return mask_bit
262
+
263
+
264
+ def is_frame_deflated(frame_bytes):
265
+ """
266
+ Determine whether a WebSocket frame is deflated (compressed).
267
+
268
+ Parameters:
269
+ - frame_bytes (bytes): The raw bytes of the WebSocket frame.
270
+
271
+ Returns:
272
+ - bool: True if the frame is deflated (compressed), False otherwise.
273
+ """
274
+ if len(frame_bytes) < 1:
275
+ raise ValueError("Frame is too short to determine deflation status.")
276
+
277
+ # The first byte of the frame header contains the RSV1 bit
278
+ first_byte = frame_bytes[0]
279
+
280
+ # The RSV1 bit is the second most significant bit (bit 6)
281
+ rsv1 = (first_byte & 0x40) != 0 # 0x40 is 0100 0000 in binary
282
+
283
+ return rsv1
284
+
285
+
286
+ class _WebsocketRequestParse:
287
+ """
288
+ THIS IS ONLY FOR THE REFERENCE IT IS NOT CURRENTLY USED OR SHOULD BE USED.
289
+ Parse the websocket request and return the data
290
+ """
291
+ def __init__(
292
+ self,
293
+ enable_logging: bool = False,
294
+ ):
295
+ """
296
+ Initialize the websocket parser.
297
+
298
+ :param enable_logging: bool: Enable logging for the websocket protocol.
299
+ """
300
+ # noinspection PyTypeChecker
301
+ self.request_bytes: bytes = None
302
+
303
+ # Set up extensions
304
+ permessage_deflate_factory = ServerPerMessageDeflateFactory()
305
+
306
+ # Create the protocol instance
307
+ self.protocol = ServerProtocol(
308
+ extensions=[permessage_deflate_factory],
309
+ )
310
+ # At this state the protocol.state is State.CONNECTING
311
+
312
+ if enable_logging:
313
+ logging.basicConfig(level=logging.DEBUG)
314
+ self.protocol.logger.setLevel(logging.DEBUG)
315
+
316
+ def parse(
317
+ self,
318
+ request_bytes: bytes
319
+ ) -> Union[str, bytes, Request]:
320
+ """
321
+ Parse the websocket request and return the data
322
+
323
+ :param request_bytes: bytes: The raw bytes of the websocket request.
324
+ :return: Request: The parsed request object.
325
+ """
326
+
327
+ self.protocol.receive_data(request_bytes)
328
+ events = self.protocol.events_received()
329
+ for event in events:
330
+ if isinstance(event, Request):
331
+ # Accept the handshake.
332
+ # After the response is sent, it means the handshake was successful, the protocol.state is State.OPEN
333
+ # Only after this state we can parse frames.
334
+ response = self.protocol.accept(event)
335
+ self.protocol.send_response(response)
336
+ return event
337
+ elif isinstance(event, Frame):
338
+ frame = event
339
+ if frame.opcode == Opcode.TEXT:
340
+ message = frame.data.decode('utf-8')
341
+ return message
342
+ elif frame.opcode == Opcode.BINARY:
343
+ return frame.data
344
+
345
+ """
346
+ # Handle control frames, these are here for the future references.
347
+ elif frame.opcode == Opcode.CLOSE:
348
+ close_info = Close.parse(frame.data)
349
+ print(f"Connection closed by client: {close_info.code}, {close_info.reason}")
350
+ # Send a close frame in response if not already sent
351
+ if self.protocol.state == self.protocol.OPEN:
352
+ self.protocol.send_close()
353
+ elif frame.opcode == Opcode.PING:
354
+ # Respond to ping with pong
355
+ self.protocol.send_pong(frame.data)
356
+ elif frame.opcode == Opcode.PONG:
357
+ print("Received pong")
358
+ """
359
+
360
+
361
+ class _WebsocketResponseParse:
362
+ """
363
+ THIS IS ONLY FOR THE REFERENCE IT IS NOT CURRENTLY USED OR SHOULD BE USED.
364
+ Parse the websocket response and return the data
365
+ """
366
+ def __init__(
367
+ self,
368
+ enable_logging: bool = False,
369
+ ):
370
+ """
371
+ Initialize the websocket parser.
372
+
373
+ :param enable_logging: bool: Enable logging for the websocket protocol.
374
+ """
375
+ # noinspection PyTypeChecker
376
+ self.response_bytes: bytes = None
377
+
378
+ # Set up extensions
379
+ permessage_deflate_factory = ClientPerMessageDeflateFactory()
380
+
381
+ # Parse the WebSocket URI.
382
+ # Since we're parsing the response, we don't need the URI, but the protocol object requires it.
383
+ # So we will just use a dummy URI.
384
+ wsuri = parse_uri('ws://example.com/websocket')
385
+
386
+ # Create the protocol instance
387
+ self.protocol = ClientProtocol(
388
+ wsuri,
389
+ extensions=[permessage_deflate_factory],
390
+ )
391
+
392
+ if enable_logging:
393
+ logging.basicConfig(level=logging.DEBUG)
394
+ # self.protocol.logger.setLevel(logging.DEBUG)
395
+ self.protocol.debug = True
396
+
397
+ # Perform the handshake and emulate the connection and request sending.
398
+ request = self.protocol.connect()
399
+ self.protocol.send_request(request)
400
+ _ = self.protocol.data_to_send()
401
+ # At this state the protocol.state is State.CONNECTING
402
+
403
+ def parse(
404
+ self,
405
+ response_bytes: bytes
406
+ ) -> Union[str, bytes, Response]:
407
+ """
408
+ Parse the websocket response and return the data
409
+
410
+ :param response_bytes: bytes: The raw bytes of the websocket response.
411
+ :return: The parsed response.
412
+ """
413
+
414
+ self.protocol.receive_data(response_bytes)
415
+ events = self.protocol.events_received()
416
+ for event in events:
417
+ if isinstance(event, Response):
418
+ # Accept the handshake.
419
+ # After the response is sent, it means the handshake was successful, the protocol.state is State.OPEN
420
+ # Only after this state we can parse frames.
421
+ try:
422
+ self.protocol.process_response(event)
423
+ except InvalidHeaderValue as e:
424
+ headers = event.headers
425
+ self.protocol.extensions = self.protocol.process_extensions(headers)
426
+ self.protocol.subprotocol = self.protocol.process_subprotocol(headers)
427
+ self.protocol.state = OPEN
428
+ return event
429
+ elif isinstance(event, Frame):
430
+ frame = event
431
+ if frame.opcode == Opcode.TEXT:
432
+ message = frame.data.decode('utf-8')
433
+ return message
434
+ elif frame.opcode == Opcode.BINARY:
435
+ return frame.data
436
+
437
+ """
438
+ # Handle control frames, these are here for the future references.
439
+ elif frame.opcode == Opcode.CLOSE:
440
+ close_info = Close.parse(frame.data)
441
+ print(f"Connection closed by client: {close_info.code}, {close_info.reason}")
442
+ # Send a close frame in response if not already sent
443
+ if self.protocol.state == self.protocol.OPEN:
444
+ self.protocol.send_close()
445
+ elif frame.opcode == Opcode.PING:
446
+ # Respond to ping with pong
447
+ self.protocol.send_pong(frame.data)
448
+ elif frame.opcode == Opcode.PONG:
449
+ print("Received pong")
450
+ """
@@ -198,6 +198,7 @@ class CertificateAuthority(object):
198
198
 
199
199
  cert.set_issuer(cert.get_subject())
200
200
  cert.set_pubkey(key)
201
+
201
202
  cert.add_extensions([
202
203
  crypto.X509Extension(b"basicConstraints",
203
204
  True,
@@ -1,5 +1,6 @@
1
1
  from typing import Union
2
2
  import os
3
+ import warnings
3
4
 
4
5
  from ..print_api import print_api
5
6
  from ..file_io import file_io
@@ -9,6 +10,7 @@ from cryptography.x509 import Certificate
9
10
  from cryptography.hazmat.primitives.asymmetric import rsa
10
11
  from cryptography.hazmat.primitives import serialization
11
12
  from cryptography.hazmat.primitives import hashes
13
+ from cryptography.utils import CryptographyDeprecationWarning
12
14
 
13
15
 
14
16
  """
@@ -22,7 +24,10 @@ OID_TO_BUILDER_CLASS_EXTENSION_NAME: dict = {
22
24
  }
23
25
 
24
26
 
25
- def convert_object_to_x509(certificate):
27
+ def convert_object_to_x509(
28
+ certificate: Union[str, bytes, Certificate],
29
+ print_kwargs: dict = None
30
+ ) -> x509.Certificate | None:
26
31
  """Convert certificate to x509 object.
27
32
 
28
33
  :param certificate: any object that can be converted to x509 object.
@@ -32,13 +37,16 @@ def convert_object_to_x509(certificate):
32
37
  string that is PEM certificate will be converted to bytes, then x509.Certificate
33
38
  bytes of PEM or DER will be converted to x509.Certificate.
34
39
  x509.Certificate will be returned as is.
35
- :return: certificate in x509 object of 'cryptography' module.
40
+ :param print_kwargs: dict, additional arguments to pass to the print function.
41
+ :return: certificate in x509 object of 'cryptography' module. Or None if the certificate is not valid.
36
42
  """
37
43
 
38
44
  # Check if 'certificate' is a string and a path.
39
- if isinstance(certificate, str) and os.path.isfile(certificate):
45
+ if isinstance(certificate, str):
46
+ if not os.path.isfile(certificate):
47
+ raise FileNotFoundError(f'File not found: {certificate}')
40
48
  # Import the certificate from the path.
41
- certificate = file_io.read_file(certificate, file_mode='rb')
49
+ certificate = file_io.read_file(certificate, file_mode='rb', **(print_kwargs or {}))
42
50
 
43
51
  # Check if 'certificate' is a bytes object and PEM format.
44
52
  # We're checking if it starts with '-----BEGIN ' since the pem certificate can include PRIVATE KEY and will be
@@ -80,14 +88,27 @@ def convert_pem_to_x509_object(certificate: Union[str, bytes]) -> x509.Certifica
80
88
  return x509.load_pem_x509_certificate(certificate)
81
89
 
82
90
 
83
- def convert_der_to_x509_object(certificate: bytes):
91
+ def convert_der_to_x509_object(certificate: bytes) -> x509.Certificate | None:
84
92
  """Convert DER certificate from socket to x509 object.
85
93
 
86
94
  :param certificate: bytes, certificate to convert.
87
95
  :return: certificate in x509 object of 'cryptography' module.
88
96
  """
89
97
 
90
- return x509.load_der_x509_certificate(certificate)
98
+ # Some certificates in the store can have zero or negative serial number.
99
+ # We will skip them, since they're deprecated by the cryptography library.
100
+
101
+ try:
102
+ with warnings.catch_warnings():
103
+ # Turn the deprecation warning into an exception we can trap.
104
+ warnings.filterwarnings("error", category=CryptographyDeprecationWarning)
105
+ converted_certificate = x509.load_der_x509_certificate(certificate)
106
+ except (CryptographyDeprecationWarning, ValueError):
107
+ return None # serial was 0/negative → skip
108
+ if converted_certificate.serial_number <= 0: # belt-and-braces
109
+ return None
110
+
111
+ return converted_certificate
91
112
 
92
113
 
93
114
  def convert_x509_object_to_pem_bytes(certificate) -> bytes:
@@ -151,8 +172,8 @@ def copy_extensions_from_old_cert_to_new_cert(
151
172
  builder = x509.CertificateBuilder()
152
173
  builder = builder.subject_name(certificate.subject)
153
174
  builder = builder.issuer_name(certificate.issuer)
154
- builder = builder.not_valid_before(certificate.not_valid_before)
155
- builder = builder.not_valid_after(certificate.not_valid_after)
175
+ builder = builder.not_valid_before(certificate.not_valid_before_utc)
176
+ builder = builder.not_valid_after(certificate.not_valid_after_utc)
156
177
  builder = builder.serial_number(certificate.serial_number)
157
178
 
158
179
  # We're using the new private key that we will sign with the new certificate later.