diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..3c080a83 --- /dev/null +++ b/.gitignore @@ -0,0 +1,166 @@ +.vscode/launch.json +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +.vscode/ + +# C extensions +*.so + +# Distribution / packaging +.Python +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +share/python-wheels/ +*.egg-info/ +.installed.cfg +*.egg +MANIFEST + +# PyInstaller +# Usually these files are written by a python script from a template +# before PyInstaller builds the exe, so as to inject date/other infos into it. +*.manifest +*.spec + +# Installer logs +pip-log.txt +pip-delete-this-directory.txt + +# Unit test / coverage reports +htmlcov/ +.tox/ +.nox/ +.coverage +.coverage.* +.cache +nosetests.xml +coverage.xml +*.cover +*.py,cover +.hypothesis/ +.pytest_cache/ +cover/ + +# Translations +*.mo +*.pot + +# Django stuff: +*.log +local_settings.py +db.sqlite3 +db.sqlite3-journal + +# Flask stuff: +instance/ +.webassets-cache + +# Scrapy stuff: +.scrapy + +# Sphinx documentation +docs/_build/ + +# PyBuilder +.pybuilder/ +target/ + +# Jupyter Notebook +.ipynb_checkpoints + +# IPython +profile_default/ +ipython_config.py + +# pyenv +# For a library or package, you might want to ignore these files since the code is +# intended to run in multiple environments; otherwise, check them in: +# .python-version + +# pipenv +# According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control. +# However, in case of collaboration, if having platform-specific dependencies or dependencies +# having no cross-platform support, pipenv may install dependencies that don't work, or not +# install all needed dependencies. +#Pipfile.lock + +# poetry +# Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control. +# This is especially recommended for binary packages to ensure reproducibility, and is more +# commonly ignored for libraries. +# https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control +#poetry.lock + +# pdm +# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control. +#pdm.lock +# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it +# in version control. +# https://pdm.fming.dev/latest/usage/project/#working-with-version-control +.pdm.toml +.pdm-python +.pdm-build/ + +# PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm +__pypackages__/ + +# Celery stuff +celerybeat-schedule +celerybeat.pid + +# SageMath parsed files +*.sage.py + +# Environments +.env +.venv +env/ +venv/ +ENV/ +env.bak/ +venv.bak/ + +# Spyder project settings +.spyderproject +.spyproject + +# Rope project settings +.ropeproject + +# mkdocs documentation +/site + +# mypy +.mypy_cache/ +.dmypy.json +dmypy.json + +# Pyre type checker +.pyre/ + +# pytype static type analyzer +.pytype/ + +# Cython debug symbols +cython_debug/ + +# PyCharm +# JetBrains specific template is maintained in a separate JetBrains.gitignore that can +# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore +# and can be added to the global gitignore or merged into this file. For a more nuclear +# option (not recommended) you can uncomment the following to ignore the entire idea folder. +#.idea/ + diff --git a/lab_3/ctext.txt b/lab_3/ctext.txt new file mode 100644 index 00000000..a8508831 Binary files /dev/null and b/lab_3/ctext.txt differ diff --git a/lab_3/dctext.txt b/lab_3/dctext.txt new file mode 100644 index 00000000..fd13b2ba --- /dev/null +++ b/lab_3/dctext.txt @@ -0,0 +1,19 @@ +тальник восемь любимых песен + +мне кажется скоро выйдет что то новое у группы тальник никаких точных сведений у меня нет но есть такое чувство весна же на дворе если осень пора обязательных ежегодных альбомов от пореза на собаке то весна самое время для тальника но даже если новинок не будет материала у дуэта и так немало хватит чтобы грамотно встретить приход тепла я решил собрать небольшой плейлистик из любимых песен саши и светы сгодится для начинающих слушателей ну а опытные тальниковеды просто переслушают по кайфу ссылки на плейлист в конце поста + +белая улыбка о песнях группы тальник нередко пишут как о русских поп песнях конца девяностых которые ты слушал вполуха в детстве по радио из соседней комнаты и которые вдруг вплывают в памяти спустя много лет подобной хонтологией творчество дуэта не исчерпывается но одна из её лучших иллюстраций вот этот хит с альбома музыкайф с величайшей строчкой я иду и мятный холодок во рту + +белый белый белый день тальник светлый неотразимый поп номер вроде бы лежащий перед нами как на ладони но в то же время ускользающий куда то прямо на глазах при первом прослушивании мне показалось что в песне поётся уши разморозились к утру а не лужи и с тех пор я не могу перестать об этом думать + +неизбежная близость тальник тёмный деконструированный поп с залипающим в глитче голосом и текстом про тающую тьму кофе жидкую грязь и слишком жидкие гвозди внутри а что не мятным холодком единым + +давай прервёмся тальник лёгкий парящий в закатных облаках брейкбит под который света предлагает прерваться и посмотреть на небо почему почему что оно такое клёвое + +крылья наверное самый прямодушный поп номер группы тальник в придачу чудесный клип который во первых доказывает что ии видео могут быть не кринжовыми если подойди к делу с фантазией а во вторых физически передаёт зрителю ощущение что весна опять пришла и лучики тепла + +рисунки в пустоту коротенькая песня с моим любимым текстом из всего каталога тальника на бетонных берегах промзоны рисунки в пустоту затихшие стоят вагоны и травами цветут цвет надежды песня с которой всё началось маленький обледенелый шедевр как писала афиша в этой песне много пустот и странных провалов в ней всего одна строчка повторяется по кругу но этих девяти слов хватает чтобы сказать сразу обо всем о чём нужно + +всё пронесётся моя любимая песня может быть не только у тальника а вообще ещё один шедевр минимализма сперва мы слышим лишь смурной голос под глубокую басовую линию, затем в воздухе начинают мерцать клавишные будто накрапывает дождь из пустоты нарастает ритм музыка незаметно обрастает деталями, а потом разом накрывает на тебя с головой + +как морская волна текст наполнен ностальгией и сожалением по проходящему времени скроется в прошлом самый важный миг, и вот он уже кажется пошлым но в то же время фраза все пронесется зазвучит успокаивающе и даже обнадеживающе ведь и плохое тоже пронесется мантра и гимн последних лет \ No newline at end of file diff --git a/lab_3/hybrid_cryptosystem.py b/lab_3/hybrid_cryptosystem.py new file mode 100644 index 00000000..5d3f2a9f --- /dev/null +++ b/lab_3/hybrid_cryptosystem.py @@ -0,0 +1,231 @@ +import os +from cryptography.hazmat.primitives.asymmetric import rsa, padding +from cryptography.hazmat.primitives import padding as pd +from cryptography.hazmat.primitives import serialization, hashes +from cryptography.hazmat.primitives.serialization import load_pem_public_key, load_pem_private_key +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes +from cryptography.hazmat.primitives.asymmetric.rsa import (RSAPublicKey, RSAPrivateKey) +from tools import Tools +import json + +class SerelizationKey: + """Класс для сериализации и десериализации ключей""" + + def deser_key_public(public_pem: str) -> RSAPublicKey: + """ + Десериализация публичного ключа из PEM файла + + Args: + public_pem (str): Путь к файлу с публичным ключом + + Returns: + RSAPublicKey: Десериализованный публичный ключ + """ + with open(public_pem, 'rb') as pem_in: + public_bytes = pem_in.read() + d_public_key = load_pem_public_key(public_bytes) + return d_public_key + + def deser_key_private(private_pem: str) -> RSAPrivateKey: + """ + Десериализация приватного ключа из PEM файла + + Args: + private_pem (str): Путь к файлу с приватным ключом + + Returns: + RSAPrivateKey: Десериализованный приватный ключ + """ + with open(private_pem, 'rb') as pem_in: + private_bytes = pem_in.read() + d_private_key = load_pem_private_key(private_bytes,password=None,) + return d_private_key + + def ser_public_key(public_key: RSAPublicKey, public_pem: str): + """ + Сериализация публичного ключа в PEM файл + + Args: + public_key (RSAPublicKey): Публичный ключ для сериализации + public_pem (str): Путь для сохранения публичного ключа + """ + with open(public_pem, 'wb') as public_out: + public_out.write(public_key.public_bytes(encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo)) + + def ser_private_key(private_key: RSAPrivateKey, private_pem: str): + """ + Сериализация приватного ключа в PEM файл + + Args: + private_key (RSAPrivateKey): Приватный ключ для сериализации + private_pem (str): Путь для сохранения приватного ключа + """ + with open(private_pem, 'wb') as private_out: + private_out.write(private_key.private_bytes(encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption())) + +class GenerationKey: + """Класс для генерации симметричных и асимметричных ключей""" + + def gen_key_sym(num_of_bit: int): + """ + Генерация симметричного ключа заданной длины + + Args: + num_of_bit (int): Длина ключа в битах (128, 192, 256) + """ + settings = Tools.read_json("settings.json") + key = os.urandom(num_of_bit//8) + Tools.save_txt_binary(settings["path_sym_key"], key) + + def gen_key_asym() -> tuple[RSAPublicKey, RSAPrivateKey]: + """ + Генерация пары асимметричных ключей (приватный и публичный) + + Returns: + tuple: (private_key, public_key) - пара RSA ключей + """ + keys = rsa.generate_private_key( + public_exponent=65537, + key_size=2048 + ) + private_key = keys + public_key = keys.public_key() + return private_key, public_key + +class Crypt: + """Класс для выполнения криптографических операций""" + + def ass_crypt(public_key: RSAPublicKey, path_sym_key: str): + """ + Асимметричное шифрование симметричного ключа + + Args: + public_key (RSAPublicKey): Публичный ключ для шифрования + path_sym_key (str): Путь к файлу с симметричным ключом + """ + text = Tools.read_txt_binary(path_sym_key) + c_text = public_key.encrypt(text, padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()),algorithm=hashes.SHA256(),label=None)) + file_name = path_sym_key + with open(file_name, 'wb') as key_file: + key_file.write(c_text) + + def ass_decrtpt(private_key_path, path_sym_key): + text = Tools.read_txt_binary(path_sym_key) + private_key = SerelizationKey.deser_key_private(private_key_path) + dc_text = private_key.decrypt(bytes(text),padding.OAEP(mgf=padding.MGF1(algorithm=hashes.SHA256()),algorithm=hashes.SHA256(),label=None)) + return dc_text + + def encrypt_camellia_text(text_path: str, key: bytes, ctext_path: str, iv_path: str): + """ + Шифрование текста алгоритмом Camellia в режиме CBC + + Args: + text_path (str): Путь до исходного текстового файла + key (bytes): Симметричный ключ для шифрования + ctext_path (str): Путь для сохранения зашифрованного текста + iv_path (str): Путь для сохранения вектора инициализации (IV) + + """ + + text = Tools.read_txt(text_path) + padder = pd.ANSIX923(128).padder() + text_bytes = text.encode('UTF-8') if isinstance(text, str) else text + padded_text = padder.update(text_bytes) + padder.finalize() + iv = os.urandom(16) + cipher = Cipher(algorithms.Camellia(key), modes.CBC(iv)) + encryptor = cipher.encryptor() + cipher_text = encryptor.update(padded_text) + encryptor.finalize() + Tools.save_txt_binary(ctext_path, cipher_text) + Tools.save_txt_binary(iv_path, iv) + + def decrypt_camellia_text(cipher_text_path: str, key: bytes, iv_path: str, dcipher_text_path: str): + """ + Дешифрование текста алгоритмом Camellia + + Args: + cipher_text_path (str): Путь к файлу с зашифрованным текстом + key (bytes): Симметричный ключ для дешифрования + iv_path (str): Путь к файлу с вектором инициализации + dcipher_text_path (str): Путь для сохранения расшифрованного текста + + """ + iv = Tools.read_txt_binary(iv_path) + cipher_text = Tools.read_txt_binary(cipher_text_path) + cipher = Cipher(algorithms.Camellia(key), modes.CBC(iv)) + decryptor = cipher.decryptor() + + padded_text = decryptor.update(cipher_text) + decryptor.finalize() + + unpadder = pd.ANSIX923(128).unpadder() + original_text = unpadder.update(padded_text) + unpadder.finalize() + original_text = original_text.decode('UTF-8') + Tools.save_txt(dcipher_text_path, original_text) + +class Modes: + + """Класс для работы с режимами гибридной криптосистемы""" + + def hybrid_sys_key_gen(path_sym_key: str, path_private_key: str, path_public_key: str): + """ + Генерация ключей для гибридной криптосистемы + + Args: + path_sym_key (str): Путь для сохранения симметричного ключа + path_private_key (str): Путь для сохранения приватного ключа + path_public_key (str): Путь для сохранения публичного ключа + """ + with open('settings.json') as json_file: + settings = json.load(json_file) + key_length = settings["num_of_bit"] + if not key_length in [128, 192, 256]: + raise ValueError(f"Неверная длина ключа, может быть 128, 192, 256, получено {key_length}") + print("Генерация симметричного ключа шифрования...") + GenerationKey.gen_key_sym(key_length) + print("Генерация aссиметричных ключей шифрования...") + private_key, public_key = GenerationKey.gen_key_asym() + print("Серилизация aссиметричных ключей шифрования...") + SerelizationKey.ser_private_key(private_key, path_private_key) + SerelizationKey.ser_public_key(public_key, path_public_key) + print("Шифрование публичного ключа...") + Crypt.ass_crypt(public_key, path_sym_key) + print("Готово!") + + def data_encryption(path_sym_key: str, path_private_key: str, text_path: str, ctext_path: str, iv_path: str): + """ + Шифрование данных с использованием гибридной системы + + Args: + path_sym_key (str): Путь к зашифрованному симметричному ключу + path_private_key (str): Путь к приватному ключу для дешифрования симметричного ключа + text_path (str): Путь к исходному текстовому файлу + ctext_path (str): Путь для сохранения зашифрованного текста + iv_path (str): Путь для сохранения вектора инициализации + """ + print("Распаковка ключа шифрования...") + key = Crypt.ass_decrtpt(path_private_key, path_sym_key) + print("Шифрование текста...") + Crypt.encrypt_camellia_text(text_path, key ,ctext_path, iv_path) + print("Готово!") + + def data_decryption(path_sym_key: str, path_private_key: str, ctext_path: str, dctext_path: str, iv_path: str): + """ + Дешифрование данных с использованием гибридной системы + + Args: + path_sym_key (str): Путь к зашифрованному симметричному ключу + path_private_key (str): Путь к приватному ключу для дешифрования симметричного ключа + ctext_path (str): Путь к зашифрованному тексту + dctext_path (str): Путь для сохранения расшифрованного текста + iv_path (str): Путь к файлу с вектором инициализации + """ + print("Распаковка ключа шифрования...") + key = Crypt.ass_decrtpt(path_private_key, path_sym_key) + print("Дешифровка текста...") + Crypt.decrypt_camellia_text(ctext_path, key, iv_path, dctext_path) + print("Готово!") + + + \ No newline at end of file diff --git a/lab_3/iv.txt b/lab_3/iv.txt new file mode 100644 index 00000000..88bee55e --- /dev/null +++ b/lab_3/iv.txt @@ -0,0 +1 @@ +~urN #P;# \ No newline at end of file diff --git a/lab_3/main.py b/lab_3/main.py new file mode 100644 index 00000000..0d029a16 --- /dev/null +++ b/lab_3/main.py @@ -0,0 +1,46 @@ +import argparse +from hybrid_cryptosystem import Modes +from cryptography.exceptions import InvalidKey, UnsupportedAlgorithm +from tools import Tools + +def main(): + + """ Точка входа в приложение """ + try: + settings_file = 'settings.json' + Tools.is_file_exists(settings_file) + settings = Tools.read_json(settings_file) + parser = argparse.ArgumentParser() + group = parser.add_mutually_exclusive_group(required = True) + group.add_argument('-gen','--generation',help='Запускает режим генерации ключей') + group.add_argument('-enc','--encryption',help='Запускает режим шифрования') + group.add_argument('-dec','--decryption',help='Запускает режим дешифрования') + + args = parser.parse_args() + if args.generation is not None: + + Modes.hybrid_sys_key_gen(settings["path_sym_key"], settings["path_private_key"], settings["path_public_key"]) + + elif args.encryption is not None: + + Modes.data_encryption(settings["path_sym_key"], settings["path_private_key"], settings["path_text"], settings["path_ctext"], settings["path_iv"]) + + else: + Modes.data_decryption(settings["path_sym_key"], settings["path_private_key"], settings["path_ctext"], settings["path_dctext"], settings["path_iv"]) + except UnsupportedAlgorithm as e: + print(f"Cryptographic algorithm not supported: {e}") + except InvalidKey as e: + print(f"Key serialization error: {e}") + except PermissionError as e: + print(f"PermissionError: {e}") + except FileNotFoundError as e: + print(f"FileNotFoundError: {e}") + except ValueError as e: + print(f"ValueError: {e}") + except KeyError as e: + print(f"Missing setting: {e}") + except Exception as e: + print(f"Error: {e}") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/lab_3/private_key.pem b/lab_3/private_key.pem new file mode 100644 index 00000000..0feb333b --- /dev/null +++ b/lab_3/private_key.pem @@ -0,0 +1,27 @@ +-----BEGIN RSA PRIVATE KEY----- +MIIEowIBAAKCAQEAxRa0JGRHAGJTd1twzB9cQUOVAqLHMELq0uuOAyhNXO8foRik +iN9ZU8N7pf/3i43RvfSbTRPje06+uFxHP6WxGFHiTUmhEP1mJ0moNY0wuR/a9tUp +Ydeu28OiKZrnDtRIQj/g6uaAFO9sNHGan4f3SKQIgg7b4ZZkLa4WNdF6iiikEEUz +RqldLj6ENWjJnRxiZC7BoKkPJojbOSpxeIhQJBEIQLQZzvNpekPCPgGufrM2V2CV +dF8Q65Qy8ciqfXONqHz2zuyb80cHwX5aZKaPrum8jw+XBPIMwpASNQb7zXetaih0 +ToWzp9Xt85v7aMxZw+ZUZVaszOO5q6LcqG8oBQIDAQABAoIBAAL4W6u7VCOYfiWe +4k7WzFl41uj+TxsP1k43IEG1bLo+EBkc9midC95UJGVxyFP/n0qdNVSLYtX9b48Q +Z78Sjh4g9WFdsiVZc3dOzmllY/eiPoOr3jU8HoWi1Hgk8YVhPYG8FZv/1pKaMhec +CUaNGMGtCsP7Zj5nbKgqLVA10Wj9rk02zsKQ6IzyPixHNmM/myKiJjuwAP0vA10d +zY/GMr+2GaoyTfdUS4QMJlU8y85S+f9QthBDrPJdNnMI5foPxiP2cxHGquLE0My9 +aHpGqNOTOVS2dcJ1kuJzdFe6xfc9tbdWr/W7DTVVnqFUrrul/aqVkiKyhmJL2o/0 +sCO7hXECgYEA9sog0wj1ZX/lf4YxRPd64AoAymG3UBgKY2Ra/2Pld1zEVl3UIlV/ +nBlLgnXOwrfYtDnwhQru5Jv4SWKPFysbg61CSCyVwxKd2YmFXGNnieZkck/ET0gw +HOaPYlhuxBPppmjvAY97/8ut7XITpKo/ma72JHoLIcZLhHB4dIYgpPECgYEAzHG5 +b67q0re+1ynL/1ZHMdwvlN6W9ElsPzbPWP7wPP8D3ZNO3NUuRdgvbNjy38VAQ9tk +HtRtH+XdkIL3HQG4HIE2gC29sfL5GbOSFbaCRYz+uWVpdVoLjToYdRQ6XLWH0U/J +kH9z8GMW0Co0VAPDZiiM1IvG2ZI3aDNWcjzOpFUCgYEAxVyScZLZhUmKBLkRj+EG +0ELPrQoGarQ/IotwRoviR9nVXmh9LNQJUJHeFEAYkRor4L3xPsSmlgaxwiZi3pln +0J9vwX5+alnqCL10/JS2ic7Jh62JBgpqB8LO6kxkkMSH98A1W58fAEsoFhtD4E0S +vdnxGOzNGD6XyBuWCUH2u1ECgYAyLXNa4u6Fe02el4aXC8D7jtQq0JryKBcmSLiJ +WkDyZ5C2UFJZxwTNZar2JyUtIMeOIAmpu29xFcTUlCA8gVCDxn7u5MsbIsxar4Ua +SSoeEeXu8OH6SHhvWCPqkGVmnbNQ/yk0iuOUy20s+D7Ro/7QUwGFpSzP96AbtgXw +kdYR5QKBgGl1uAMI0LLQGqIo0AAYTRA3N5TIcK4hhXQFRRSXHQtnItEK0JUzw9BE +pm4yNnvW482s+N7ts9/LHr9o7zst8OZl14iZMMoYZJ0Wi0KK8HE3yyWUFRGVRkPz +AoQHF6NxuxDY44Jm0g+fTL2yYkgIkyppBeF+Mu55ZkbbGNhBhMoV +-----END RSA PRIVATE KEY----- diff --git a/lab_3/public_key.pem b/lab_3/public_key.pem new file mode 100644 index 00000000..89e21db5 --- /dev/null +++ b/lab_3/public_key.pem @@ -0,0 +1,9 @@ +-----BEGIN PUBLIC KEY----- +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxRa0JGRHAGJTd1twzB9c +QUOVAqLHMELq0uuOAyhNXO8foRikiN9ZU8N7pf/3i43RvfSbTRPje06+uFxHP6Wx +GFHiTUmhEP1mJ0moNY0wuR/a9tUpYdeu28OiKZrnDtRIQj/g6uaAFO9sNHGan4f3 +SKQIgg7b4ZZkLa4WNdF6iiikEEUzRqldLj6ENWjJnRxiZC7BoKkPJojbOSpxeIhQ +JBEIQLQZzvNpekPCPgGufrM2V2CVdF8Q65Qy8ciqfXONqHz2zuyb80cHwX5aZKaP +rum8jw+XBPIMwpASNQb7zXetaih0ToWzp9Xt85v7aMxZw+ZUZVaszOO5q6LcqG8o +BQIDAQAB +-----END PUBLIC KEY----- diff --git a/lab_3/settings.json b/lab_3/settings.json new file mode 100644 index 00000000..30e6868a --- /dev/null +++ b/lab_3/settings.json @@ -0,0 +1,10 @@ +{ + "path_sym_key" : "sym_key.txt", + "path_private_key" : "private_key.pem", + "path_public_key" : "public_key.pem", + "path_text" : "text.txt", + "path_ctext" : "ctext.txt", + "path_iv" : "iv.txt", + "path_dctext" : "dctext.txt", + "num_of_bit" : 256 +} \ No newline at end of file diff --git a/lab_3/sym_key.txt b/lab_3/sym_key.txt new file mode 100644 index 00000000..97ddc373 Binary files /dev/null and b/lab_3/sym_key.txt differ diff --git a/lab_3/text.txt b/lab_3/text.txt new file mode 100644 index 00000000..fd13b2ba --- /dev/null +++ b/lab_3/text.txt @@ -0,0 +1,19 @@ +тальник восемь любимых песен + +мне кажется скоро выйдет что то новое у группы тальник никаких точных сведений у меня нет но есть такое чувство весна же на дворе если осень пора обязательных ежегодных альбомов от пореза на собаке то весна самое время для тальника но даже если новинок не будет материала у дуэта и так немало хватит чтобы грамотно встретить приход тепла я решил собрать небольшой плейлистик из любимых песен саши и светы сгодится для начинающих слушателей ну а опытные тальниковеды просто переслушают по кайфу ссылки на плейлист в конце поста + +белая улыбка о песнях группы тальник нередко пишут как о русских поп песнях конца девяностых которые ты слушал вполуха в детстве по радио из соседней комнаты и которые вдруг вплывают в памяти спустя много лет подобной хонтологией творчество дуэта не исчерпывается но одна из её лучших иллюстраций вот этот хит с альбома музыкайф с величайшей строчкой я иду и мятный холодок во рту + +белый белый белый день тальник светлый неотразимый поп номер вроде бы лежащий перед нами как на ладони но в то же время ускользающий куда то прямо на глазах при первом прослушивании мне показалось что в песне поётся уши разморозились к утру а не лужи и с тех пор я не могу перестать об этом думать + +неизбежная близость тальник тёмный деконструированный поп с залипающим в глитче голосом и текстом про тающую тьму кофе жидкую грязь и слишком жидкие гвозди внутри а что не мятным холодком единым + +давай прервёмся тальник лёгкий парящий в закатных облаках брейкбит под который света предлагает прерваться и посмотреть на небо почему почему что оно такое клёвое + +крылья наверное самый прямодушный поп номер группы тальник в придачу чудесный клип который во первых доказывает что ии видео могут быть не кринжовыми если подойди к делу с фантазией а во вторых физически передаёт зрителю ощущение что весна опять пришла и лучики тепла + +рисунки в пустоту коротенькая песня с моим любимым текстом из всего каталога тальника на бетонных берегах промзоны рисунки в пустоту затихшие стоят вагоны и травами цветут цвет надежды песня с которой всё началось маленький обледенелый шедевр как писала афиша в этой песне много пустот и странных провалов в ней всего одна строчка повторяется по кругу но этих девяти слов хватает чтобы сказать сразу обо всем о чём нужно + +всё пронесётся моя любимая песня может быть не только у тальника а вообще ещё один шедевр минимализма сперва мы слышим лишь смурной голос под глубокую басовую линию, затем в воздухе начинают мерцать клавишные будто накрапывает дождь из пустоты нарастает ритм музыка незаметно обрастает деталями, а потом разом накрывает на тебя с головой + +как морская волна текст наполнен ностальгией и сожалением по проходящему времени скроется в прошлом самый важный миг, и вот он уже кажется пошлым но в то же время фраза все пронесется зазвучит успокаивающе и даже обнадеживающе ведь и плохое тоже пронесется мантра и гимн последних лет \ No newline at end of file diff --git a/lab_3/tools.py b/lab_3/tools.py new file mode 100644 index 00000000..0caa5fac --- /dev/null +++ b/lab_3/tools.py @@ -0,0 +1,89 @@ +import json +from typing import Any, Dict +import os +from pathlib import Path + + +class Tools: + @staticmethod + def read_txt_binary(file_name: str) -> bytes: + """Считывает текcт из .txt файла + + Args: + file_name (str): Путь к файлу + + Returns: + str: Содержимое файла + """ + with open(file_name, 'rb') as f: + text: str = f.read() + return text + + @staticmethod + def read_txt(file_name: str) -> str: + """Считывает текcт из .txt файла + + Args: + file_name (str): Путь к файлу + + Returns: + str: Содержимое файла + """ + with open(file_name, 'r', encoding="utf-8") as f: + text: str = f.read() + return text + + @staticmethod + def save_txt(file_name: str, text: str): + """Удаляет содержимое файла и сохраняет в нём новый текст. + Если файла не существует, создаёт его. + + Args: + file_name (str): Путь к файлу + text (str): Текст, который необходимо сохранить + """ + with open(file_name, 'w', encoding="utf-8") as f: + f.write(text) + + @staticmethod + def save_txt_binary(file_name: str, text: bytes): + """Удаляет содержимое файла и сохраняет в нём новый текст. + Если файла не существует, создаёт его. + + Args: + file_name (str): Путь к файлу + text (str): Текст, который необходимо сохранить + """ + with open(file_name, 'wb') as f: + f.write(text) + + @staticmethod + def read_json(file_name: str) -> Dict[str, Any]: + """Считывает данные из .json файла + + Args: + file_name (str): Путь к файлу + + Returns: + Dict[str, Any]: Словарь объектов, содеражавшихся в .json файле + """ + with open(file_name, 'r', encoding='utf-8') as f: + return(json.load(f)) + + @staticmethod + def is_file_exists(file_path: str): + """Проверяет, существует ли файл + Args: + file_path (str): Путь к файлу + Raises: + FileNotFoundError: Если файл не существует + ValueError: Не является файлом + PermissionError: Нету доступа к файлу + """ + path = Path(file_path) + if not path.exists(): + raise FileNotFoundError(f"Файл не найден: {file_path}") + if not path.is_file(): + raise ValueError(f"Не является файлом: {file_path}") + if not os.access(file_path, os.R_OK): + raise PermissionError(f"Нету доступа к файлу: {file_path}") \ No newline at end of file