Enigma — шифрование текста
Enigma может в три касания зашифровать текст.
Enigma есть и для iOS и для компьютеров Mac.
Договоритесь с собеседником о Вашем «Ключе» шифрования, (например число «563»), и он сможет расшифровать Ваше сообщение.
Приложение Enigma может быть использовано для шифрования данных представляющих ценность только в режиме ключа «Кодовое слово», подобрать ключ в этом случае гораздо сложнее чем когда ключ состоит из трёх цифр.
* в качестве ключа шифрования можно указывать дату и время;
* в тексте могут быть любые символы включая смайлики;
* длина шифра не соответствует длине шифруемого сообщения;
* не имея специальной подготовки и средств взломать шифр практически невозможно;
* полученный шифр можно зашифровать еще раз.
Последнее обновление
4 окт. 2017 г.
Инструменты
Безопасность данных
В этом разделе разработчики могут указать, как приложения собирают и используют данные. Подробнее о безопасности данных…
Нет информации.
Оценки и отзывы
Оценки и отзывы проверены info_outline
arrow_forward
Оценки и отзывы проверены info_outline
phone_android Телефон
Сергей Русанов
more_vert
- Пожаловаться
24 сентября 2020 г.
Приложение классное только интересно ,на сколько оно надёжно ,, и сложность расшифровки для сторонних квалифицированных личностей ?
Программная реализация шифровальной машины «Энигма» на языке Си
В своё время для преподавания, в промежутке между двумя предметами ОАиП (Основы алгоритмизации и программирования) и КСЗИ (Криптографические средства защиты информации), я реализовывал шифровальную машину «Энигма», чтобы показать как можно применять: 1) программирование для изучения криптографии; 2) криптографию для изучения программирования. В определённой мере, реализация классических шифров очень хорошо может прокачивать не только понимание внутреннего механизма работы того или другого алгоритм шифрования, но также и навыки самого программирования, потому как реализация любого шифра — это в первую очередь алгоритмическая задача.
В данной работе не будет исторического введения в понимание того, что представляет собой шифровальная машина «Энигма» в довоенное и в военное время, как она модифицировалась, а также как она взламывалась. На всё это существует большое количество написанных работ как на хабре, так и в книгах. Для людей, кому интересно всё вышеописанное я оставлю список литературы, с которым вы можете ознакомиться.
Механизмы шифрования
Шифровальная машина «Энигма» внешне выглядит как печатающая машинка, за исключением того факта, что шифруемые символы не печатаются автоматически на определённый лист бумаги, а указываются на панели посредством загорания лампочки.

Шифровальная машина «Энигма» обладает тремя основными механизмами.
- Роторы. Сердце всех шифровальных машин того времени. Со стороны классической криптографии они реализуют полиалфавитный алгоритм шифрования, а их определённо выстроенная позиция представляет собой один из основных ключей шифрования. Каждый ротор не эквивалентен другому ротору, потому как обладает своей специфичной настройкой. Военным на выбор давалось пять роторов, три из которых они вставляли в «Энигму». Выбор позиций, для вставки роторов, также играл свою роль, потому как образовывал свойство некоммутативности. Иными словами если существует настройка трёх роторов равная (A, B, C), то её замена на настройку (B, A, C) будет приводить к иному результату шифрования. Это говорит о том, что существовало (5!) / (2!) = 60 комбинаций выбора и подстановки. Далее, каждый ротор обладал 26-тью гранями, где каждая грань представляла собой нумерацию английского алфавита. Выбор одной определённой грани из 26-ти также представляло собой инициализацию ключа шифрования. Т.к. в сумме у нас находится три ротора, то всевозможных вариаций ключа будет существовать 26³ = 17 576. Итого, если учитывать все факторы выбора, подстановки и установки, то количество всевозможных ключей только на основе роторов будет равно ((5!) / (2!)) * (26³) = 60 * 17 576 = 1 054 560. Чисто технически, для того времени это количество комбинаций уже можно было эффективно взламывать, нанимая большое количество людей из третьего мира, выплачивая им копейки.

- Рефлектор (отражатель). Статичный механизм, позволяющий шифровальным машинам типа «Энигма» не вводить помимо операции шифрования дополнительную операцию расшифрования. Связано это с тем, что в терминологии классической криптографии рефлектор представляет собой просто частный случай моноалфавитного шифра — парный шифр, особенностью которого является инволютивность шифрования, где функция шифрования E становится равной функции расшифрования D, иными словами E = D. Это приводит к следующим выводам: если существует сообщение M, то справедливы становятся следующие утверждения D(E(M)) = E(E(M)) = M, E(E(E(M))) = E(M) = D(M). Парный шифр крайне примитивен в своей реализации, так допустим у нас существует в алфавите всего 4 символа . Т.к. парный шифр — это разновидность моноалфавитного, то существует всегда и постоянно связь 1к1 для открытого и закрытого символов. Далее, если выбираем связь A=>B, то это говорит о том, что символ A должен зашифровываться символом B. Чтобы у нас появилась инволютивная связь, нам необходимо выбрать теперь символ B и привязать его к A, иными словами создать связь B=>A при шифровании. Вследствие этого применяем свойство транзитивности и получаем: если A=>B и B=>A, то A=B. Также делаем и с оставшимися символами C=D. Теперь дважды шифруя символ A, мы будем неявно применять композицию функций D*E, вследствие чего будем вновь получать символ A.

- Коммутаторы. Своеобразный «множитель» возможных вариаций ключей шифрования. Представляет собой также как и рефлектор парный шифр, но в отличие от последнего является динамическим механизмом, то-есть редактируемым и сменяемым. Коммутаторы, можно их рассматривать как некие кабеля, вставляются в коммутационную панель, на которой изображены символы английского алфавита. Один коммутатор имеет два конца, каждый из которых вставляется в два отверстия коммутационной панели. Связь коммутатора с двумя отверствиями, над которыми изображены символы алфавита и представляет собой связь парного шифра между выбранными двумя символами. Так например, если коммутатор был вставлен в два отверстия (Q, D), то это говорит о том, что Q и D стали парными символами при шифровании. На одну шифровальную машину давалось десять коммутаторов, существовало 26 возможных отверстий в коммутационной панели (количество символов алфавита), один коммутатор одновременно связывал два символа такой панели, то-есть все коммутаторы затрагивают в общей сложности 20 отверстий. Всё это приводит к следующему количеству возможных расстановок коммутаторов на коммутационной панели (26!) / (6! * 10! * 2 10 ) = 150 738 274 937 250 [(постепенный_выбор_всех_букв_алфавита) / (оставшиеся_шесть_неиспользуемых_символов * двадцать_коммутаторов_как_десять_пар * все_десять_пар_коммутативны)].

Итого, учитывая все вышеописанные механизмы, а точнее роторы и коммутаторы, можно вычислить количество всех возможных ключей, которое будет равно [((5!) / (2!)) * (26³)] * [(26!) / (6! * 10! * 2 10 )] = 60 * 17 576 * 150 738 274 937 250 = 158 962 555 217 826 360 000. В суммарной сложности, если не учитывать уязвимость «Энигмы» на невозможность шифрования одного и того же символа самим собой, то вышеуказанное количество ключей будет сопоставимо количеству в битовом представлении ~= 2 67 , что грубо (прям очень грубо) говоря больше, чем количество ключей в шифре DES = 2 56 .
Интересной особенностью здесь скорее является даже не количество всех возможных ключей, что для того и сегодняшнего времени всё равно является крайне большим числом (хоть уже и не надёжным со стороны современной криптографии), а комбинация нескольких подстановочных шифров классической криптографии, успешно сочитающихся между собой. Так например, в «Энигме» существуют роторы, представляющие собой полиалфавитный шифр, который при достаточно длинном ключе и его случайности становится сложновзламываемым. Но роторы сами по себе предлагают очень малое количество всех возможных комбинаций, хоть и являются самыми сложными механизмами в «Энигме». На помощь приходят коммутаторы, которые сами по себе не являются надёжными, потому как представляют собой частный случай более общего моноалфавитного шифра — шифра простой подстановки. Шифр простой подстановки хоть и обладает крайне большим количеством ключей 26! = 403 291 461 126 605 635 584 000 000, тем не менее, очень подвержен атакам частотного криптоанализа и атакам по маске. Но так или иначе, «Энигма» показала, что если объединить два этих шифра воедино, то может получиться достаточно хороший синтез, при котором наследуется сложность полиалфавитного шифра и количество ключей моноалфавитного.
Принцип работы
Для упрощения понимания работы шифровальной машины «Энигма» предположим, что у нас существует алфавит всего из восьми символов = A, B, C, D, E, F, G, H>. Из этого следует, что сами роторы, рефлектор и соответственно коммутаторы ограничены этим же размером. Первое, что нам необходимо сделать — это спроектировать рефлектор и роторы под 8-ми символьный алфавит, как самую базовую основу шифровальной машины «Энигма».
Предположим, что рефлектор имеет следующие связи символов между собой: A=C, B=E, D=H, F=G. Если бы количество символов алфавита было бы нечётным, то шифровальную машину «Энигма» нельзя было бы спроектировать таким образом, каким её проектировали изначально авторы, потому как возникала бы возможность шифрования одного и того же символа самим собой за счёт оставшейся связи рефлектора X=X. Визуально, рефлектор можно изобразить в виде следующей схемы.

Работа рефлектора крайне проста. Если на вход поступает символ A, то рефлектор возвратит нам символ C, если поступает символ H, то рефлектор вернёт символ D и т.д.
Далее, нам необходимо точно таким же образом составить роторы. В отличие от рефлектора, где значения всегда статичны, в роторах значения постоянно меняются. Можно сказать, что каждый ротор представляет собой два диска, где один находится всегда в статичном положении, а другой вращается либо при нажатии символов, либо при совершении полного круга одного из роторов. Для начала создадим один ротор и проведём его к рефлектору. Результат будет выглядить следующим образом.

Левая часть ротора для нас будет являться статичной, в то время как правая часть динамична (P.S. наверное лучше было сделать правую часть всё же статичной, а левую динамичной для простоты понимания ввода символов в ротор, тем не менее, механику и получаемый результат такая инверсия никак не изменяет). Ротор — это полиалфавитный шифр, который меняет свой ключ шифрования после каждого введённого символа. Так например, в нашем случае, если мы будем шифровать комбинацию символов AAA, то мы будем получать на выходе ротора результат равный GHA (БЕЗ УЧЁТА РЕФЛЕКТОРА), с условием того, что динамическая часть ротора будет вращаться всегда вниз после нажатого символа.
Теперь, если мы также попытаемся зашифровать комбинацию символов AAA, но пропустим её не только через один ротор, но и последовательно через рефлектор, то получим уже следующий результат: CBE. Суть алгоритма можно представить на примере шифрования первого символа A. Выходом ротора для A будет символ G, символ G попадает на рефлектор и отражается в символ F (потому как таковой является парным), после уже рефлектор подаёт на вход ротору (но в обратном направлении) символ F, который на выходе ротора преобразуется в символ C. Таким образом, символ A шифруется символом C.
На этом же примере мы можем легко проверить, что символ A никогда не зашифруется самим собой, потому как у рефлектора не существует парного символа к самому себе. Для того, чтобы это проверить, достаточно зашифровать 8 раз символ A. На выходе мы будем получать следующий результат: CBEHGBDF (тут главное не ошибиться в количестве сдвигов ротора). Далее, если мы продолжим шифровать символ A, то придём к цикличности шифруемой последовательности, потому как у одного ротора существует только 8 возможных позиций.
Теперь же давайте добавим ещё два ротора с другими вариациями, поставим их последовательно, и проведём их композицию также к рефлектору.

Теперь, предположим, что я начинаю шифровать какой-либо символ, тогда по механике «Энигмы» сдвинется только самый ближний ротор к входу, то-есть правый. Как только он совершит свой полный цикл вращения, то на одну позицию повернётся следующий за ним ротор, то-есть средний. Как только средний ротор совершит свой полный цикл, то на одну позицию повернётся следующий за ним ротор, то-есть левый. Такое действие можно продолжать ещё очень долго, но исходя из истории, остановимся только на трёх роторах.
На вышеописанном примере процесс шифрования лучше уже показывать визуально, т.к. система связей значительно усложняется. Так например, если я буду шифровать три раза символ A, то он станет равен сначала символу C, потом символу G, а потом символу E. Это можно показать на следующих схемах.



Теперь, когда мы разобрались с работой роторов и рефлектора, мы можем усложнить работу «Энигмы» коммутаторами и коммутационной панелью соответственно. Коммутаторы начинают использоваться сразу после ввода символа и перед выходом. В аналогии с современной симметричной криптографией такой механизм можно представить как отбеливающую функцию.

Примерно таким образом у нас и работает шифровальная машина «Энигма.
Программная реализация
Как мы увидели, основной сложностью реализации для нас будет являться связь нескольких роторов между собой таким образом, чтобы допускался не только поток шифрования в одну сторону к рефлектору, но и от него обратно к начальному ротору. Коммутационная панель будет являться для нас излишним, добавочным механизмом, а потому таковой вы можете попробывать реализовать сами, дополнив код.
Первым делом создаём структуру «Энигмы», которая будет содержать рефлектор (отражатель), роторы, количество используемых роторов, их размер, а также количество произведённых вращений первого ротора.
typedef struct enigma_t < uint64_t counter; uint8_t size_rotor; uint8_t num_rotors; uint8_t *reflector; uint8_t **rotors; >enigma_t;
Далее объявляем конструктор и деструктор объектов типа enigma_t , а также функции установки рефлектора и роторов.
extern enigma_t *enigma_new(uint8_t size_rotor, uint8_t num_rotors) < enigma_t *enigma = (enigma_t*)malloc(sizeof(enigma_t)); if (enigma == NULL) < return NULL ; >enigma->size_rotor = size_rotor; enigma->num_rotors = num_rotors; enigma->counter = 0; enigma->reflector = (uint8_t*)malloc(sizeof(uint8_t)*size_rotor); enigma->rotors = (uint8_t**)malloc(sizeof(uint8_t*)*num_rotors); for (int i = 0; i < num_rotors; ++i) < enigma->rotors[i] = (uint8_t*)malloc(sizeof(uint8_t)*size_rotor); > return enigma; > extern void enigma_free(enigma_t *enigma) < for (int i = 0; i < enigma->num_rotors; ++i) < free(enigma->rotors[i]) ; > free(enigma->rotors); free(enigma->reflector); free(enigma); > extern void enigma_set_reflector(enigma_t *enigma, uint8_t *reflector) < for (int i = 0; i < enigma->size_rotor; ++i) < enigma->reflector[i] = reflector[i]; > > extern void enigma_set_rotor(enigma_t *enigma, uint8_t num, uint8_t *rotor) < for (int i = 0; i < enigma->size_rotor; ++i) < enigma->rotors[num][i] = rotor[i]; > >
Теперь приступим к кульминационному моменту, а именно шифрованию. Мы знаем, что особенностью «Энигмы» является не только её «машинная среда», но также и инволютивность шифрования/расшифрования. Иными словами, в «Энигме» не существует переключателей между шифрованием и расшифрованием, потому как шифрование = расшифрование. Единственным разделяющим моментом здесь служат позиции роторов и их настройка.
// На вход подаётся состояние «Энигмы» (enigma) и шифруемое число (code). // На выходе получаем зашифрованное число (code) и успешность выполнения (valid). // code - это закодированный символ вида A=0, B=1, C=2, . X=23, Y=24, Z=25. extern uint8_t enigma_encrypt(enigma_t *enigma, uint8_t code, int *valid) < uint64_t rotor_queue; uint8_t new_code; // Проверяем, что code принадлежит диапазону алфавита от 0 до 25 включительно. // Иначе свидетельствуем об ошибке выполнения. if (code >enigma->size_rotor) < *valid = 0; return 0; >// Используем переменную, которая будет являться состоянием // шифруемого символа. new_code = code; // Проходим сквозь все роторы в сторону рефлектора, постоянно обновляя состояние символа. // Символ в такой концепции представляет собой индекс массива, по которому находим новый символ как значение массива. for (int i = 0; i < enigma->num_rotors; ++i) < new_code = enigma->rotors[i][new_code]; > // Получаем новое состояние символа от рефлектора. new_code = enigma->reflector[new_code]; // Теперь проходим сквозь все роторы, но уже от рефлектора, также постоянно обновляя состояние символа. // Символ в такой концепции представляет собой значение массива, по которому находим новый символ как индекс массива. for (int i = enigma->num_rotors-1; i >= 0; --i) < new_code = enigma_rotor_find(enigma, i, new_code, valid); if (*valid == 0) < return 0; >> // После того, как прошли по роторам в сторону рефлектора и обратно // настало время обновить состояние самих роторов, их позицию. // Увеличиваем счётчик шифрованных символов и устанавливаем // по умолчанию очередь ротора = 1. // Это говорит о том, что первый ротор должен обновлять своё состояние // каждый введённый символ. rotor_queue = 1; enigma->counter += 1; for (int i = 0; i < enigma->num_rotors; ++i) < // Обновляем состояние ротора по его очереди. if (enigma->counter % rotor_queue == 0) < enigma_rotor_shift(enigma, i); >// Увеличиваем очередь каждого последующего ротора, т.к. // поворот одного ротора должен производиться только после полного // цикла предыдущего ротора. rotor_queue *= enigma->size_rotor; > // Возвращаем шифрованный символ. *valid = 1; return new_code; > // Функция обычного сдвига массива на одну единицу вправо. static void enigma_rotor_shift(enigma_t *enigma, uint8_t num) < char temp = enigma->rotors[num][enigma->size_rotor-1]; for (int i = enigma->size_rotor-1; i > 0; --i) < enigma->rotors[num][i] = enigma->rotors[num][i-1]; > enigma->rotors[num][0] = temp; > // Функция обычного нахождения элемента в массиве. static uint8_t enigma_rotor_find(enigma_t *enigma, uint8_t num, uint8_t code, int *valid) < for (int i = 0; i < enigma->size_rotor; ++i) < if (enigma->rotors[num][i] == code) < *valid = 1; return i; >> *valid = 0; return 0; >
Чтобы шифровать не закодированные символы, а конкретно символы, мы можем написать функции кодирования/декодирования поступаемых символов или кодов непосредственно.
typedef struct encoder_t < uint8_t size_alph; uint8_t *alphabet; >encoder_t; extern encoder_t *encoder_new(uint8_t size_alph) < encoder_t *encoder = (encoder_t*)malloc(sizeof(encoder_t)); if (encoder == NULL) < return NULL; >encoder->size_alph = size_alph; encoder->alphabet = (uint8_t*)malloc(sizeof(uint8_t)*size_alph); return encoder; > extern void encoder_free(encoder_t *encoder) < free(encoder->alphabet); free(encoder); > extern void encoder_set_alphabet(encoder_t *encoder, uint8_t *alphabet) < for (int i = 0; i < encoder->size_alph; ++i) < encoder->alphabet[i] = alphabet[i]; > > extern uint8_t encoder_encode(encoder_t *encoder, uint8_t ch, int *found) < for (int i = 0; i < encoder->size_alph; ++i) < if (encoder->alphabet[i] == ch) < *found = 1; return i; >> *found = 0; return 0; > extern uint8_t encoder_decode(encoder_t *encoder, uint8_t code, int *valid) < if (code >= encoder->size_alph) < *valid = 0; return 0; >*valid = 1; return encoder->alphabet[code]; >
Далее, всё что нам остаётся сделать — это создать конкретные роторы и рефлектор, привязать их к объекту «Энигмы», создать объект кодировщика, а также для полноты реализовать ввод текста.
Установка роторов и рефлектора в «Энигму»
uint8_t num_rotors = 3; uint8_t reflector[] = ; uint8_t *rotors[] = < (uint8_t[]), (uint8_t[]), (uint8_t[]), >; enigma_t *enigma = enigma_new(size_alph, num_rotors); enigma_set_reflector(enigma, reflector); for (int i = 0; i < num_rotors; ++i) < enigma_set_rotor(enigma, i, rotors[i]); >. // Не забываем освободить память объекта «Энигмы» после выполнения всех действий. enigma_free(enigma);
Создание кодировщика
uint8_t alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; uint8_t size_alph = (uint8_t)strlen((char*)alphabet); encoder_t *encoder = encoder_new(size_alph); encoder_set_alphabet(encoder, alphabet); . // Не забываем освободить память объекта кодировщика после выполнения всех действий. encoder_free(enigma);
uint8_t enc_ch, dec_ch; int ch, flag; while(1) < ch = getchar(); // Остановить шифрование после введённого символа ~ if (ch == '~') < putchar('\n'); break; >// Кодируем символ в числовое представление вида A=0, B=1, C=2, . enc_ch = encoder_encode(encoder, (uint8_t)ch, &flag); if (flag == 0) < // Если не получается закодировать ->просто выводим этот символ. putchar(ch); continue; > // Шифруем код символа. enc_ch = enigma_encrypt(enigma, enc_ch, &flag); if (flag == 0) < fprintf(stderr, "\nencoder put to enigma unknown code (%d)\n", enc_ch); break; >// Декодируем код шифрованного символа. dec_ch = encoder_decode(encoder, enc_ch, &flag); if (flag == 0) < fprintf(stderr, "\nenigma put to decoder unknown code (%d)\n", enc_ch); break; >// Выводим шифрованный символ. putchar(dec_ch); >
Запускаем приложение и проверяем его работу на примере шифрования.
> AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA FWYMCEMOQVXWWDFEEOKMOVXZBDWYMCEMOQVXWWDFEEOKMOVXZBDFYMCEMOQVXWWDFEEOKMOVXZBDFWMCEMOQVXWWDFEEOKMOVXZBDFWYCEMO
Теперь попытаемся расшифровать полученный результат. Для этого останавливаем работу приложения и вновь его запускаем, чтобы состояние машины «Энигма» выставилась на начальные позиции.
> FWYMCEMOQVXWWDFEEOKMOVXZBDWYMCEMOQVXWWDFEEOKMOVXZBDFYMCEMOQVXWWDFEEOKMOVXZBDFWMCEMOQVXWWDFEEOKMOVXZBDFWYCEMO AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Можем теперь зашифровать и что-то более нормальное.
> HELLOWORLD TDZVSRYVKP
Заключение
В ходе проделанной работы мы рассмотрели не только то, как шифровальная машина «Энигма» работает изнутри, но и также смогли реализовать её на языке программирования Си. В коде мы только не затронули использование коммутаторов, но это крайне легко сделать, дополнив код функциями шифрования до вызова функции enigma_encrypt и соответственно после (либо, что более правильно, прописать это внутри самой функции enigma_encrypt ).
Полный исходный код шифровальной машины «Энигма»
#include #include #include #include // ENCODER typedef struct encoder_t < uint8_t size_alph; uint8_t *alphabet; >encoder_t; extern encoder_t *encoder_new(uint8_t size_alph); extern void encoder_free(encoder_t *encoder); extern void encoder_set_alphabet(encoder_t *encoder, uint8_t *alphabet); extern uint8_t encoder_encode(encoder_t *encoder, uint8_t ch, int *found); extern uint8_t encoder_decode(encoder_t *encoder, uint8_t code, int *valid); // ENIGMA typedef struct enigma_t < uint64_t counter; uint8_t size_rotor; uint8_t num_rotors; uint8_t *reflector; uint8_t **rotors; >enigma_t; extern enigma_t *enigma_new(uint8_t size_rotor, uint8_t num_rotors); extern void enigma_free(enigma_t *enigma); extern void enigma_set_reflector(enigma_t *enigma, uint8_t *reflector); extern void enigma_set_rotor(enigma_t *enigma, uint8_t num, uint8_t *rotor); extern uint8_t enigma_encrypt(enigma_t *enigma, uint8_t code, int *valid); static void enigma_rotor_shift(enigma_t *enigma, uint8_t num); static uint8_t enigma_rotor_find(enigma_t *enigma, uint8_t num, uint8_t code, int *valid); int main(void) < // INIT ENCODER uint8_t alphabet[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"; uint8_t size_alph = (uint8_t)strlen((char*)alphabet); encoder_t *encoder = encoder_new(size_alph); encoder_set_alphabet(encoder, alphabet); // INIT ENIGMA uint8_t num_rotors = 3; uint8_t reflector[] = ; uint8_t *rotors[] = < (uint8_t[]), (uint8_t[]), (uint8_t[]), >; enigma_t *enigma = enigma_new(size_alph, num_rotors); enigma_set_reflector(enigma, reflector); for (int i = 0; i < num_rotors; ++i) < enigma_set_rotor(enigma, i, rotors[i]); >// INPUT CHARS uint8_t enc_ch, dec_ch; int ch, flag; while(1) < ch = getchar(); // STOP IF CHAR = '~' if (ch == '~') < putchar('\n'); break; >// ENCODE enc_ch = encoder_encode(encoder, (uint8_t)ch, &flag); if (flag == 0) < // just print unknown char putchar(ch); continue; >// ENCRYPT/DECRYPT enc_ch = enigma_encrypt(enigma, enc_ch, &flag); if (flag == 0) < fprintf(stderr, "\nencoder put to enigma unknown code (%d)\n", enc_ch); break; >// DECODE dec_ch = encoder_decode(encoder, enc_ch, &flag); if (flag == 0) < fprintf(stderr, "\nenigma put to decoder unknown code (%d)\n", enc_ch); break; >putchar(dec_ch); > // FREE ENCODER AND ENIGMA encoder_free(encoder); enigma_free(enigma); return 0; > // ENCODER extern encoder_t *encoder_new(uint8_t size_alph) < encoder_t *encoder = (encoder_t*)malloc(sizeof(encoder_t)); if (encoder == NULL) < return NULL; >encoder->size_alph = size_alph; encoder->alphabet = (uint8_t*)malloc(sizeof(uint8_t)*size_alph); return encoder; > extern void encoder_free(encoder_t *encoder) < free(encoder->alphabet); free(encoder); > extern void encoder_set_alphabet(encoder_t *encoder, uint8_t *alphabet) < for (int i = 0; i < encoder->size_alph; ++i) < encoder->alphabet[i] = alphabet[i]; > > extern uint8_t encoder_encode(encoder_t *encoder, uint8_t ch, int *found) < for (int i = 0; i < encoder->size_alph; ++i) < if (encoder->alphabet[i] == ch) < *found = 1; return i; >> *found = 0; return 0; > extern uint8_t encoder_decode(encoder_t *encoder, uint8_t code, int *valid) < if (code >= encoder->size_alph) < *valid = 0; return 0; >*valid = 1; return encoder->alphabet[code]; > // ENIGMA extern enigma_t *enigma_new(uint8_t size_rotor, uint8_t num_rotors) < enigma_t *enigma = (enigma_t*)malloc(sizeof(enigma_t)); if (enigma == NULL) < return NULL ; >enigma->size_rotor = size_rotor; enigma->num_rotors = num_rotors; enigma->counter = 0; enigma->reflector = (uint8_t*)malloc(sizeof(uint8_t)*size_rotor); enigma->rotors = (uint8_t**)malloc(sizeof(uint8_t*)*num_rotors); for (int i = 0; i < num_rotors; ++i) < enigma->rotors[i] = (uint8_t*)malloc(sizeof(uint8_t)*size_rotor); > return enigma; > extern void enigma_free(enigma_t *enigma) < for (int i = 0; i < enigma->num_rotors; ++i) < free(enigma->rotors[i]) ; > free(enigma->rotors); free(enigma->reflector); free(enigma); > extern void enigma_set_reflector(enigma_t *enigma, uint8_t *reflector) < for (int i = 0; i < enigma->size_rotor; ++i) < enigma->reflector[i] = reflector[i]; > > extern void enigma_set_rotor(enigma_t *enigma, uint8_t num, uint8_t *rotor) < for (int i = 0; i < enigma->size_rotor; ++i) < enigma->rotors[num][i] = rotor[i]; > > extern uint8_t enigma_encrypt(enigma_t *enigma, uint8_t code, int *valid) < uint64_t rotor_queue; uint8_t new_code; if (code >= enigma->size_rotor) < *valid = 0; return 0; >new_code = code; // code -> rotors for (int i = 0; i < enigma->num_rotors; ++i) < new_code = enigma->rotors[i][new_code]; > // code -> reflector new_code = enigma->reflector[new_code]; // reflector -> code // rotors -> code for (int i = enigma->num_rotors-1; i >= 0; --i) < new_code = enigma_rotor_find(enigma, i, new_code, valid); if (*valid == 0) < return 0; >> // shift rotors rotor_queue = 1; enigma->counter += 1; for (int i = 0; i < enigma->num_rotors; ++i) < if (enigma->counter % rotor_queue == 0) < enigma_rotor_shift(enigma, i); >rotor_queue *= enigma->size_rotor; > *valid = 1; return new_code; > static void enigma_rotor_shift(enigma_t *enigma, uint8_t num) < char temp = enigma->rotors[num][enigma->size_rotor-1]; for (int i = enigma->size_rotor-1; i > 0; --i) < enigma->rotors[num][i] = enigma->rotors[num][i-1]; > enigma->rotors[num][0] = temp; > static uint8_t enigma_rotor_find(enigma_t *enigma, uint8_t num, uint8_t code, int *valid) < for (int i = 0; i < enigma->size_rotor; ++i) < if (enigma->rotors[num][i] == code) < *valid = 1; return i; >> *valid = 0; return 0; >
Список литературы
- Тайная история шифров и их расшифровки. С. Сингх
- Основы классической криптологии. М. Адаменко
- 158 962 555 217 826 360 000 (Энигма, русская озвучка). (не совсем литература)
- Немного криптографии: загадочная и легендарная Enigma
- Энигма. Материал из Википедии
- программирование
- криптография
- шифрование
- шифровальная машина
- язык си
- энигма
Алгоритм Энигмы
В данном сообществе я нашел много статей про известную шифровальную машинку «Enigma», но нигде из них не описывался подробный алгоритм ее работы. Наверняка многие скажут, что это не нуждается в афишировании, — я же надеюсь, что кому-нибудь да будет полезно об этом узнать. С чего все началось? Во времена Первой мировой войны большой популярностью пользовался шифр Playfair. Его суть заключалась в том, что буквы латинского алфавита записывались в квадрат 5х5, после чего буквы исходного алфавита разбивались по парам. Далее, используя квадрат в качестве ключа, эти биграммы заменяли на другие по определенному алгоритму. Преимущество данного шифра было в том, что он не требовал дополнительных устройств, и, как правило, к тому моменту, когда сообщение расшифровывали, оно уже теряло актуальность. Еще одним способом тайнописи был шифратор Джефферсона.
Сие устройство состояло из определенного количества дисков, нанизанных на единую ось (обычно, дисков было 36). Каждый из них делился на 26 частей, каждая из которых обозначало букву. Буквы на дисках были расставлены в случайном порядке. Оператор путем вращения дисков набирал нужное сообщение, а затем переписывал другую строчку. Человек, принявший данное сообщение, должен был обладать точно таким же устройством с точно такой же расстановкой букв. И тот и другой способы были относительно неплохими для тех времен, но учитывая, что человечество вступило уже в XX век, возникла необходимость механизации процесса шифрования. В 1920 году голландский изобретатель Александр Кох изобрел первую роторную шифровальную машинку. Затем, на нее получили патент немецкие изобретатели, которые усовершенствовали ее и выпустили в производство, под названием «Enigma» (от греч. – загадка). Таким образом, эта машинка приобреталась многими фирмами, которые желали сохранить в тайне свои переписки. В этом и состояла вся гениальность Энигмы – все знали алгоритм шифрования, но никто не мог подобрать нужный ключ, так как число возможных комбинаций превосходило 15 квадриллионов. Если хотите узнать, каким образом Энигму взламывали, советую прочитать книгу Саймона Сингха «Книга шифров». Подытоживая все вышесказанное, хочу сказать, что шифр Энигмы являлся некой смесью шифратора Джефферсона и шифра Цезаря.
Итак, приступим к изучению алгоритма. На данном сайте имеется очень неплохой симулятор, который в доступной и наглядной форме показывает весь процесс целиком и полностью. Давайте же разберем принцип работы трехроторной Энигмы. В ней имелось три отсека для помещения трех роторов и дополнительный отсек для размещения рефлектора. Всего за время Второй мировой войны было изготовлено восемь роторов и четыре рефлектора, но одновременно могло использоваться ровно столько, на сколько была рассчитана машина. Каждый ротор имел 26 сечений, что соответствовало отдельной букве алфавита, а так же 26 контактов для взаимодействия с соседними роторами. Как только оператор нажимал на нужную букву, — замыкалась электрическая цепь, в результате чего появлялась шифрованная буква. Замыкание цепи происходило за счет рефлектора.
На рисунке представлена иллюстрация нажатия клавиши «А» с последующей дешифрацией в букву «G». После ввода буквы крайний правый ротор перемещался вперед, меняя тем самым ключ. Так каким же образом одна букву заменялась на другую? Как я уже говорил, для Энигмы было разработано восемь различных роторов. Внутри каждого из них было установлено 26 различных коммутаций. Здесь представлена подробная спецификация на каждый из них. Например, если на вход первого ротора поступала буква «N», то на выходе должна быть только «W» и никакая другая буква больше. Попади это буква на второй ротор, она бы уже преобразовалась в «T» и т.д. То есть, каждый ротор выполнял четко поставленную задачу в плане коммуникации. А какую же роль играли кольца? Рассмотрим следующий пример. Установим роторы III, II и I, а порядок колец «C», «U» и «Q».


Нажмем на клавишу «A». Крайний правый ротор повернется вперед на один шаг, то есть, буква «Q» перейдет в «R». Ротор посередине также повернется вперед на букву «V», но об этом я расскажу чуть позже. Итак, наша буква «А» начинает путешествие с первого отсека, в котором установлен ротор I и на котором выставлена уже буква «R». Уже перед тем как попасть на первый ротор буква претерпевает свое первое преобразование, а именно: сложение с буквой «R» по модулю 26. Фактически, это шифр Цезаря. Если пронумеровать все буквы от 0 до 25, то буква «А» будет как раз таки нулевой. Значит, результатом сложения будет буква «R». Далее, мы с вами знаем, что в первом отсеке ротор I, а в его конструкции заложено, что буква «R» всегда переходит в «U». Теперь на очереди второй отсек с ротором II. Опять, перед попаданием на второй ротор, теперь уже буква «U» меняется по несколько иному алгоритму: к ней прибавляется разница значений последующего ротора и предыдущего. Поясню. На втором роторе ожидает нас буква «V», а на предыдущем, — «R», их разница равна четырем буквам, и именно они прибавляются к нашей букве «U». Поэтому, на второй ротор поступает буква «Y». Далее по таблице находим, что во втором роторе букве «Y» соответствует «O». Далее опять смотрим разницу букв «C» и «V», — она равна семи. Значит, букву «O» сдвигаем на семь позиций и получаем «V». В роторе III «V» переходит в «M». Перед тем как попасть на рефлектор, из нашей буквы вычитается буква «C», преображая ее в букву «K». Далее происходит отражение. Если вы заметите, то в каждом роторе образуются большие циклические группы, например: (A – E – L – T – P – H – Q – X – R – U), а в рефлекторе они разбиты по парам: (A — Y)(B — R)(C — U) и т.д. Это сделано для того, чтобы потом это возможно было расшифровать. Предположим, что установлен рефлектор B, в котором «K» заменяется на «N» (и наоборот). Половина пути пройдена. Теперь мы опять прибавляем значение буквы «С», получив тем самым букву «P». Здесь наоборот, в строке третьего ротора находим «P» и смотрим, в при нажатии какой буквы она бы появилась. Это буква «H». Преобразование в третьем роторе закончено. Теперь из этой буквы вычитается разница букв «C» и «V», то есть семь. Получаем букву «A». Во втором роторе она переходит саму в себя, поэтому оставляем ее без изменений. Далее, вычитаем разницу букв «V» и «R», то есть четверку и получаем букву «W». В первом роторе её обратно преобразование отображается в букву «N». Остается только вычесть из нее букву «R» и получим искомую букву «W». Как видите, алгоритм работы машинки оказался не таким сложным каким казался. Для усовершенствования шифра немцы внедрили коммутационную панель, которая позволяла попарно менять местами буквы. Если мы соединим буквы «Q» и «W», то при вводе той же «A» мы получили бы «Q», так как по факту должна быть «W», но она заменена буквой «Q». Вот прилагаемая схема действия.
Осталось лишь рассказать про смещения роторов относительно друг друга. Правый ротор поворачивался всегда при нажатии клавиши на один шаг. Например, для ротора I эта позиция равна букве «R». Именно поэтому в нашем примере второй ротор повернулся: первый ротор прошел через букву «R». Далее, пройдя через определенную позицию, правый ротор приводил в движение левый на один шаг. В более усовершенствованных моделях левый ротор прокручивался два, а то и три раза.
В завершение скажу, что здесь собраны некоторые материалы по взлому Энигмы, которые могут быть полезны. Надеюсь, что данная статья кому-нибудь пригодится.
- Информационная безопасность
- Криптография
Вскрытие шифра Энигма: математика, спасшая мир

Во время Второй мировой войны одно из самых важных достижений в области разведки было связано с шифровальной машиной, известной как «Энигма». Вражеские силы использовали шифр Энигмы для защиты своих коммуникаций и передвижений войск. Но благодаря математикам и их гениальности в период научного прорыва была разработана стратегия, которая спасла мир.
На протяжении многих лет после создания шифра в 1918 году никто не смог взломать устройство Энигмы. Британская разведка понимала необходимость его расшифровки, чтобы получать разведывательные данные и перехватывать вражеские сообщения. Тем временем, математики (Алан Тьюринг и его команда) в Блетчли Парк в Великобритании сформировали специальную группу криптографов с целью вскрытия шифра.
Шифр Энигмы использовал ассортимент механических и электрических компонентов для шифрования сообщений. Ключевым элементом был ротор, меняющий положение на каждую нажатую клавишу. Это создавало множество комбинаций для каждого символа и делало расшифровку невозможной без знания соответствующего ключа на каждый день.
Математики осознали, что нужно усовершенствовать методы расшифровки. С помощью математического анализа и статистических методов исследователи начали распознавать закономерности в зашифрованных сообщениях. Однако непредсказуемое изменение ключей и настройки роторов делало задачу затруднительной.
И вот в 1939 году, когда Великобритания объявила войну Германии, Алан Тьюринг и его команда разработали электромеханическую машину под названием «Бомба». «Бомба» способна была автоматически проверять множество возможных комбинаций роторов и помогала взламывать шифр Энигмы.
Но это было еще не все. Для эффективного использования «Бомбы» необходимо было знать некоторые начальные позиции роторов и настройки. И здесь на сцену выходит команда польских математиков, которая уже до войны столкнулась с проблемами шифра Энигмы. Они разработали метод «перехватчика клавиатуры», с помощью которого удалось получить некоторые ключевые настройки.
Работая в секрете, математики Великобритании и Польши сумели расшифровать защищенные сообщения, передаваемые немецкими войсками. Их труд и научные открытия помогли понять вражеские намерения и принимать стратегические решения, которые повлияли на исход войны.
Расшифровка шифра Энигмы сократила длительность войны и спасла множество жизней. Математики, взломавшие этот сложный криптоанализ, внесли огромный вклад в развитие современной криптографии, а также в область информационной безопасности.
История расшифровки шифра Энигмы — это великое достижение современной математики. Она показывает, как наука и интеллект могут влиять на политическую, военную и геополитическую картину мира.