This commit is contained in:
2026-04-02 16:40:25 +03:00
parent c373e8f5d9
commit 642e3cf0c7
17 changed files with 815 additions and 0 deletions

1
lab5/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
key.txt

1
lab5/.python-version Normal file
View File

@@ -0,0 +1 @@
3.14

75
lab5/README.md Normal file
View File

@@ -0,0 +1,75 @@
# Практическая работа №5 — Конфиденциальный обмен сообщениями
Клиент-серверное приложение для обмена сообщениями по TCP с поддержкой
шифрования 3DES-CBC (с затравкой), контроля целостности SHA-256 и
аутентификации отправителя HMAC-SHA256.
## Установка
```bash
uv sync
```
## Ключ шифрования
Ключ хранится в текстовом файле (не менее 32 символов). Права на файл — только
чтение владельцем (`600`). Программа проверяет права при запуске и отказывается
работать, если файл доступен группе или другим пользователям.
```bash
openssl rand -hex 32 > key.txt
chmod 600 key.txt
```
## Запуск
### Сервер
```bash
uv run main.py server --port 9000 # без шифрования
uv run main.py server --port 9000 --encrypt # 3DES-CBC
uv run main.py server --port 9000 --encrypt --integrity # + SHA-256
uv run main.py server --port 9000 --encrypt --integrity --test-integrity # отправка с повреждённым хэшем
uv run main.py server --port 9000 --encrypt --hmac # 3DES + HMAC-SHA256
uv run main.py server --port 9000 --hmac # только HMAC (без шифрования)
uv run main.py server --port 9000 --key /path/to/key.txt --encrypt # другой файл ключа
```
### Клиент
```bash
uv run main.py client 127.0.0.1 9000
uv run main.py client 127.0.0.1 9000 --encrypt
uv run main.py client 127.0.0.1 9000 --encrypt --integrity
uv run main.py client 127.0.0.1 9000 --encrypt --integrity --test-integrity
uv run main.py client 127.0.0.1 9000 --encrypt --hmac
uv run main.py client 127.0.0.1 9000 --encrypt --hmac --test-integrity
```
## Режимы работы
| Флаг | Описание |
|------|----------|
| *(без флагов)* | Открытый текст, без проверки целостности |
| `--encrypt` | Шифрование 3DES-CBC с затравкой (соль + IV генерируются случайно для каждого сообщения) |
| `--integrity` | Контроль целостности — к сообщению прикладывается SHA-256 хэш открытого текста |
| `--hmac` | HMAC-SHA256 — контроль целостности + аутентификация отправителя (требует ключ, несовместим с `--integrity`) |
| `--test-integrity` | Режим тестирования: отправляется заведомо некорректный хэш/HMAC (требует `--integrity` или `--hmac`) |
## Анализ трафика с помощью tcpdump
Для наблюдения за передаваемыми данными удобно использовать `tcpdump`.
При работе на одной машине (loopback):
```bash
sudo tcpdump -i lo -X -n tcp port 9000
```
При работе по сети (замените `eth0` на имя сетевого интерфейса):
```bash
sudo tcpdump -i eth0 -X -n tcp port 9000
```
- **Без шифрования** — в дампе видны сообщения открытым текстом (JSON с полем `"data": "текст сообщения"`).
- **С шифрованием** — в дампе видны только шифротекст, соль и IV в hex; читаемый текст отсутствует.

68
lab5/lab5.md Normal file
View File

@@ -0,0 +1,68 @@
# Практическая работа №5
по дисциплине «Защита информации»
**Тема работы:** «Разработка клиент-серверного приложения для конфиденциального обмена сообщениями»
**Преподаватель:** Силиненко А.В.
**Email:** a_silinenko@mail.ru
## 1. Цель и задачи работы
### 1.1. Цель работы
Разработка клиент-серверного приложения для конфиденциального обмена сообщениями с использованием шифрования и контроля целостности.
### 1.2. Задачи работы
- получение базовых знаний по использованию криптографических функций библиотеки `crypto` в ОС Linux или другой библиотеки, обеспечивающей реализацию требований задания;
- разработка клиент-серверного приложения для обмена шифрованными сообщениями с контролем целостности;
- проверка работы приложения в различных режимах использования с помощью программы анализа трафика.
## 2. Требования к приложению
### 2.1. Общие требования
Разработать приложение типа «клиент-сервер», позволяющее обмениваться короткими сообщениями между клиентской и серверной частями приложения с обеспечением шифрования и контроля целостности.
### 2.2. Общие требования
- протокол взаимодействия: TCP;
- длина сообщения: не более 80 символов;
- полнодуплексный обмен сообщениями: возможность одновременного приема и передачи сообщений;
- IP-адрес и порт серверной части передается клиентской части приложения в качестве параметров запуска в командной строке;
- приложение должно быть способно функционировать как при запуске клиентской и серверной частей приложения на одном компьютере, так и на разных сетевых узлах;
- язык написания программы: любой.
### 2.3. Требования к функции шифрования
- приложение должно поддерживать функцию шифрования сообщений на основе симметричного шифрования с использованием библиотеки `crypto`, входящей в API `openssl`, или другой аналогичной библиотеки;
- алгоритм симметричного шифрования: согласно индивидуальному заданию;
В моём варианте это 3DES
- ключ шифрования длиной не менее 32 символов задается в файле;
- права доступа к файлу: чтение только владельцем;
- использование опции шифрования задается ключом командной строки при запуске программы.
### 2.4. Требования к функции контроля целостности
- приложение должно поддерживать функцию контроля целостности на основе подсчета хэш-значения передаваемого сообщения;
- алгоритм вычисления хэш-значения - в соответствии с индивидуальным заданием (см. практическую работу №2);
- использование опции контроля целостности задается ключом командной строки при запуске программы;
- предусмотреть режим тестирования, в котором при включенной опции контроля целостности отправляются сообщения с некорректным хэш-значением.
### 2.5. Необязательные требования
- реализовать шифрование с «затравкой» («солью»), обеспечивающей разные шифрованные сообщения при одном и том же открытом тексте;
- реализовать для разработанного приложения опцию выработки и использования сессионного ключа на основе алгоритма Диффи-Хеллмана вместо заранее заданного ключа.
## 3. Требования к отчету
### 3.1. В разделе отчета, посвященном данной работе, должны быть приведены
- актуальность темы работы в контексте курса;
- цели и задачи работы;
- краткое описание особенностей используемого алгоритма симметричного шифрования, а также шифрования «с затравкой» и алгоритма Диффи-Хеллмана, если реализованы соответствующие требования;
- команды компиляции и сборки исполняемых модулей приложения;
- описание процедуры проверки работы приложения с примерами, включая выводы программы анализа трафика, подтверждающие реализацию требований передачи сообщений без шифрования, с шифрованием, с шифрованием и контролем целостности;
- описание проверки опции контроля целостности, в которой отправляются сообщения с некорректным хэш-значением;
- описание проверки шифрования «с затравкой» и алгоритма Диффи-Хеллмана, если реализованы соответствующие требования;
- текст разработанного приложения (клиентской и серверной частей) с комментариями - в приложении к отчету;
- выводы по проделанной работе.

365
lab5/main.py Normal file
View File

@@ -0,0 +1,365 @@
from __future__ import annotations
import argparse
import hashlib
import hmac
import json
import os
import socket
import stat
import struct
import sys
import threading
from cryptography.hazmat.decrepit.ciphers.algorithms import TripleDES
from cryptography.hazmat.primitives.ciphers import Cipher, modes
from cryptography.hazmat.primitives import padding as sym_padding
MAX_MSG_LEN = 80
DEFAULT_KEY_FILE = "key.txt"
def check_key_permissions(path: str) -> None:
st = os.stat(path)
mode = st.st_mode
bad = stat.S_IRGRP | stat.S_IWGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IWOTH | stat.S_IXOTH
if mode & bad:
current = oct(mode & 0o777)
print(f"[!] Key file permissions too open ({current}): {path}")
print(f" Run: chmod 600 {path}")
sys.exit(1)
def load_key(path: str) -> str:
if not os.path.isfile(path):
print(f"[!] Key file not found: {path}")
sys.exit(1)
check_key_permissions(path)
with open(path) as f:
key = f.read().strip()
if len(key) < 32:
print(f"[!] Key must be at least 32 characters (got {len(key)})")
sys.exit(1)
return key
def derive_3des_key(file_key: str, salt: bytes) -> bytes:
return hashlib.sha256(file_key.encode() + salt).digest()[:24]
def encrypt_message(plaintext: str, file_key: str) -> tuple[bytes, bytes, bytes]:
salt = os.urandom(16)
key = derive_3des_key(file_key, salt)
iv = os.urandom(8)
padder = sym_padding.PKCS7(64).padder()
padded = padder.update(plaintext.encode()) + padder.finalize()
encryptor = Cipher(TripleDES(key), modes.CBC(iv)).encryptor()
ct = encryptor.update(padded) + encryptor.finalize()
return salt, iv, ct
def decrypt_message(salt: bytes, iv: bytes, ct: bytes, file_key: str) -> str:
key = derive_3des_key(file_key, salt)
decryptor = Cipher(TripleDES(key), modes.CBC(iv)).decryptor()
padded = decryptor.update(ct) + decryptor.finalize()
unpadder = sym_padding.PKCS7(64).unpadder()
plaintext = unpadder.update(padded) + unpadder.finalize()
return plaintext.decode()
def compute_hash(text: str) -> str:
return hashlib.sha256(text.encode()).hexdigest()
def compute_hmac(file_key: str, text: str, salt: bytes) -> str:
return hmac.new(file_key.encode(), text.encode() + salt, hashlib.sha256).hexdigest()
def recv_exactly(sock: socket.socket, n: int) -> bytes | None:
buf = b""
while len(buf) < n:
chunk = sock.recv(n - len(buf))
if not chunk:
return None
buf += chunk
return buf
def send_packet(sock: socket.socket, data: bytes) -> None:
sock.sendall(struct.pack("!I", len(data)) + data)
def recv_packet(sock: socket.socket) -> dict | None:
header = recv_exactly(sock, 4)
if header is None:
return None
length = struct.unpack("!I", header)[0]
payload = recv_exactly(sock, length)
if payload is None:
return None
return json.loads(payload.decode())
def do_send(
sock: socket.socket,
text: str,
file_key: str | None,
use_encrypt: bool,
use_integrity: bool,
use_hmac: bool,
test_integrity: bool,
) -> None:
msg: dict = {}
print(f" [TX] plaintext: {text}")
enc_salt: bytes | None = None
if use_encrypt:
assert file_key is not None
salt, iv, ct = encrypt_message(text, file_key)
enc_salt = salt
derived_hex = derive_3des_key(file_key, salt).hex()
msg["encrypted"] = True
msg["salt"] = salt.hex()
msg["iv"] = iv.hex()
msg["data"] = ct.hex()
print(f" [TX] salt: {salt.hex()}")
print(f" [TX] 3DES key: {derived_hex}")
print(f" [TX] IV: {iv.hex()}")
print(f" [TX] ciphertext: {ct.hex()}")
else:
msg["encrypted"] = False
msg["data"] = text
if use_integrity:
h = compute_hash(text)
if test_integrity:
h = hashlib.sha256(b"CORRUPTED_" + os.urandom(4)).hexdigest()
print(f" [TX] SHA-256: {h} (CORRUPTED!)")
else:
print(f" [TX] SHA-256: {h}")
msg["hash"] = h
if use_hmac:
assert file_key is not None
hmac_salt = enc_salt if enc_salt is not None else os.urandom(16)
h = compute_hmac(file_key, text, hmac_salt)
msg["hmac_salt"] = hmac_salt.hex()
if test_integrity:
h = hmac.new(b"WRONG_KEY", os.urandom(8), hashlib.sha256).hexdigest()
print(f" [TX] HMAC salt: {hmac_salt.hex()}")
print(f" [TX] HMAC-256: {h} (CORRUPTED!)")
else:
print(f" [TX] HMAC salt: {hmac_salt.hex()}")
print(f" [TX] HMAC-256: {h}")
msg["hmac"] = h
send_packet(sock, json.dumps(msg).encode())
print()
def do_recv(
msg: dict,
file_key: str | None,
) -> str | None:
encrypted = msg.get("encrypted", False)
if encrypted:
salt = bytes.fromhex(msg["salt"])
iv = bytes.fromhex(msg["iv"])
ct = bytes.fromhex(msg["data"])
print(f" [RX] ciphertext: {ct.hex()}")
print(f" [RX] salt: {salt.hex()}")
print(f" [RX] IV: {iv.hex()}")
if file_key is None:
print(" [RX] ERROR: message is encrypted but no key loaded")
return None
derived_hex = derive_3des_key(file_key, salt).hex()
print(f" [RX] 3DES key: {derived_hex}")
try:
text = decrypt_message(salt, iv, ct, file_key)
except Exception as e:
print(f" [RX] decryption failed: {e}")
return None
print(f" [RX] decrypted: {text}")
else:
text = msg["data"]
print(f" [RX] plaintext: {text}")
if "hash" in msg:
received_hash = msg["hash"]
computed_hash = compute_hash(text)
ok = received_hash == computed_hash
print(f" [RX] recv hash: {received_hash}")
print(f" [RX] calc hash: {computed_hash}")
if ok:
print(" [RX] integrity: OK")
else:
print(" [RX] integrity: FAIL - message may have been tampered with!")
if "hmac" in msg:
hmac_salt = bytes.fromhex(msg["hmac_salt"])
received_hmac = msg["hmac"]
if file_key is None:
print(" [RX] ERROR: HMAC present but no key loaded")
else:
computed_h = compute_hmac(file_key, text, hmac_salt)
ok = hmac.compare_digest(received_hmac, computed_h)
print(f" [RX] HMAC salt: {hmac_salt.hex()}")
print(f" [RX] recv HMAC: {received_hmac}")
print(f" [RX] calc HMAC: {computed_h}")
if ok:
print(" [RX] HMAC auth: OK - sender verified")
else:
print(" [RX] HMAC auth: FAIL - sender NOT verified!")
print()
return text
def receiver_thread(
sock: socket.socket,
file_key: str | None,
stop_event: threading.Event,
) -> None:
try:
while not stop_event.is_set():
msg = recv_packet(sock)
if msg is None:
print("\n[*] Connection closed by remote side.")
stop_event.set()
break
do_recv(msg, file_key)
except OSError:
if not stop_event.is_set():
print("\n[*] Connection lost.")
stop_event.set()
def sender_thread(
sock: socket.socket,
file_key: str | None,
use_encrypt: bool,
use_integrity: bool,
use_hmac: bool,
test_integrity: bool,
stop_event: threading.Event,
) -> None:
try:
while not stop_event.is_set():
try:
text = input()
except EOFError:
stop_event.set()
break
if stop_event.is_set():
break
if len(text) > MAX_MSG_LEN:
print(f" [!] Message too long ({len(text)} > {MAX_MSG_LEN}), truncated.")
text = text[:MAX_MSG_LEN]
do_send(sock, text, file_key, use_encrypt, use_integrity, use_hmac, test_integrity)
except OSError:
if not stop_event.is_set():
stop_event.set()
def run_session(
sock: socket.socket,
addr: tuple,
file_key: str | None,
use_encrypt: bool,
use_integrity: bool,
use_hmac: bool,
test_integrity: bool,
) -> None:
flags = []
if use_encrypt:
flags.append("3DES")
if use_integrity:
flags.append("SHA-256")
if use_hmac:
flags.append("HMAC-SHA256")
if test_integrity:
flags.append("test-integrity")
mode_str = ", ".join(flags) if flags else "plaintext"
print(f"[*] Session with {addr[0]}:{addr[1]} [{mode_str}]")
print("[*] Type messages and press Enter to send. Ctrl+C / Ctrl+D to quit.\n")
stop = threading.Event()
rx = threading.Thread(target=receiver_thread, args=(sock, file_key, stop), daemon=True)
tx = threading.Thread(target=sender_thread, args=(sock, file_key, use_encrypt, use_integrity, use_hmac, test_integrity, stop), daemon=True)
rx.start()
tx.start()
try:
while not stop.is_set():
stop.wait(0.5)
except KeyboardInterrupt:
print("\n[*] Interrupted.")
stop.set()
sock.close()
def run_server(args: argparse.Namespace) -> None:
file_key = load_key(args.key) if (args.encrypt or args.hmac) else None
srv = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
srv.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
srv.bind(("0.0.0.0", args.port))
srv.listen(1)
print(f"[*] Listening on 0.0.0.0:{args.port} ...")
conn, addr = srv.accept()
print(f"[*] Client connected: {addr[0]}:{addr[1]}")
srv.close()
run_session(conn, addr, file_key, args.encrypt, args.integrity, args.hmac, args.test_integrity)
def run_client(args: argparse.Namespace) -> None:
file_key = load_key(args.key) if (args.encrypt or args.hmac) else None
sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.connect((args.host, args.port))
print(f"[*] Connected to {args.host}:{args.port}")
run_session(sock, (args.host, args.port), file_key, args.encrypt, args.integrity, args.hmac, args.test_integrity)
def build_parser() -> argparse.ArgumentParser:
parser = argparse.ArgumentParser(description="Encrypted messaging (3DES-CBC + SHA-256)")
sub = parser.add_subparsers(dest="role", required=True)
for name, sp in [("server", sub.add_parser("server", help="Start server")),
("client", sub.add_parser("client", help="Start client"))]:
if name == "server":
sp.add_argument("--port", type=int, required=True)
else:
sp.add_argument("host")
sp.add_argument("port", type=int)
sp.add_argument("--encrypt", action="store_true", help="Enable 3DES-CBC encryption")
sp.add_argument("--integrity", action="store_true", help="SHA-256 integrity check")
sp.add_argument("--hmac", action="store_true", help="HMAC-SHA256 integrity + authentication")
sp.add_argument("--test-integrity", action="store_true", help="Send corrupted hash/HMAC")
sp.add_argument("--key", default=DEFAULT_KEY_FILE, help="Path to key file (default: key.txt)")
return parser
def main() -> None:
args = build_parser().parse_args()
if args.integrity and args.hmac:
print("[!] --integrity and --hmac are mutually exclusive")
sys.exit(1)
if args.test_integrity and not (args.integrity or args.hmac):
print("[!] --test-integrity requires --integrity or --hmac")
sys.exit(1)
if args.role == "server":
run_server(args)
else:
run_client(args)
if __name__ == "__main__":
main()

9
lab5/pyproject.toml Normal file
View File

@@ -0,0 +1,9 @@
[project]
name = "lab5"
version = "0.1.0"
description = "Add your description here"
readme = "README.md"
requires-python = ">=3.14"
dependencies = [
"cryptography>=46.0.6",
]

109
lab5/uv.lock generated Normal file
View File

@@ -0,0 +1,109 @@
version = 1
revision = 3
requires-python = ">=3.14"
[[package]]
name = "cffi"
version = "2.0.0"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "pycparser", marker = "implementation_name != 'PyPy'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" },
{ url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" },
{ url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" },
{ url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" },
{ url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" },
{ url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" },
{ url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" },
{ url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" },
{ url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" },
{ url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" },
{ url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" },
{ url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" },
{ url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" },
{ url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" },
{ url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" },
{ url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" },
{ url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" },
{ url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" },
{ url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" },
{ url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" },
{ url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" },
{ url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" },
]
[[package]]
name = "cryptography"
version = "46.0.6"
source = { registry = "https://pypi.org/simple" }
dependencies = [
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
]
sdist = { url = "https://files.pythonhosted.org/packages/a4/ba/04b1bd4218cbc58dc90ce967106d51582371b898690f3ae0402876cc4f34/cryptography-46.0.6.tar.gz", hash = "sha256:27550628a518c5c6c903d84f637fbecf287f6cb9ced3804838a1295dc1fd0759", size = 750542, upload-time = "2026-03-25T23:34:53.396Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/47/23/9285e15e3bc57325b0a72e592921983a701efc1ee8f91c06c5f0235d86d9/cryptography-46.0.6-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:64235194bad039a10bb6d2d930ab3323baaec67e2ce36215fd0952fad0930ca8", size = 7176401, upload-time = "2026-03-25T23:33:22.096Z" },
{ url = "https://files.pythonhosted.org/packages/60/f8/e61f8f13950ab6195b31913b42d39f0f9afc7d93f76710f299b5ec286ae6/cryptography-46.0.6-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:26031f1e5ca62fcb9d1fcb34b2b60b390d1aacaa15dc8b895a9ed00968b97b30", size = 4275275, upload-time = "2026-03-25T23:33:23.844Z" },
{ url = "https://files.pythonhosted.org/packages/19/69/732a736d12c2631e140be2348b4ad3d226302df63ef64d30dfdb8db7ad1c/cryptography-46.0.6-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:9a693028b9cbe51b5a1136232ee8f2bc242e4e19d456ded3fa7c86e43c713b4a", size = 4425320, upload-time = "2026-03-25T23:33:25.703Z" },
{ url = "https://files.pythonhosted.org/packages/d4/12/123be7292674abf76b21ac1fc0e1af50661f0e5b8f0ec8285faac18eb99e/cryptography-46.0.6-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:67177e8a9f421aa2d3a170c3e56eca4e0128883cf52a071a7cbf53297f18b175", size = 4278082, upload-time = "2026-03-25T23:33:27.423Z" },
{ url = "https://files.pythonhosted.org/packages/5b/ba/d5e27f8d68c24951b0a484924a84c7cdaed7502bac9f18601cd357f8b1d2/cryptography-46.0.6-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:d9528b535a6c4f8ff37847144b8986a9a143585f0540fbcb1a98115b543aa463", size = 4926514, upload-time = "2026-03-25T23:33:29.206Z" },
{ url = "https://files.pythonhosted.org/packages/34/71/1ea5a7352ae516d5512d17babe7e1b87d9db5150b21f794b1377eac1edc0/cryptography-46.0.6-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:22259338084d6ae497a19bae5d4c66b7ca1387d3264d1c2c0e72d9e9b6a77b97", size = 4457766, upload-time = "2026-03-25T23:33:30.834Z" },
{ url = "https://files.pythonhosted.org/packages/01/59/562be1e653accee4fdad92c7a2e88fced26b3fdfce144047519bbebc299e/cryptography-46.0.6-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:760997a4b950ff00d418398ad73fbc91aa2894b5c1db7ccb45b4f68b42a63b3c", size = 3986535, upload-time = "2026-03-25T23:33:33.02Z" },
{ url = "https://files.pythonhosted.org/packages/d6/8b/b1ebfeb788bf4624d36e45ed2662b8bd43a05ff62157093c1539c1288a18/cryptography-46.0.6-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:3dfa6567f2e9e4c5dceb8ccb5a708158a2a871052fa75c8b78cb0977063f1507", size = 4277618, upload-time = "2026-03-25T23:33:34.567Z" },
{ url = "https://files.pythonhosted.org/packages/dd/52/a005f8eabdb28df57c20f84c44d397a755782d6ff6d455f05baa2785bd91/cryptography-46.0.6-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:cdcd3edcbc5d55757e5f5f3d330dd00007ae463a7e7aa5bf132d1f22a4b62b19", size = 4890802, upload-time = "2026-03-25T23:33:37.034Z" },
{ url = "https://files.pythonhosted.org/packages/ec/4d/8e7d7245c79c617d08724e2efa397737715ca0ec830ecb3c91e547302555/cryptography-46.0.6-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:d4e4aadb7fc1f88687f47ca20bb7227981b03afaae69287029da08096853b738", size = 4457425, upload-time = "2026-03-25T23:33:38.904Z" },
{ url = "https://files.pythonhosted.org/packages/1d/5c/f6c3596a1430cec6f949085f0e1a970638d76f81c3ea56d93d564d04c340/cryptography-46.0.6-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:2b417edbe8877cda9022dde3a008e2deb50be9c407eef034aeeb3a8b11d9db3c", size = 4405530, upload-time = "2026-03-25T23:33:40.842Z" },
{ url = "https://files.pythonhosted.org/packages/7e/c9/9f9cea13ee2dbde070424e0c4f621c091a91ffcc504ffea5e74f0e1daeff/cryptography-46.0.6-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:380343e0653b1c9d7e1f55b52aaa2dbb2fdf2730088d48c43ca1c7c0abb7cc2f", size = 4667896, upload-time = "2026-03-25T23:33:42.781Z" },
{ url = "https://files.pythonhosted.org/packages/ad/b5/1895bc0821226f129bc74d00eccfc6a5969e2028f8617c09790bf89c185e/cryptography-46.0.6-cp311-abi3-win32.whl", hash = "sha256:bcb87663e1f7b075e48c3be3ecb5f0b46c8fc50b50a97cf264e7f60242dca3f2", size = 3026348, upload-time = "2026-03-25T23:33:45.021Z" },
{ url = "https://files.pythonhosted.org/packages/c3/f8/c9bcbf0d3e6ad288b9d9aa0b1dee04b063d19e8c4f871855a03ab3a297ab/cryptography-46.0.6-cp311-abi3-win_amd64.whl", hash = "sha256:6739d56300662c468fddb0e5e291f9b4d084bead381667b9e654c7dd81705124", size = 3483896, upload-time = "2026-03-25T23:33:46.649Z" },
{ url = "https://files.pythonhosted.org/packages/01/41/3a578f7fd5c70611c0aacba52cd13cb364a5dee895a5c1d467208a9380b0/cryptography-46.0.6-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:2ef9e69886cbb137c2aef9772c2e7138dc581fad4fcbcf13cc181eb5a3ab6275", size = 7117147, upload-time = "2026-03-25T23:33:48.249Z" },
{ url = "https://files.pythonhosted.org/packages/fa/87/887f35a6fca9dde90cad08e0de0c89263a8e59b2d2ff904fd9fcd8025b6f/cryptography-46.0.6-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7f417f034f91dcec1cb6c5c35b07cdbb2ef262557f701b4ecd803ee8cefed4f4", size = 4266221, upload-time = "2026-03-25T23:33:49.874Z" },
{ url = "https://files.pythonhosted.org/packages/aa/a8/0a90c4f0b0871e0e3d1ed126aed101328a8a57fd9fd17f00fb67e82a51ca/cryptography-46.0.6-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d24c13369e856b94892a89ddf70b332e0b70ad4a5c43cf3e9cb71d6d7ffa1f7b", size = 4408952, upload-time = "2026-03-25T23:33:52.128Z" },
{ url = "https://files.pythonhosted.org/packages/16/0b/b239701eb946523e4e9f329336e4ff32b1247e109cbab32d1a7b61da8ed7/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:aad75154a7ac9039936d50cf431719a2f8d4ed3d3c277ac03f3339ded1a5e707", size = 4270141, upload-time = "2026-03-25T23:33:54.11Z" },
{ url = "https://files.pythonhosted.org/packages/0f/a8/976acdd4f0f30df7b25605f4b9d3d89295351665c2091d18224f7ad5cdbf/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:3c21d92ed15e9cfc6eb64c1f5a0326db22ca9c2566ca46d845119b45b4400361", size = 4904178, upload-time = "2026-03-25T23:33:55.725Z" },
{ url = "https://files.pythonhosted.org/packages/b1/1b/bf0e01a88efd0e59679b69f42d4afd5bced8700bb5e80617b2d63a3741af/cryptography-46.0.6-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:4668298aef7cddeaf5c6ecc244c2302a2b8e40f384255505c22875eebb47888b", size = 4441812, upload-time = "2026-03-25T23:33:57.364Z" },
{ url = "https://files.pythonhosted.org/packages/bb/8b/11df86de2ea389c65aa1806f331cae145f2ed18011f30234cc10ca253de8/cryptography-46.0.6-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:8ce35b77aaf02f3b59c90b2c8a05c73bac12cea5b4e8f3fbece1f5fddea5f0ca", size = 3963923, upload-time = "2026-03-25T23:33:59.361Z" },
{ url = "https://files.pythonhosted.org/packages/91/e0/207fb177c3a9ef6a8108f234208c3e9e76a6aa8cf20d51932916bd43bda0/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:c89eb37fae9216985d8734c1afd172ba4927f5a05cfd9bf0e4863c6d5465b013", size = 4269695, upload-time = "2026-03-25T23:34:00.909Z" },
{ url = "https://files.pythonhosted.org/packages/21/5e/19f3260ed1e95bced52ace7501fabcd266df67077eeb382b79c81729d2d3/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:ed418c37d095aeddf5336898a132fba01091f0ac5844e3e8018506f014b6d2c4", size = 4869785, upload-time = "2026-03-25T23:34:02.796Z" },
{ url = "https://files.pythonhosted.org/packages/10/38/cd7864d79aa1d92ef6f1a584281433419b955ad5a5ba8d1eb6c872165bcb/cryptography-46.0.6-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:69cf0056d6947edc6e6760e5f17afe4bea06b56a9ac8a06de9d2bd6b532d4f3a", size = 4441404, upload-time = "2026-03-25T23:34:04.35Z" },
{ url = "https://files.pythonhosted.org/packages/09/0a/4fe7a8d25fed74419f91835cf5829ade6408fd1963c9eae9c4bce390ecbb/cryptography-46.0.6-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:8e7304c4f4e9490e11efe56af6713983460ee0780f16c63f219984dab3af9d2d", size = 4397549, upload-time = "2026-03-25T23:34:06.342Z" },
{ url = "https://files.pythonhosted.org/packages/5f/a0/7d738944eac6513cd60a8da98b65951f4a3b279b93479a7e8926d9cd730b/cryptography-46.0.6-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:b928a3ca837c77a10e81a814a693f2295200adb3352395fad024559b7be7a736", size = 4651874, upload-time = "2026-03-25T23:34:07.916Z" },
{ url = "https://files.pythonhosted.org/packages/cb/f1/c2326781ca05208845efca38bf714f76939ae446cd492d7613808badedf1/cryptography-46.0.6-cp314-cp314t-win32.whl", hash = "sha256:97c8115b27e19e592a05c45d0dd89c57f81f841cc9880e353e0d3bf25b2139ed", size = 3001511, upload-time = "2026-03-25T23:34:09.892Z" },
{ url = "https://files.pythonhosted.org/packages/c9/57/fe4a23eb549ac9d903bd4698ffda13383808ef0876cc912bcb2838799ece/cryptography-46.0.6-cp314-cp314t-win_amd64.whl", hash = "sha256:c797e2517cb7880f8297e2c0f43bb910e91381339336f75d2c1c2cbf811b70b4", size = 3471692, upload-time = "2026-03-25T23:34:11.613Z" },
{ url = "https://files.pythonhosted.org/packages/c4/cc/f330e982852403da79008552de9906804568ae9230da8432f7496ce02b71/cryptography-46.0.6-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:12cae594e9473bca1a7aceb90536060643128bb274fcea0fc459ab90f7d1ae7a", size = 7162776, upload-time = "2026-03-25T23:34:13.308Z" },
{ url = "https://files.pythonhosted.org/packages/49/b3/dc27efd8dcc4bff583b3f01d4a3943cd8b5821777a58b3a6a5f054d61b79/cryptography-46.0.6-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:639301950939d844a9e1c4464d7e07f902fe9a7f6b215bb0d4f28584729935d8", size = 4270529, upload-time = "2026-03-25T23:34:15.019Z" },
{ url = "https://files.pythonhosted.org/packages/e6/05/e8d0e6eb4f0d83365b3cb0e00eb3c484f7348db0266652ccd84632a3d58d/cryptography-46.0.6-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:ed3775295fb91f70b4027aeba878d79b3e55c0b3e97eaa4de71f8f23a9f2eb77", size = 4414827, upload-time = "2026-03-25T23:34:16.604Z" },
{ url = "https://files.pythonhosted.org/packages/2f/97/daba0f5d2dc6d855e2dcb70733c812558a7977a55dd4a6722756628c44d1/cryptography-46.0.6-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:8927ccfbe967c7df312ade694f987e7e9e22b2425976ddbf28271d7e58845290", size = 4271265, upload-time = "2026-03-25T23:34:18.586Z" },
{ url = "https://files.pythonhosted.org/packages/89/06/fe1fce39a37ac452e58d04b43b0855261dac320a2ebf8f5260dd55b201a9/cryptography-46.0.6-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:b12c6b1e1651e42ab5de8b1e00dc3b6354fdfd778e7fa60541ddacc27cd21410", size = 4916800, upload-time = "2026-03-25T23:34:20.561Z" },
{ url = "https://files.pythonhosted.org/packages/ff/8a/b14f3101fe9c3592603339eb5d94046c3ce5f7fc76d6512a2d40efd9724e/cryptography-46.0.6-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:063b67749f338ca9c5a0b7fe438a52c25f9526b851e24e6c9310e7195aad3b4d", size = 4448771, upload-time = "2026-03-25T23:34:22.406Z" },
{ url = "https://files.pythonhosted.org/packages/01/b3/0796998056a66d1973fd52ee89dc1bb3b6581960a91ad4ac705f182d398f/cryptography-46.0.6-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:02fad249cb0e090b574e30b276a3da6a149e04ee2f049725b1f69e7b8351ec70", size = 3978333, upload-time = "2026-03-25T23:34:24.281Z" },
{ url = "https://files.pythonhosted.org/packages/c5/3d/db200af5a4ffd08918cd55c08399dc6c9c50b0bc72c00a3246e099d3a849/cryptography-46.0.6-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:7e6142674f2a9291463e5e150090b95a8519b2fb6e6aaec8917dd8d094ce750d", size = 4271069, upload-time = "2026-03-25T23:34:25.895Z" },
{ url = "https://files.pythonhosted.org/packages/d7/18/61acfd5b414309d74ee838be321c636fe71815436f53c9f0334bf19064fa/cryptography-46.0.6-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:456b3215172aeefb9284550b162801d62f5f264a081049a3e94307fe20792cfa", size = 4878358, upload-time = "2026-03-25T23:34:27.67Z" },
{ url = "https://files.pythonhosted.org/packages/8b/65/5bf43286d566f8171917cae23ac6add941654ccf085d739195a4eacf1674/cryptography-46.0.6-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:341359d6c9e68834e204ceaf25936dffeafea3829ab80e9503860dcc4f4dac58", size = 4448061, upload-time = "2026-03-25T23:34:29.375Z" },
{ url = "https://files.pythonhosted.org/packages/e0/25/7e49c0fa7205cf3597e525d156a6bce5b5c9de1fd7e8cb01120e459f205a/cryptography-46.0.6-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9a9c42a2723999a710445bc0d974e345c32adfd8d2fac6d8a251fa829ad31cfb", size = 4399103, upload-time = "2026-03-25T23:34:32.036Z" },
{ url = "https://files.pythonhosted.org/packages/44/46/466269e833f1c4718d6cd496ffe20c56c9c8d013486ff66b4f69c302a68d/cryptography-46.0.6-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:6617f67b1606dfd9fe4dbfa354a9508d4a6d37afe30306fe6c101b7ce3274b72", size = 4659255, upload-time = "2026-03-25T23:34:33.679Z" },
{ url = "https://files.pythonhosted.org/packages/0a/09/ddc5f630cc32287d2c953fc5d32705e63ec73e37308e5120955316f53827/cryptography-46.0.6-cp38-abi3-win32.whl", hash = "sha256:7f6690b6c55e9c5332c0b59b9c8a3fb232ebf059094c17f9019a51e9827df91c", size = 3010660, upload-time = "2026-03-25T23:34:35.418Z" },
{ url = "https://files.pythonhosted.org/packages/1b/82/ca4893968aeb2709aacfb57a30dec6fa2ab25b10fa9f064b8882ce33f599/cryptography-46.0.6-cp38-abi3-win_amd64.whl", hash = "sha256:79e865c642cfc5c0b3eb12af83c35c5aeff4fa5c672dc28c43721c2c9fdd2f0f", size = 3471160, upload-time = "2026-03-25T23:34:37.191Z" },
]
[[package]]
name = "lab5"
version = "0.1.0"
source = { virtual = "." }
dependencies = [
{ name = "cryptography" },
]
[package.metadata]
requires-dist = [{ name = "cryptography", specifier = ">=46.0.6" }]
[[package]]
name = "pycparser"
version = "3.0"
source = { registry = "https://pypi.org/simple" }
sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" }
wheels = [
{ url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" },
]

BIN
report/img/lab5-enc-int.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

BIN
report/img/lab5-enc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 72 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

BIN
report/img/lab5-hmac.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 163 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

BIN
report/img/lab5-plain.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

BIN
report/img/lab5-salt.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 79 KiB

View File

@@ -178,6 +178,7 @@
\item Практическая работа №2. Разработка и исследование системы аутентификации и авторизации. Данная практическая работа посвящена разработке системы доступа пользователей к конфиденциальным данным и исследованию стойкости паролей к атаке методом грубой силы. В ходе выполнения работы реализованы утилита управления пользователями, утилита доступа к конфиденциальным данным и программа перебора паролей, а также проведено экспериментальное исследование зависимости времени взлома от длины пароля.
\item Практическая работа №3. Реализация моделей дискреционного и мандатного управления доступом. Данная практическая работа посвящена расширению системы из работы №2 путём реализации моделей DAC (дискреционный доступ) и MAC (мандатный доступ) на основе модели Белла–Лападулы.
\item Практическая работа №4. Межсетевое экранирование средствами \texttt{iptables}. Данная практическая работа посвящена настройке межсетевого экрана в Linux, формированию политики фильтрации сетевого трафика и экспериментальной проверке пропуска разрешённых соединений и блокировки запрещённых пакетов с использованием \texttt{tcpdump}.
\item Практическая работа №5. Разработка клиент-серверного приложения для конфиденциального обмена сообщениями. Данная практическая работа посвящена разработке TCP-приложения с поддержкой шифрования 3DES-CBC с затравкой и контроля целостности на основе SHA-256, а также проверке корректности шифрования с помощью анализа трафика.
\end{enumerate}
\newpage
@@ -956,6 +957,183 @@ $ sudo tcpdump -i any -n icmp
В ходе практической работы №4 на стенде из двух виртуальных машин VirtualBox настроена политика межсетевого экранирования средствами \texttt{iptables}. Реализована схема с политикой \texttt{DROP} по умолчанию и разрешающими правилами для loopback, DNS, исходящего ping, входящего ping от адреса \texttt{192.168.100.2} и HTTP/HTTPS. Экспериментальная проверка подтвердила пропуск разрешённого трафика и блокировку соединений, не описанных в политике. Анализ трафика утилитой \texttt{tcpdump} продемонстрировал соответствие наблюдаемой сетевой активности заданным правилам фильтрации.
\newpage
\section{Разработка клиент-серверного приложения для конфиденциального обмена сообщениями}
\subsection{Актуальность темы}
Обеспечение конфиденциальности и целостности данных при передаче по сети является одной из ключевых задач информационной безопасности. Даже при использовании защищённых протоколов транспортного уровня понимание механизмов симметричного шифрования и контроля целостности на уровне приложения позволяет проектировать системы с дополнительным эшелоном защиты. Практическая реализация клиент-серверного приложения с шифрованием и хэш-контролем в рамках курса <<Защита информации>> закрепляет навыки работы с криптографическими примитивами и демонстрирует их влияние на передаваемый трафик.
\subsection{Цели и задачи работы}
Цель работы: разработка клиент-серверного приложения для обмена короткими сообщениями по протоколу TCP с обеспечением шифрования и контроля целостности.
Задачи работы:
\begin{enumerate}
\item Получить базовые знания по использованию криптографических функций библиотеки \texttt{cryptography} в Python.
\item Разработать клиент-серверное приложение для обмена шифрованными сообщениями с контролем целостности.
\item Проверить работу приложения в различных режимах, в том числе с помощью программы анализа трафика \texttt{tcpdump}.
\end{enumerate}
\subsection{Описание алгоритма шифрования}
\textbf{3DES (Triple DES)} — симметричный блочный шифр, основанный на трёхкратном применении алгоритма DES в режиме EDE (EncryptDecryptEncrypt) с тремя независимыми подключами. Размер блока — 64 бита (8 байт), длина ключа — 192 бита (24 байта, из которых 168 бит являются эффективными ключевыми битами, а оставшиеся 24 бита — биты чётности). Несмотря на то что 3DES считается устаревшим и уступает AES по производительности, он по-прежнему обеспечивает достаточный уровень стойкости для учебных задач.
В разработанном приложении используется режим сцепления блоков \textbf{CBC} (Cipher Block Chaining): каждый блок открытого текста перед шифрованием складывается по модулю 2 с предыдущим блоком шифротекста (для первого блока — с вектором инициализации IV). Это обеспечивает зависимость каждого блока шифротекста от всех предшествующих блоков, затрудняя анализ повторяющихся фрагментов. Дополнение до границы блока выполняется по схеме PKCS7.
\textbf{Шифрование с затравкой (солью).} Для каждого сообщения генерируется случайная 16-байтовая затравка (salt). Ключ 3DES длиной 24 байта вычисляется как первые 24 байта хэша SHA-256 от конкатенации мастер-ключа из файла и затравки: $K_{\mathrm{3DES}} = \mathrm{SHA\text{-}256}(K_{\mathrm{file}} \mathbin\| \mathrm{salt})[{:}24]$. Кроме того, для каждого сообщения генерируется случайный 8-байтовый IV. Затравка и IV передаются вместе с шифротекстом в открытом виде, что позволяет получателю воспроизвести ключ. Благодаря уникальности затравки и IV одинаковые открытые тексты порождают различные шифротексты, что исключает атаки на основе повторения.
\textbf{Контроль целостности} реализован с помощью хэш-функции SHA-256 (в соответствии с индивидуальным заданием из практической работы №2). При включённой опции контроля целостности отправитель вычисляет хэш открытого текста сообщения и передаёт его вместе с данными. Получатель вычисляет хэш самостоятельно и сравнивает с полученным значением; при расхождении выводится предупреждение о нарушении целостности.
\textbf{HMAC-SHA256.} В качестве альтернативы простому SHA-256 реализован режим HMAC (Hash-based Message Authentication Code) на основе SHA-256. В отличие от обычного хэша, HMAC использует секретный ключ: $\mathrm{HMAC} = \mathrm{HMAC\text{-}SHA256}(K_{\mathrm{file}},\; \mathrm{plaintext} \mathbin\| \mathrm{salt})$. Это даёт два преимущества: (1)~получатель может убедиться, что отправитель владеет тем же секретным ключом (аутентификация), а не только в том, что сообщение не было изменено; (2)~случайная затравка, подмешиваемая к сообщению, обеспечивает различные значения HMAC для одинаковых открытых текстов. Режимы \texttt{-{}-integrity} (SHA-256) и \texttt{-{}-hmac} (HMAC-SHA256) являются взаимоисключающими.
\subsection{Описание реализации}
Приложение реализовано на языке Python~3.14 в виде единого файла \texttt{main.py} (исходный код приведён в приложении~9). Для криптографических операций используется библиотека \texttt{cryptography}. Управление зависимостями осуществляется менеджером \texttt{uv}.
\textbf{Протокол обмена.} Сообщения передаются по TCP в формате: 4~байта длины (big-endian) + JSON-объект. Структура JSON-объекта:
\begin{verbatim}
{
"encrypted": true/false,
"salt": "hex (16 байт)",
"iv": "hex (8 байт)",
"data": "hex шифротекст / открытый текст",
"hash": "SHA-256 hex (если включён контроль)"
}
\end{verbatim}
\textbf{Полнодуплексный обмен} обеспечивается двумя потоками: один читает ввод пользователя и отправляет сообщения, второй принимает входящие сообщения и выводит их на экран.
\textbf{Режимы запуска} задаются ключами командной строки: \texttt{-{}-encrypt} включает шифрование 3DES-CBC, \texttt{-{}-integrity} включает контроль целостности SHA-256, \texttt{-{}-hmac} включает контроль целостности и аутентификацию HMAC-SHA256 (несовместим с \texttt{-{}-integrity}), \texttt{-{}-test-integrity} активирует режим отправки сообщений с заведомо некорректным хэш-значением (требует \texttt{-{}-integrity} или \texttt{-{}-hmac}).
\textbf{Защита ключа.} Мастер-ключ считывается из файла (по умолчанию \texttt{key.txt}). При запуске программа проверяет права доступа к файлу ключа и завершает работу с ошибкой, если файл доступен для чтения группе или другим пользователям (права более открытые, чем \texttt{600}).
\subsection{Команды сборки и запуска}
Установка зависимостей:
\begin{verbatim}
uv sync
\end{verbatim}
Создание файла ключа:
\begin{verbatim}
openssl rand -hex 32 > key.txt
chmod 600 key.txt
\end{verbatim}
Запуск сервера и клиента (примеры):
\begin{verbatim}
uv run main.py server --port 9000
uv run main.py client 127.0.0.1 9000
uv run main.py server --port 9000 --encrypt
uv run main.py client 127.0.0.1 9000 --encrypt
uv run main.py server --port 9000 --encrypt --integrity
uv run main.py client 127.0.0.1 9000 --encrypt --integrity
\end{verbatim}
\subsection{Проверка работы приложения}
\subsubsection{Передача сообщений без шифрования}
При запуске без флагов сообщения передаются открытым текстом. На рисунке~\ref{fig:lab5-plain} показан вывод серверной и клиентской частей приложения.
\begin{figure}[H]
\centering
\includegraphics[width=0.8\linewidth]{img/lab5-plain.png}
\caption{Обмен сообщениями без шифрования}
\label{fig:lab5-plain}
\end{figure}
Анализ трафика утилитой \texttt{tcpdump} (рис.~\ref{fig:lab5-plain-tcpdump}) подтверждает, что текст сообщения передаётся в открытом виде и может быть прочитан непосредственно из дампа пакетов.
\begin{figure}[H]
\centering
\includegraphics[width=0.85\linewidth]{img/lab5-plain-tcpdump.png}
\caption{Дамп трафика (\texttt{tcpdump}) при передаче без шифрования — текст виден}
\label{fig:lab5-plain-tcpdump}
\end{figure}
\subsubsection{Передача сообщений с шифрованием}
При включённой опции \texttt{-{}-encrypt} сообщения шифруются алгоритмом 3DES-CBC с затравкой. На рисунке~\ref{fig:lab5-enc} показан вывод приложения: видны параметры шифрования (соль, производный ключ, IV, шифротекст) и результат расшифровки.
\begin{figure}[H]
\centering
\includegraphics[width=0.85\linewidth]{img/lab5-enc.png}
\caption{Обмен сообщениями с шифрованием 3DES-CBC}
\label{fig:lab5-enc}
\end{figure}
Дамп трафика (рис.~\ref{fig:lab5-enc-tcpdump}) показывает, что в пакетах присутствует только шифротекст в виде hex-строк; читаемый текст сообщения отсутствует.
\begin{figure}[H]
\centering
\includegraphics[width=0.85\linewidth]{img/lab5-enc-tcpdump.png}
\caption{Дамп трафика при передаче с шифрованием — текст не виден}
\label{fig:lab5-enc-tcpdump}
\end{figure}
\subsubsection{Шифрование с контролем целостности}
При одновременном использовании флагов \texttt{-{}-encrypt} и \texttt{-{}-integrity} к зашифрованному сообщению добавляется хэш SHA-256 открытого текста. На стороне получателя после расшифровки вычисляется собственный хэш и сравнивается с полученным (рис.~\ref{fig:lab5-enc-int}).
\begin{figure}[H]
\centering
\includegraphics[width=0.85\linewidth]{img/lab5-enc-int.png}
\caption{Обмен сообщениями с шифрованием и контролем целостности}
\label{fig:lab5-enc-int}
\end{figure}
\subsubsection{Тестирование контроля целостности с некорректным хэшем}
В режиме \texttt{-{}-test-integrity} отправитель намеренно передаёт некорректный хэш. Получатель обнаруживает расхождение и выводит предупреждение (рис.~\ref{fig:lab5-test-int}).
\begin{figure}[H]
\centering
\includegraphics[width=0.85\linewidth]{img/lab5-test-int.png}
\caption{Обнаружение некорректного хэш-значения}
\label{fig:lab5-test-int}
\end{figure}
\subsubsection{Демонстрация шифрования с затравкой}
При отправке одного и того же сообщения дважды шифротексты различаются благодаря случайной затравке и IV (рис.~\ref{fig:lab5-salt}).
\begin{figure}[H]
\centering
\includegraphics[width=0.85\linewidth]{img/lab5-salt.png}
\caption{Разные шифротексты для одинаковых сообщений (эффект затравки)}
\label{fig:lab5-salt}
\end{figure}
\subsubsection{Контроль целостности и аутентификация с HMAC-SHA256}
При использовании флага \texttt{-{}-hmac} вместо \texttt{-{}-integrity} к каждому сообщению прикладывается код HMAC-SHA256, вычисленный с использованием секретного ключа и случайной затравки. На рисунке~\ref{fig:lab5-hmac} показан обмен сообщениями в режиме \texttt{-{}-encrypt -{}-hmac}: видны HMAC-затравка, вычисленный HMAC на стороне отправителя и результат проверки на стороне получателя. При отправке одного и того же сообщения повторно значение HMAC отличается благодаря новой затравке.
\begin{figure}[H]
\centering
\includegraphics[width=0.85\linewidth]{img/lab5-hmac.png}
\caption{Обмен сообщениями с HMAC-SHA256: аутентификация и контроль целостности}
\label{fig:lab5-hmac}
\end{figure}
На рисунке~\ref{fig:lab5-hmac-fail} показана проверка режима \texttt{-{}-test-integrity} совместно с \texttt{-{}-hmac}: отправитель передаёт намеренно некорректный HMAC, получатель обнаруживает несоответствие и сообщает о неудачной аутентификации.
\begin{figure}[H]
\centering
\includegraphics[width=0.85\linewidth]{img/lab5-hmac-fail.png}
\caption{Обнаружение некорректного HMAC — отправитель не аутентифицирован}
\label{fig:lab5-hmac-fail}
\end{figure}
\subsection{Выводы}
В ходе практической работы №5 разработано клиент-серверное приложение для конфиденциального обмена сообщениями по протоколу TCP. Реализовано шифрование 3DES-CBC с затравкой, обеспечивающей различные шифротексты при одинаковых открытых текстах, контроль целостности на основе хэш-функции SHA-256 и режим HMAC-SHA256, обеспечивающий одновременно контроль целостности и аутентификацию отправителя. Экспериментальная проверка с помощью \texttt{tcpdump} подтвердила, что при включённом шифровании содержимое сообщений не передаётся в открытом виде, а при включённом контроле целостности получатель успешно обнаруживает намеренно повреждённые сообщения.
\newpage
\section*{Заключение}
\addcontentsline{toc}{section}{Заключение}
@@ -968,6 +1146,8 @@ $ sudo tcpdump -i any -n icmp
В ходе выполнения практической работы №4 на стенде из двух виртуальных машин VirtualBox настроена политика межсетевого экранирования средствами \texttt{iptables}. Реализованы правила фильтрации, разрешающие loopback-трафик, DNS, ICMP и HTTP/HTTPS, а все прочие соединения блокируются. Корректность работы правил подтверждена тестовыми соединениями и анализом пакетов утилитой \texttt{tcpdump}.
В ходе выполнения практической работы №5 разработано клиент-серверное приложение для конфиденциального обмена сообщениями по протоколу TCP. Реализовано шифрование 3DES-CBC с затравкой, контроль целостности SHA-256 и режим HMAC-SHA256 для аутентификации отправителя. Анализ трафика утилитой \texttt{tcpdump} подтвердил, что при включённом шифровании содержимое сообщений не доступно в открытом виде, а режим тестирования продемонстрировал корректное обнаружение нарушений целостности.
\newpage
\printbibliography[heading=bibintoc]
@@ -1021,4 +1201,11 @@ $ sudo tcpdump -i any -n icmp
\addcontentsline{toc}{section}{Приложение 8}
\lstinputlisting{../lab3/setup.sh}
\newpage
\section*{Приложение 9}
\subsubsection*{main.py}
\addcontentsline{toc}{section}{Приложение 9}
\label{app:lab5-main}
\lstinputlisting{../lab5/main.py}
\end{document}