diff --git a/lab1/caesar.py b/lab1/caesar.py new file mode 100644 index 0000000..f77637e --- /dev/null +++ b/lab1/caesar.py @@ -0,0 +1,29 @@ +def encrypt(plaintext: str, shift: int) -> str: + """ + Функция encrypt возвращает входную строку plaintext, + зашифрованную шифром Цезаря на входной сдвиг shift + по ascii-кодам отдельных символов строки + """ + total_symbols = 126 - 31 + output = '' + + interval_begin = 32 + interval_end = 126 + for symbol in plaintext: + + if interval_begin <= ord(symbol) <= interval_end: + output += chr((ord(symbol) - interval_begin + shift) % total_symbols + interval_begin) + else: + output += symbol + + return output + +def decrypt(ciphertext: str, shift: int) -> str: + """ + Функция decrypt возвращает строку ciphertext, + расшифрованную шифром Цезаря на входной сдвиг shift + по ascii-кодам отдельных символов строки + путем вызова функции encrypt со сдвигом -shift, + то есть шифрует на противоположный сдвиг + """ + return encrypt(ciphertext, -shift) \ No newline at end of file diff --git a/lab1/hack.py b/lab1/hack.py new file mode 100644 index 0000000..8bc6490 --- /dev/null +++ b/lab1/hack.py @@ -0,0 +1,69 @@ +import caesar +import random + + +def error(text: str) -> int: + """ + для каждого символа входной строки сравнивает + эталонную процентную частоту использования символа + и аналогичную величину для входной строки. + """ + + # словарь эталонной процентной частоты использования символов с ascii-кодами в отрезке [32,126] + # сгенерирован с помощью DeepSeek + standart_fraquency = { + ' ': 17.1662, + 'e': 10.2911, 't': 7.9483, 'a': 7.4825, 'o': 7.2201, + 'n': 6.7703, 'i': 6.3456, 's': 6.1473, 'r': 5.9951, + 'h': 5.2448, 'l': 4.1253, 'd': 3.7879, 'c': 3.3854, + 'u': 2.7584, 'm': 2.7055, 'f': 2.4063, 'p': 2.1885, + 'g': 2.0306, 'w': 1.9541, 'y': 1.8510, 'b': 1.8194, + 'v': 1.0077, 'k': 0.9678, 'x': 0.1923, 'j': 0.1533, + 'q': 0.0957, 'z': 0.0776, + 'E': 0.6492, 'T': 0.5976, 'A': 0.5801, 'O': 0.5043, + 'N': 0.4920, 'I': 0.4653, 'S': 0.4301, 'R': 0.4200, + 'H': 0.3853, 'L': 0.3201, 'D': 0.2852, 'C': 0.2725, + 'U': 0.1901, 'M': 0.1782, 'F': 0.1512, 'P': 0.1403, + 'G': 0.1252, 'W': 0.1201, 'Y': 0.1152, 'B': 0.1052, + 'V': 0.0721, 'K': 0.0652, 'X': 0.0452, 'J': 0.0352, + 'Q': 0.0252, 'Z': 0.0201, + ',': 1.2345, '.': 1.1201, '"': 0.8654, "'": 0.7543, + '?': 0.4521, '!': 0.3521, ':': 0.2854, ';': 0.2154, + '-': 0.1854, '(': 0.1452, ')': 0.1452, '[': 0.0452, + ']': 0.0452, '{': 0.0252, '}': 0.0252, '<': 0.0152, + '>': 0.0152, '/': 0.1254, '\\': 0.0152, '|': 0.0102, + '`': 0.0854, '~': 0.0152, '@': 0.0352, '#': 0.0452, + '$': 0.0854, '%': 0.0452, '^': 0.0152, '&': 0.0552, + '*': 0.0652, '_': 0.0954, '+': 0.0352, '=': 0.0452, + '0': 1.2345, '1': 0.9854, '2': 0.7543, '3': 0.4521, + '4': 0.3521, '5': 0.2854, '6': 0.2154, '7': 0.1854, + '8': 0.1452, '9': 0.1252 + } + + + letters = set(text) + + #анаголичный словарь для входной строки: + fraquency = {} + for letter in letters: + fraquency[letter] = 100 * text.count(letter) / len(text) + + #возвращаем общюю ошибку -- сумму разниц процентов использования для каждого символа + return sum(abs(fraquency[letter] - standart_fraquency[letter]) for letter in letters) + + +def hack(ciphertext: str) -> tuple[str, int]: + """ + перебором всевозможных сдвигов выбирает текст с минимальной ошибкой + возвращет полученный текст и сдвиг,минимизирующий ошибку + """ + min_error = float('+inf') + interval_begin = 32 + interval_end = 126 + for shift in range(interval_end - interval_begin + 1): + decrypted_error = error(caesar.decrypt(ciphertext, shift)) + if decrypted_error < min_error: + min_error = decrypted_error + answer = (caesar.decrypt(ciphertext, shift), shift) + + return answer diff --git a/lab1/rsa.py b/lab1/rsa.py new file mode 100644 index 0000000..69d127b --- /dev/null +++ b/lab1/rsa.py @@ -0,0 +1,61 @@ +def is_prime(n: int) -> bool: + return n >= 2 and all(n % divider !=0 for divider in range(2,int(n**0.5)+1)) + +def gcd(a: int, b: int) -> int: + while b !=0: + a, b = b, a % b + return a + +def multiplicative_inverse(e: int, phi:int) -> int: + """ + ищет натуральное число, которое при умножении на e по модулю phi даёт 1 + с помощью последовательного перебора натуральных n + """ + assert gcd(e, phi) == 1 + n = 0 + while (phi * n + 1) % e != 0: + n+=1 + return (phi * n + 1)//e + + #return pow(e, -1, phi) + +def generate_keypair(p: int, q: int) -> tuple[tuple[int, int], tuple[int, int]]: + """ + генерирует открытый и закрытый ключи + """ + n = p * q + phi = (p - 1) * (q - 1) + e = 2 + while gcd(e,phi) != 1: + e+=1 + d = multiplicative_inverse(e, phi) + + return ((e, n), (d, n)) + +def encrypt(public_key: tuple[int, int], text: str) -> list[int]: + """ + шифрует входную строку с помощью закрытого ключа + возвращает список зашифрованных ascii-кодов + """ + + e, n = public_key + cipher_list = [None for _ in range(len(text))] + for i in range(len(text)): + #зашифровывает символ, находя остаток от деления на n его ascii-кода в степени e + encrypted_symbol = pow(ord(text[i]), e, n) + cipher_list[i] = encrypted_symbol + return cipher_list + +def decrypt(private_key: tuple[int, int], cipher_list: list[int]) -> str: + """ + дешифрует входную строку с помощью закрытого ключа + возвращает список зашифрованных ascii-кодов + """ + d, n = private_key + messege = '' + for encrypted_symbol in cipher_list: + #расшифровывает символ, находя остаток от деления на n его ascii-кода в степени d + decrypted_symbol = pow(encrypted_symbol,d,n) + messege+= chr(decrypted_symbol) + return messege + diff --git a/lab1/vigenere.py b/lab1/vigenere.py new file mode 100644 index 0000000..67986dc --- /dev/null +++ b/lab1/vigenere.py @@ -0,0 +1,51 @@ +def encrypt(plaintext: str, keyword: str) -> str: + """ + Функция шифрует входную строку шифром Виженера с помощью ключевого слова + """ + alphabet = sorted(list(set(('qwertyuiopasdfghjklzxcvbnm')))) + assert len(alphabet) == 26 + a_ascii_code = ord('a') + key_numbers = [ord(letter) - a_ascii_code for letter in keyword.lower() if letter in alphabet] + + + total_symbols = 126 - 31 + output = '' + + interval_begin = 32 + interval_end = 126 + for i in range(len(plaintext)): + symbol = plaintext[i] + shift = key_numbers[i % len(key_numbers)] + + if interval_begin <= ord(symbol) <= interval_end: + output += chr((ord(symbol) - interval_begin + shift) % total_symbols + interval_begin) + else: + output += symbol + + return output + +def decrypt(ciphertext: str, keyword: str) -> str: + """ + Функция дешифрует входную строку шифром Виженера с помощью ключевого слова + [шифрует с противоположным сдвигом] + """ + alphabet = sorted(list(set(('qwertyuiopasdfghjklzxcvbnm')))) + assert len(alphabet) == 26 + a_ascii_code = ord('a') + key_numbers = [ord(letter) - a_ascii_code for letter in keyword.lower() if letter in alphabet] + + total_symbols = 126 - 31 + output = '' + + interval_begin = 32 + interval_end = 126 + for i in range(len(ciphertext)): + symbol = ciphertext[i] + shift = key_numbers[i % len(key_numbers)] + + if interval_begin <= ord(symbol) <= interval_end: + output += chr((ord(symbol) - interval_begin - shift) % total_symbols + interval_begin) + else: + output += symbol + + return output \ No newline at end of file