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.
- swp_protocol-0.0.1/PKG-INFO +276 -0
- swp_protocol-0.0.1/README.md +249 -0
- swp_protocol-0.0.1/setup.cfg +4 -0
- swp_protocol-0.0.1/setup.py +32 -0
- swp_protocol-0.0.1/swp_protocol/__init__.py +8 -0
- swp_protocol-0.0.1/swp_protocol/_encoder.py +29 -0
- swp_protocol-0.0.1/swp_protocol/exceptions.py +16 -0
- swp_protocol-0.0.1/swp_protocol/sekret_wrapper_protocol.py +132 -0
- swp_protocol-0.0.1/swp_protocol/swp_enums.py +7 -0
- swp_protocol-0.0.1/swp_protocol.egg-info/PKG-INFO +276 -0
- swp_protocol-0.0.1/swp_protocol.egg-info/SOURCES.txt +15 -0
- swp_protocol-0.0.1/swp_protocol.egg-info/dependency_links.txt +1 -0
- swp_protocol-0.0.1/swp_protocol.egg-info/requires.txt +1 -0
- swp_protocol-0.0.1/swp_protocol.egg-info/top_level.txt +1 -0
- swp_protocol-0.0.1/tests/test_encoder.py +27 -0
- swp_protocol-0.0.1/tests/test_sekret_wrapper_protocol.py +107 -0
|
@@ -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,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,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,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
|
+
|
|
@@ -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"
|