Читая статьи с 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 довольно слаб по своей алгоритмической структуре и мне не удалось найти подробностей о его применении и анализе, однако, он может послужить основой для создания собственных алгоритмов шифрования (в комбинации с другими функциями, усиливающими лавинный эффект и криптостойкость алгоритма) или может быть использован в качестве скоростного шифра в приложениях, не требующих повышенного уровня криптобезопасности.