В 2017 году российской компанией КриптоПро был предложен новый AEAD режим шифрования Multilinear Galois Mode (MGM). Спустя некоторое время он был принят в российские и международные стандарты. Так как в интернете я смог найти только общее описание работы этого режима шифрования без конкретной реализации (ссылки в конце статьи), то я решил попробовать свои силы и реализовать очень упрощённую версию своими руками... Для начала разберёмся со вспомогательными, но важными для понимания вещами. Шифрование - это отображение вида, которое осуществляет преобразование открытого текста P в шифротекст С с иcпользованием ключа шифрования K. Примером такого отображения может служить "шифр Цезаря", о котором можно почитать в Википедии. Режим шифрования - это подход к шифрованию открытого текста P, при котором он разбивается на блоки текста заведомо фиксированной длины. Далее каждый блок шифруется некоторым отображением (ключ К - общий и заранее выбран), а потом они собираются обратно вместе в шифротекст С. AEAD - это класс блочных режимов шифрования, при котором часть сообщения шифруется, часть остается открытой, и всё сообщение целиком аутентифицировано. Иными словами, если, например, проводить аналогию с почтовыми письмами, то внутренность письма вы шифруете (запечатываете), адрес отправителя и адрес получателя оставляете снаружи конверта (не зашифрованными), чтобы почтовые службы смогли корректно обработать ваше письмо, и ещё дополнительно вы делаете приписку, с помощью которой получатель сможет проверить (аутентифицировать), что все данные как внутри, так и снаружи письма были сохранены. Арифметика полей Галуа встречается повсеместно в криптографии, и понимание принципов её работы в программном коде необходимо. Режим шифрования AEAD-MGM от чисел из полей Галуа GF(2n) требует реализации только 2 операций - сложения и умножения. Примеры, которые приводятся дальше в этой части, я взял из вспомогательной статьи. Она рекомендуется к прочтению, чтобы сложить более полное понимание об арифметике полей Галуа GF(2n). Теперь перейдём к реализации. Для начала определим класс чисел в полях Галуа GF(2n): Теперь определим операцию сложения в поле GF(2n), которая по сути своей является обычной побитовой операцией XOR: (операция  в полях GF(2n) также является побитовой операцией XOR) Чтобы определить операцию умножения , нам сначала нужно понять, как отличие в операции  влияет на неё. Из школы нам известен алгоритм умножения в столбик: Как мы видим, операцию  мы здесь применяем для того, чтобы сложить два промежуточных результата от умножения 17 на 5, и 17 на 20. Если представить число 25 как , то становится видно, что операция  используется для сложения результатов умножения числа 17 на числа при разных степенях 10. Если мы применим теперь наши рассуждения для разложений чисел по основанию 2, то результат не изменится.  Теперь, вспоминая что в полях GF(2n)  является XOR, получим Или  Таким образом получаем первую часть нашего метода умножения в полях GF(2n): Замечу, что если мы хотим перемножать числа, которые не помещаются в машинное слово процессора (например, 128 разрядные), то нам стоит немного переписать этот метод. На этот раз мы будем раскладывать не только правое число, но и левое, суммируя по модулю 2 кол-во единиц при одинаковой степени 2. Операции используемые здесь проще в реализации и легче проверяются. Вторая часть операции умножения в полях Галуа заключается в том, чтобы как бы "вместить обратно" число, полученное от MulMod2 в интервал [1, 2n). Для этого вводится специальный порождающий многочлен - число в интервале [2n, 2n + 2n), на которое мы делим результат от MulMod2. Полученное при этом число будет лежать в интервале [1, 2n). Из школьной программы мы знаем, что для обычной арифметики остаток от деления на число m лежит в интервале [0, m). Почему же тогда арифметика в полях Галуа GF(2n) для порождающего многочлена (2n + x), 0 < x < 2n не даёт нам получить остаток (2n + y), 0 < y < 2n? Ответ тут в том, что если бы мы получили остаток (2n + y), то тогда мы бы смогли бы применить следующую операцию Так как 0 < x < 2n и 0 < y < 2n, а операция XOR сохраняет количество разрядов в числе, то z будет принадлежать интервалу [0, 2n). Так как 0 не встречается, то его отбрасываем, получая интервал [1, 2n). По итогу остаток (2n + y) преобразовался в остаток z, который принадлежит интервалу [1, 2n), а это противоречит предположению, что мы можем получить остаток (2n + y) Вспомним школьное деление столбиком Если это переписать в немного другом виде, то получится Остатки всегда должны быть меньше 12 * 10x, а результаты делений 13/12, 15/12, 30/12 должны лежать в интервале [0, 10), так как 10 - основание системы счисления. В двоичной системе счисления происходит всё то же самое: Теперь посмотрим на то же самое деление в столбик, если мы заменим на  Проверим, что полученный нами ответ удовлетворяет MulMod2 Как видно, наш подход к делению оказался верным. Осталось только реализовать это в коде. Для реализации воспользуемся тем свойством алгебры полей Галуа GF(2n), что если есть два числа то мы можем ввести Если сравнить это с делением столбиком, то а - делимое, b - делитель, q - бит, который мы должны записать в частное, - промежуточный результат для следующей итерации... Как только станет меньше b, мы должны вернуть остаток. Итого код получается следующим: Теперь совмещая всё вместе, получим итоговый код для операции умножения в полях Галуа  GF(2n): Теперь, разобравшись со всеми сложными, но важными моментами, мы готовы перейти к процессу шифрования AEAD-MGM... Основной файл с описанием, на который я буду опираться, можно найти здесь: По сути процесс шифрования представляет собой: Генерацию nonce (уникального для каждого обмена сообщениями) Генерацию  и  Генерацию массивов Y, Z, H. Деление входных данных (A и P) на блоки выбранного нами размера (32 бита) Преобразование  и генерацию имитовставки Т Для упрощения реализации в коде будем считать, что P = N блоков по 32 бита информации в каждом A = M блоков по 32 бита информации в каждом Максимальный размер сообщений  Доопределим incr_l, incr_r и ещё несколько вспомогательных функций: Ну и наконец сам алгоритм: Безусловно, режим шифрования AEAD-MGM в такой реализации не покрывает множество тонких моментов, но для понимания идей работы его, я думаю, вполне достаточно. Полный код на С++: AEAD-MGM https://files.stroyinf.ru/Index2/1/4293727/4293727270.htm https://tc26.ru/upload/iblock/b28/Режимы работы блочных шифров, реализующие аутентифицированное шифрование_1 редакция.pdf https://eprint.iacr.org/2019/123.pdf Арифметика полей Галуа GF(2n): https://habr.com/ru/post/212095/ https://itnan.ru/post.php?c=1&p=528142 https://habr.com/ru/post/279197/