swp-protocol 0.0.1__tar.gz

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.
@@ -0,0 +1,276 @@
1
+ Metadata-Version: 2.4
2
+ Name: swp_protocol
3
+ Version: 0.0.1
4
+ Summary: protocol for wrapping packets
5
+ Home-page: https://github.com/sekret01/SWP
6
+ Author: Sekret
7
+ Author-email: asinskijp188@gmail.com
8
+ Classifier: Programming Language :: Python :: 3.8
9
+ Classifier: Programming Language :: Python :: 3.9
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Requires-Python: >=3.8
16
+ Description-Content-Type: text/markdown
17
+ Requires-Dist: cryptography>=48.0.0
18
+ Dynamic: author
19
+ Dynamic: author-email
20
+ Dynamic: classifier
21
+ Dynamic: description
22
+ Dynamic: description-content-type
23
+ Dynamic: home-page
24
+ Dynamic: requires-dist
25
+ Dynamic: requires-python
26
+ Dynamic: summary
27
+
28
+
29
+ # Sekret Wrapper Protocol (SWP)
30
+
31
+ Протокол для оборачивания пакетов. Используется для передачи данных между узлами прокси.
32
+
33
+ ## Установка
34
+
35
+ ```bash
36
+ pip install swp
37
+ ```
38
+
39
+ ---
40
+
41
+ ## Список объектов
42
+
43
+ - `SWP`
44
+ - `ProtocolError`
45
+ - `AesGcmCrypto`
46
+ - `MessageType`
47
+
48
+ ---
49
+
50
+ ### `SWP`
51
+
52
+ Класс, реализующий функции упаковки данных в протокол SWP и распаковки обратно. Не создаёт объекты, работает с байтами.
53
+
54
+ **Атрибуты класса:**
55
+
56
+ | Атрибут | Тип | Значение по умолчанию | Описание |
57
+ |---------|-----|----------------------|----------|
58
+ | `AESGCM` | `bytes` | `b""` | Ключ для AES-GCM шифрования |
59
+ | `AUTO_ENCRYPT` | `bool` | `False` | Флаг автоматического шифрования полезной нагрузки |
60
+
61
+ ---
62
+
63
+ ### `ProtocolError`
64
+
65
+ Базовый класс ошибок протокола.
66
+
67
+ **Наследники:**
68
+ - `AesGcmKeyError` — ошибка, связанная с AES-GCM ключом (неверная длина или отсутствие)
69
+ - `HeaderNotCompliteError` — заголовок SWP-пакета получен не полностью
70
+
71
+ ---
72
+
73
+ ### `AesGcmCrypto`
74
+
75
+ Внутренний класс шифрования данных пакета. По умолчанию автоматическое шифрование выключено. Данный модуль не предполагает вмешательства разработчика.
76
+
77
+ ---
78
+
79
+ ### `MessageType`
80
+
81
+ Класс-перечисление типов сообщений.
82
+
83
+ | Тип | Значение | Описание |
84
+ |-----|----------|----------|
85
+ | `MSG_CONNECT` | `0x01` | Соединение с сервером |
86
+ | `MSG_DATA` | `0x02` | Передача сообщения |
87
+ | `MSG_CLOSE` | `0x03` | Отключение от сервера |
88
+
89
+ > **Note:** Предназначен для использования в функции `SWP.pack()`.
90
+
91
+ ---
92
+
93
+ ## Формат пакета SWP
94
+
95
+ | Поле | Размер (байт) | Тип | Описание |
96
+ |------|--------------|-----|----------|
97
+ | `magic` | 3 | `bytes` | Магическое число `\x53\x57\x50` |
98
+ | `msg_type` | 1 | `byte` | Тип сообщения |
99
+ | `target_len` | 1 | `byte` | Длина поля `target` |
100
+ | `payload_len` | 2 | `short` | Длина поля `payload` |
101
+ | `target` | `target_len` | `bytes` | Адрес получателя (домен, IP:port) |
102
+ | `payload` | `payload_len` | `bytes` | Полезная нагрузка |
103
+
104
+ ---
105
+
106
+ ## Пример использования
107
+
108
+ ```python
109
+ from swp import SWP, MessageType
110
+
111
+ # Загрузка ключа — автошифрование включается автоматически
112
+ SWP.load_aesgcm(b"32_byte_key_for_aes_gcm_encryption!!")
113
+
114
+ # Упаковка сообщения (будет зашифровано)
115
+ packet = SWP.pack(
116
+ msg_type=MessageType.MSG_DATA,
117
+ target=b"https://example-domain.ex",
118
+ payload=b"Hello, world!"
119
+ )
120
+
121
+ # Распаковка
122
+ data = SWP.unpack(packet)
123
+ print(data["msg_type"], data["target"], data["payload"])
124
+
125
+ # Отключение шифрования
126
+ SWP.set_auto_encrypt(False)
127
+ ```
128
+
129
+ ---
130
+
131
+ ## Методы класса SWP
132
+
133
+ ---
134
+
135
+ ### `reset()`
136
+
137
+ Сброс параметров класса до начальных значений.
138
+
139
+ | Параметр | Тип | Описание |
140
+ |----------|-----|----------|
141
+ | — | — | — |
142
+
143
+ **Возвращает:**
144
+ `None`
145
+
146
+ ---
147
+
148
+ ### `set_auto_encrypt(value: bool) -> None`
149
+
150
+ Включение или отключение автоматического шифрования тела пакетов.
151
+
152
+ | Параметр | Тип | Описание |
153
+ |----------|-----|----------|
154
+ | `value` | `bool` | `True` — включить автоматическое шифрование, `False` — выключить |
155
+
156
+ **Возвращает:**
157
+ `None`
158
+
159
+ **Исключения:**
160
+ `AesGcmKeyError` — если не загружен AES-GCM ключ при попытке включить шифрование.
161
+
162
+ ---
163
+
164
+ ### `pack(msg_type, target, payload=b"") -> bytes`
165
+
166
+ Создание SWP-пакета из переданных данных.
167
+
168
+ | Параметр | Тип | Описание |
169
+ |----------|-----|----------|
170
+ | `msg_type` | `int` | Тип сообщения (рекомендуется использовать `MessageType`) |
171
+ | `target` | `bytes` | Адрес получателя |
172
+ | `payload` | `bytes` | Полезная нагрузка (по умолчанию пустая строка) |
173
+
174
+ **Возвращает:**
175
+ `bytes` — сформированный SWP-пакет (заголовок + `target` + `payload`).
176
+
177
+ > **Note:** Если включён `AUTO_ENCRYPT`, `payload` будет зашифрован перед упаковкой.
178
+
179
+ ---
180
+
181
+ ### `unpack_header(buffer) -> tuple[bytes, int, int, int]`
182
+
183
+ Извлечение данных заголовка из сырого пакета.
184
+
185
+ | Параметр | Тип | Описание |
186
+ |----------|-----|----------|
187
+ | `buffer` | `bytes` | Сырой SWP-пакет (полный или частичный) |
188
+
189
+ **Возвращает:**
190
+ `tuple[bytes, int, int, int]` — кортеж из четырёх элементов:
191
+ (`PROTOCOL_MAGIC`, `msg_type`, `target_len`, `payload_len`)
192
+
193
+ **Исключения:**
194
+ `HeaderNotCompliteError` — если размер буфера меньше размера заголовка (`HEADER_SIZE`).
195
+
196
+ ---
197
+
198
+ ### `unpack(buffer) -> dict`
199
+
200
+ Полная распаковка SWP-пакета с расшифровкой (если включена).
201
+
202
+ | Параметр | Тип | Описание |
203
+ |----------|-----|----------|
204
+ | `buffer` | `bytes` | Полный SWP-пакет |
205
+
206
+ **Возвращает:**
207
+ `dict` — словарь с ключами:
208
+ - `"msg_type"` (`int`) — тип сообщения,
209
+ - `"target"` (`bytes`) — адрес получателя,
210
+ - `"payload"` (`bytes`) — полезная нагрузка (расшифрованная, если `AUTO_ENCRYPT = True`).
211
+
212
+ ---
213
+
214
+ ### `get_full_package_size(buffer) -> int`
215
+
216
+ Определение полного размера пакета по его заголовку.
217
+
218
+ | Параметр | Тип | Описание |
219
+ |----------|-----|----------|
220
+ | `buffer` | `bytes` | Начало SWP-пакета (минимум размер заголовка) |
221
+
222
+ **Возвращает:**
223
+ `int` — полный размер пакета (`HEADER_SIZE + target_len + payload_len`).
224
+ `-1` — если переданных данных меньше размера заголовка.
225
+
226
+ ---
227
+
228
+ ### `load_aesgcm(key) -> None`
229
+
230
+ Загрузка 32-байтного AES-GCM ключа и автоматическое включение шифрования.
231
+
232
+ | Параметр | Тип | Описание |
233
+ |----------|-----|----------|
234
+ | `key` | `bytes` | Ключ шифрования (должен быть ровно 32 байта) |
235
+
236
+ **Возвращает:**
237
+ `None`
238
+
239
+ **Исключения:**
240
+ `AesGcmKeyError` — если длина ключа не равна 32.
241
+
242
+ > **Note:** Метод загружает ключ в `SWP.AESGCM` и автоматически устанавливает `SWP.AUTO_ENCRYPT = True`.
243
+
244
+ ---
245
+
246
+ ### `encode(message) -> bytes`
247
+
248
+ Шифрование сообщения с использованием загруженного AES-GCM ключа.
249
+
250
+ | Параметр | Тип | Описание |
251
+ |----------|-----|----------|
252
+ | `message` | `str` или `bytes` | Исходное сообщение (строка автоматически кодируется в UTF-8) |
253
+
254
+ **Возвращает:**
255
+ `bytes` — зашифрованное сообщение.
256
+
257
+ ---
258
+
259
+ ### `decode(message) -> bytes`
260
+
261
+ Расшифровка сообщения.
262
+
263
+ | Параметр | Тип | Описание |
264
+ |----------|-----|----------|
265
+ | `message` | `bytes` | Зашифрованное сообщение |
266
+
267
+ **Возвращает:**
268
+ `bytes` — расшифрованное сообщение.
269
+
270
+ ---
271
+
272
+ ## Информация о проекте
273
+
274
+ - **Версия:** 0.0.1
275
+ - **Python:** 3.8+
276
+ - **Репозиторий:** [github.com/sekret01/SWP](https://github.com/sekret01/SWP.git)
@@ -0,0 +1,249 @@
1
+
2
+ # Sekret Wrapper Protocol (SWP)
3
+
4
+ Протокол для оборачивания пакетов. Используется для передачи данных между узлами прокси.
5
+
6
+ ## Установка
7
+
8
+ ```bash
9
+ pip install swp
10
+ ```
11
+
12
+ ---
13
+
14
+ ## Список объектов
15
+
16
+ - `SWP`
17
+ - `ProtocolError`
18
+ - `AesGcmCrypto`
19
+ - `MessageType`
20
+
21
+ ---
22
+
23
+ ### `SWP`
24
+
25
+ Класс, реализующий функции упаковки данных в протокол SWP и распаковки обратно. Не создаёт объекты, работает с байтами.
26
+
27
+ **Атрибуты класса:**
28
+
29
+ | Атрибут | Тип | Значение по умолчанию | Описание |
30
+ |---------|-----|----------------------|----------|
31
+ | `AESGCM` | `bytes` | `b""` | Ключ для AES-GCM шифрования |
32
+ | `AUTO_ENCRYPT` | `bool` | `False` | Флаг автоматического шифрования полезной нагрузки |
33
+
34
+ ---
35
+
36
+ ### `ProtocolError`
37
+
38
+ Базовый класс ошибок протокола.
39
+
40
+ **Наследники:**
41
+ - `AesGcmKeyError` — ошибка, связанная с AES-GCM ключом (неверная длина или отсутствие)
42
+ - `HeaderNotCompliteError` — заголовок SWP-пакета получен не полностью
43
+
44
+ ---
45
+
46
+ ### `AesGcmCrypto`
47
+
48
+ Внутренний класс шифрования данных пакета. По умолчанию автоматическое шифрование выключено. Данный модуль не предполагает вмешательства разработчика.
49
+
50
+ ---
51
+
52
+ ### `MessageType`
53
+
54
+ Класс-перечисление типов сообщений.
55
+
56
+ | Тип | Значение | Описание |
57
+ |-----|----------|----------|
58
+ | `MSG_CONNECT` | `0x01` | Соединение с сервером |
59
+ | `MSG_DATA` | `0x02` | Передача сообщения |
60
+ | `MSG_CLOSE` | `0x03` | Отключение от сервера |
61
+
62
+ > **Note:** Предназначен для использования в функции `SWP.pack()`.
63
+
64
+ ---
65
+
66
+ ## Формат пакета SWP
67
+
68
+ | Поле | Размер (байт) | Тип | Описание |
69
+ |------|--------------|-----|----------|
70
+ | `magic` | 3 | `bytes` | Магическое число `\x53\x57\x50` |
71
+ | `msg_type` | 1 | `byte` | Тип сообщения |
72
+ | `target_len` | 1 | `byte` | Длина поля `target` |
73
+ | `payload_len` | 2 | `short` | Длина поля `payload` |
74
+ | `target` | `target_len` | `bytes` | Адрес получателя (домен, IP:port) |
75
+ | `payload` | `payload_len` | `bytes` | Полезная нагрузка |
76
+
77
+ ---
78
+
79
+ ## Пример использования
80
+
81
+ ```python
82
+ from swp import SWP, MessageType
83
+
84
+ # Загрузка ключа — автошифрование включается автоматически
85
+ SWP.load_aesgcm(b"32_byte_key_for_aes_gcm_encryption!!")
86
+
87
+ # Упаковка сообщения (будет зашифровано)
88
+ packet = SWP.pack(
89
+ msg_type=MessageType.MSG_DATA,
90
+ target=b"https://example-domain.ex",
91
+ payload=b"Hello, world!"
92
+ )
93
+
94
+ # Распаковка
95
+ data = SWP.unpack(packet)
96
+ print(data["msg_type"], data["target"], data["payload"])
97
+
98
+ # Отключение шифрования
99
+ SWP.set_auto_encrypt(False)
100
+ ```
101
+
102
+ ---
103
+
104
+ ## Методы класса SWP
105
+
106
+ ---
107
+
108
+ ### `reset()`
109
+
110
+ Сброс параметров класса до начальных значений.
111
+
112
+ | Параметр | Тип | Описание |
113
+ |----------|-----|----------|
114
+ | — | — | — |
115
+
116
+ **Возвращает:**
117
+ `None`
118
+
119
+ ---
120
+
121
+ ### `set_auto_encrypt(value: bool) -> None`
122
+
123
+ Включение или отключение автоматического шифрования тела пакетов.
124
+
125
+ | Параметр | Тип | Описание |
126
+ |----------|-----|----------|
127
+ | `value` | `bool` | `True` — включить автоматическое шифрование, `False` — выключить |
128
+
129
+ **Возвращает:**
130
+ `None`
131
+
132
+ **Исключения:**
133
+ `AesGcmKeyError` — если не загружен AES-GCM ключ при попытке включить шифрование.
134
+
135
+ ---
136
+
137
+ ### `pack(msg_type, target, payload=b"") -> bytes`
138
+
139
+ Создание SWP-пакета из переданных данных.
140
+
141
+ | Параметр | Тип | Описание |
142
+ |----------|-----|----------|
143
+ | `msg_type` | `int` | Тип сообщения (рекомендуется использовать `MessageType`) |
144
+ | `target` | `bytes` | Адрес получателя |
145
+ | `payload` | `bytes` | Полезная нагрузка (по умолчанию пустая строка) |
146
+
147
+ **Возвращает:**
148
+ `bytes` — сформированный SWP-пакет (заголовок + `target` + `payload`).
149
+
150
+ > **Note:** Если включён `AUTO_ENCRYPT`, `payload` будет зашифрован перед упаковкой.
151
+
152
+ ---
153
+
154
+ ### `unpack_header(buffer) -> tuple[bytes, int, int, int]`
155
+
156
+ Извлечение данных заголовка из сырого пакета.
157
+
158
+ | Параметр | Тип | Описание |
159
+ |----------|-----|----------|
160
+ | `buffer` | `bytes` | Сырой SWP-пакет (полный или частичный) |
161
+
162
+ **Возвращает:**
163
+ `tuple[bytes, int, int, int]` — кортеж из четырёх элементов:
164
+ (`PROTOCOL_MAGIC`, `msg_type`, `target_len`, `payload_len`)
165
+
166
+ **Исключения:**
167
+ `HeaderNotCompliteError` — если размер буфера меньше размера заголовка (`HEADER_SIZE`).
168
+
169
+ ---
170
+
171
+ ### `unpack(buffer) -> dict`
172
+
173
+ Полная распаковка SWP-пакета с расшифровкой (если включена).
174
+
175
+ | Параметр | Тип | Описание |
176
+ |----------|-----|----------|
177
+ | `buffer` | `bytes` | Полный SWP-пакет |
178
+
179
+ **Возвращает:**
180
+ `dict` — словарь с ключами:
181
+ - `"msg_type"` (`int`) — тип сообщения,
182
+ - `"target"` (`bytes`) — адрес получателя,
183
+ - `"payload"` (`bytes`) — полезная нагрузка (расшифрованная, если `AUTO_ENCRYPT = True`).
184
+
185
+ ---
186
+
187
+ ### `get_full_package_size(buffer) -> int`
188
+
189
+ Определение полного размера пакета по его заголовку.
190
+
191
+ | Параметр | Тип | Описание |
192
+ |----------|-----|----------|
193
+ | `buffer` | `bytes` | Начало SWP-пакета (минимум размер заголовка) |
194
+
195
+ **Возвращает:**
196
+ `int` — полный размер пакета (`HEADER_SIZE + target_len + payload_len`).
197
+ `-1` — если переданных данных меньше размера заголовка.
198
+
199
+ ---
200
+
201
+ ### `load_aesgcm(key) -> None`
202
+
203
+ Загрузка 32-байтного AES-GCM ключа и автоматическое включение шифрования.
204
+
205
+ | Параметр | Тип | Описание |
206
+ |----------|-----|----------|
207
+ | `key` | `bytes` | Ключ шифрования (должен быть ровно 32 байта) |
208
+
209
+ **Возвращает:**
210
+ `None`
211
+
212
+ **Исключения:**
213
+ `AesGcmKeyError` — если длина ключа не равна 32.
214
+
215
+ > **Note:** Метод загружает ключ в `SWP.AESGCM` и автоматически устанавливает `SWP.AUTO_ENCRYPT = True`.
216
+
217
+ ---
218
+
219
+ ### `encode(message) -> bytes`
220
+
221
+ Шифрование сообщения с использованием загруженного AES-GCM ключа.
222
+
223
+ | Параметр | Тип | Описание |
224
+ |----------|-----|----------|
225
+ | `message` | `str` или `bytes` | Исходное сообщение (строка автоматически кодируется в UTF-8) |
226
+
227
+ **Возвращает:**
228
+ `bytes` — зашифрованное сообщение.
229
+
230
+ ---
231
+
232
+ ### `decode(message) -> bytes`
233
+
234
+ Расшифровка сообщения.
235
+
236
+ | Параметр | Тип | Описание |
237
+ |----------|-----|----------|
238
+ | `message` | `bytes` | Зашифрованное сообщение |
239
+
240
+ **Возвращает:**
241
+ `bytes` — расшифрованное сообщение.
242
+
243
+ ---
244
+
245
+ ## Информация о проекте
246
+
247
+ - **Версия:** 0.0.1
248
+ - **Python:** 3.8+
249
+ - **Репозиторий:** [github.com/sekret01/SWP](https://github.com/sekret01/SWP.git)
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,32 @@
1
+ from setuptools import setup, find_packages
2
+
3
+
4
+ def readme():
5
+ with open('README.md', 'r', encoding="utf-8") as f:
6
+ return f.read()
7
+
8
+
9
+ setup(
10
+ name='swp_protocol',
11
+ version='0.0.1',
12
+ author='Sekret',
13
+ author_email='asinskijp188@gmail.com',
14
+ description='protocol for wrapping packets',
15
+ long_description=readme(),
16
+ long_description_content_type='text/markdown',
17
+ packages=find_packages(include=['swp_protocol', 'swp_protocol.*']),
18
+ install_requires=[
19
+ 'cryptography>=48.0.0',
20
+ ],
21
+ classifiers=[
22
+ 'Programming Language :: Python :: 3.8',
23
+ 'Programming Language :: Python :: 3.9',
24
+ 'Programming Language :: Python :: 3.10',
25
+ 'Programming Language :: Python :: 3.11',
26
+ 'Programming Language :: Python :: 3.12',
27
+ 'License :: OSI Approved :: MIT License',
28
+ 'Operating System :: OS Independent',
29
+ ],
30
+ python_requires='>=3.8',
31
+ url='https://github.com/sekret01/SWP',
32
+ )
@@ -0,0 +1,8 @@
1
+ from .sekret_wrapper_protocol import SWP
2
+ from .exceptions import (
3
+ ProtocolError,
4
+ AesGcmKeyError,
5
+ HeaderNotCompliteError
6
+ )
7
+
8
+ from .swp_enums import MessageType
@@ -0,0 +1,29 @@
1
+ import os
2
+
3
+ from cryptography.hazmat.primitives.ciphers.aead import AESGCM
4
+ from swp_protocol.exceptions import AesGcmKeyError
5
+
6
+
7
+ class AesGcmCrypto:
8
+ NONCE_SIZE = 12
9
+
10
+ def __init__(self, key: bytes):
11
+ if len(key) != 32:
12
+ raise AesGcmKeyError(f"Длина ключа AESGCM должна быть равна 32, текущая длина: {len(key)}")
13
+ self.aesgcm = AESGCM(key=key)
14
+
15
+
16
+ def encode(self, payload: bytes) -> bytes:
17
+ """ Шифровка данных """
18
+ nonce = os.urandom(self.NONCE_SIZE)
19
+ ciphertext = self.aesgcm.encrypt(nonce, payload, None)
20
+ return nonce + ciphertext
21
+
22
+ def decode(self, encrypted_payload: bytes) -> bytes:
23
+ """ Расшифровка данных """
24
+ if len(encrypted_payload) < self.NONCE_SIZE:
25
+ raise ValueError("Invalid encrypted data")
26
+ nonce = encrypted_payload[:self.NONCE_SIZE]
27
+ ciphertext = encrypted_payload[self.NONCE_SIZE:]
28
+ return self.aesgcm.decrypt(nonce, ciphertext, None)
29
+
@@ -0,0 +1,16 @@
1
+
2
+ class ProtocolError(Exception):
3
+ """ Базовый класс ошибок протокола """
4
+ def __init__(self, message: str):
5
+ super().__init__(message)
6
+
7
+
8
+ class AesGcmKeyError(ProtocolError):
9
+ """ Ошибка ключа шифрования """
10
+ pass
11
+
12
+
13
+ class HeaderNotCompliteError(ProtocolError):
14
+ """ Ошибка заголовка протокола """
15
+ def __init__(self):
16
+ super().__init__(f"Недостаточно данных для заголовка")
@@ -0,0 +1,132 @@
1
+ import struct
2
+
3
+ from swp_protocol.exceptions import AesGcmKeyError, HeaderNotCompliteError
4
+ from swp_protocol._encoder import AesGcmCrypto
5
+
6
+
7
+ class SWP:
8
+ """
9
+ Sekret Wrapper Protocol
10
+
11
+ Протокол, оборачиваемый поверх нужного протокола
12
+ Вид протокола:
13
+ [sid][msg_type][target_len][payload_len][target][payload]
14
+ """
15
+
16
+ PROTOCOL_MAGIC = b"\x53\x57\x50"
17
+ HEADER_FMT = "!3sBBH"
18
+ HEADER_SIZE = struct.calcsize(HEADER_FMT)
19
+
20
+ MSG_CONNECT = 0x01 # Начало подключения
21
+ MSG_DATA = 0x02 # Передача данных
22
+ MSG_CLOSE = 0x03 # Завершение подключения
23
+
24
+ AESGCM: bytes = b""
25
+ AUTO_ENCRYPT: bool = False
26
+
27
+ def __init__(self, buffer: bytes = None):
28
+ if len(buffer) < SWP.HEADER_SIZE:
29
+ return
30
+
31
+ header = struct.unpack(SWP.HEADER_FMT, buffer[:SWP.HEADER_SIZE])
32
+
33
+ self.msg_type = header[2] # BYTE - тип запроса
34
+ target_len = header[3] # BYTE - длина адреса в payload
35
+ payload_len = header[4] # SHORT - длина target
36
+
37
+ offset = SWP.HEADER_SIZE
38
+ self.target = buffer[offset:offset + target_len]
39
+ self.payload = buffer[offset + target_len : offset + target_len + payload_len]
40
+
41
+ @classmethod
42
+ def reset(cls):
43
+ """ Сброс параметров """
44
+ cls.AESGCM = b""
45
+ cls.AUTO_ENCRYPT = False
46
+
47
+ @classmethod
48
+ def set_auto_encrypt(cls, value: bool) -> None:
49
+ """ Установка автоматического шифрования тела """
50
+ if (SWP.AESGCM == b""):
51
+ raise AesGcmKeyError("Отсутствует AESGCM ключ для шифрования. Выполните команду SWP.load_aesgcm")
52
+ SWP.AUTO_ENCRYPT = value
53
+
54
+ @classmethod
55
+ def pack(cls,
56
+ msg_type: int,
57
+ target: bytes,
58
+ payload: bytes = b""
59
+ ) -> bytes:
60
+ """ Создание пакета из данных """
61
+ if (cls.AUTO_ENCRYPT):
62
+ payload = cls.encode(message=payload)
63
+
64
+ target_len = len(target)
65
+ payload_len = len(payload)
66
+
67
+ print(f"magic:[{cls.PROTOCOL_MAGIC}], type:[{msg_type}], tglen: [{target_len}], plen:[{payload_len}], tg:[{target}], p:[{payload}]")
68
+
69
+ header = struct.pack(
70
+ cls.HEADER_FMT,
71
+ cls.PROTOCOL_MAGIC,
72
+ msg_type,
73
+ target_len,
74
+ payload_len
75
+ )
76
+ return header + target + payload
77
+
78
+ @classmethod
79
+ def unpack_header(cls, buffer: bytes) -> tuple[bytes, int, int, int]:
80
+ """ Извлечь данные заголовка из пакета """
81
+ if len(buffer) < SWP.HEADER_SIZE:
82
+ raise HeaderNotCompliteError()
83
+ return struct.unpack(SWP.HEADER_FMT, buffer[:SWP.HEADER_SIZE])
84
+
85
+ @classmethod
86
+ def unpack(cls, buffer: bytes) -> dict:
87
+ """ Извлечь данные из пакета """
88
+ _, msg_type, target_len, payload_len = SWP.unpack_header(buffer)
89
+ offset = cls.HEADER_SIZE
90
+ target = buffer[offset:offset + target_len]
91
+ offset += target_len
92
+ payload = buffer[offset:offset + payload_len]
93
+
94
+ if cls.AUTO_ENCRYPT:
95
+ payload = cls.decode(message=payload)
96
+
97
+ return {
98
+ "msg_type": msg_type,
99
+ "target": target,
100
+ "payload": payload
101
+ }
102
+
103
+ @classmethod
104
+ def get_full_package_size(cls, buffer: bytes) -> int:
105
+ """ Определение полного размера пакета """
106
+ if len(buffer) < SWP.HEADER_SIZE:
107
+ return -1
108
+ _, _, target_size, payload_size = cls.unpack_header(buffer)
109
+ return cls.HEADER_SIZE + target_size + payload_size
110
+
111
+ @classmethod
112
+ def load_aesgcm(cls, key: bytes) -> None:
113
+ """ Загрузка AESGCM ключа для автоматического шифрования тела пакетов """
114
+ if len(key) != 32:
115
+ raise AesGcmKeyError(f"Длина ключа AESGCM должна быть равна 32, текущая длина: {len(key)}")
116
+ cls.AESGCM = key
117
+ cls.AUTO_ENCRYPT = True
118
+
119
+ @classmethod
120
+ def encode(cls, message: str | bytes) -> bytes:
121
+ """ Шифрование сообщения """
122
+ crypto = AesGcmCrypto(cls.AESGCM)
123
+ if isinstance(message, str):
124
+ message = message.encode()
125
+ return crypto.encode(payload=message)
126
+
127
+ @classmethod
128
+ def decode(cls, message: bytes) -> bytes:
129
+ """ Расшифровка сообщения """
130
+ crypto = AesGcmCrypto(cls.AESGCM)
131
+ return crypto.decode(encrypted_payload=message)
132
+
@@ -0,0 +1,7 @@
1
+ import enum
2
+
3
+ class MessageType(enum.Enum):
4
+
5
+ MSG_CONNECT = 0x01 # Начало подключения
6
+ MSG_DATA = 0x02 # Передача данных
7
+ MSG_CLOSE = 0x03 # Завершение подключения
@@ -0,0 +1,276 @@
1
+ Metadata-Version: 2.4
2
+ Name: swp_protocol
3
+ Version: 0.0.1
4
+ Summary: protocol for wrapping packets
5
+ Home-page: https://github.com/sekret01/SWP
6
+ Author: Sekret
7
+ Author-email: asinskijp188@gmail.com
8
+ Classifier: Programming Language :: Python :: 3.8
9
+ Classifier: Programming Language :: Python :: 3.9
10
+ Classifier: Programming Language :: Python :: 3.10
11
+ Classifier: Programming Language :: Python :: 3.11
12
+ Classifier: Programming Language :: Python :: 3.12
13
+ Classifier: License :: OSI Approved :: MIT License
14
+ Classifier: Operating System :: OS Independent
15
+ Requires-Python: >=3.8
16
+ Description-Content-Type: text/markdown
17
+ Requires-Dist: cryptography>=48.0.0
18
+ Dynamic: author
19
+ Dynamic: author-email
20
+ Dynamic: classifier
21
+ Dynamic: description
22
+ Dynamic: description-content-type
23
+ Dynamic: home-page
24
+ Dynamic: requires-dist
25
+ Dynamic: requires-python
26
+ Dynamic: summary
27
+
28
+
29
+ # Sekret Wrapper Protocol (SWP)
30
+
31
+ Протокол для оборачивания пакетов. Используется для передачи данных между узлами прокси.
32
+
33
+ ## Установка
34
+
35
+ ```bash
36
+ pip install swp
37
+ ```
38
+
39
+ ---
40
+
41
+ ## Список объектов
42
+
43
+ - `SWP`
44
+ - `ProtocolError`
45
+ - `AesGcmCrypto`
46
+ - `MessageType`
47
+
48
+ ---
49
+
50
+ ### `SWP`
51
+
52
+ Класс, реализующий функции упаковки данных в протокол SWP и распаковки обратно. Не создаёт объекты, работает с байтами.
53
+
54
+ **Атрибуты класса:**
55
+
56
+ | Атрибут | Тип | Значение по умолчанию | Описание |
57
+ |---------|-----|----------------------|----------|
58
+ | `AESGCM` | `bytes` | `b""` | Ключ для AES-GCM шифрования |
59
+ | `AUTO_ENCRYPT` | `bool` | `False` | Флаг автоматического шифрования полезной нагрузки |
60
+
61
+ ---
62
+
63
+ ### `ProtocolError`
64
+
65
+ Базовый класс ошибок протокола.
66
+
67
+ **Наследники:**
68
+ - `AesGcmKeyError` — ошибка, связанная с AES-GCM ключом (неверная длина или отсутствие)
69
+ - `HeaderNotCompliteError` — заголовок SWP-пакета получен не полностью
70
+
71
+ ---
72
+
73
+ ### `AesGcmCrypto`
74
+
75
+ Внутренний класс шифрования данных пакета. По умолчанию автоматическое шифрование выключено. Данный модуль не предполагает вмешательства разработчика.
76
+
77
+ ---
78
+
79
+ ### `MessageType`
80
+
81
+ Класс-перечисление типов сообщений.
82
+
83
+ | Тип | Значение | Описание |
84
+ |-----|----------|----------|
85
+ | `MSG_CONNECT` | `0x01` | Соединение с сервером |
86
+ | `MSG_DATA` | `0x02` | Передача сообщения |
87
+ | `MSG_CLOSE` | `0x03` | Отключение от сервера |
88
+
89
+ > **Note:** Предназначен для использования в функции `SWP.pack()`.
90
+
91
+ ---
92
+
93
+ ## Формат пакета SWP
94
+
95
+ | Поле | Размер (байт) | Тип | Описание |
96
+ |------|--------------|-----|----------|
97
+ | `magic` | 3 | `bytes` | Магическое число `\x53\x57\x50` |
98
+ | `msg_type` | 1 | `byte` | Тип сообщения |
99
+ | `target_len` | 1 | `byte` | Длина поля `target` |
100
+ | `payload_len` | 2 | `short` | Длина поля `payload` |
101
+ | `target` | `target_len` | `bytes` | Адрес получателя (домен, IP:port) |
102
+ | `payload` | `payload_len` | `bytes` | Полезная нагрузка |
103
+
104
+ ---
105
+
106
+ ## Пример использования
107
+
108
+ ```python
109
+ from swp import SWP, MessageType
110
+
111
+ # Загрузка ключа — автошифрование включается автоматически
112
+ SWP.load_aesgcm(b"32_byte_key_for_aes_gcm_encryption!!")
113
+
114
+ # Упаковка сообщения (будет зашифровано)
115
+ packet = SWP.pack(
116
+ msg_type=MessageType.MSG_DATA,
117
+ target=b"https://example-domain.ex",
118
+ payload=b"Hello, world!"
119
+ )
120
+
121
+ # Распаковка
122
+ data = SWP.unpack(packet)
123
+ print(data["msg_type"], data["target"], data["payload"])
124
+
125
+ # Отключение шифрования
126
+ SWP.set_auto_encrypt(False)
127
+ ```
128
+
129
+ ---
130
+
131
+ ## Методы класса SWP
132
+
133
+ ---
134
+
135
+ ### `reset()`
136
+
137
+ Сброс параметров класса до начальных значений.
138
+
139
+ | Параметр | Тип | Описание |
140
+ |----------|-----|----------|
141
+ | — | — | — |
142
+
143
+ **Возвращает:**
144
+ `None`
145
+
146
+ ---
147
+
148
+ ### `set_auto_encrypt(value: bool) -> None`
149
+
150
+ Включение или отключение автоматического шифрования тела пакетов.
151
+
152
+ | Параметр | Тип | Описание |
153
+ |----------|-----|----------|
154
+ | `value` | `bool` | `True` — включить автоматическое шифрование, `False` — выключить |
155
+
156
+ **Возвращает:**
157
+ `None`
158
+
159
+ **Исключения:**
160
+ `AesGcmKeyError` — если не загружен AES-GCM ключ при попытке включить шифрование.
161
+
162
+ ---
163
+
164
+ ### `pack(msg_type, target, payload=b"") -> bytes`
165
+
166
+ Создание SWP-пакета из переданных данных.
167
+
168
+ | Параметр | Тип | Описание |
169
+ |----------|-----|----------|
170
+ | `msg_type` | `int` | Тип сообщения (рекомендуется использовать `MessageType`) |
171
+ | `target` | `bytes` | Адрес получателя |
172
+ | `payload` | `bytes` | Полезная нагрузка (по умолчанию пустая строка) |
173
+
174
+ **Возвращает:**
175
+ `bytes` — сформированный SWP-пакет (заголовок + `target` + `payload`).
176
+
177
+ > **Note:** Если включён `AUTO_ENCRYPT`, `payload` будет зашифрован перед упаковкой.
178
+
179
+ ---
180
+
181
+ ### `unpack_header(buffer) -> tuple[bytes, int, int, int]`
182
+
183
+ Извлечение данных заголовка из сырого пакета.
184
+
185
+ | Параметр | Тип | Описание |
186
+ |----------|-----|----------|
187
+ | `buffer` | `bytes` | Сырой SWP-пакет (полный или частичный) |
188
+
189
+ **Возвращает:**
190
+ `tuple[bytes, int, int, int]` — кортеж из четырёх элементов:
191
+ (`PROTOCOL_MAGIC`, `msg_type`, `target_len`, `payload_len`)
192
+
193
+ **Исключения:**
194
+ `HeaderNotCompliteError` — если размер буфера меньше размера заголовка (`HEADER_SIZE`).
195
+
196
+ ---
197
+
198
+ ### `unpack(buffer) -> dict`
199
+
200
+ Полная распаковка SWP-пакета с расшифровкой (если включена).
201
+
202
+ | Параметр | Тип | Описание |
203
+ |----------|-----|----------|
204
+ | `buffer` | `bytes` | Полный SWP-пакет |
205
+
206
+ **Возвращает:**
207
+ `dict` — словарь с ключами:
208
+ - `"msg_type"` (`int`) — тип сообщения,
209
+ - `"target"` (`bytes`) — адрес получателя,
210
+ - `"payload"` (`bytes`) — полезная нагрузка (расшифрованная, если `AUTO_ENCRYPT = True`).
211
+
212
+ ---
213
+
214
+ ### `get_full_package_size(buffer) -> int`
215
+
216
+ Определение полного размера пакета по его заголовку.
217
+
218
+ | Параметр | Тип | Описание |
219
+ |----------|-----|----------|
220
+ | `buffer` | `bytes` | Начало SWP-пакета (минимум размер заголовка) |
221
+
222
+ **Возвращает:**
223
+ `int` — полный размер пакета (`HEADER_SIZE + target_len + payload_len`).
224
+ `-1` — если переданных данных меньше размера заголовка.
225
+
226
+ ---
227
+
228
+ ### `load_aesgcm(key) -> None`
229
+
230
+ Загрузка 32-байтного AES-GCM ключа и автоматическое включение шифрования.
231
+
232
+ | Параметр | Тип | Описание |
233
+ |----------|-----|----------|
234
+ | `key` | `bytes` | Ключ шифрования (должен быть ровно 32 байта) |
235
+
236
+ **Возвращает:**
237
+ `None`
238
+
239
+ **Исключения:**
240
+ `AesGcmKeyError` — если длина ключа не равна 32.
241
+
242
+ > **Note:** Метод загружает ключ в `SWP.AESGCM` и автоматически устанавливает `SWP.AUTO_ENCRYPT = True`.
243
+
244
+ ---
245
+
246
+ ### `encode(message) -> bytes`
247
+
248
+ Шифрование сообщения с использованием загруженного AES-GCM ключа.
249
+
250
+ | Параметр | Тип | Описание |
251
+ |----------|-----|----------|
252
+ | `message` | `str` или `bytes` | Исходное сообщение (строка автоматически кодируется в UTF-8) |
253
+
254
+ **Возвращает:**
255
+ `bytes` — зашифрованное сообщение.
256
+
257
+ ---
258
+
259
+ ### `decode(message) -> bytes`
260
+
261
+ Расшифровка сообщения.
262
+
263
+ | Параметр | Тип | Описание |
264
+ |----------|-----|----------|
265
+ | `message` | `bytes` | Зашифрованное сообщение |
266
+
267
+ **Возвращает:**
268
+ `bytes` — расшифрованное сообщение.
269
+
270
+ ---
271
+
272
+ ## Информация о проекте
273
+
274
+ - **Версия:** 0.0.1
275
+ - **Python:** 3.8+
276
+ - **Репозиторий:** [github.com/sekret01/SWP](https://github.com/sekret01/SWP.git)
@@ -0,0 +1,15 @@
1
+ README.md
2
+ setup.cfg
3
+ setup.py
4
+ swp_protocol/__init__.py
5
+ swp_protocol/_encoder.py
6
+ swp_protocol/exceptions.py
7
+ swp_protocol/sekret_wrapper_protocol.py
8
+ swp_protocol/swp_enums.py
9
+ swp_protocol.egg-info/PKG-INFO
10
+ swp_protocol.egg-info/SOURCES.txt
11
+ swp_protocol.egg-info/dependency_links.txt
12
+ swp_protocol.egg-info/requires.txt
13
+ swp_protocol.egg-info/top_level.txt
14
+ tests/test_encoder.py
15
+ tests/test_sekret_wrapper_protocol.py
@@ -0,0 +1 @@
1
+ cryptography>=48.0.0
@@ -0,0 +1 @@
1
+ swp_protocol
@@ -0,0 +1,27 @@
1
+ from swp_protocol._encoder import AesGcmCrypto, AesGcmKeyError
2
+ import pytest
3
+
4
+
5
+ class TestAesGcmKeyError:
6
+
7
+ def get_test_aesgcm_key(self) -> bytes:
8
+ return ("abcd" * 8).encode()
9
+
10
+ def test_encode(self):
11
+ crypto = AesGcmCrypto(key=self.get_test_aesgcm_key())
12
+ msg = b"test message"
13
+ res = crypto.encode(msg)
14
+ assert res != msg
15
+
16
+ def test_decode(self):
17
+ crypto = AesGcmCrypto(key=self.get_test_aesgcm_key())
18
+ msg = b"test message"
19
+ res = crypto.encode(msg)
20
+
21
+ after_decode = crypto.decode(res)
22
+ assert after_decode == msg
23
+
24
+ def test_key_len_error(self):
25
+ with pytest.raises(AesGcmKeyError) as ex:
26
+ _ = AesGcmCrypto(b"len_not_32")
27
+ assert ex is not None
@@ -0,0 +1,107 @@
1
+ import pytest
2
+
3
+ from swp_protocol.sekret_wrapper_protocol import SWP
4
+ from swp_protocol.swp_enums import MessageType
5
+ from swp_protocol.exceptions import AesGcmKeyError, HeaderNotCompliteError
6
+
7
+
8
+ class TestSWP:
9
+
10
+ @pytest.fixture(scope="function", autouse=True)
11
+ def reset_swp(self):
12
+ SWP.reset()
13
+
14
+ def test_pack(self):
15
+ msg = b"message"
16
+ swp_message = SWP.pack(
17
+ msg_type=MessageType.MSG_CONNECT.value,
18
+ target=b"127.0.0.1",
19
+ payload=msg
20
+ )
21
+ expect_val = b"SWP" + b"\x01" + b"\x09" + b"\x00\x07" + b"127.0.0.1" + b"message"
22
+ assert swp_message == expect_val
23
+
24
+ def test_unpack(self):
25
+ msg = b"message"
26
+ swp_message = SWP.pack(
27
+ msg_type=MessageType.MSG_CONNECT.value,
28
+ target=b"127.0.0.1",
29
+ payload=msg
30
+ )
31
+
32
+ unpack_res = SWP.unpack(buffer=swp_message)
33
+ assert unpack_res["msg_type"] == 0x01
34
+ assert unpack_res["target"] == b"127.0.0.1"
35
+ assert unpack_res["payload"] == b"message"
36
+
37
+ def test_get_full_package_size(self):
38
+ msg = b"message"
39
+ target = b"127.0.0.1"
40
+ swp_message = SWP.pack(
41
+ msg_type=MessageType.MSG_CONNECT.value,
42
+ target=target,
43
+ payload=msg
44
+ )
45
+
46
+ expect_val = 7 + len(target) + len(msg)
47
+ assert SWP.get_full_package_size(swp_message) == expect_val
48
+
49
+ def test_unpack_header(self):
50
+ msg = b"message"
51
+ target = b"127.0.0.1"
52
+ swp_message = SWP.pack(
53
+ msg_type=MessageType.MSG_CONNECT.value,
54
+ target=target,
55
+ payload=msg
56
+ )
57
+
58
+ exp_val = (b"SWP", 0x01, 9, 7)
59
+ assert SWP.unpack_header(swp_message) == exp_val
60
+
61
+ def test_unpack_header__header_not_complite(self):
62
+ with pytest.raises(HeaderNotCompliteError) as ex:
63
+ SWP.unpack_header(b"SWP\x01\x09")
64
+ assert ex is not None
65
+
66
+ def test_set_auto_encode(self):
67
+ SWP.load_aesgcm(b"a" * 32)
68
+ SWP.set_auto_encrypt(True)
69
+ assert SWP.AUTO_ENCRYPT == True
70
+
71
+ def test_set_auto_encode_after_load_key(self):
72
+ SWP.load_aesgcm(b"a" * 32)
73
+ assert SWP.AUTO_ENCRYPT == True
74
+
75
+ def test_set_auto_encode__not_load_key(self):
76
+ with pytest.raises(AesGcmKeyError) as ex:
77
+ SWP.set_auto_encrypt(True)
78
+ assert ex is not None
79
+
80
+ def test_pack_with_auto_encrypt(self):
81
+ SWP.load_aesgcm(b"a" * 32)
82
+ msg = b"message"
83
+ target = b"127.0.0.1"
84
+ swp_message = SWP.pack(
85
+ msg_type=MessageType.MSG_CONNECT.value,
86
+ target=target,
87
+ payload=msg
88
+ )
89
+
90
+ expect_val = b"SWP" + b"\x01" + b"\x09" + b"\x00\x07" + b"127.0.0.1" + b"message"
91
+ assert swp_message[:3] == expect_val[:3]
92
+ assert swp_message[3:] != expect_val[3:]
93
+
94
+ def test_unpack_with_auto_encrypt(self):
95
+ SWP.load_aesgcm(b"a" * 32)
96
+ msg = b"message"
97
+ target = b"127.0.0.1"
98
+ swp_message = SWP.pack(
99
+ msg_type=MessageType.MSG_CONNECT.value,
100
+ target=target,
101
+ payload=msg
102
+ )
103
+
104
+ decode_data = SWP.unpack(swp_message)
105
+ assert decode_data["msg_type"] == 0x01
106
+ assert decode_data["target"] == b"127.0.0.1"
107
+ assert decode_data["payload"] == b"message"