diff --git a/lab_2/README.md b/lab2/README.md similarity index 100% rename from lab_2/README.md rename to lab2/README.md diff --git a/lab2/consts.json b/lab2/consts.json new file mode 100644 index 000000000..2967a41e9 --- /dev/null +++ b/lab2/consts.json @@ -0,0 +1,7 @@ +{ + "m": 8, + "pi": [0.2148, 0.3672, 0.2305, 0.1875], + "sequence_cpp": "sequence_cpp.txt", + "sequence_java": "sequence_java.txt", + "result": "result.json" +} \ No newline at end of file diff --git a/lab2/gen_cpp.cpp b/lab2/gen_cpp.cpp new file mode 100644 index 000000000..8a755a650 --- /dev/null +++ b/lab2/gen_cpp.cpp @@ -0,0 +1,24 @@ +#include +#include +#include +#include + +void generate_bit_sequence() { + std::random_device rd; + std::mt19937_64 gen(rd()); + std::uniform_int_distribution dist(0, 1); + + std::bitset<128> bits; + for (int i = 0; i < 128; ++i) { + bits[i] = dist(gen); + } + + std::ofstream output_file("sequence_cpp.txt"); + output_file << bits; + output_file.close(); +} + +int main() { + generate_bit_sequence(); + return 0; +} \ No newline at end of file diff --git a/lab2/gen_java.java b/lab2/gen_java.java new file mode 100644 index 000000000..b831f7d8a --- /dev/null +++ b/lab2/gen_java.java @@ -0,0 +1,13 @@ +import java.io.FileWriter; +import java.io.IOException; + +public class Main { + public static void main(String[] args) { + StringBuilder bits = new StringBuilder(128); + for (int i = 0; i < 128; i++) { + bits.append((int) (Math.random() * 2)); + } + String sequence = bits.toString(); + System.out.println(sequence); + } +} \ No newline at end of file diff --git a/lab2/main.py b/lab2/main.py new file mode 100644 index 000000000..aa344ced0 --- /dev/null +++ b/lab2/main.py @@ -0,0 +1,27 @@ +from tests import frequency_bit_test, identical_consecutive_bits_test, longest_sequence_of_ones_test +from work_with_files import read_file, read_json, write_json + + +def main(): + try: + constants = read_json('consts.json') + + sequence_cpp = read_file(constants['sequence_cpp']) + sequence_java = read_file(constants['sequence_java']) + + stats = { + "C++ frequency bit test": frequency_bit_test(sequence_cpp), + "Java frequency bit test": frequency_bit_test(sequence_java), + "C++ identical bit sequence": identical_consecutive_bits_test(sequence_cpp), + "Java identical bit sequence": identical_consecutive_bits_test(sequence_java), + "C++ longest run of ones": longest_sequence_of_ones_test(sequence_cpp,constants['pi'],constants['m']), + "Java longest run of ones": longest_sequence_of_ones_test(sequence_java,constants['pi'],constants['m']) + } + + write_json(constants['result'], stats) + + except Exception as e: + print(e) + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/lab2/result.json b/lab2/result.json new file mode 100644 index 000000000..2efaad225 --- /dev/null +++ b/lab2/result.json @@ -0,0 +1,8 @@ +{ + "C++ frequency bit test": 0.8596837951986662, + "Java frequency bit test": 0.4795001221869535, + "C++ identical bit sequence": 0.2899841563937249, + "Java identical bit sequence": 0.39923839789091575, + "C++ longest run of ones": 0.26745460619217515, + "Java longest run of ones": 0.21815226058275772 +} diff --git a/lab2/sequence_cpp.cpp b/lab2/sequence_cpp.cpp new file mode 100644 index 000000000..9e6c2189a --- /dev/null +++ b/lab2/sequence_cpp.cpp @@ -0,0 +1 @@ +11010011110110000011000011011001010000101001001000001100111101111101001011100011111011110111010110000011101100110000000100111011 \ No newline at end of file diff --git a/lab2/sequence_java.txt b/lab2/sequence_java.txt new file mode 100644 index 000000000..948c34e15 --- /dev/null +++ b/lab2/sequence_java.txt @@ -0,0 +1 @@ +10011101011001000111111110010010101100000100011011110011110001110001010000011111000110001100001110110001000110100010001001010010 \ No newline at end of file diff --git a/lab2/tests.py b/lab2/tests.py new file mode 100644 index 000000000..c1a98350e --- /dev/null +++ b/lab2/tests.py @@ -0,0 +1,80 @@ +import math + +from scipy.special import gammainc + + +def frequency_bit_test(sequence: str) -> float: + """ + Frequency bit test + :param sequence: bit sequence + :return: p-value + """ + n = len(sequence) + x = 0 + for i in sequence: + match i: + case '1': + x += 1 + case '0': + x += -1 + + s_n = abs(x) / math.sqrt(n) + p_value = math.erfc(s_n / math.sqrt(2)) + return p_value + + +def identical_consecutive_bits_test(sequence: str) -> float: + """ + Test for identical consecutive bits + :param sequence: bit sequence + :return: p-value + """ + n = len(sequence) + zeta = sequence.count('1') / n + + if abs(zeta - 0.5) >= (2 / math.sqrt(n)): + return 0.0 + else: + v_n = sum(1 for i in range(n - 1) if sequence[i] != sequence[i + 1]) + p_value = math.erfc(abs(v_n - 2 * n * zeta * (1 - zeta)) / (2 * math.sqrt(2 * n) * zeta * (1 - zeta))) + return p_value + + +def longest_sequence_of_ones_test(sequence: str, pi: list, m: int) -> float: + """ + Test for the longest sequence of ones in a block + :param sequence: bit sequence + :param pi: list of probabilities + :param m: block size + :return: p-value + """ + v = [0, 0, 0, 0] + + blocks = [sequence[i:i + m] for i in range(0, len(sequence), m)] + + for block in blocks: + counter = 0 + ones_count = 0 + + for i in block: + if i == '1': + counter += 1 + ones_count = max(ones_count, counter) + else: + counter = 0 + match ones_count: + case 0 | 1: + v[0] += 1 + case 2: + v[1] += 1 + case 3: + v[2] += 1 + case _: + v[3] += 1 + + chi_square = 0 + for i in range(0, 4): + chi_square += (((v[i] - 16 * pi[i]) ** 2) / (16 * pi[i])) + + p_value = gammainc(3 / 2, chi_square / 2) + return p_value \ No newline at end of file diff --git a/lab2/work_with_files.py b/lab2/work_with_files.py new file mode 100644 index 000000000..50e94f99c --- /dev/null +++ b/lab2/work_with_files.py @@ -0,0 +1,51 @@ +import json + + +def read_file(file_name: str) -> str: + """ + Reads the contents of the file + :param file_name: file name + :return: file contents + """ + try: + with open(file_name, 'r', encoding='utf-8') as file: + return file.read() + except FileNotFoundError: + raise FileNotFoundError(f"File not found") + except IOError: + raise IOError(f"Error reading file") + except Exception as e: + raise Exception("Error: {e}") + + +def read_json(file_name: str) -> dict: + """ + Reads the contents of a json file + :param file_name: file name + :return: dictionary + """ + try: + with open(file_name, 'r', encoding='utf-8') as file: + return json.load(file) + except FileNotFoundError: + raise FileNotFoundError(f"File not found") + except IOError: + raise IOError(f"Error reading file") + except Exception as e: + raise Exception("Error: {e}") + + +def write_json(file_name: str, text:float)->None: + """ + Writes the content to a json file + :param file_name: file name + :param text: text + :return: None + """ + try: + with open(file_name, 'w', encoding='utf-8') as file: + json.dump(text, file, ensure_ascii=False, indent=4) + except IOError: + raise IOError(f"Couldn't write to a file") + except Exception as e: + raise Exception("Error: {e}") \ No newline at end of file diff --git a/lab_3/asymmetric_cripto.py b/lab_3/asymmetric_cripto.py new file mode 100644 index 000000000..1006ca315 --- /dev/null +++ b/lab_3/asymmetric_cripto.py @@ -0,0 +1,80 @@ +from cryptography.hazmat.primitives import hashes, serialization +from cryptography.hazmat.primitives.asymmetric import padding, rsa +from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey, RSAPublicKey + + +class AsymmetricEncryption: + """ Класс для асимметричного шифрования с использованием алгоритма RSA """ + + @staticmethod + def generate_rsa_keys() -> tuple[RSAPrivateKey, RSAPublicKey]: + """ + Генерирует пару RSA ключей + :return: кортеж из приватного и публичного ключей + """ + private_key = rsa.generate_private_key( + public_exponent=65537, + key_size=2048 + ) + return private_key, private_key.public_key() + + @staticmethod + def serialization_asymmetric_public_key(public_key: RSAPublicKey) -> bytes: + """ + Сериализует публичный RSA ключ в PEM формат + :param public_key: публичный ключ + :return: публичный ключ в PEM формате + """ + pem_public_key = public_key.public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo + ) + return pem_public_key + + @staticmethod + def serialization_asymmetric_private_key(private_key: RSAPrivateKey) -> bytes: + """ + Сериализует приватный RSA ключ в PEM формат + :param private_key: приватный ключ + :return: приватный ключ в PEM формате + """ + pem_private_key = private_key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption() + ) + return pem_private_key + + @staticmethod + def rsa_encrypt(public_key: RSAPublicKey, data: bytes) -> bytes: + """ + Шифрует данные + :param public_key: публичный ключ + :param data: данные + :return: зашифрованные данные + """ + return public_key.encrypt( + data, + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=None + ) + ) + + @staticmethod + def rsa_decrypt(private_key: RSAPrivateKey, encrypted_data: bytes) -> bytes: + """ + Расшифровывает данные + :param private_key: приватный ключ + :param encrypted_data: зашифрованные данные + :return: расшифрованные данные + """ + return private_key.decrypt( + encrypted_data, + padding.OAEP( + mgf=padding.MGF1(algorithm=hashes.SHA256()), + algorithm=hashes.SHA256(), + label=None + ) + ) \ No newline at end of file diff --git a/lab_3/decrypted_text.txt b/lab_3/decrypted_text.txt new file mode 100644 index 000000000..36e4d809f --- /dev/null +++ b/lab_3/decrypted_text.txt @@ -0,0 +1 @@ +У него в пятнадцати верстах от постоялого дворика хорошее имение в двести душ, или, как он выражается с тех пор, как размежевался с крестьянами и завел «ферму», — в две тысячи десятин земли. Отец его, боевой генерал 1812 года, полуграмотный, грубый, но не злой русский человек, всю жизнь свою тянул лямку, командовал сперва бригадой, потом дивизией и постоянно жил в провинции, где в силу своего чина играл довольно значительную роль. Николай Петрович родился на юге России, подобно старшему своему брату Павлу, о котором речь впереди, и воспитывался до четырнадцатилетнего возраста дома, окруженный дешевыми гувернерами, развязными, но подобострастными адъютантами и прочими полковыми и штабными личностями. Родительница его, из фамилии Колязиных, в девицах Agathe, а в генеральшах Агафоклея Кузьминишна Кирсанова, принадлежала к числу «матушек-командирш», носила пышные чепцы и шумные шелковые платья, в церкви подходила первая ко кресту, говорила громко и много, допускала детей утром к ручке, на ночь их благословляла, — словом, жила в свое удовольствие. В качестве генеральского сына Николай Петрович — хотя не только не отличался храбростью, но даже заслужил прозвище трусишки — должен был, подобно брату Павлу, поступить в военную службу; но он переломил себе ногу в самый тот день, когда уже прибыло известие об его определении, и, пролежав два месяца в постели, на всю жизнь остался «хроменьким». Отец махнул на него рукой и пустил его по штатской. Он повез его в Петербург, как только ему минул восемнадцатый год, и поместил его в университет. Кстати, брат его о ту пору вышел офицером в гвардейский полк. Молодые люди стали жить вдвоем, на одной квартире, под отдаленным надзором двоюродного дяди с материнской стороны, Ильи Колязина, важного чиновника. \ No newline at end of file diff --git a/lab_3/encrypted_symmetric_key.bin b/lab_3/encrypted_symmetric_key.bin new file mode 100644 index 000000000..a4e87ebec Binary files /dev/null and b/lab_3/encrypted_symmetric_key.bin differ diff --git a/lab_3/main.py b/lab_3/main.py new file mode 100644 index 000000000..7a9083d98 --- /dev/null +++ b/lab_3/main.py @@ -0,0 +1,171 @@ +import argparse + +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import serialization + +from asymmetric_cripto import AsymmetricEncryption +from symmetric_crypto import SymmetricalEncryption +from work_with_files import read_binary_file, write_binary_file, read_json, write_json + + +def generate_keys(key_size, public_key_path: str, + private_key_path: str, + encrypted_symmetric_key_path: str ) -> None: + """ + Генерирует все ключи + :param key_size: размер симметричного ключа + :param public_key_path: путь для сохранения публичного RSA ключа + :param private_key_path: путь для сохранения приватного RSA ключа + :param encrypted_symmetric_key_path: путь для сохранения зашифрованного симметричного ключа + """ + try: + symmetric_key = SymmetricalEncryption.generate_key(key_size) + private_key, public_key = AsymmetricEncryption.generate_rsa_keys() + + serialized_public = AsymmetricEncryption.serialization_asymmetric_public_key(public_key) + serialized_private = AsymmetricEncryption.serialization_asymmetric_private_key(private_key) + + encrypted_symmetric_key = AsymmetricEncryption.rsa_encrypt(public_key, symmetric_key) + + write_binary_file(public_key_path, serialized_public) + write_binary_file(private_key_path, serialized_private) + write_binary_file(encrypted_symmetric_key_path, encrypted_symmetric_key) + + except Exception as e: + raise RuntimeError(f"Ошибка в процессе генерации ключей: {str(e)}") + + +def encrypt_data(original_text_path: str, + private_key_path: str, + encrypted_symmetric_key_path: str, + encrypt_text_path: str) -> None: + """ + Шифрует данные + :param original_text_path: путь к файлу с исходными данными для шифрования + :param private_key_path: путь к файлу с приватным RSA ключом + :param encrypted_symmetric_key_path: путь к файлу с зашифрованным симметричным ключом + :param encrypt_text_path: путь для сохранения зашифрованных данных + """ + try: + original_text = read_binary_file(original_text_path) + private_key_bytes = read_binary_file(private_key_path) + encrypted_symmetric_key = read_binary_file(encrypted_symmetric_key_path) + + private_key = serialization.load_pem_private_key( + private_key_bytes, + password=None, + backend=default_backend() + ) + + symmetric_key = AsymmetricEncryption.rsa_decrypt(private_key, encrypted_symmetric_key) + + encrypted_text = SymmetricalEncryption.encrypt_data(original_text, symmetric_key) + write_binary_file(encrypt_text_path, encrypted_text) + except Exception as e: + raise RuntimeError(f"Ошибка в процессе шифрования данных: {str(e)}") + + +def decrypt_data(encrypted_text_path: str, + private_key_path: str, + encrypted_symmetric_key_path: str, + decrypted_text_path: str) -> None: + """ + Дешифрует данные + :param encrypted_text_path: путь к файлц с зашифрованными данными + :param private_key_path: путь к файлу с приватным RSA ключом + :param encrypted_symmetric_key_path: путь к файлу с зашифрованным симметричным ключом + :param decrypted_text_path: путь к файлу для сохранения расшифрованных данных + :return: + """ + try: + encrypted_text = read_binary_file(encrypted_text_path) + private_key_bytes = read_binary_file(private_key_path) + encrypted_symmetric_key = read_binary_file(encrypted_symmetric_key_path) + + private_key = serialization.load_pem_private_key( + private_key_bytes, + password=None, + backend=default_backend() + ) + + symmetric_key = AsymmetricEncryption.rsa_decrypt(private_key, encrypted_symmetric_key) + + decrypted_text = SymmetricalEncryption.decrypt_data(encrypted_text, symmetric_key) + write_binary_file(decrypted_text_path, decrypted_text) + except Exception as e: + raise RuntimeError(f"Ошибка в процессе дешифрования данных: {str(e)}") + + +def update_key_size(key_size: int) -> None: + """ + Изменяет размер ключа в settings.json + :param key_size: новый размер ключа + """ + try: + if key_size not in [128, 192, 256]: + raise ValueError("Размер ключа не подходит. Допустимые значения: 128, 192, 256") + + settings = read_json('settings.json') + settings['key_size'] = key_size + write_json('settings.json', settings) + print(f"Размер ключа изменен на {key_size} бит") + + except Exception as e: + raise RuntimeError(f"Ошибка при изменении размера ключа: {str(e)}") + + +def main() -> None: + try: + parser = argparse.ArgumentParser() + + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('-gen', '--generation', action='store_true', help='Режим генерации ключей') + group.add_argument('-enc', '--encryption', action='store_true', help='Режим шифрования') + group.add_argument('-dec', '--decryption', action='store_true', help='Режим дешифрования') + group.add_argument('-key','--key-size', type=int, help='Изменить размер ключа') + + args = parser.parse_args() + + settings = read_json('settings.json') + + match (args.generation, args.encryption, args.decryption, args.key_size): + case (True, False, False, None): + print(f"Запуск генерации ключей ({settings['key_size']} бит)...") + generate_keys( + settings['key_size'], + settings['public_key'], + settings['private_key'], + settings['encrypted_symmetric_key'] + ) + print("Генерация ключей завершена успешно!") + + case (False, True, False, None): + print("Запуск шифрования данных...") + encrypt_data( + settings['original_text'], + settings['private_key'], + settings['encrypted_symmetric_key'], + settings['encrypted_text'] + ) + print("Шифрование завершено успешно!") + + case (False, False, True, None): + print("Запуск дешифрования данных...") + decrypt_data( + settings['encrypted_text'], + settings['private_key'], + settings['encrypted_symmetric_key'], + settings['decrypted_text'] + ) + print("Дешифрование завершено успешно!") + + case (False, False, False, size): + update_key_size(size) + case _: + raise ValueError("Неверная комбинация режимов работы") + + except Exception as e: + print(f"Ошибка: {str(e)}") + +if __name__ == '__main__': + main() \ No newline at end of file diff --git a/lab_3/original_text.txt b/lab_3/original_text.txt new file mode 100644 index 000000000..36e4d809f --- /dev/null +++ b/lab_3/original_text.txt @@ -0,0 +1 @@ +У него в пятнадцати верстах от постоялого дворика хорошее имение в двести душ, или, как он выражается с тех пор, как размежевался с крестьянами и завел «ферму», — в две тысячи десятин земли. Отец его, боевой генерал 1812 года, полуграмотный, грубый, но не злой русский человек, всю жизнь свою тянул лямку, командовал сперва бригадой, потом дивизией и постоянно жил в провинции, где в силу своего чина играл довольно значительную роль. Николай Петрович родился на юге России, подобно старшему своему брату Павлу, о котором речь впереди, и воспитывался до четырнадцатилетнего возраста дома, окруженный дешевыми гувернерами, развязными, но подобострастными адъютантами и прочими полковыми и штабными личностями. Родительница его, из фамилии Колязиных, в девицах Agathe, а в генеральшах Агафоклея Кузьминишна Кирсанова, принадлежала к числу «матушек-командирш», носила пышные чепцы и шумные шелковые платья, в церкви подходила первая ко кресту, говорила громко и много, допускала детей утром к ручке, на ночь их благословляла, — словом, жила в свое удовольствие. В качестве генеральского сына Николай Петрович — хотя не только не отличался храбростью, но даже заслужил прозвище трусишки — должен был, подобно брату Павлу, поступить в военную службу; но он переломил себе ногу в самый тот день, когда уже прибыло известие об его определении, и, пролежав два месяца в постели, на всю жизнь остался «хроменьким». Отец махнул на него рукой и пустил его по штатской. Он повез его в Петербург, как только ему минул восемнадцатый год, и поместил его в университет. Кстати, брат его о ту пору вышел офицером в гвардейский полк. Молодые люди стали жить вдвоем, на одной квартире, под отдаленным надзором двоюродного дяди с материнской стороны, Ильи Колязина, важного чиновника. \ No newline at end of file diff --git a/lab_3/settings.json b/lab_3/settings.json new file mode 100644 index 000000000..816eecaa0 --- /dev/null +++ b/lab_3/settings.json @@ -0,0 +1,9 @@ +{ + "public_key" : "public_key.pem", + "private_key" : "private_key.pem", + "encrypted_symmetric_key" : "encrypted_symmetric_key.bin", + "original_text" : "original_text.txt", + "encrypted_text" : "encrypted_text.txt", + "decrypted_text" : "decrypted_text.txt", + "key_size": 256 +} \ No newline at end of file diff --git a/lab_3/symmetric_crypto.py b/lab_3/symmetric_crypto.py new file mode 100644 index 000000000..e3e01d22d --- /dev/null +++ b/lab_3/symmetric_crypto.py @@ -0,0 +1,73 @@ +import os + +from cryptography.hazmat.backends import default_backend +from cryptography.hazmat.primitives import padding +from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes + + +class SymmetricalEncryption: + """ Класс для симметричного шифрования с использованием алгоритма AES """ + + @staticmethod + def generate_key(key_size: int) -> bytes: + """ + Генерирует ключ + :param key_size: размер ключа в битах + :return: ключ + """ + return os.urandom(key_size // 8) + + @staticmethod + def padding_data(data: bytes) -> bytes: + """ + Добавляет паддинг к данным + :param data: данные + :return: данные с добавленным паддингом + """ + padder = padding.ANSIX923(128).padder() + padded_data = padder.update(data) + padder.finalize() + return padded_data + + @staticmethod + def encrypt_data(data: bytes, key: bytes) -> bytes: + """ + Шифрует данные + :param data: данные + :param key: ключ + :return: зашифрованные данные + """ + iv = os.urandom(16) + + cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend()) + encryptor = cipher.encryptor() + ciphertext = encryptor.update(SymmetricalEncryption.padding_data(data)) + encryptor.finalize() + + return iv + ciphertext + + @staticmethod + def unpadding_data(data: bytes) -> bytes: + """ + Удаляет паддинг из данных + :param data: данные + :return: данные без паддинга + """ + unpadder = padding.ANSIX923(128).unpadder() + unpadded_data = unpadder.update(data) + unpadder.finalize() + return unpadded_data + + @staticmethod + def decrypt_data(data: bytes, key: bytes) -> bytes: + """ + Расшифровывает данные + :param data: данные + :param key: ключ + :return: расшифрованные данные + """ + iv = data[:16] + ciphertext = data[16:] + + cipher = Cipher(algorithms.AES(key), modes.CBC(iv), backend=default_backend()) + decryptor = cipher.decryptor() + padded_data = decryptor.update(ciphertext) + decryptor.finalize() + + return SymmetricalEncryption.unpadding_data(padded_data) diff --git a/lab_3/work_with_files.py b/lab_3/work_with_files.py new file mode 100644 index 000000000..d6b3927ab --- /dev/null +++ b/lab_3/work_with_files.py @@ -0,0 +1,65 @@ +import json + + +def read_json(file_name: str) -> dict: + """ + Считывает содержимое json-файла + :param file_name: путь к файлу + :return: словарь + """ + try: + with open(file_name, 'r', encoding='utf-8') as file: + return json.load(file) + except FileNotFoundError: + raise FileNotFoundError(f"Файл не найден") + except IOError: + raise IOError(f"Ошибка чтения файла") + except Exception as e: + raise Exception(f"Ошибка: {str(e)}") + + +def write_json(file_name: str, text: dict) -> None: + """ + Записывает данные в json-файл + :param file_name: путь к файлу + :param text: данные + """ + try: + with open(file_name, 'w', encoding='utf-8') as file: + json.dump(text, file, ensure_ascii=False, indent=4) + except IOError: + raise IOError(f"Ошибка записи в файл") + except Exception as e: + raise Exception(f"Ошибка: {str(e)}") + + +def read_binary_file(file_name: str) -> bytes: + """ + Считывает содержимое бинарного файла + :param file_name: путь к файлу + :return: содержимое файла в бинарном формате + """ + try: + with open(file_name, 'rb') as file: + return file.read() + except FileNotFoundError: + raise FileNotFoundError(f"Файл не найден") + except IOError: + raise IOError(f"Ошибка чтения файла") + except Exception as e: + raise Exception(f"Ошибка: {str(e)}") + + +def write_binary_file(file_name: str, text: bytes) -> None: + """ + Записывает данные в бинарный файл + :param file_name: путь к файлу + :param text: данные для записи + """ + try: + with open(file_name, 'wb') as file: + file.write(text) + except IOError: + raise IOError(f"Ошибка записи в файл") + except Exception as e: + raise Exception(f"Ошибка: {str(e)}")