Простые алгоритмы шифрования

Тема в разделе "WASM.CRYPTO", создана пользователем Mikl___, 18 май 2025.

  1. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.229
    Накопилась целая библиотека программ шифрования/дешифрования на python'e. Это учебные примеры, требовалось показать суть алгоритмов, поэтому выбран python. Жалко будет если пропадут
    Шифры


    Шифр Цезаря

    Шифр подстановки. Основан на сдвиге букв алфавита на фиксированное число позиций, влево или вправо в закольцованном алфавите. Число позиций составляет ключ шифра. Взлом осуществляется путем перебора всех возможных вариантов ключа.
    Мораль:
    1. для усложнения взлома включите в алфавит прописные и строчные латинские буквы, прописные и строчные буквы кириллицы, пробел, скобки (круглые «()», квадратные «[]», фигурные «{}», угловые «<>»), знаки препинания, цифры. Чем больше мощность алфавита ― тем больше вариантов ключа.
    2. Еще одна защита ― изменить порядок букв в алфавите :)
    Шифрование/дешифрование/взлом
    Код (Python):
    1. import re
    2. SYMBOLS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ '
    3. class Caesar(object):
    4.     def __init__(self,key):
    5.         self.key = key % len(SYMBOLS)
    6.  
    7.     def remove_punctuation(self,text,filter='[^A-Z\s]'):
    8.         return re.sub(filter,'',text.upper())
    9.  
    10.     def encipher(self,string):
    11.         string = self.remove_punctuation(string)
    12.         ret = ''
    13.         for symbol in string.upper():
    14.             if symbol in SYMBOLS:
    15.                 symbolIndex = SYMBOLS.find(symbol)
    16.                 ret += SYMBOLS[(symbolIndex - self.key) % len(SYMBOLS)]
    17.         return ret
    18.     def decipher(self,string):
    19.         self.key *= -1
    20.         return self.encipher(string)
    21.  
    22. def main():
    23.     plaintext = 'defend the east wall of the castle'
    24.     print("Исходный текст:\n" + plaintext)
    25.     ciphertext = Caesar(key=3).encipher(plaintext)
    26.     print("\nЗашифрованный текст:\n" + ciphertext)
    27.     plaintext = Caesar(key=3).decipher(ciphertext)
    28.     print("\nРасшифрованный текст:\n" + plaintext)
    29.     print("\nВзлом шифра\n")
    30.     for key in range(1, len(SYMBOLS)-1):
    31.             text = Caesar(key).decipher(ciphertext)
    32.             print('Key #%s: %s' % (key, text))
    33.  
    34. # Функция main() вызывается только в том случае, если файл Caesar.py был
    35. # запущен как программа, а не импортируется в виде модуля другой программой
    36. if __name__ == '__main__':
    37.     main()
     

    Вложения:

    • 10.gif
      10.gif
      Размер файла:
      567,9 КБ
      Просмотров:
      479
    Последнее редактирование: 31 авг 2025
    Asyroino, MaKsIm, q2e74 и ещё 1-му нравится это.
  2. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.229

    Аффинный шифр

    Моноалфавитный шифр подстановки. При помощи модульной арифметики для каждой буквы [math]x[/math] исходного алфавита, вычисляется новая буква [math]y[/math], которая заменит [math]x[/math] в шифротексте. Функция шифрования [math]y=E(x)[/math] каждой букве [math]x[/math] соответствует [math]y=(a\cdot x+b)\bmod n[/math], [math]n[/math] — размер алфавита, пара [math]a[/math] и [math]b[/math] — ключ шифра. Значение [math]a[/math] должно быть выбрано таким, чтобы [math]a[/math] и [math]n[/math] были взаимно простыми числами. Функция расшифрования [math]x=D(y)[/math] каждой букве [math]y[/math] соответствует [math]x=(a^{−1}(y−b))\bmod n[/math], [math]a^{−1}[/math] — обратное к [math]a[/math] число по модулю [math]n[/math] удовлетворяет уравнению [math]1=(a\cdot a^{−1})\bmod n[/math].

    Шифрование/дешифрование
    Код (Python):
    1. SYMBOLS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890 !?.'
    2. import random, sys
    3. def getKeyParts(key):
    4.     keyA = key // len(SYMBOLS)
    5.     keyB = key % len(SYMBOLS)
    6.     return (keyA, keyB)
    7. def gcd(a, b):
    8.     # Возвращает НОД чисел а и b, используя алгоритм Евклида
    9.     while a != 0:
    10.         a, b = b % a, a
    11.     return b
    12. def findModInverse(a, m):
    13.     # Возвращает модульное обращение а % m, т.е. число х, что а * х % m = 1
    14.     if gcd(a, m) != 1:
    15.         return None # модульное обращение отсутствует, если а и m не являются взаимно простыми числами
    16.     # Используем расширенный алгоритм Евклида
    17.     u1, u2, u3 = 1, 0, a
    18.     v1, v2, v3 = 0, 1, m
    19.     while v3 != 0:
    20.         q = u3 // v3 # // - это оператор целочисленного деления
    21.         v1, v2, v3, u1, u2, u3 = (u1 - q * v1), (u2 - q * v2), (u3 - q * v3), v1, v2, v3
    22.     return u1 % m
    23. def checkKeys(keyA, keyB, mode):
    24.     if keyA == 1 and mode == 'encrypt':
    25.         sys.exit('Cipher is weak if key A is 1. Choose a different key.')
    26.     if keyB == 0 and mode == 'encrypt':
    27.         sys.exit('Cipher is weak if key B is 0. Choose a different key.')
    28.     if keyA < 0 or keyB < 0 or keyB > len(SYMBOLS) - 1:
    29.         sys.exit('Key A must be greater than 0 and Key B must be between 0 and %s.' % (len(SYMBOLS) - 1))
    30.     if gcd(keyA, len(SYMBOLS)) != 1:
    31.         sys.exit('Key A (%s) and the symbol set size (%s) are not relatively prime. Choose a different key.' % (keyA, len(SYMBOLS)))
    32. def encryptMessage(key, message):
    33.     keyA, keyB = getKeyParts(key)
    34.     checkKeys(keyA, keyB, 'encrypt')
    35.     ciphertext = ''
    36.     for symbol in message:
    37.         if symbol in SYMBOLS:
    38.             # Зашифровать символ
    39.             symbolIndex = SYMBOLS.find(symbol)
    40.             ciphertext += SYMBOLS[(symbolIndex * keyA + keyB) % len(SYMBOLS)]
    41.         else:
    42.             ciphertext += symbol # присоединить символ без шифрования
    43.     return ciphertext
    44. def decryptMessage(key, message):
    45.     keyA, keyB = getKeyParts(key)
    46.     checkKeys(keyA, keyB, 'decrypt')
    47.     plaintext = ''
    48.     modInverseOfKeyA = findModInverse(keyA, len(SYMBOLS))
    49.     for symbol in message:
    50.         if symbol in SYMBOLS:
    51.             # Дешифровать символ
    52.             symbolIndex = SYMBOLS.find(symbol)
    53.             plaintext += SYMBOLS[(symbolIndex - keyB) * modInverseOfKeyA % len(SYMBOLS)]
    54.         else:
    55.             plaintext += symbol # присоединить символ без дешифрования
    56.     return plaintext
    57. def main():
    58.     Key = 2894
    59.     fileobj=open("decrypted.txt",'r',encoding='utf-8')
    60.     Message = fileobj.read()
    61.     fileobj.close()
    62.     encryptedMessage = encryptMessage(Key, Message)
    63.     print(encryptedMessage)
    64.     translated = decryptMessage(Key, encryptedMessage)
    65.     print(translated)
    66. # Если файл affineCipher.py выполняется как программа, а не импортируется как модуль, вызвать функцию main()
    67. if __name__ == '__main__':
    68.     main()
     

    Вложения:

    • 05.gif
      05.gif
      Размер файла:
      2,1 МБ
      Просмотров:
      488
    Последнее редактирование: 23 июл 2025
    q2e74, MaKsIm и Research нравится это.
  3. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.229

    Взлом аффинного шифра

    Аффинный шифр немного сложнее шифра Цезаря, но не обеспечивает большей безопасности. Количество возможных ключей если в сообщении используются только символы латинского алфавита (len(SYMBOLS)=26) равно 311. Для взлома сообщения зашифрованного аффинным шифром компьютер должен перебрать все возможные ключи и выбирать лучший. Чтобы найти лучший ключ, мы должны попытаться расшифровать зашифрованный текст с каждым ключом, а затем определить «пригодность» каждого фрагмента расшифрованного текста.
    Аффинный шифр имеет 2 ключа, [math]a[/math] и [math]b[/math]. [math]b[/math] может находиться в диапазоне от 1 до 25 (сдвиг на 0 и сдвиг на 26 соответствуют незашифрованному тексту), [math]a[/math] может иметь любое из значений 1, 3, 5, 7, 9, 11, 15, 17, 19, 21, 23, 25, значение [math]a[/math] не должно быть кратно делителям 26 (2 и 13). Мы перебираем каждую из этих возможных комбинаций, которых насчитывается [math]311=12\cdot 26-1[/math], определяем пригодность каждой комбинации, а затем выбираем лучшую.

    affineCipher.py

    Код (Python):
    1. SYMBOLS = 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЪЭЮЯ1234567890 !?.'
    2. import random, sys
    3. def getKeyParts(key):
    4.     keyA = key // len(SYMBOLS)
    5.     keyB = key % len(SYMBOLS)
    6.     return (keyA, keyB)
    7. def gcd(a, b):
    8.     # Возвращает НОД чисел а и b, используя алгоритм Евклида
    9.     while a != 0:
    10.         a, b = b % a, a
    11.     return b
    12. def findModInverse(a, m):
    13.     # Возвращает модульное обращение а % m, т.е. число х, что а * х % m = 1
    14.     if gcd(a, m) != 1:
    15.         return None # модульное обращение отсутствует, если а и m не являются взаимно простыми числами
    16.     # Используем расширенный алгоритм Евклида
    17.     u1, u2, u3 = 1, 0, a
    18.     v1, v2, v3 = 0, 1, m
    19.     while v3 != 0:
    20.         q = u3 // v3 # // - это оператор целочисленного деления
    21.         v1, v2, v3, u1, u2, u3 = (u1 - q * v1), (u2 - q * v2), (u3 - q * v3), v1, v2, v3
    22.     return u1 % m
    23. def checkKeys(keyA, keyB, mode):
    24.     if keyA == 1 and mode == 'encrypt':
    25.         sys.exit('Cipher is weak if key A is 1. Choose a different key.')
    26.     if keyB == 0 and mode == 'encrypt':
    27.         sys.exit('Cipher is weak if key B is 0. Choose a different key.')
    28.     if keyA < 0 or keyB < 0 or keyB > len(SYMBOLS) - 1:
    29.         sys.exit('Key A must be greater than 0 and Key B must be between 0 and %s.' % (len(SYMBOLS) - 1))
    30.     if gcd(keyA, len(SYMBOLS)) != 1:
    31.         sys.exit('Key A (%s) and the symbol set size (%s) are not relatively prime. Choose a different key.' % (keyA, len(SYMBOLS)))
    32. def encryptMessage(key, message):
    33.     keyA, keyB = getKeyParts(key)
    34.     checkKeys(keyA, keyB, 'encrypt')
    35.     ciphertext = ''
    36.     for symbol in message.upper():
    37.         if symbol in SYMBOLS:
    38.             # Зашифровать символ
    39.             symbolIndex = SYMBOLS.find(symbol)
    40.             ciphertext += SYMBOLS[(symbolIndex * keyA + keyB) % len(SYMBOLS)]
    41.         else:
    42.             ciphertext += symbol # присоединить символ без шифрования
    43.     return ciphertext
    44. def decryptMessage(key, message):
    45.     keyA, keyB = getKeyParts(key)
    46.     checkKeys(keyA, keyB, 'decrypt')
    47.     plaintext = ''
    48.     modInverseOfKeyA = findModInverse(keyA, len(SYMBOLS))
    49.     for symbol in message:
    50.         if symbol in SYMBOLS:
    51.             # Дешифровать символ
    52.             symbolIndex = SYMBOLS.find(symbol)
    53.             plaintext += SYMBOLS[(symbolIndex - keyB) * modInverseOfKeyA % len(SYMBOLS)]
    54.         else:
    55.             plaintext += symbol # присоединить символ без дешифрования
    56.     return plaintext
    57. def main():
    58.     Key = 2894
    59.     fileobj=open("decrypted3.txt",'r',encoding='utf-8')
    60.     Message = fileobj.read()
    61.     fileobj.close()
    62.     print("Исходный текст:\n" + Message)
    63.     encryptedMessage = encryptMessage(Key, Message)
    64.     print("\nЗашифрованный текст:\n" + encryptedMessage)
    65.     fileobj=open("encrypted3.txt",'w',encoding='utf-8')
    66.     fileobj.write(encryptedMessage)
    67.     fileobj.close()
    68.     translated = decryptMessage(Key, encryptedMessage)
    69.     print("\nРасшифрованный текст:\n" + translated)
    70. # Если файл affineCipher.py выполняется как программа, а не импортируется как модуль, вызвать функцию main()
    71. if __name__ == '__main__':
    72.     main()

    detectRussian.py

    Код (Python):
    1. # модуль распознавания русского языка, чтобы использовать данный модуль введите следующие строки:
    2. #   import detectRussian
    3. #   detectRussian.isRussian(someString) # возвращает True или False
    4. #(в каталоге программы должен быть файл "russian.txt" со всеми русскими
    5. #словами, содержащий по одному слову в строке.
    6. UPPERLETTERS = 'АББГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯ'
    7. LETTERS_AND_SPACE = UPPERLETTERS + UPPERLETTERS.lower() + ' \t\n'
    8. def loadDictionary():
    9.     dictionaryFile = open('russian.txt','r',encoding='utf-8') # получаем объект файла словаря
    10.     russianWords = {} # создается переменная russianWords в виде пустого словаря
    11.     for word in dictionaryFile.read().split('\n'):
    12.         russianWords[word] = None
    13.     dictionaryFile.close()
    14.     return russianWords
    15. #Строковый метод split() разбивает переданную ему строку по переносам,
    16. #возвращая список из нескольких строк
    17. RUSSIAN_WORDS = loadDictionary()
    18. # вызывается функция loadDictionary() и возвращаемый ею словарь сохраняется
    19. # в переменной RUSSIAN_WORDS.
    20. def getRussianCount(message):
    21.     message = message.lower() #символы строки преобразуются в верхний регистр
    22.     message = removeNonLetters(message) #с помощью функции removeNonLetters()
    23.     # из строки удаляются все небуквенные символы, такие как числа и знаки
    24.     #препинания
    25.     possibleWords = message.split() #метод split() разбивает строку на слова
    26.     #и сохраняет их в переменной possibleWords
    27.     if possibleWords == []:
    28.         return 0.0 # слова отсутствуют, поэтому возвращаем 0.0.
    29.     matches = 0 #для счетчика mаtсhеs устанавливается нулевое значение
    30.     for word in possibleWords: #В цикле for мы проходим по всем словам в списке
    31.     #possibleWords и проверяем существование каждого из них в словаре RUSSIAN_WORDS
    32.         if word in RUSSIAN_WORDS:
    33.             matches += 1 #Если слово содержится в словаре, значение счетчика
    34.             #mаtсhеs инкрементируется
    35.     return float(matches)/len(possibleWords)
    36. def removeNonLetters(message):
    37.     lettersOnly = []
    38.     for symbol in message:
    39.         if symbol in LETTERS_AND_SPACE:
    40.             lettersOnly.append(symbol)
    41.     return ''.join(lettersOnly)
    42. def isRussian(message, wordPercentage=20, letterPercentage=85):
    43.     # По умолчанию 20% слов должны быть в файле словаря,
    44.     # а 85% символов сообщения должны быть буквами или
    45.     # пробелами (а не знаками препинания или числами)
    46.     wordsMatch = getRussianCount(message) * 100 >= wordPercentage
    47.     numLetters = len(removeNonLetters(message))
    48.     messageLettersPercentage = float(numLetters) / len(message) * 100
    49.     lettersMatch = messageLettersPercentage >= letterPercentage
    50.     return wordsMatch and lettersMatch

    affineHacker.py

    Код (Python):
    1. # Программа для взлома аффинного шифра
    2. import affineCipher, detectRussian, time, sys
    3. SILENT_MODE = False
    4. def gcd(a, b):
    5.     # Возвращает НОД чисел а и b, используя алгоритм Евклида
    6.     while a != 0:
    7.         a, b = b % a, a
    8.     return b
    9. def main():
    10.     # Текст копируется из файла encrypted.txt
    11.     fileObj = open('encrypted3.txt','r',encoding='utf-8')
    12.     myMessage = fileObj.read()
    13.     fileObj.close()
    14.     hackedMessage = hackAffine(myMessage)
    15.     if hackedMessage != None:
    16.         # Вывод дешифрованного текста на экран; для удобства копируем его в буфер обмена
    17.         print(hackedMessage)
    18.     else:
    19.         print('Не удалось взломать шифрование.')
    20. def hackAffine(message):
    21.     print('Взламываю...')
    22.     # Работу программы на Python можно в любой момент прервать, нажав <Ctrl+C>
    23.     print('(Нажми Ctrl-C для выхода из программы.)')
    24.     # Перебор всех возможных ключей методом грубой силы
    25.     for key in range(len(affineCipher.SYMBOLS) ** 2):
    26.         keyA = affineCipher.getKeyParts(key)[0]
    27.         if gcd(keyA, len(affineCipher.SYMBOLS)) != 1:
    28.             continue
    29.         startTime = time.time()
    30.         decryptedText = affineCipher.decryptMessage(key, message)
    31.         russianPercentage = round(detectRussian.getRussianCount(decryptedText) * 100, 2)
    32.         totalTime = round(time.time() - startTime, 3)
    33.         if russianPercentage > 1:
    34.             print('Процент русских слов: %5s%%   Ключ: %4s' % (russianPercentage,key))
    35.             if detectRussian.isRussian(decryptedText):
    36.                 if russianPercentage > 40:
    37.                     # Подтвердить, что найден правильный ключ
    38.                     print()
    39.                     print('Возможный результат дешифровки:')
    40.                     print('Ключ: %s' % (key))
    41.                     print('Расшифрованное сообщение: ' + decryptedText[:200])
    42.                     print()
    43.                     print('Нажми на D чтобы завершить взлом, или любую другую клавишу чтобы продолжить:')
    44.                     response = input('> ')
    45.                     if response.strip().upper().startswith('D'):
    46.                         fileObj = open('decrypted2.txt','w',encoding='utf-8')
    47.                         fileObj.write(decryptedText)
    48.                         fileObj.close()
    49.                         return decryptedText
    50.     return None
    51. #Если файл affineHacker.py выполняется как программа, а не импортируется как модуль, вызвать функцию main()
    52. if __name__ == '__main__':
    53.     main()
    54.  
     

    Вложения:

    • encrypted3.txt
      Размер файла:
      5,1 КБ
      Просмотров:
      266
    • russian.txt
      Размер файла:
      1,9 МБ
      Просмотров:
      281
    • decrypted3.txt
      Размер файла:
      5,1 КБ
      Просмотров:
      279
    • 00.gif
      00.gif
      Размер файла:
      1.008,8 КБ
      Просмотров:
      494
    Последнее редактирование: 24 авг 2025
    MaKsIm и q2e74 нравится это.
  4. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.229

    Шифр Виженэра

    Полиалфавитный шифр с использованием ключевого слова. Простая форма многоалфавитной замены. Шифр Виженэра состоит из последовательности нескольких шифров Цезаря с различными значениями сдвига. Для зашифровывания используется таблица алфавитов (tabula recta или квадрат Виженэра). На каждом этапе шифрования используются различные алфавиты, выбираемые в зависимости от символа ключевого слова.
    Открытый текст = ATTACK AT DAWN ключ = LEMON

    открытый текстATTACKATDOWN
    mi0191902100193142213
    ключLEMONLEMONLE
    ki114121413114121413114
    ci=(mi+ki)mod 261123514152145171717
    шифротекстLXFOPVEFRNHR
    Шифрование/дешифрование
    Код (Python):
    1.  
    2. SYMBOLS = 'АБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЬЫЪЭЮЯ 0123456789.,\n'
    3. def main():
    4.     with open('decrypted.txt','r',encoding='utf-8') as file:
    5.               myMessage = file.read()
    6.     print('Исходный текст:\n',myMessage)
    7.     myKey = 'Пушкин'
    8.     encryptedMessage = encryptVigenere(myKey, myMessage)
    9.     with open('encrypted.txt','w',encoding='utf-8') as file:
    10.           file.write(encryptedMessage)
    11.     print('Зашифрованный текст:\n',encryptedMessage)
    12.     Message = decryptVigenere(myKey, encryptedMessage)
    13.     print("\nРасшифрованный текст:\n" + Message)
    14. def encryptVigenere(key, message):
    15.     translated = [] # для создания зашифрованной строки
    16.     keyIndex = 0
    17.     key = key.upper()
    18.     for symbol in message.upper(): # Просмотр каждого символа в строке
    19.         if symbol in SYMBOLS:
    20.             num = SYMBOLS.find(symbol)
    21.             num += SYMBOLS.find(key[keyIndex]) # складываем если шифруем
    22.             num %= len(SYMBOLS) # для учета завертывания строки
    23.             # Добавляем зашифрованный символ в конец translated:
    24.             translated.append(SYMBOLS[num])
    25.         else:
    26.             translated.append(symbol)# Добавить символ без шифрования
    27.         keyIndex += 1 # Переходим к следующему символу ключа
    28.         keyIndex %= len(key)
    29.     return ''.join(translated)
    30. def decryptVigenere(key, message):
    31.     translated = [] # для создания расшифрованной строки
    32.     keyIndex = 0
    33.     key = key.upper()
    34.     for symbol in message.upper(): # Просмотр каждого символа в строке
    35.         if symbol in SYMBOLS:
    36.             num = SYMBOLS.find(symbol)
    37.             num -= SYMBOLS.find(key[keyIndex]) # вычитаем если расшифровываем
    38.             num %= len(SYMBOLS) # для учета завертывания строки
    39.             # Добавляем зашифрованный/расшифрованный символ в конец translated:
    40.             translated.append(SYMBOLS[num])
    41.         else:
    42.             translated.append(symbol)# Добавить символ без дешифрования.
    43.         keyIndex += 1 # Переходим к следующему символу ключа
    44.         keyIndex %= len(key)
    45.     return ''.join(translated)
    46. #Функция main() вызывается только в том случае, если файл vigenereCipher.py был
    47. #запущен как программа, а не импортируется в виде модуля другой программой
    48. if __name__ == '__main__':
    49.     main()
     

    Вложения:

    • 06.gif
      06.gif
      Размер файла:
      377,8 КБ
      Просмотров:
      479
    Последнее редактирование: 18 сен 2025
    q2e74 нравится это.
  5. MaKsIm

    MaKsIm Active Member

    Публикаций:
    0
    Регистрация:
    11 фев 2008
    Сообщения:
    223
    А в mode boolean уже не подходит? Надо обязательно строку?

    или хотя бы так
    Код (Python):
    1. def decryptCaesar(key,message):
    2.     translated=''
    3.     for symbol in message.upper():
    4.         if symbol in SYMBOLS:
    5.             symbolIndex = SYMBOLS.find(symbol)
    6.             translated += SYMBOLS[(symbolIndex + key) % len(SYMBOLS)]
    7.         else:
    8.             translated += symbol
    9.     return translated
    10. def encryptCaesar(key,message):
    11.     key *= -1
    12.     return decryptCaesar(key,message)
     
    q2e74 и Mikl___ нравится это.
  6. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.229

    Шифр Enigma

    шифрование/дешифрование
    Код (Python):
    1. # Программа шифрования и дешифрования на основе шифра Enigma
    2. LETTERS   = "ABCDEFGHIJKLMNOPQRSTUVWXYZ :-,.?'"
    3. REFLECTOR = "QYHOGNECVPUZTFDJAXWMKISRBL :-,.?'"
    4. DISKA     = "JGDQOXUSCAMIFRVTPNEWKBLZYH :-,.?'"
    5. DISKB     = "NTZPSFBOKMWRCJDIVLAEYUXHGQ :-,.?'"
    6. DISKC     = "JVIUBHTCDYAKEQZPOSGXNRMWFL :-,.?'"
    7. class plugBoard:
    8.      def __init__(self):
    9.           self.connections = []
    10.  
    11.      #Добавить провод к коммутационной панели
    12.      def addWire(self, letterA, letterB):
    13.           #конвертировать letterA в букву
    14.           letterA = LETTERS.find(letterA.upper())
    15.           #конвертировать letterB в букву
    16.           letterB = LETTERS.find(letterB.upper())
    17.           self.connections.append([letterA, letterB])
    18.  
    19.      #Получите букву с другой стороны соединения
    20.      def getLetter(self, charLetter):
    21.           #Пройдитесь по всем соединениям, чтобы найти соответствующую букву
    22.           for i in self.connections:
    23.                if i[0] == charLetter:
    24.                     return i[1]
    25.                elif i[1] == charLetter:
    26.                     return i[0]            
    27.           #Если буква не найдена, вернуть входящий символ
    28.           return charLetter;
    29.  
    30. class reflectorWheel:
    31.      def __init__(self, settings):
    32.           self.rotorWires = []
    33.           self.numOfSymbols = len(LETTERS)  
    34.           self.readSettings(settings)
    35.  
    36.      #Прочитать настройки
    37.      def readSettings(self, settingsStr):  
    38.           pos = 0;
    39.           for c in settingsStr:
    40.                self.rotorWires.append(LETTERS.find(c.upper()))
    41.                pos += 1
    42.        
    43.      #Занять следующую позицию
    44.      def getConnection(self, symbolNumber):
    45.           return self.rotorWires[symbolNumber]
    46.  
    47. class rotorWheel:
    48.      def __init__(self, settings = "", turnOverSymbol = "a"):
    49.           self.rotorWires = []
    50.           self.numOfSymbols = len(LETTERS)
    51.           self.turnOverSymbol = turnOverSymbol  
    52.           if settings == "":
    53.                self.scrambleConnections()
    54.           else:
    55.                self.readSettings(settings)
    56.        
    57.      #Прочитать настройки
    58.      def readSettings(self, settingsStr):  
    59.           pos = 0;
    60.           for c in settingsStr:
    61.                self.rotorWires.append(LETTERS.find(c))
    62.                pos += 1
    63.        
    64.      #Зашифрованное соединение
    65.      def scrambleConnections(self):
    66.           for i in range(self.numOfSymbols):
    67.                foundUnique = 0
    68.                while not foundUnique:
    69.                     foundUnique = 1
    70.                     randomMatch = random.randint(0, self.numOfSymbols)
    71.                     for wire in self.rotorWires:
    72.                          if wire == randomMatch:
    73.                               foundUnique = 0
    74.                self.rotorWires.append(randomMatch)
    75.        
    76.      #Повернуть список
    77.      def rotate(self):
    78.           self.rotorWires = [self.rotorWires[-1]] + self.rotorWires[:-1]
    79.  
    80.      #Получить позицию на другой стороне, 0 = налево, 1 = направо
    81.      def getConnection(self, symbolNumber, direction):
    82.           if direction == 1:
    83.                return self.rotorWires[symbolNumber]
    84.           else:
    85.                pos = 0
    86.                for wire in self.rotorWires:
    87.                     if wire == symbolNumber:
    88.                          return pos
    89.                     pos += 1
    90.  
    91. class rotorColumn:
    92.      def __init__(self):
    93.           self.rotorWheels = []
    94.           self.enteredLetters = 0
    95.  
    96.      #Поворотный механизм
    97.      def turnOver(self):
    98.           #Повернуть первый диск, rightMost
    99.           self.rotorWheels[0].rotate()  
    100.           currentPos = 0
    101.           #Поверните первый диск и проверьте, достигает ли он turnOverSymbol
    102.           for rotorWheel in self.rotorWheels:
    103.                #Если бы диск достиг turnOverSymbol, повернуть диск вправо
    104.                if rotorWheel.rotorWires[0] == rotorWheel.turnOverSymbol:
    105.                     #If rotorWheel exists on the left
    106.                     if currentPos < len(self.rotorWheels):
    107.                          self.rotorWheels[currentPos + 1].rotate()
    108.                currentPos += 1
    109.        
    110.      #добавить rotorWheel в колонку
    111.      def addWheel(self, rotorWheel):
    112.           self.rotorWheels.append(rotorWheel)
    113.  
    114.      def sendThroughSymbol(self, symbolValue, direction):
    115.           currentValue = symbolValue
    116.           if direction == 1:
    117.                for rotorWheel in self.rotorWheels:
    118.                     currentValue = rotorWheel.getConnection(currentValue, direction)
    119.           else:
    120.                for rotorWheel in reversed(self.rotorWheels):
    121.                     currentValue = rotorWheel.getConnection(currentValue, 0)
    122.           return currentValue
    123.  
    124. class enigmaMachine:
    125.      def __init__(self):  
    126.           self.rotorColumn = rotorColumn()
    127.           self.plugBoard = plugBoard()
    128.           self.reflector = None
    129.  
    130.      def encryptStr(self, message):
    131.           encryptedMessage = ""
    132.           for c in message:
    133.                newSymbol = self.getSymbol(LETTERS.find(c.upper()))
    134.                encryptedMessage += LETTERS[newSymbol]
    135.           return encryptedMessage        
    136.  
    137.      #передать букву
    138.      def getSymbol(self, symbolVal):  
    139.           #Через коммутационная панель
    140.           nextSymbol = self.plugBoard.getLetter(symbolVal)  
    141.           #Через rotorColumn
    142.           nextSymbol = self.rotorColumn.sendThroughSymbol(symbolVal, 1)  
    143.           #Через рефлектор
    144.           nextSymbol = self.reflector.getConnection(nextSymbol)
    145.           #обратно через rotorColumn
    146.           nextSymbol = self.rotorColumn.sendThroughSymbol(nextSymbol, 0)
    147.           #обратно через коммутационную панель
    148.           nextSymbol = self.plugBoard.getLetter(nextSymbol)  
    149.           #вращать диски
    150.           self.rotorColumn.turnOver()  
    151.           return nextSymbol
    152.  
    153.      #добавить диск на ротор
    154.      def addWheel(self, settings = "", turnOverSymbol = "A"):
    155.           tempRotorWheel = rotorWheel(settings, turnOverSymbol)
    156.           self.rotorColumn.addWheel(tempRotorWheel)
    157.  
    158.      #добавить reflectorWheel в Энигму
    159.      def addReflector(self, settings = ""):
    160.           self.reflector = reflectorWheel(settings)
    161.    
    162. def main():
    163.      #создаем Энигму A
    164.      enigmaMachineA = enigmaMachine()
    165.      #настраиваем начальные позиции дисков
    166.      enigmaMachineA.addWheel(DISKA, "Q")
    167.      enigmaMachineA.addWheel(DISKB, "E")
    168.      enigmaMachineA.addWheel(DISKC, "V")
    169.      #добавляем рефлектор
    170.      enigmaMachineA.addReflector(REFLECTOR)
    171.      #создаем Энигму B
    172.      enigmaMachineB = enigmaMachine()
    173.      #настраиваем начальные позиции дисков
    174.      enigmaMachineB.addWheel(DISKA, "Q")
    175.      enigmaMachineB.addWheel(DISKB, "E")
    176.      enigmaMachineB.addWheel(DISKC, "V")
    177.      #добавляем рефлектор
    178.      enigmaMachineB.addReflector(REFLECTOR)
    179.      fileobj=open("decrypted.txt",'r',encoding='utf-8')
    180.      plain_text=fileobj.read()
    181.      fileobj.close()
    182.      print("Исходный текст:\n" + plain_text)
    183.      message = ''
    184.      for letter in plain_text.upper():
    185.         if letter in LETTERS:
    186.              message += letter
    187.         elif letter == '\n':
    188.              message += 'q'  
    189.      encryptedMessage = enigmaMachineA.encryptStr(message.lower())
    190.      print("\nЗашифрованный текст:\n" + encryptedMessage)
    191.      decryptedMessage = enigmaMachineB.encryptStr(encryptedMessage)
    192.      Message = ''
    193.      for i in range(1,len(decryptedMessage)):
    194.          if decryptedMessage[i-1] == 'Q' and decryptedMessage[i] != 'U':
    195.              Message += '\n'
    196.          else:
    197.              Message += decryptedMessage[i-1]
    198.      print("\nРасшифрованный текст:\n" + Message.lower())
    199. # Функция main() вызывается только в том случае, если файл EnigmaMachine.py был
    200. # запущен как программа, а не импортируется в виде модуля другой программой
    201. if __name__ == '__main__':
    202.     main()
     

    Вложения:

    • 04.gif
      04.gif
      Размер файла:
      411,7 КБ
      Просмотров:
      462
    Последнее редактирование: 22 июл 2025
    q2e74 и MaKsIm нравится это.
  7. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.229

    Шифр Хилла

    полиграммный шифр подстановки, основан на линейной алгебре и модульной арифметике. Шифр, который позволил на практике одновременно оперировать более чем с тремя символами.

    Шифрование/дешифрование
    Код (Python):
    1. # Шифр Хилла
    2. import numpy as np
    3. LETTERS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ ,!'
    4. def main():
    5.     fileobj=open("decrypted.txt",'r',encoding='utf-8')
    6.     plain_text=fileobj.read()
    7.     fileobj.close()
    8.     print("Исходный текст:\n" + plain_text)
    9.     message = ''
    10.     for letter in plain_text.upper():
    11.         if letter in LETTERS:
    12.              message += letter
    13.         elif letter == '\n':
    14.              message += 'q'
    15.     key = np.array([[ 6,13,18, 9,20],
    16.                     [24,16, 2,24, 7],
    17.                     [ 1,10,23,11,22],
    18.                     [ 2,13,15,16, 3],
    19.                     [ 7, 1, 7,21,17]])
    20.     encrypted_text = hill_cipher_encrypt(key,message)
    21.     print('зашифрованный текст:\n',encrypted_text)
    22.     decryptedMessage = hill_cipher_decrypt(key,encrypted_text)
    23.     Message = ''
    24.     for i in range(1,len(decryptedMessage)):
    25.         if decryptedMessage[i-1] == 'Q' and decryptedMessage[i] != 'U':
    26.             Message += '\n'
    27.         else:
    28.             Message += decryptedMessage[i-1]
    29.     print('расшифрованный текст:\n',Message)
    30. def matrixinvmod26(M):
    31.     Minv = np.linalg.inv(M)
    32.     Mdet = np.linalg.det(M)
    33.     Mod26invTable = {}
    34.     for m in range(len(LETTERS)):
    35.         for n in range(len(LETTERS)):
    36.             if (m*n)%len(LETTERS)== 1:
    37.                 Mod26invTable[m] = n
    38.  
    39.     Mdet26 = Mdet % len(LETTERS)
    40.     if Mdet26 in Mod26invTable:
    41.         Mdetinv26 = Mod26invTable[Mdet26]
    42.     else:
    43.         Mdetinv26 = None
    44.     Madj = Mdet*Minv
    45.     Madj26 = Madj % len(LETTERS)
    46.     Minv26 = (Mdetinv26*Madj26) % len(LETTERS)
    47.     Minv26 = np.matrix.round(Minv26, 0)% len(LETTERS)
    48.     return Minv26
    49. #Функция предназначена шифрования сообщений
    50. def hill_cipher_encrypt(M, plaintext):
    51.     plaintext = plaintext.upper()
    52.     while len(plaintext) % 5 != 0:
    53.         plaintext += "Q"
    54.     char_list = [LETTERS.find(c) for c in plaintext]
    55.     cit=[]
    56.     for i in range(0,len(char_list),5):
    57.             sublist = char_list[i:i+5]
    58.             mat_mul = np.matmul(M,sublist) % len(LETTERS)
    59.             mult_res = [LETTERS[i] for i in mat_mul]
    60.             cit=cit+mult_res
    61.     c=''.join(cit)
    62.     return c
    63. #Функция предназначена для дешифрования сообщений
    64. def hill_cipher_decrypt(M, ciphertext):
    65.     ciphertext = ciphertext.upper()
    66.     Minv = matrixinvmod26(M)
    67.     Minv = Minv.astype(int)
    68.     char_list = [LETTERS.find(c) for c in ciphertext]
    69.     pla=[]
    70.     for i in range(0,len(char_list),5):
    71.             sublist = char_list[i:i+5]
    72.             mat_mul = np.matmul(Minv,sublist) % len(LETTERS)
    73.             mult_res = [LETTERS[i] for i in mat_mul]
    74.             pla=pla+mult_res
    75.     while pla[-1] == 'Q':
    76.         pla.pop()
    77.     p=''.join(pla)
    78.     return p
    79. # Если файл HillCipher.py выполняется как программа
    80. # (а не импортируется как модуль), вызвать функцию main()
    81. if __name__ == '__main__':
    82.     main()

    Взлом шифра Хилла, использующего матрицу 2 на 2

    Поскольку шифр Хилла является линейным, нам нужно найти только 2 соответствия биграмм для определения ключевой матрицы. Например, если бы мы знали, что «th» шифруется как «gk», а «er» шифруется как «bd», можно решить систему уравнений и найти матрицу ключей шифрования. Воспользуемся этим фактом, чтобы взломать шифр.
    Метод, который здесь представлен, зависит от знания некоторых слов в сообщении. Представим, что есть зашифрованный текст: fupcmtgzkyukbqfjhuktzkkixtta И мы знаем, что где-то в тексте встречается «of the». Это означает, что один из следующих вариантов является правильным (шифр Хилла 2х2 шифрует пары символов):
    fupcmtgzkyukbqfjhuktzkkixtta
    1ofthe
    2
    o​
    fthe
    3ofthe
    4
    o​
    fthe
    5ofthe
    6
    o​
    fthe
    . . .
    18
    o​
    fthe
    . . .
    и так далее. Если бы вторая строка была правильной, тогда мы бы получили следующее: PC → FT, т.е. биграмма PC расшифровывается как FT, а MT → HE. Создадим систему уравнений заменив A на 0, B на 1, O на 14 и т. д.:
    [math]D\begin{bmatrix} P \\ C \end{bmatrix}=\begin{bmatrix} F \\ T \end{bmatrix}\Rightarrow D\begin{bmatrix} 15 \\ 2 \end{bmatrix}=\begin{bmatrix} 5 \\ 19 \end{bmatrix} \bmod 26[/math]
    а также следующее уравнение:
    [math]D\begin{bmatrix} M \\ T \end{bmatrix}=\begin{bmatrix} H \\ E \end{bmatrix}\Rightarrow D\begin{bmatrix} 12 \\ 19 \end{bmatrix}=\begin{bmatrix} 7 \\ 4 \end{bmatrix} \bmod 26[/math]
    Мы хотим найти матрицу [math]D[/math], которая является ключом дешифрования. Сначала объединим два уравнения в одно: [math]D\begin{bmatrix} 15 & 12 \\ 2 & 19 \end{bmatrix}=\begin{bmatrix} 5 & 7 \\ 19 & 4 \end{bmatrix} \bmod 26[/math] Тогда [math]D[/math] равно: [math]D=\begin{bmatrix} 5 & 7 \\ 19 & 4 \end{bmatrix}\begin{pmatrix}\begin{bmatrix} 15 & 12 \\ 2 & 19 \end{bmatrix}\end{pmatrix}^{-1} \bmod 26[/math]
    Нам нужно получить остаток по модулю 26 от инвертированной матрицы. Важно знать об обратном по модулю значении, определителе матрицы и присоединённой (союзной, взаимной) матрице. [math]P=\begin{bmatrix} a & b \\ c & d \end{bmatrix}[/math] матрица, которую мы инвертируем. [math]d[/math] — определитель [math]P[/math]. Требуется найти [math]P^{-1}[/math] (обратную [math]P[/math]), такую, что [math](P\times P^{-1})\bmod 26= E[/math], где [math]E[/math] — квадратная матрица, у которой все элементы главной диагонали равны 1, а остальные элементы равны 0.
    [math]E=\begin{bmatrix}1 & \cdots & 0\\ \vdots & \ddots & \vdots\\ 0 & \cdots & 1 \end{bmatrix}[/math] Если известно [math]P[/math], тогда [math]P^{-1}=d^{-1}\times adj(P)=\frac{\displaystyle{1}}{\displaystyle{ad-bc}}\begin{bmatrix} d & -b \\ -c & a \end{bmatrix}[/math], где [math](d\times d^{-1})\bmod 26= 1[/math], [math]adj(P)[/math] — присоединённая матрица [math]P[/math]. Определитель матрицы, которую мы инвертируем, равен [math]d=(a\cdot d-b\cdot c)\bmod 26= (15\cdot 19 - 12\cdot 2)\bmod 26= 261\bmod 26=1[/math]. Нам также нужно найти обратный определитель [math]d^{-1}[/math], который, к счастью, в данном случае равен [math]1[/math], потому что [math]1^{-1}=1[/math] определитель [math]d[/math] является своим собственным обратным определителем. Присоединённая матрица выглядит следующим образом:
    [math]adj\begin{pmatrix}\begin{bmatrix} 15 & 12 \\ 2 & 19 \end{bmatrix}\end{pmatrix}=\begin{bmatrix} 19 & -12 \\ -2 & 15 \end{bmatrix}[/math]
    Рассчитаем обратную матрицу: [math]P^{-1}=1^{-1}\times adj\begin{pmatrix}\begin{bmatrix} 15 & 12 \\ 2 & 19 \end{bmatrix}\end{pmatrix}\bmod 26=1\times\begin{bmatrix} 19 & -12 \\ -2 & 15 \end{bmatrix}\bmod 26=\begin{bmatrix} 19 & 14 \\ 24 & 15 \end{bmatrix}[/math]
    Находим [math]D[/math]:
    [math]D=\begin{bmatrix} 5 & 7 \\ 19 & 4 \end{bmatrix}\begin{pmatrix}\begin{bmatrix} 15 & 12 \\ 2 & 19 \end{bmatrix}\end{pmatrix}^{-1}\bmod 26=\begin{bmatrix} 5 & 7 \\ 19 & 4 \end{bmatrix}\begin{bmatrix} 19 & 14 \\ 24 & 15 \end{bmatrix}\bmod 26=\begin{bmatrix} 263 & 175 \\ 457 & 326 \end{bmatrix}\bmod 26=\begin{bmatrix} 3 & 19 \\ 15 & 14 \end{bmatrix}[/math]
    Теперь у нас есть возможный ключ к расшифровке шифротекста. Если мы попытаемся расшифровать с его помощью наше предложение, то получим:frfthezyssqyvfetlvbafvaconfz Что не является правильным ответом. Что пошло не так? Это означает, что одно из наших первоначальных предположений было неверным, предположение заключалось в том, что фрагмент «of the» начинался со второй позиции. Чтобы определить фактический ключ, нужно попробовать протащить «of the» через каждую позицию, пока не получим английский текст на выходе. Если бы мы использовали смещение 18, тогда бы получили совпадения KT → FT и ZK → HE, повторив описанную выше процедуру, получим матрицу: [math]D=\begin{bmatrix} 17 & 5 \\ 18 & 23 \end{bmatrix}[/math] Теперь, пытаемся расшифровать зашифрованный текст, получим:defendtheeastwallofthecastle Чего мы и хотели. Техника, которую мы здесь использовали, называется «перетаскивание шаблона», которая может быть очень утомительной, если выполняется вручную. Проще написать программу, которая сделает это за нас.
    Обобщение только до зашифрованного текста
    Если у нас нет шаблонной строки — фрагмента открытого текста, нам придется внести изменения в алгоритм расшифрования. Можно по очереди перетаскивать набор вероятных квадрограмм вдоль зашифрованного текста. Самая распространенная квадрограмма в английском языке — «THAT». Делаем «THAT» нашим шаблоном и выполняем описанную выше процедуру для всех различных стартовых позиций. Если не получим в результате английский текст — пробуем вторую по распространенности квадрограмму 'THER'. Продолжаем пробовать, пока не получим расшифрованное предложение, похожее на предложение на английском языке.

    Криптостойкость

    Стандартный шифр Хилла уязвим для атаки по выбранному открытому тексту, потому что в нём используются линейные операции. Криптоаналитик, который перехватит [math]n^2[/math] пар символ сообщения/символ шифротекста сможет составить систему линейных уравнений, которую обычно несложно решить. Если окажется, что система не решаема, то необходимо всего лишь добавить ещё несколько пар символ сообщения/символ шифротекста. Такого рода расчёты средствами обычных алгоритмов линейной алгебры требует совсем немного времени. В связи с этим для увеличения криптостойкости в него должны быть добавлены какие-либо нелинейные операции. Комбинирование линейных операций, как в шифре Хилла, и нелинейных шагов привело к созданию подстановочно-перестановочной сети (например, сеть Фейстеля). Поэтому с определённой точки зрения можно рассматривать современные блочные шифры как вид полиграммных шифров.

    Длина ключа

    Длина ключа — это двоичный логарифм от количества всех возможных ключей. Существует [math]26^{n^2}[/math] матриц размера [math]n\times n[/math]. Значит, [math]log _{2}(26^{n^2})\approx 4{,}7\cdot n^2[/math] — верхняя грань длины ключа для шифра Хилла, использующего матрицы [math]n\times n[/math]. Это только верхняя грань, поскольку не каждая матрица обратима, а только такие матрицы могут быть ключом.
     

    Вложения:

    • decrypted.txt
      Размер файла:
      2,4 КБ
      Просмотров:
      325
    Последнее редактирование: 27 авг 2025
    q2e74 и MaKsIm нравится это.
  8. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.229

    Шифр 4 квадратов

    Симметрическое шифрование, модифицированный вариант шифра Плейфера. Шифрует пары букв, попадает в категорию полиграфических подстановочных шифров. Использование биграмм делает шифр 4 квадратов менее восприимчивым к частотному криптоанализу, так как он должен быть применен к 262=676 возможным парам букв латинского алфавита, а не только к 26 символам для монографического замещения. Частотный анализ для биграмм возможен, но гораздо сложнее, для того, чтобы этот анализ был полезен, требуется больше шифротекста.
    Шифрование/дешифрование
    Код (Python):
    1. # Программа шифрования и дешифрования на основе шифра 4 квадратов
    2. import re
    3. nonLettersAndSpacePattern =  re.compile('[^A-Z\s]')
    4. class Foursquare(object):
    5.     def __init__(self, key1, key2):
    6.         self.key1 = [k.upper() for k in key1]
    7.         self.key2 = [k.upper() for k in key2]
    8.         self.alph = 'ABCDEFGHIKLMNOPQRSTUVWXYZ' # нет буквы J
    9.         assert len(self.key1)==len(self.alph), 'длина key1 не равна 25'
    10.         assert len(self.key2)==len(self.alph), 'длина key2 не равна 25'
    11.    
    12.     def encipher_pair(self,a,b):  
    13.         arow,acol = self.alph.index(a)//5, self.alph.index(a)%5
    14.         brow,bcol = self.alph.index(b)//5, self.alph.index(b)%5
    15.         return (self.key1[arow*5+bcol], self.key2[brow*5+acol])
    16.    
    17.     def decipher_pair(self,a,b):
    18.         arow,acol = self.key1.index(a)//5, self.key1.index(a)%5
    19.         brow,bcol = self.key2.index(b)//5, self.key2.index(b)%5
    20.         return (self.alph[arow*5+bcol], self.alph[brow*5+acol])
    21.    
    22.     def encipher(self,string):  
    23.         if len(string)%2 == 1: string = string + 'X'
    24.         ret = ''
    25.         for c in range(0,len(string),2):
    26.             a,b = self.encipher_pair(string[c],string[c+1])
    27.             ret += a + b
    28.         return ret
    29.     def decipher(self,string):
    30.         ret = ''
    31.         for c in range(0,len(string.upper()),2):
    32.             a,b = self.decipher_pair(string[c],string[c+1])
    33.             ret += a + b
    34.         return ret
    35. def main():
    36.     fileobj=open("decrypted.txt",'r',encoding='utf-8')
    37.     text=fileobj.read()
    38.     fileobj.close()
    39.     print(text)
    40.     text = nonLettersAndSpacePattern.sub('', text.upper())
    41.     text = text.replace('J','I')
    42.     text = text.replace('\n',' ')
    43.     plain_text = text.replace(' ','Q')
    44.     fs = Foursquare(key1='zgptfoihmuwdrcnykeqaxvsbl',key2='mfnbdcrhsaxyogvituewlqzkp')
    45.     cipher_text = fs.encipher(plain_text)
    46.     print(cipher_text)
    47.     text = fs.decipher(cipher_text)
    48.     Message = ''
    49.     for i in range(1,len(text)):
    50.         if text[i-1] == 'Q' and text[i] != 'U':
    51.             Message += ' '
    52.         else:
    53.             Message += text[i-1]
    54.     print(Message)
    55. # Функция main() вызывается только в том случае, если файл foursqure.py был
    56. # запущен как программа, а не импортируется в виде модуля другой программой  
    57. if __name__ == '__main__':
    58.     main()
     

    Вложения:

    • decrypted.txt
      Размер файла:
      2,4 КБ
      Просмотров:
      291
    Последнее редактирование: 14 июл 2025
    MaKsIm нравится это.
  9. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.229

    Шифр апоЖ

    Шифр подстановки, каждый символ алфавита заменяется на «отзеркаленный» ([math]A\leftrightarrow Z[/math], [math]B\leftrightarrow Y[/math], [math]...[/math]). Из-за простоты, шифр уязвим к частотному анализу.
    Шифрование/дешифрование
    Код (Python):
    1. SYMBOLS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ '
    2. import re
    3. class Atbash(object):
    4.     def __init__(self,key):
    5.         self.key = [k.upper() for k in key]
    6.  
    7.     def remove_punctuation(self,text,filter='[^A-Z ]'):
    8.         return re.sub(filter,'',text.upper())
    9.  
    10.     def encipher(self,string):
    11.         string = self.remove_punctuation(string)
    12.         ret = ''
    13.         for symbol in string.upper():
    14.             if symbol in SYMBOLS:
    15.                 symbolIndex = SYMBOLS.find(symbol)
    16.                 ret += self.key[symbolIndex]
    17.         return ret
    18.     def decipher(self,string):
    19.         return self.encipher(string)
    20.  
    21. def main():
    22.     atbash = Atbash(SYMBOLS[::-1])
    23.     plaintext = 'defend the east wall of the castle'
    24.     ciphertext = atbash.encipher(plaintext)
    25.     print(ciphertext)
    26.     plaintext = atbash.decipher(ciphertext)
    27.     print(plaintext)
    28. # Функция main() вызывается только в том случае, если файл apozh.py был
    29. # запущен как программа, а не импортируется в виде модуля другой программой
    30. if __name__ == '__main__':
    31.     main()
     

    Вложения:

    • decrypted.txt
      Размер файла:
      2,4 КБ
      Просмотров:
      322
    Последнее редактирование: 30 авг 2025
    MaKsIm нравится это.
  10. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.229

    Шифр bifid

    Шифр, сочетающий квадрат Полибия с транспозицией и использующий дробление для достижения диффузии.

    шифрование/дешифрование
    Код (Python):
    1. import re
    2. class PolybiusSquare(object):
    3.     def __init__(self,key,size,chars=None):
    4.         self.key = ''.join([k.upper() for k in key])
    5.         self.chars = chars or 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[:size]
    6.         self.size = size
    7.         assert len(self.key)==size*size, 'в init недопустимый ключ: длина ключа должна быть size*size, ключ имеет длину'+str(len(key))
    8.         assert len(self.chars)==size, 'недопустимые символы в init: must have length=size, has length '+str(len(chars))
    9.     def remove_punctuation(self,text,filter='[^A-Z]'):
    10.         return re.sub(filter,'',text.upper())
    11.     def encipher_char(self,ch):      
    12.         row = self.key.index(ch) // self.size
    13.         col = self.key.index(ch) % self.size
    14.         return self.chars[row] + self.chars[col]
    15.  
    16.     def decipher_pair(self,pair):
    17.         row = self.chars.index(pair[0])
    18.         col = self.chars.index(pair[1])
    19.         return self.key[row*self.size + col]
    20.     def encipher(self,string): # Строка для шифрования
    21.         string = self.remove_punctuation(string)#,filter='[^'+self.key+']')
    22.         ret = ''
    23.         for c in range(0,len(string)):
    24.             ret += self.encipher_char(string[c])
    25.         return ret # Зашифрованная строка. Зашифрованный текст будет в два раза длиннее открытого текста.
    26.     def decipher(self,string): # Строка для расшифровки
    27.         string = self.remove_punctuation(string)#,filter='[^'+self.chars+']')
    28.         ret = ''
    29.         for i in range(0,len(string),2):
    30.             ret += self.decipher_pair(string[i:i+2])
    31.         return ret # Расшифрованная строка. Открытый текст будет в два раза короче шифртекста
    32. class Bifid(object):
    33.     def __init__(self,key='phqgmeaylnofdxkrcvszwbuti',period=5):
    34.         self.key = [k.upper() for k in key]
    35.         self.pb = PolybiusSquare(self.key,size=5)
    36.         self.period = period
    37.         assert len(key)==25, 'в init неверный ключ: длина должна быть 25, сейчас длина '+str(len(key))
    38.         assert period>=1, 'неверный период в init: период должен быть >= 1'
    39.      
    40.     def encipher(self,string): # Строка для шифрования.
    41.         string = self.pb.remove_punctuation(string)
    42.         step1 = self.pb.encipher(string)
    43.         evens = step1[::2]
    44.         odds = step1[1::2]
    45.         step2 = []
    46.         for i in range(0,len(string),self.period):
    47.             step2 += evens[i:int(i+self.period)]
    48.             step2 += odds[i:int(i+self.period)]
    49.         return self.pb.decipher(''.join(step2)) # Зашифрованная строка
    50.     def decipher(self,string): # Строка для расшифровки
    51.         ret = ''
    52.         string = string.upper()
    53.         rowseq,colseq = [],[]
    54.         # берем блоки длиной period, преобразуем из них rowseq, colseq
    55.         for i in range(0,len(string),self.period):
    56.             tempseq = []
    57.             for j in range(0,self.period):
    58.                 if i+j >= len(string): continue
    59.                 tempseq.append(self.key.index(string[i + j]) // 5)
    60.                 tempseq.append(self.key.index(string[i + j]) % 5)
    61.             rowseq.extend(tempseq[0:int(len(tempseq)/2)])
    62.             colseq.extend(tempseq[int(len(tempseq)/2):])
    63.         for i in range(len(rowseq)):
    64.             ret += self.key[rowseq[i]*5 + colseq[i]]
    65.         return ret # Расшифрованная строка
    66. def main():
    67.     bf = Bifid('phqgmeaylnofdxkrcvszwbuti',5)
    68.     fileobj = open("decrypted.txt",'r',encoding='utf-8')
    69.     plain_text = fileobj.read()
    70.     fileobj.close()
    71.     print(plain_text)
    72.     plain_text = plain_text.replace('  ',' ')
    73.     plain_text = plain_text.replace('j','i')
    74.     plain_text = plain_text.replace('\n','qq')
    75.     plain_text = plain_text.replace(' ','Q')
    76.     cipher_text = bf.encipher(plain_text)
    77.     print(cipher_text)
    78.     plain_text = bf.decipher(cipher_text)
    79.     Message = ''
    80.     for i in range(1,len(plain_text)):
    81.         if plain_text[i-1] == 'Q' and plain_text[i] == 'Q':
    82.             Message += '\n'
    83.             i += 1
    84.         elif plain_text[i-1] == 'Q' and plain_text[i] != 'U':
    85.             Message += ' '        
    86.         else:
    87.             Message += plain_text[i-1]
    88.     print(Message)
    89. # Функция main() вызывается только в том случае, если файл bifid.py был
    90. # запущен как программа, а не импортируется в виде модуля другой программой
    91. if __name__ == '__main__':
    92.     main()
     

    Вложения:

    • decrypted.txt
      Размер файла:
      2,4 КБ
      Просмотров:
      232
    MaKsIm нравится это.
  11. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.229

    Шифр trifid

    Шифр сочетающий методы дробления и транспозиции для достижения определенного уровня путаницы и распространения. Буквы зашифрованного текста зависят от трех букв открытого текста и трех букв ключа.
    Процесс шифрования:
    1. Подготовка открытого текста. Из него удаляют пробелы, знаки препинания и любые неалфавитные символы.
    2. Определение алфавита шифра Trifid. Буквы присваивают 3-х мерным координатам на основе слоев, строк и столбцов.
    3. Группировка по размеру. Организованные координаты группируют по указанному размеру группы, сортируют по столбцам.
    4. Перестановка координат по группе. Внутри каждой группы координаты перекомбинируют по горизонтали, по 3 координаты на группу, в результате получаются новые координаты.
    5. Генерация зашифрованного текста. С помощью новых координат находят соответствующие буквы в 3-х мерной сетке и подставляют их для зашифрованного текста
    Код (Python):
    1. from itertools import product
    2. key='APCZ WRLFBDKOTYUQGENHXMIVS'
    3. chars = (1,2,3)
    4. IND2L = dict(zip(list(product(chars,repeat=3)),key))
    5. L2IND = dict(zip(key,list(product(chars,repeat=3))))
    6.        
    7. def encipher(string):
    8.     ctext = ""
    9.     for c in string.upper():
    10.         ctext += ''.join([str(i) for i in L2IND[c]])
    11.     return ctext
    12. def decipher(string):
    13.     ret = ''
    14.     for i in range(0,len(string),3):
    15.         ind = tuple([int(string[i+k]) for k in [0,1,2]])
    16.         ret += IND2L[ind]
    17.     return ret
    18. def main():
    19.     plaintext = 'defend the east wall of the castle'
    20.     ciphertext = encipher(plaintext)
    21.     print(ciphertext)
    22.     plaintext = decipher(ciphertext)
    23.     print(plaintext)
    24. # Функция main() вызывается только в том случае, если файл trifid.py был
    25. # запущен как программа, а не импортируется в виде модуля другой программой
    26. if __name__ == '__main__':
    27.     main()
     
    MaKsIm нравится это.
  12. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.229

    Шифр Бофора

    полиалфавитный подстановочный шифр
    Шифрование/дешифрование
    Код (Python):
    1. SYMBOLS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ '
    2. import re
    3. class Beaufort(object):
    4.     def __init__(self,key):
    5.         self.key = [k.upper() for k in key]
    6.    
    7.     def remove_punctuation(self,text,filter='[^A-Z ]'):
    8.         return re.sub(filter,'',text.upper())
    9.  
    10.     def a2i(self,ch):
    11.         ch = ch.upper()
    12.         arr = dict([(SYMBOLS[i],i) for i in range(len(SYMBOLS))])
    13.         return arr[ch]
    14.     def i2a(self,i):
    15.         i = i % len(SYMBOLS)  
    16.         return SYMBOLS[i]
    17.  
    18.     def encipher(self,string):
    19.         string = self.remove_punctuation(string)
    20.         ret = ''
    21.         for (i,c) in enumerate(string):
    22.             i = i%len(self.key)
    23.             ret += self.i2a(self.a2i(self.key[i])-self.a2i(c))
    24.         return ret
    25.     def decipher(self,string):      
    26.         return self.encipher(string)
    27.  
    28. def main():
    29.     beaufort = Beaufort('HELLO')
    30.     plaintext = 'defend the east wall of the castle'
    31.     ciphertext = beaufort.encipher(plaintext)
    32.     print(ciphertext)
    33.     plaintext = beaufort.decipher(ciphertext)
    34.     print(plaintext)
    35. # Функция main() вызывается только в том случае, если файл beaufort.py был
    36. # запущен как программа, а не импортируется в виде модуля другой программой
    37. if __name__ == '__main__':
    38.     main()
     
    Последнее редактирование: 23 июл 2025
  13. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.229

    Шифр Гронсфельда

    полиалфавитный подстановочный шифр. Длина ключа (K) должна быть равной длине исходного текста. Для этого циклически записывают ключ до тех пор, пока его длина не будет соответствовать длине исходного текста
    Шифрация/дешифрация
    Код (Python):
    1. SYMBOLS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ '
    2. import re
    3. class Gronsfeld(object):
    4.     def __init__(self,key):
    5.         self.key = key
    6.    
    7.     def remove_punctuation(self,text,filter='[^A-Z ]'):
    8.         return re.sub(filter,'',text.upper())
    9.  
    10.     def a2i(self,ch):
    11.         ch = ch.upper()
    12.         arr = dict([(SYMBOLS[i],i) for i in range(len(SYMBOLS))])
    13.         return arr[ch]
    14.     def i2a(self,i):
    15.         i = i % len(SYMBOLS)    
    16.         return SYMBOLS[i]
    17.     def encipher(self,string):
    18.         string = self.remove_punctuation(string)
    19.         ret = ''
    20.         for (i,c) in enumerate(string):
    21.             i = i%len(self.key)
    22.             ret += self.i2a(self.a2i(c) + self.key[i])
    23.         return ret
    24.     def decipher(self,string):
    25.         ret = ''
    26.         for (i,c) in enumerate(string):
    27.             i = i%len(self.key)
    28.             ret += self.i2a(self.a2i(c) - self.key[i])
    29.         return ret      
    30. def main():
    31.     plaintext = 'defend the east wall of the castle'
    32.     gronsfeld = Gronsfeld([5, 4, 7, 9, 8, 5, 8, 2, 0, 9, 8, 4, 3])
    33.     ciphertext = gronsfeld.encipher(plaintext)
    34.     print(ciphertext)
    35.     plaintext = gronsfeld.decipher(ciphertext)
    36.     print(plaintext)
    37. # Функция main() вызывается только в том случае, если файл gronsfeld.py был
    38. # запущен как программа, а не импортируется в виде модуля другой программой
    39. if __name__ == '__main__':
    40.     main()
     
    Последнее редактирование: 23 июл 2025
  14. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.229

    Шифр «Квадрат Полибия»

    Относится к шифру простой замены.

    Шаг 1: Формирование таблицы шифрования

    К каждому языку отдельно составляется таблица шифрования с одинаковым (не обязательно) количеством пронумерованных строк и столбцов, параметры которой зависят от его мощности (количества букв в алфавите). Берутся два целых числа, произведение которых ближе всего к количеству букв в языке — получаем нужное число строк и столбцов. Затем вписываем в таблицу все буквы алфавита подряд — по одной в каждую клетку. При нехватке клеток можно вписать в одну две буквы (редко употребляющиеся или схожие по употреблению). «Квадрат Полибия» представляет собой квадрат [math]5\times 5[/math], столбцы и строки которого нумеруются цифрами от [math]1[/math] до [math]5[/math]. В каждую клетку этого квадрата записывается одна буква. каждой букве соответствует пара чисел, и шифрованное сообщение превращается в последовательность пар чисел. Расшифровывается путем нахождения буквы, стоящей на пересечении строки и столбца. Буквы могут вписываться в таблицу в произвольном порядке — заполнение таблицы в этом случае и является ключом

    Шаг 2: Принцип шифрования

    Существует несколько методов шифрования с помощью квадрата Полибия.
    Метод 1
    12345
    1ABCDE
    2FGHI/JK
    3LMNOP
    4QRSTU
    5VWXYZ
    Шифруем слово «sometext»: для шифрования в квадрате находим букву текста и вставляем в шифротекст нижнюю от неё в том же столбце. Столбец считаем закольцованным. Если буква находится в нижней строке, тогда берем верхнюю букву из того же столбца.
    Таблица координат
    Открытый текстSOMETEXT
    ШифротекстXTRKYKCY
    Метод 2
    Сообщение преобразуется в координаты по квадрату Полибия, координаты записываются вертикально.
    открытый текстSOMETEXT
    Вертикальная координата34254534
    Горизонтальная координата43314154
    Затем координаты считывают по строкам 34 25 45 34 43 31 41 54, координаты преобразуются в буквы по этому же квадрату
    Вертикальная координата32434345
    Горизонтальная координата45543114
    шифротекстSWYSOCDU
    Метод 3
    Усложнённый вариант: полученный первичный шифротекст (После второго метода шифрования, цифровой) шифруется вторично. При этом он выписывается без разбиения на пары: 34 25 45 34 43 31 41 54. Полученная последовательность сдвигается циклически влево на один шаг (нечётное количество шагов, т.е. цифра 3 перемещается в конец): 4 25 45 34 43 31 41 54 3 Последовательность вновь разбивается в группы по два 42 54 53 44 33 14 15 43 и по таблице заменяется на окончательный шифротекст:
    Вертикальная координата45543114
    Горизонтальная координата24343453
    шифротекстIUPTNQVO
    Для оценки шифра учитывают два фактора:
    1. Квадрат Полибия заполнен буквами произвольно
    2. При использование шифра квадраты заменяют периодически
    Тогда анализ предыдущих сообщений ничего не даёт, так как к моменту раскрытия шифра он может быть заменён.
    Буквы могут вписываться в таблицу в произвольном порядке — заполнение таблицы в этом случае и является ключом. Для латинского алфавита в первую клетку можно вписать одну из 25 букв, во вторую — одну из 24, в третью — одну из 23 и т. д. Получаем максимальное количество ключей для шифра на таблице латинского алфавита: [math]25!\approx 1.55\cdot 10^{25}[/math]
    Для дешифрования сообщения требуется знание алфавита и ключа, с помощью которого составлялась таблица шифрования. Так как произвольный порядок букв тяжело запомнить, поэтому пользователь шифра должен иметь при себе ключ — квадрат. Появляется опасность тайного ознакомления с ключом посторонних лиц. В качестве компромиссного решения был предложен ключ — пароль. Пароль выписывается без повторов букв в квадрат; в оставшиеся клетки в алфавитном порядке выписываются буквы алфавита, отсутствующие в пароле.
    Криптоанализ
    Один из методов атак на шифр — частотный анализ. Распределение букв в шифротексте сравнивается с распределением букв в алфавите исходного сообщения. Буквы с наибольшей частотой в шифротексте заменяются на букву с наибольшей частотой из алфавита, если он известен. Вероятность успешного вскрытия повышается с увеличением длины шифротекста, поскольку распределения статистические. Существуют множество различных таблиц о распределении букв в том или ином языке, но ни одна из них не содержит окончательной информации — даже порядок букв может отличаться в различных таблицах. Распределение сильно зависит от типа текста: проза, разговорный язык, технический язык и т. п. Квадрат Полибия является шифром замены, неустойчивым к частотной атаке.

    Шифрование/дешифрование

    Код (Python):
    1. import re
    2. SYMBOLS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ '
    3. class PolybiusSquare(object):
    4.     def __init__(self,key,size,chars=None):
    5.         self.key = ''.join([k.upper() for k in key])
    6.         self.chars = chars or 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[:size]
    7.         self.size = size
    8.         assert len(self.key)==size*size, 'invalid key in init: must have length size*size, has length '+str(len(key))
    9.         assert len(self.chars)==size, 'invalid chars in init: must have length=size, has length '+str(len(chars))
    10.  
    11.     def remove_punctuation(self,text,filter='[^A-Z]'):
    12.         return re.sub(filter,'',text.upper())
    13.  
    14.     def encipher_char(self,ch):
    15.         row = self.key.index(ch) // self.size
    16.         col = self.key.index(ch) % self.size
    17.         return self.chars[row] + self.chars
    18.  
    19.     def decipher_pair(self,pair):
    20.         row = self.chars.index(pair[0])
    21.         col = self.chars.index(pair[1])
    22.         return self.key[row*self.size + col]
    23.     def encipher(self,string):
    24.         string = self.remove_punctuation(string)
    25.         ret = ''
    26.         for c in range(0,len(string)):
    27.             ret += self.encipher_char(string[c])
    28.         return ret
    29.     def decipher(self,string):
    30.         ret = ''
    31.         for i in range(0,len(string),2):
    32.             ret += self.decipher_pair(string[i:i+2])
    33.         return ret
    34. def main():
    35.     polybius = PolybiusSquare('APCZWRLFBDKOTYUQGENHXMIVS',5,'MKSBU')
    36.     plaintext = 'defend the east wall of the castle'
    37.     print(plaintext)
    38.     plaintext = plaintext.replace(' ','Q')
    39.     ciphertext = polybius.encipher(plaintext)
    40.     print(ciphertext)
    41.     plaintext = polybius.decipher(ciphertext)
    42.     plaintext = plaintext.replace('Q',' ')
    43.     print(plaintext)
    44. # Функция main() вызывается только в том случае, если файл polybius.py был
    45. # запущен как программа, а не импортируется в виде модуля другой программой
    46. if __name__ == '__main__':
    47.     main()
     
    Последнее редактирование: 18 сен 2025
  15. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.229

    ADFGX шифр

    Шифр построен на соединении базовых операций замены и перестановки. Часть шифра, отвечающая замене, основывается на квадрате Полибия.
    Процесс шифрования начинается с заполнения квадрата 5×5, каждая ячейка квадрата заполняется буквами латиницы (I и J шифруются одинаково). Адрес строки и столбца квадрата задаются буквами: «A», «D», «F», «G» и «X». Заполнение ячеек происходит в случайном порядке, поэтому получатель должен знать точное расположение каждого элемента для дешифровки сообщений.
    ADFGX
    AFNHEQ
    DRDZOC
    FI/JSAGU
    GBVKPW
    XXMYTL
    На первом шаге каждая буква сообщения заменяется на пару букв, обозначающих строку и столбец соответствующей буквы в квадрате. HELLO WORLD → AF.AG.XX.XX.DG.GX.DG.DA.XX.DD
    На втором шаге для усложнения криптоанализа применяется перестановка. Перестановка осуществляется в зависимости от ключевого слова, которое должно быть известно получателю. Создается новая сетка, в верхней строке которой записываются буквы ключевого слова. Затем под этим словом построчно записывается, полученный на первом шаге зашифрованный текст. Далее буквы ключевого слова переставляются в алфавитном порядке вместе с соответствующими им столбцами сетки. После чего буквы каждого столбца выписываются поочередно сверху вниз. Из полученной последовательности букв при помощи квадрата создают шифротекст.
    BATTLE_ABELTT
    AFAGXXFAXXAG
    XXDGGXXXXGDG
    DGDAXXGDXXDA
    DDDD
    HELLO WORLD → FXGDAXDDXXXXGXADDGGA → UVQDLLWNOB
    Шифрование/дешифрование
    Код (Python):
    1. import re
    2. nonLettersAndSpacePattern =  re.compile('[^A-Z ]')
    3. class ADFGX(object):
    4.     def __init__(self,key,keyword):
    5.         self.key = [k.upper() for k in key]
    6.         self.keyword = keyword
    7.         assert len(key)==25, 'invalid key in init: must have length 25, has length '+str(len(key))
    8.         assert len(keyword)>0, 'invalid keyword in init: should have length >= 1'
    9.  
    10.     def encipher(self,string):
    11.         step1 = PolybiusSquare(self.key,size=5,chars='ADFGX').encipher(string)
    12.         step2 = ColTrans(self.keyword).encipher(step1)
    13.         return step2
    14.     def decipher(self,string):
    15.         step2 = ColTrans(self.keyword).decipher(string)
    16.         step1 = PolybiusSquare(self.key,size=5,chars='ADFGX').decipher(step2)
    17.         return step1
    18. class ColTrans(object):
    19.     def __init__(self,keyword):
    20.         self.keyword = keyword.upper()
    21.         assert len(keyword)>0, 'invalid keyword in init: should be >= 1'
    22.     # return the sorted indices of a word e.g. 'german' = [2,1,5,3,0,4] '''
    23.     def sortind(self,word):
    24.         t1 = [(word[i],i) for i in range(len(word))]
    25.         t2 = [(k[1],i) for i,k in enumerate(sorted(t1))]
    26.         return [q[1] for q in sorted(t2)]
    27.  
    28.     # return the unsorted indices of a word '''
    29.     def unsortind(self,word):
    30.         t1 = [(word[i],i) for i in range(len(word))]
    31.         return [q[1] for q in sorted(t1)]
    32.  
    33.     def encipher(self,string):
    34.         ret = ''
    35.         ind = self.sortind(self.keyword)
    36.         for i in range(len(self.keyword)):
    37.             ret += string[ind.index(i)::len(self.keyword)]
    38.         return ret
    39.     # deciphering is messy because the columns may be ragged '''
    40.     def decipher(self,string):
    41.         ret = ['_']*len(string)
    42.         L,M = len(string),len(self.keyword)
    43.         ind = self.unsortind(self.keyword)
    44.         upto = 0
    45.         for i in range(len(self.keyword)):
    46.             thiscollen = (int)(L/M)
    47.             if ind[i]< L%M: thiscollen += 1
    48.             ret[ind[i]::M] = string[upto:upto+thiscollen]
    49.             upto += thiscollen
    50.         return ''.join(ret)
    51.  
    52. class PolybiusSquare(object):
    53.     def __init__(self,key,size=5,chars=None):
    54.         self.key = ''.join([k.upper() for k in key])
    55.         self.chars = chars or 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'[:size]
    56.         self.size = size
    57.         assert len(self.key)==size*size, 'invalid key in init: must have length size*size, has length '+str(len(key))
    58.         assert len(self.chars)==size, 'invalid chars in init: must have length=size, has length '+str(len(chars))
    59.     def encipher_char(self,ch):
    60.         row = self.key.index(ch) // self.size
    61.         col = self.key.index(ch) % self.size
    62.         return self.chars[row] + self.chars[col]
    63.  
    64.     def decipher_pair(self,pair):
    65.         row = self.chars.index(pair[0])
    66.         col = self.chars.index(pair[1])
    67.         return self.key[row*self.size + col]
    68.     def encipher(self,string):
    69.         ret = ''
    70.         for c in range(0,len(string)):
    71.             ret += self.encipher_char(string[c])
    72.         return ret
    73.     def decipher(self,string):
    74.         ret = ''
    75.         for i in range(0,len(string),2):
    76.             ret += self.decipher_pair(string[i:i+2])
    77.         return ret
    78. def main():
    79.     fileobj=open("decrypted.txt",'r',encoding='utf-8')
    80.     plaintext = fileobj.read()
    81.     print("Исходный текст:\n" + plaintext)
    82.     fileobj.close()
    83.     plaintext = nonLettersAndSpacePattern.sub('', plaintext.upper())
    84.     plaintext = plaintext.replace('J','I')
    85.     plaintext = plaintext.replace('  ',' ')
    86.     plaintext = plaintext.replace('\n','QQ')
    87.     plaintext = plaintext.replace(' ','Q')
    88.     a = ADFGX('phqgmeaylnofdxkrcvszwbuti','HELLO')
    89.     ciphertext = a.encipher(plaintext)
    90.     print("\nЗашифрованный текст:\n" + ciphertext)
    91.     plaintext = a.decipher(ciphertext)
    92.     Message = ''
    93.     for i in range(1,len(plaintext)):
    94.         if plaintext[i-1] == 'Q' and plaintext[i] == 'Q':
    95.             Message += '\n'
    96.             i += 1
    97.         elif plaintext[i-1] == 'Q' and plaintext[i] != 'U' and plaintext[i] != 'Q':
    98.             Message += ' '
    99.         else:
    100.             Message += plaintext[i-1]
    101.     print("\nРасшифрованный текст:\n" + Message)
    102. # Функция main() вызывается только в том случае, если файл adfgx.py был
    103. # запущен как программа, а не импортируется в виде модуля другой программой
    104. if __name__ == '__main__':
    105.     main()
     
    Последнее редактирование: 23 июл 2025
    MaKsIm нравится это.
  16. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.229

    Шифр с автоключом

    Шифр, который включает открытый текст в ключ. Ключ генерируется из сообщения в автоматическом режиме путем выбора определенных букв из текста или путем добавления короткого первичного ключа в начало сообщения.
    Существуют две формы шифра c автоключом: ключевой и текстовый шифры с автоключом. Ключевой шифр с автоключом использует предыдущие элементы ключевого потока для определения следующего элемента в ключевом потоке. Текстовый шифр с автоключом использует предыдущий текст сообщения для определения следующего элемента в ключевом потоке.
    В криптографии самосинхронизирующие потоковые шифры — шифры с автоключом.
    Шифрование/дешифрование
    Код (Python):
    1. SYMBOLS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ '
    2. import re
    3. class Autokey(object):
    4.     def __init__(self,key):
    5.         self.key = [k.upper() for k in key]
    6.  
    7.     def remove_punctuation(self,text,filter='[^A-Z ]'):
    8.         return re.sub(filter,'',text.upper())
    9.  
    10.     def a2i(self,ch):
    11.         ch = ch.upper()
    12.         arr = dict([(SYMBOLS[i],i) for i in range(len(SYMBOLS))])
    13.         return arr[ch]
    14.  
    15.     def i2a(self,i):
    16.         i = i % len(SYMBOLS)  
    17.         return SYMBOLS[i]
    18.  
    19.     def encipher(self,string):
    20.         string = self.remove_punctuation(string)
    21.         ret = ''
    22.         for (i,c) in enumerate(string):
    23.             if i<len(self.key):
    24.                 offset = self.a2i(self.key[i])
    25.             else:
    26.                 offset = self.a2i(string[i-len(self.key)])
    27.             ret += self.i2a(self.a2i(c)+offset)
    28.         return ret
    29.     def decipher(self,string):
    30.         string = self.remove_punctuation(string)
    31.         ret = ''
    32.         for (i,c) in enumerate(string):
    33.             if i<len(self.key):
    34.                 offset = self.a2i(self.key[i])
    35.             else:
    36.                 offset = self.a2i(ret[i-len(self.key)])        
    37.             ret += self.i2a(self.a2i(c)-offset)
    38.         return ret
    39. def main():
    40.     autokey = Autokey('HELLO')
    41.     plaintext = 'defend the east wall of the castle'
    42.     ciphertext = autokey.encipher(plaintext)
    43.     print(ciphertext)
    44.     plaintext = autokey.decipher(ciphertext)
    45.     print(plaintext)
    46. # Функция main() вызывается только в том случае, если файл autokey.py был
    47. # запущен как программа, а не импортируется в виде модуля другой программой
    48. if __name__ == '__main__':
    49.     main()
     
    Последнее редактирование: 23 июл 2025
    MaKsIm нравится это.
  17. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.229

    Один из вариантов шифра подстановки

    Заполнение квадрата «шагом шахматного коня»
    Код (Python):
    1. #заполнение текстом ходом шахматного коня
    2. import re
    3. LETTERS = 'АБВГДЕЁЖЗИЙКЛМНОПРСТУФЦЧШЩЬЫЪЭЮЯ'
    4. SIDE = 16
    5. MOVES = [(1,2),(-1,2),(2,1),(2,-1),(1,-2),(-1,-2),(-2,-1),(-2,1)]
    6. board = [[ 0 for x in range(SIDE) ] for y in range(SIDE) ]
    7. def test_move(row, col):
    8.     return ( 0 <= row < SIDE ) and ( 0 <= col < SIDE ) and ( board[row][col] == 0 )
    9. def remove_punctuation(text,filter='[^А-Я]'):
    10.         return re.sub(filter,'',text.upper())
    11.  
    12. def count_next_moves(pos):
    13.     goodMoves = 0
    14.     for m in MOVES:
    15.         if test_move(pos[0] + m[0], pos[1] + m[1]):
    16.             goodMoves += 1
    17.     return goodMoves
    18. def find_path(firstMove):
    19.     movesCollection = [(firstMove,[(firstMove[0]+m[0],firstMove[1]+m[1],firstMove[2]+1) for m in MOVES if test_move(firstMove[0]+m[0],firstMove[1]+m[1])])]
    20.     board[firstMove[0]][firstMove[1]] = firstMove[2]
    21.  
    22.     while len(movesCollection) > 0:
    23.         topMoves = movesCollection[-1][1]
    24.         if len(topMoves) == 0:
    25.             lastMove = movesCollection[-1][0]
    26.             board[lastMove[0]][lastMove[1]] = 0
    27.             movesCollection.pop()
    28.         else:
    29.             lastMove = topMoves.pop()
    30.             board[lastMove[0]][lastMove[1]] = lastMove[2]
    31.             if lastMove[2] == ( SIDE * SIDE ):
    32.                 return True
    33.             nextMoves = [(lastMove[0]+m[0],lastMove[1]+m[1],lastMove[2]+1) for m in MOVES if test_move(lastMove[0]+m[0],lastMove[1]+m[1])]
    34.             if len(nextMoves) == 0:
    35.                 board[lastMove[0]][lastMove[1]] = 0
    36.             else:
    37.                 nextMoves.sort(key=count_next_moves, reverse=True)
    38.                 movesCollection.append((lastMove, nextMoves))
    39.     return False
    40. def dump_board(SIDE):
    41.  
    42.     message = '''Кавалергарды, век недолог,
    43.                  И потому так сладок он.
    44.                  Поет труба, откинут полог,
    45.                  И где-то слышен сабель звон.
    46.                  Еще рокочет голос струнный,
    47.                  Но командир уже в седле…
    48.                  Не обещайте деве юной
    49.                  Любови вечной на земле!
    50.                  Течет шампанское рекою,
    51.                  И взгляд туманится слегка,
    52.                  И все как будто под рукою,
    53.                  И все как будто на века.
    54.                  Но как ни сладок мир подлунный —
    55.                  Лежит тревога на челе…
    56.                  Не обещайте деве юной
    57.                  Любови вечной на земле!'''
    58.     message = remove_punctuation(message)
    59.     print('   ', end=' ')
    60.     for i in range(65,SIDE+65):
    61.         print(chr(i), end=' ')
    62.     print()
    63.     print('  \u250C',end='\u2500')
    64.     for i in range(SIDE):
    65.         print('\u2500',end='\u2500')
    66.     print('\u2510')
    67.     for i in range(SIDE):
    68.         m = str(i+1)+'\u2502'
    69.         if i<9: m = ' ' + m        
    70.         print(m, end=' ')
    71.         for j in range(SIDE):
    72.             print(message[board[i][j]-1].upper(), end=' ')
    73.  
    74.         print('\u2502')
    75.     print('  \u2514',end='\u2500')
    76.     for i in range(SIDE):
    77.         print('\u2500',end='\u2500')
    78.     print('\u2518')
    79.  
    80. def main():
    81.     SIDE = int(input("Введи размер стороны шахматной доски не больше 17\n>"))
    82.     h = ord(input("Введи координаты первой клетки по горизонтали от A до "+chr(SIDE+64)+"\n>").upper()) - ord('A')
    83.     v = input("Введи координаты первой клетки по вертикали от 1 до "+str(SIDE)+"\n>")
    84.     v = int(v)-1
    85.  
    86.     print('Шифрую:')
    87.     firstMove = (v, h, 1)
    88.     find_path(firstMove)
    89.     dump_board(SIDE)
    90.  
    91. # Если файл ChessKnight.py выполняется как программа,
    92. # а не импортируется как модуль, вызвать функцию main().
    93. if __name__ == '__main__':
    94.     main()

    Шифрование

    Номера строк перемешивают по известному алгоритму. Выписываем строка за строкой содержимое шахматной доски в одну длинную строку. Шифротекст готов.

    Дешифрование

    1. Длинный текст разбивают на известную длину строк [math]n[/math] (1-ая часть ключа). Если количество символов в тексте меньше чем [math]n^2[/math] оставшуюся часть дополняют «мусором»
    2. По известному алгоритму восстанавливают порядок строк (2-ая часть ключа)
    3. Считывают текст, начиная с известной клетки (3-ья часть ключа)
    4. Направление считывания ходом «шахматного коня» (существует несколько способов заполнения шахматной доски) ― это 4-ая часть ключа.
     
    Последнее редактирование: 30 авг 2025
    MaKsIm нравится это.
  18. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.229

    Аффинный шифр

    Шифрование/расшифровка/взлом методом полного перебора
    Код (Python):
    1. import re
    2. from math import log10
    3. SYMBOLS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ '
    4. class ngram_score(object):
    5.     def __init__(self,ngramfile,sep=' '):#load a file containing ngrams and counts, calculate log probabilities '''
    6.         self.ngrams = {}
    7.         filebuffer = open(ngramfile,encoding="UTF-8")
    8.         for line in filebuffer:
    9.             key,count = line.split(sep)      
    10.             self.ngrams[key] = int(count)
    11.         self.L = len(key)
    12.         self.N = sum(self.ngrams.values())
    13.         #calculate log probabilities
    14.         for key in self.ngrams.keys():
    15.             self.ngrams[key] = log10(float(self.ngrams[key])/self.N)
    16.         self.floor = log10(0.01/self.N)
    17.     def score(self,text):# compute the score of text
    18.         score = 0
    19.         ngrams = self.ngrams.__getitem__
    20.         for i in range(len(text)-self.L+1):
    21.             if text[i:i+self.L] in self.ngrams: score += ngrams(text[i:i+self.L])
    22.             else: score += self.floor    
    23.         return score
    24.  
    25. def findModInverse(a, m):#Возвращает модульное обращение для (а % m),
    26.     # число х при котором (а * х) % m = 1
    27.     # если gcd(a, m) != 1 значит модульное обращение отсутствует и
    28.     # а и m невзаимнопростые числа
    29.     # Используем расширенный алгоритм Евклида
    30.     u1, u2, u3 = 1, 0, a
    31.     v1, v2, v3 = 0, 1, m
    32.     while v3 != 0:
    33.         q = u3 // v3
    34.         v1,v2,v3,u1,u2,u3=(u1 - q * v1),(u2 - q * v2),(u3 - q * v3),v1,v2,v3
    35.     return u1 % m
    36. def gcd(a, b): # Возвращает НОД чисел а и b, используя алгоритм Эвклида
    37.     while a != 0:
    38.         a, b = b % a, a
    39.     return b
    40. def remove_punctuation(text,filter='[^A-Z ]'):
    41.         return re.sub(filter,'',text.upper())
    42. class Affine(object):
    43.     def __init__(self,a,b):
    44.         self.a = a
    45.         self.b = b
    46.         self.inva = -1
    47.         for i in range(1,len(SYMBOLS),2):
    48.             if (self.a*i) % len(SYMBOLS) != 1:
    49.                 continue
    50.             else:
    51.                 self.inva = i
    52.         #assert 0 <= self.inva <= (len(SYMBOLS)-1), 'invalid key: a='+str(a)+', no inverse exists (mod len(SYMBOLS))'      
    53.  
    54.     def decipher(self,string):  
    55.         ret = ''
    56.         modInverseOfKeyA = findModInverse(self.a, len(SYMBOLS))
    57.         for symbol in string:
    58.             symbolIndex = SYMBOLS.find(symbol)
    59.             ret += SYMBOLS[(symbolIndex - self.b)*modInverseOfKeyA % len(SYMBOLS)]
    60.         return ret
    61.  
    62.     def encipher(self,string):
    63.         ret = ''
    64.         for symbol in string.upper():
    65.             symbolIndex = SYMBOLS.find(symbol)
    66.             ret += SYMBOLS[(symbolIndex * self.a + self.b) % len(SYMBOLS)]
    67.         return ret
    68.  
    69. def main():
    70.     plaintext = 'The cipher is less secure than a substitution cipher'
    71.     aff = Affine(17,5)
    72.     print('Исходный текст:\n',plaintext)
    73.     plaintext = remove_punctuation(plaintext.upper())
    74.     ciphertext = aff.encipher(plaintext)
    75.     print('\nЗашифрованный текст:\n',ciphertext)
    76.     plaintext = aff.decipher(ciphertext)
    77.     print('\nРасшифрованный текст:\n',plaintext)
    78.     print('\nВзламываю...')
    79.     fitness = ngram_score('english_quadgrams.txt') # load our quadgram statistics
    80.     scores = []
    81.     print(' A \u2502 B \u2502'+' '*20+'plaintext'+' '*21+'\u2502fitness')
    82.     print(('\u2500'*3+'\u253C')*2+'\u2500' * 50+'\u253C'+'\u2500'*8)
    83.     x_old = -600.0
    84.     for i in range(len(SYMBOLS)):
    85.         if gcd(i, len(SYMBOLS)) == 1:
    86.             for j in range(0,len(SYMBOLS)-1):
    87.                 text = re.sub('[^A-Z]','',Affine(i,j).decipher(ciphertext))
    88.                 x = fitness.score(text)
    89.                 scores.extend([(x,(i,j))])
    90.                 if x > x_old:
    91.                     print('%2s \u2502%2s \u2502%s\u2502%.2f' % (i,j,Affine(i,j).decipher(ciphertext)[:50],x))
    92.                     x_old = x
    93.     print(('\u2500'*3+'\u2534')*2+'\u2500'*50+'\u2534'+'\u2500'*8)
    94.     max_key = max(scores)
    95.     print('лучшие кандидаты на ключи (a,b) = '+str(max_key[1])+':')
    96.     print('Расшифрованный с помощью этих ключей текст:\n'+Affine(max_key[1][0],max_key[1][1]).decipher(ciphertext))
    97.  
    98. #Если файл affineHacker.py выполняется как программа, а не импортируется как модуль, вызвать функцию main()
    99. if __name__ == '__main__':
    100.     main()
     
    MaKsIm нравится это.
  19. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.229

    Аффинный шифр

    Шифрование/расшифровка/взлом методом полного перебора
    Код (Python):
    1. import re
    2. SYMBOLS = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ \n:.,"?'
    3.  
    4. def gcd(a, b): # Возвращает НОД чисел а и b, используя алгоритм Эвклида
    5.     while a != 0:
    6.         a, b = b % a, a
    7.     return b
    8.  
    9. def findModInverse(a, m):#Возвращает модульное обращение для (а % m),
    10.     # число х при котором (а * х) % m = 1
    11.     # если gcd(a, m) != 1 значит модульное обращение отсутствует и
    12.     # а и m невзаимнопростые числа
    13.     # Используем расширенный алгоритм Евклида
    14.     u1, u2, u3 = 1, 0, a
    15.     v1, v2, v3 = 0, 1, m
    16.     while v3 != 0:
    17.         q = u3 // v3
    18.         v1,v2,v3,u1,u2,u3=(u1 - q * v1),(u2 - q * v2),(u3 - q * v3),v1,v2,v3
    19.     return u1 % m
    20.  
    21. def encryptMessage(keyA, keyB, message):# Зашифровать символ
    22.     text = ''
    23.     for symbol in message.upper():    
    24.         symbolIndex = SYMBOLS.find(symbol)
    25.         text += SYMBOLS[(symbolIndex * keyA + keyB) % len(SYMBOLS)]    
    26.     return text
    27.  
    28. def decryptMessage(keyA, keyB, message):# Дешифровать символ
    29.     text = ''
    30.     modInverseOfKeyA = findModInverse(keyA, len(SYMBOLS))
    31.     for symbol in message:
    32.         symbolIndex = SYMBOLS.find(symbol)    
    33.         text += SYMBOLS[(symbolIndex - keyB)*modInverseOfKeyA % len(SYMBOLS)]    
    34.     return text
    35.  
    36. def remove_punctuation(text,filter='[^A-Z \n:.,"?]'):
    37.         return re.sub(filter,'',text.upper())
    38.  
    39. def loadDictionary():
    40.     dictionaryFile = open('english.txt') # получаем объект файла словаря
    41.     englishWords = {} #создается переменная englishWords в виде пустого словаря
    42.     for word in dictionaryFile.read().split('\n'):
    43.         englishWords[word] = None
    44.     dictionaryFile.close()
    45.     return englishWords
    46. #Строковый метод split() разбивает переданную ему строку по переносам,
    47. #возвращая список из нескольких строк
    48. ENGLISH_WORDS = loadDictionary()
    49. # вызывается функция loadDictionary() и возвращаемый ею словарь сохраняется
    50. # в переменной ENGLISH_WORDS.
    51.  
    52. def getEnglishCount(message):
    53.     message = message.upper() #символы строки преобразуются в верхний регистр
    54.     message = remove_punctuation(message) #с помощью функции removeNonLetters()
    55.     # из строки удаляются все небуквенные символы, числа и знаки препинания
    56.     possibleWords = message.split() #метод split() разбивает строку на слова
    57.     #и сохраняет их в переменной possibleWords
    58.     if possibleWords == []:
    59.         return 0.0 # слова отсутствуют, поэтому возвращаем 0.0.
    60.     matches = 0 #для счетчика mаtсhеs устанавливается нулевое значение
    61.     for word in possibleWords: #В цикле for мы проходим по всем словам в списке
    62.     #possibleWords и проверяем существование каждого из них в словаре ENGLISH_WORDS
    63.         if word in ENGLISH_WORDS:
    64.             matches += 1 #Если слово содержится в словаре, значение счетчика
    65.             #mаtсhеs инкрементируется
    66.     return float(matches)/len(possibleWords)
    67.  
    68. def main():
    69.     keyA, keyB = 17, 5
    70.     with open('Carroll.txt', 'r', encoding='utf-8') as file:
    71.         plaintext = file.read()    
    72.     print('Исходный текст:\n',plaintext)
    73.     key = (keyA,keyB)
    74.     print('\nКлючи (a,b) = ',key)
    75.     plaintext = remove_punctuation(plaintext.upper())
    76.     ciphertext = encryptMessage(keyA, keyB, plaintext)
    77.     print('Текст зашифрованный с этими коэффициентами:\n',ciphertext[:200])
    78.     plaintext = decryptMessage(keyA, keyB, ciphertext)
    79.     print('\nРасшифрованный текст:\n',plaintext[:200])
    80.     print('\nВзламываю шифр методом грубой силы...')
    81.     englishPercentageOld = 25
    82.     for keyA in range(1,len(SYMBOLS)):
    83.         if gcd(keyA,len(SYMBOLS)) == 1:
    84.             for keyB in range(len(SYMBOLS)):
    85.                 text = decryptMessage(keyA, keyB, ciphertext)
    86.                 englishPercentage = round(getEnglishCount(text) * 100, 2)
    87.                 if englishPercentage > englishPercentageOld:
    88.                     print('Процент английских слов: %s%% КлючA #%2d КлючB #%2d: \n%s' % (englishPercentage, keyA, keyB, text))
    89.                     englishPercentageOld = englishPercentage
    90.     print('\nВзлом завершен')
    91.  
    92. if __name__ == '__main__':
    93.     main()
     
    Последнее редактирование: 14 июл 2025
    MaKsIm нравится это.
  20. Mikl___

    Mikl___ Супермодератор Команда форума

    Публикаций:
    14
    Регистрация:
    25 июн 2008
    Сообщения:
    4.229
    Взлом шифра простой замены
    с использованием словарного шаблона

    (начало)​
    Ключ шифра простой замены это перемешанный случайным образом алфавит. Допустим используется открытый текст на английском языке. При количестве букв в латинском алфавите = 26 количество возможных ключей для шифрования такого текста будет равно [math]26! \approx 40.329 \times 10^{25}[/math]
    Шифр простой замены невозможно взломать методом грубой силы из-за слишком большого количества возможных ключей. Для взлома такого шифра потребуется более сложный алгоритм, в котором для поиска возможных вариантов обратного преобразования букв зашифрованного текста в открытый текст используется словарь. Предварительный анализ шифротекста может значительно сократить количество потенциальных ключей, подлежащих тестированию, а иногда даже определить полный или частичный ключ.
    Предположим, что открытый текст состоит из английских слов, которые можно найти в англо-русском словаре. Шифротекст не будет содержать подобных слов, но в нем все равно будут встречаться группы букв, разделенных пробелами, подобно словам в предложениях. В отношении таких групп употребляют термин шифрослово. В шифре простой замены каждой букве алфавита соответствует ровно одна уникальная зашифрованная буква. Такие буквы называют шифробуквами. Каждая буква исходного текста шифруется одной конкретной шифробуквой, в исходном тексте и шифротексте будут встречаться одни и те же шаблоны слов.
    Открытому тексту "MISSISSIPPI SPILL" (Наводнение на Миссисипи) может соответствовать шифротекст "RJBBJBBJXXJ BXJHH". В открытом тексте и шифротексте первое и второе слово содержат одинаковое количество букв. И открытый текст, и шифротекст характеризуются одним и тем же шаблоном (закономерностью) расположения букв и пробелов. Повторяющиеся буквы сообщения встречаются в том же количестве и в тех же позициях шифротекста.
    Можно предположить, что каждому шифрослову соответствует слово, входящее в словарь английского языка, и их шаблоны должны совпадать. Если найдем в словаре слово, в которое дешифруется данное шифрослово, то это указало бы нам, как дешифровать каждую его шифробукву. Если с помощью данной методики удастся восстановить достаточное число шифробукв, тогда можно полностью дешифровать сообщение.

    Поиск ша6понов слов

    В качестве примера исследуем шаблон шифрослова "HGHHU". В нем видны определенные закономерности, которые должны быть свойственны и соответствующему слову в исходном тексте. Оба слова должны иметь следующие общие характеристики:
    1. длина слова — пять букв
    2. первая, третья, четвертая буквы слова — одинаковы
    3. слово содержит три разные буквы: первую, вторую и пятую
    Какие английские слова укладываются в этот шаблон? Одно из слов, удовлетворяющих описанному критерию, — слово рuрру, три буквы разные ('Р', 'U', 'Y'), их позиции соответствуют шаблону ('Р' — первая, третья и четвертая, 'U' — вторая и 'Y' — пятая). Слова mommy, bobby, lulls и nanny тоже подпадают под это описание. Все они, также как и любое другое слово из файла словаря, удовлетворяющее заданным критериям, являются возможными вариантами дешифрования слова "HGHHU".
    Чтобы представить шаблон слова в форме, которую можно обработать программой, преобразуем шаблон в набор чисел, разделенных точками. Эти числа указывают на повторяемость букв в шаблоне.
    Как составляется шаблон слова? Первой букве ставится в соответствие число, первому вхождению любой другой буквы — следующее число, на единицу превышающее предыдущее использованное. Шаблон "cat" — 0.1.2, шаблон "classification" — 0.1.2.3.3.4.5.4.0.2.6.4.7.8.
    В случае шифра простой замены не имеет значения, какой именно ключ используется для шифрования: слово исходного текста и соответствующее ему шифрослово всегда соответствуют одному и тому же шаблону. Если шаблон шифрослова "HGHHU" — 0.1.0.0.2, значит, исходное слово имеет такой же шаблон.

    Поиск возможных вариантов дешифровано 6укв

    Чтобы дешифровать шифрослово "HGHHU", в файле словаря нужно найти все слова, соответствующие шаблону 0.1.0.0.2. Такие слова называют кандидатами для данного шифрослова. Список кандидатов для шаблона "HGHHU" выглядит так:
    • рuррy
    • mommy
    • bobby
    • lulls
    • nanny
    Используя шаблоны слов, можно выдвигать предположения относительно того, в какие буквы исходного текста могут дешифровываться шифробуквы. Такие буквы называют вариантами дешифрования для данной шифробуквы. Чтобы взломать сообщение, зашифрованное с помощью шифра простой замены, нужно найти варианты дешифрования для всех шифробукв, а затем определить фактические буквы исходного текста, действуя методом исключения. В таблице приведены варианты дешифрования шаблона "HGHHU".
    ШифробуквыHGHHU
    Варианты дешифрованияPUPPY
    MOMMY
    BOBBY
    LULLS
    NANNY
    Выводы;
    1. 'Н' имеет следующие варианты дешифрования: 'Р', 'М', 'В', 'L', 'N'
    2. 'G' имеет следующие варианты дешифрования: 'U', 'О', 'А'
    3. 'U' имеет следующие варианты дешифрования: 'Y', 'S'
    4. Для остальных шифробукв нет вариантов дешифрования
    В результате формируется дешифровальный словарь, в котором буквам алфавита ставятся в соответствие потенциальные варианты дешифрования. По мере накопления зашифрованных сообщений находят варианты дешифрования для каждой буквы алфавита, в данном примере наш шифротекст включает шифробуквы 'Н', 'G' и 'U', а варианты дешифрования для остальных шифробукв не определены. Буква 'U' имеет два варианта дешифрования ('Y' и 'S') из-за перекрывания слов-кандидатов, многие из которых заканчиваются буквой 'Y'. Чем больше перекрывание, тем меньше вариантов дешифрования, тем легче вычислить, в какую букву должна быть дешифрована шифробуква. Чтобы перевести таблицу на Python, представим дешифровальный словарь значениями словарного типа (пары "ключ: значение")
    Код (Python):
    1. {'А':[],'В':[],'С':[],'D':[],'Е':[],'F':[],'G':['U','O','А'],
    2. 'Н':['Р','М','В','L','N'], 'I':[], 'J':[],'К':[],'L':[],
    3. 'М':[],'N':[],'О':[],'Р':[],'Q':[],'R':[],'S':[],'Т':[],
    4. 'U':['Y','S'],'V':[],'W':[],'Х':[],'Y':[],'Z':[])
    словарь включает 26 пар "ключ: значение": по одному ключу для каждой буквы алфавита и список вариантов дешифрования для каждой буквы. В словаре содержатся варианты дешифрования для букв 'Н', 'G' и 'U'. Всем остальным ключам соответствуют значения в виде пустых списков, [], так как варианты дешифрования для них пока не определены.
    Если удастся свести количество вариантов дешифрования для некоторой шифробуквы всего лишь к одной букве за счет перекрывания дешифровальных словарей, тогда можно расшифровать данную букву. Даже если не удастся добиться этого для всех 26 шифробукв, может оказаться так, что, взломав значительную часть шифробукв, будем в состоянии расшифровать большую часть шифротекста.

    Обзор процесса взлома

    Шифр простой замены можно взломать, используя шаблоны слов. Основные этапы процедуры взлома можно сформулировать следующим образом.
    1. Определить шаблоны для всех шифрослов в тексте
    2. Найти слова-кандидаты, в которые может быть расшифровано каждое шифрослово.
    3. Для каждого шифрослова создать словарь, представляющий варианты дешифрования для каждой шифробуквы
    4. Объединить словари в единую структуру, которую назовем пересечением словарей
    5. Удалить из пересечения буквы, для которых удалось установить, каким шифробуквам они соответствуют
    6. Дешифровать шифротекст с помощью установленных шифробукв. Чем больше слов в шифротексте, тем выше вероятность перекрывания словарей и тем меньше остается вариантов дешифрования для каждой шифробуквы. Чем длиннее сообщение, зашифрованное с помощью шифра простой замены, тем легче его взломать.
    Как можно упростить выполнение первых двух этапов процедуры взлома. Используем файл словаря. Потребуется вспомогательный модуль wordPatterns.py для получения упорядоченного списка шаблонов по каждому слову из словаря.

    Модуль WordPatterns

    Для получения шаблонов слов для каждого слова в словаре dictioпary. txt, загрузим файл makeWordPatterns.py. Убедитесь, что оба файла находятся в той же папке, что и программа simpleSubHacker.py.
    Программа makeWordPatterns.py содержит функцию getWordPattern(), которая получает строку ('puppy') и возвращает ее шаблон ('0.1.0.0.2'). После запуска программы, будет создан модуль Python wordPatterns.py, который включает единственную инструкцию присваивания, занимающую более 43 тысяч строк.
    00.png
    Переменная allPatterns содержит словарь, в котором ключами являются строки шаблонов слов, а значениями — английские слова, соответствующие данному шаблону. Чтобы найти все cлoвa, соответствующие шаблону 0.1.2.1.3.4.5.4.6.7.8, введите в интерактивной оболочке следующие инструкции.
    00.png
    В словаре allPatterns значением ключа '0.1.2.1.3.4.5.4.6.7.8' является список ['BENEFICIARY', 'HOMOGENEITY', 'MOTORCYCLES'], который содержит три слова, соответствующих данному шаблону.
    Код (Python):
    1. import pprint
    2. def getWordPattern(word):
    3.     # Возвращает строку шаблона заданного слова.
    4.     # например '0.1.2.3.4.1.2.3.5.6' для слова 'DUSTBUSTER'
    5.     word = word.upper()
    6.     nextNum = 0
    7.     letterNums = {}
    8.     wordPattern = []
    9.     for letter in word:
    10.         if letter not in letterNums:
    11.             letterNums[letter] = str(nextNum)
    12.             nextNum += 1
    13.         wordPattern.append(letterNums[letter])
    14.     return '.'.join(wordPattern)
    15. def main():
    16.     allPatterns = {}
    17.     with open('dictionary.txt',encoding='utf-8') as file:
    18.         wordList = file.read().split('\n')
    19.     for word in wordList:
    20.         # Получаем шаблон каждого слова в wordList:
    21.         pattern = getWordPattern(word)
    22.         if pattern not in allPatterns:
    23.             allPatterns[pattern] = [word]
    24.         else:
    25.             allPatterns[pattern].append(word)
    26.     # Это код, который пишет код. Файл wordPatterns.py содержит один очень,
    27.     # очень большой оператор присваивания
    28.     with open('wordPatterns.py', 'w',encoding='utf-8') as file:
    29.         file.write('allPatterns = ')
    30.         file.write(pprint.pformat(allPatterns))
    31.     print('Файл wordPatterns.py сформирован')
    32. if __name__ == '__main__':
    33.     main()
    Продолжение здесь...​
     

    Вложения:

    • dictionary.txt
      Размер файла:
      443,4 КБ
      Просмотров:
      231
    Последнее редактирование: 23 июл 2025
    MaKsIm и q2e74 нравится это.