README.md

ESCK-7

Авторский алгоритм блочного шифрования.

Автор: Д.Н. Трунов

⚠️ Внимание, любительский шифр! Любое реальное применение только на свой страх и риск.

Общее описание

Алгоритм является продолжением авторской серии блочных шифров ESCK (от Encrypting System with Changing Key(s) — Шифрующая Система с Изменяющимся Ключом), одной из особенностей которых является возможность изменять ключ после шифрования каждого блока. Во многом серия также развивалась под впечатлением от шифра Вернама с попыткой реализовать «случайность» и «одноразовость» в контексте каждой уникальной пары блока и ключа фиксированного размера. Основной упор при этом делается на неограниченное количество самодостаточных раундов (без раундовых ключей), в каждом из которых связи определяются на основе ключа и промежуточного состояния блока.

Особенности алгоритма

  • Все операции в шифре выполняются над 64-битными целыми беззнаковыми числами
  • Ключ имеет размер 256 64-битных чисел, есть встроенные функции его генерации по байтовому массиву (KeyGen) или паролю (KeyGenA)
  • Массив или пароль для генерации ключа могут иметь любую длину, но в генерации принимают участие первые 2048 байт массива или 256 символов пароля
  • Допустимо преобразовать несколько паролей и/или массивов в один ключ
  • Данные шифруются блоками по 128 бит (два 64-битных числа)
  • Шифрование блока выполняется в несколько раундов (циклов), их количество не является фиксированным и может быть задано пользователем
  • В шифровании любого блока принимает участие не только ключ, но и 64-битный номер блока, который либо установлен по умолчанию, либо меняется от блока к блоку

Режимы шифрования

Возможные режимы шифрования:

  • простой (он же ECB) – ключ не меняется и все блоки шифруются одинаково
  • счётчик блоков – шифрование с зависимостью от порядкового номера блока
  • смена ключа – после шифрования каждого блока ключ меняется встроенной функцией

Схема шифрования и его идея

Порядок шифрования блока:

  1. Для каждого из двух 64-битных элементов блока вычисляется специальная 64-битная адресная переменная: на неё влияет «сосед» по блоку, номер блока (в простом режиме он всегда равен нулю), а также значение одного из 256 элементов ключа
  2. Адресная переменная разбивается на восемь 8-битных номеров (индексов), по которым берутся элементы ключа для данного этапа шифрования
  3. Текущий элемент блока шифруется с участием взятых по вычисленным индексам элементов ключа с помощью операций XOR, NOT, сложения и циклических сдвигов
  4. И так для каждого элемента блока в каждом раунде (цикле)

Расшифровка происходит по той же схеме, только чувствительные к порядку операции выполняются в обратной последовательности.

Идея в том, что на каждом этапе будут вычисляться заранее непредсказуемые значения адресных переменных и, значит, в шифровании элемента блока примут участие разные элементы ключа. Зашифрованный элемент повлияет на адресную переменную для соседнего шифруемого, а тот – снова для текущего (в следующем раунде). От адресной же переменной зависят номера участвующих элементов ключа и потому даже незначительные изменения адреса могут существенно повлиять на результат шифрования. При достаточном количестве раундов можно ожидать, что взаимосвязь между соседними элементами блока и всеми элементами ключа будет настолько сложная, что станет равнозначной случайной.

Изменение и генерация ключа

Изменение ключа выполняется отдельной функцией GFunc, напоминающей шифрование блока, но с некоторыми отличиями:

  • шифруются не элементы блока, а сами элементы ключа;
  • в вычислении адресной переменной принимают участие оба соседних элемента ключа (следующий и предыдущий), а также сам шифруемый;
  • подобная схема шифрования является необратимой и по текущему состоянию ключа нельзя восстановить его состояние до зашифровки.

Функция GFunc перестраивает ключ так, что все его элементы зависят друг от друга и имеют «случайный» вид, что необходимо для качественного шифрования. GFunc также является основой встроенной генерации ключа. Например, при генерации по паролю символы пароля «прибавляются» (с помощью XOR) к элементам ключа и запускается несколько раундов GFunc. И так несколько раз, а в финале ещё один запуск GFunc с большим количеством раундов.

Пример применения: генерация с “солью”, шифрование и расшифровка

// пароли, "соль" и массив данных
std::string Password1;
std::string Password2;
std::vector<uint8_t> Salt(16);
std::vector<uint8_t> Data(1024);

// одноразовый ключ по одному паролю и соли (10000 циклов)
ESCK7 Cipher1;
Cipher1.KeyGenA(Password1, 10000, true); // с инициализацией
Cipher1.KeyGen(Salt, 16, 10000, false);  // без инициализации
Cipher1.SetCycles(50);                   // рабочее количество циклов
Cipher1.SetMode(ESCK7::mdBlCnt);         // режим счётчика блоков

// многоразовый ключ по двум паролям (10000 циклов)
ESCK7 Cipher2;
Cipher2.KeyGenA(Password1, 10000, true); // с инициализацией
Cipher2.KeyGenA(Password2, 10000, false);// без инициализации

// одноразовый ключ из многоразового + "соль" (10000 циклов)
ESCK7 Cip = Cipher2;
Cip.KeyGen(Salt, 16, 10000, false); // без инициализации
Cip.SetCycles(50);
Cip.SetMode(ESCK7::mdBlCnt);

// шифрование данных
Cip.Encrypt(Data, Data.size());

// обнуление номера блока и суммы после предыдущего шифрования
Cip.SetBlockNumber(0);
Cip.ResetSum();

// расшифровка данных
Cip.Decrypt(Data, Data.size());

Результаты предварительных тестов

Лавинный эффект (открытый текст → шифротекст) — 10 000 испытаний, 5 раундов шифрования:

  • Среднее изменение бит шифротекста: 49.99% (σ=4.40%)

Лавинный эффект (ключ → шифротекст) — 10 000 испытаний, изменение 1 бита ключа + 2 раунда GFunc, 5 раундов шифра:

  • Среднее изменение бит шифротекста: 50.08% (σ=4.41%)

Корреляция блоков — 50 000 блоков нулей на случайных ключах, 5 раундов:

  • Средняя корреляция между соседними блоками: 0.0015
  • Автокорреляция при сдвигах 1–16 байт: не превышает 0.002

Дифференциальный анализ — 1 000 000 блоков, ΔP=1, фиксированный ключ, 5 раундов:

  • Максимальная частота байта разности шифротекстов: 0.395%
  • Отклонение от равномерного распределения: +1.1%

Линейный анализ — выборочная проверка 5 битовых позиций, 5 раундов:

  • Максимальное смещение (bias): 0.00183 (≈2⁻⁹·¹)

Случайность шифротекстов — пройдены тесты NIST SP 800-22 (все p-value >= 0.01).

Структура файлов

ESCK7.cpp – основной код шифра, реализованный в виде объектного класса.

ESCK7.h – заголовочный файл для объектного класса.

esck7cip.cpp – пример простой консольной программы для шифрования файлов шифром ESCK-7.

Makefile – файл для сборки программы esck7cip.

Некоторые замечания

Следует отметить, что неотъемлемой частью алгоритма являются только функции шифрования и расшифровки блока (EncryptBlock и DecryptBlock из файла ESCK7.cpp), а также изменения/зашифровки ключа (функция GFunc из того же файла). Остальные функции представляют собой лишь вариант «по умолчанию» для инициализации и генерации ключа или шифрования и расшифровки данных произвольной длины. Для реализации дополнительных вариантов генерации ключа и/или шифрования и расшифровки данных допустимы следующие возможности:

  1. с помощью функции Key_data() получить указатель на массив ключа конкретного экземпляра класса и генерировать ключ выбранным способом напрямую, минуя запуск KeyGen() (нужно только не забыть установить количество циклов функцией SetCycles);
  2. реализовать нужный способ генерации ключа и шифрования/расшифровки данных вне класса, но используя его статические функции EncryptBlock(), DecryptBlock() и GFunc();
  3. реализовать отдельный класс, наследующий ESCK7, и в него добавить необходимые дополнительные функции (ключ K и другие поля класса ESCK7 будут доступны классу-потомку);
  4. создать отдельный класс, добавить в него базовые статические функции из ESCK7 (в частности, EncryptBlock, DecryptBlock и GFunc), а остальной функционал реализовать в необходимом виде внутри (подобно п. 3) или вне этого класса (подобно п.2);
  5. вынести базовые функции, в частности EncryptBlock(), DecryptBlock() и GFunc(), в отдельный файл (вне класса) и добавить к ним недостающий функционал генерации ключа и шифрования/расшифровки данных.

Критические изменения алгоритма

24.04.2025. В функциях шифрования ключа (GFunc) и блока (EncryptBlock) прибавление константы заменено прибавлением номера цикла (в DecryptBlock - отниманием).

27.02.2026. Из алгоритма исключена функция EncryptStream() шифрования в потоковом режиме (чтобы не смешивать блочный и потоковый режимы). Потоковый режим реализован в виде отдельного экспериментального класса.

01.04.2026. Из алгоритма полностью исключено применение вектора инициализации. Вместо применения уникального вектора инициализации рекомендуется создавать одноразовые ключи с участием случайной “соли”.

25.06.2026. Начата работа над ESCK-8 с альтернативной реализацией «случайности» и «одноразовости» в виде хеширования соседа по блоку и ключа. Текущая версия остаётся доступной и поддерживается.

Описание
Авторский алгоритм блочного шифрования.
Конвейеры
0 успешных
0 с ошибкой
Разработчики