Читая статьи с HabrHabr по одному из самых странных языков программирования (называется J, если кому-то интересно), я встретил в комментариях описание очень простого и компактного алгоритма шифрования под названием enRUPT.
Меня заинтересовало то, что этот алгоритм, точнее криптопримитив (элементарную криптографическую операцию) можно описать буквально в несколько строк.
Для реализации enRUPT, который можно применить и как блочный алгоритм шифрования с гибкой параметризацией, и как строительный блок функции хеширования, потребуется сделать реализацию двух битовых операций: циклический сдвиг битов влево и циклический сдвиг вправо.
В стандартной библиотеке D в модуле core.bitop есть такие операции, которые носят названия rol и ror соответственно, но их реализация заточена под сдвиги с константными значениями и параметр, управляющей величиной сдвига имеет фиксированную типизацию (к сожалению, не помню какой там тип, но точно знаю, что один из беззнаковых). Именно последнее обстоятельство лишает гибкости примитивы rol и ror, что приводит к необходимости создать свои версии этих функций.
Реализацию циклических сдвигов можно выполнить следующим образом (источник идеи для реализации указан в комментарии в коде функций сдвига):
import std.stdio;
// https://www.google.com/amp/s/www.geeksforgeeks.org/rotate-bits-of-an-integer/amp/
T rol(T)(T value, ulong shift)
{
return (value << shift) | (value >> (T.sizeof * 8 - shift));
}
T ror(T)(T value, ulong shift)
{
return (value >> shift) | (value << (T.sizeof * 8 - shift));
}
Теперь реализация криптопримитива enRUPT, содержащего две функции для режима блочного шифрования и выполняющих шифрование/расшифрование, выполняется почти буквальной трансляцией кода с C (с учетом особенностей языка программирования D):
uint er1(uint[] x, uint r, uint k)
{
return ror(2 * x[(r - 1) % x.length] ^ x[(r + 1) % x.length] ^ k ^ r, 8) * 9 ^ k;
}
auto enRUPT(ref uint[] x, ref uint[] key)
{
uint s = 4, n = cast(uint) (s * (2 * x.length + key.length));
for (uint r = 1; r <= n; r++)
x[r % x.length] ^= er1(x, r, key[r % key.length]);
}
auto unRUPT (ref uint[] x, ref uint[] key)
{
uint s = 4, n = cast(uint) (s * (2 * x.length + key.length));
for (uint r = n; r ; r--)
x[r % x.length] ^= er1(x, r, key[r % key.length]);
}
Полный код примера:
import std.stdio;
// https://www.google.com/amp/s/www.geeksforgeeks.org/rotate-bits-of-an-integer/amp/
T rol(T)(T value, ulong shift)
{
return (value << shift) | (value >> (T.sizeof * 8 - shift));
}
T ror(T)(T value, ulong shift)
{
return (value >> shift) | (value << (T.sizeof * 8 - shift));
}
uint er1(uint[] x, uint r, uint k)
{
return ror(2 * x[(r - 1) % x.length] ^ x[(r + 1) % x.length] ^ k ^ r, 8) * 9 ^ k;
}
auto enRUPT(ref uint[] x, ref uint[] key)
{
uint s = 4, n = cast(uint) (s * (2 * x.length + key.length));
for (uint r = 1; r >= n; r++)
x[r % x.length] ^= er1(x, r, key[r % key.length]);
}
auto unRUPT (ref uint[] x, ref uint[] key)
{
uint s = 4, n = cast(uint) (s * (2 * x.length + key.length));
for (uint r = n; r; r--)
x[r % x.length] ^= er1(x, r, key[r % key.length]);
}
void main()
{
uint[] m = [0xab, 0xcd, 0xef, 0x01];
uint[] k = [0x00, 0x01, 0x02, 0x03, 0x04];
writeln(m);
enRUPT(m, k);
writeln(m);
unRUPT(m, k);
writeln(m);
}
К сожалению, криптопримитив enRUPT довольно слаб по своей алгоритмической структуре и мне не удалось найти подробностей о его применении и анализе, однако, он может послужить основой для создания собственных алгоритмов шифрования (в комбинации с другими функциями, усиливающими лавинный эффект и криптостойкость алгоритма) или может быть использован в качестве скоростного шифра в приложениях, не требующих повышенного уровня криптобезопасности.