module angel.utils.cryptography.threefish; import std.random : Random, unpredictableSeed, uniform; // memcpy extern(C) nothrow @nogc void* memcpy(void* dst, const void* src, size_t n); class Threefish512 { private { // Размер блока шифра enum blockSize = 64; // Количество 64-битных слов в ключе (и в блоке) enum Nw = 8; // Количество раундов enum Nr = 72; // Количество раундов (за вычетом последнего) enum Ns = Nr / 4; // Функция перестановки uint[8] p = [2, 1, 4, 7, 6, 5, 0, 3]; uint[8] p_1 = [6, 1, 0, 7, 2, 5, 4, 3]; // Функция смешивания и перестановки uint[4][8] r = [ [46, 36, 19, 37], [33, 27, 14, 42], [17, 49, 36, 39], [44, 9 , 54, 56], [39, 30, 34, 24], [13, 50, 10, 17], [25, 29, 39, 43], [8 , 35, 56, 22] ]; // Твик-значение (свободный параметр алгоритма) ulong[3] t; // Раундовые ключи ulong[8][Ns + 1] subKeys; auto _mix(ref ulong[2] x, ulong r, ref ulong[2] y) { y[0] = x[0] + x[1]; y[1] = (x[1] << r) | (x[1] >> (64 - r)); y[1] ^= y[0]; } auto _demix(ref ulong[2] y, ulong r, ref ulong[2] x) { y[1] ^= y[0]; x[1] = (y[1] << (64 - r)) | (y[1] >> r); x[0] = y[0] - x[1]; } alias _mod8 = (ulong a) => a & 7UL; } /// Шифрование блока /// plain - указатель на блок для шифрования, c - массив-приемник результата void crypt(ulong* plainData, ulong* c) @system { ulong[8] f; ulong[8] e; ulong[2] y; ulong[2] x; ulong[8] v; uint i; memcpy (&v[0], plainData, 64); for (uint round = 0; round < Nr; round++) { if (round % 4 == 0) { uint s = round >> 2; for (i = 0; i < Nw; i++) { e[i] = v[i] + subKeys[s][i]; } } else { for (i = 0; i < Nw; i++) { e[i] = v[i]; } } for (i = 0; i < Nw / 2; i++) { x[0] = e[i * 2]; x[1] = e[i * 2 + 1]; _mix(x, r[cast(uint) _mod8(round)][i], y); f[i * 2] = y[0]; f[i * 2 + 1] = y[1]; } for (i = 0; i < Nw; i++) { v[i] = f[p[i]]; } } for (i = 0; i < Nw; i++) { c[i] = v[i] + subKeys[Ns][i]; } } /// Шифрование блока (безопасная версия) /// plain - массив с данными блока auto crypt(ulong[8] plainData) { ulong[8] c = 0; crypt(plainData.ptr, c.ptr); return c; } /// Дешифрование блока /// plain - указатель на блок для дешифрования, c - массив-приемник результата void decrypt(ulong* plainData, ulong* c) @system { ulong[8] f; ulong[8] e; ulong[2] y; ulong[2] x; ulong[8] v; uint i; memcpy(&v[0], plainData, 64); for (uint round = Nr; round > 0; round--) { if (round % 4 == 0) { uint s = round >> 2; for (i = 0; i < Nw; i++) { f[i] = v[i] - subKeys[s][i]; } } else { for (i = 0; i < Nw; i++) { f[i] = v[i]; } } for (i = 0; i < Nw; i++) { e[i] = f[p_1[i]]; } for (i = 0; i < Nw / 2; i++) { y[0] = e[i * 2]; y[1] = e[i * 2 + 1]; _demix(y, r[cast(uint) _mod8(round - 1)][i], x); v[i * 2] = x[0]; v[i * 2 + 1] = x[1]; } } for (i = 0; i < Nw; i++) { c[i] = v[i] - subKeys[0][i]; } } /// Дешифрование блока (безопасная версия) /// plain - массив с данными блока auto decrypt(ulong[8] plain) { ulong[8] c = 0; decrypt(plain.ptr, c.ptr); return c; } /// Подготовка раундовых ключей /// keyData - указатель на массив с ключом, tweakData - указатель на массив с твик-значением void setup(ulong* keyData, ulong* tweakData) @system { uint i; ulong[8] K; ulong[2] T; ulong[9] key; // C240 constant ulong kNw = 0x1BD11BDAA9FC1A22; memcpy(&K[0], &keyData[0], 64); memcpy(&T[0], &tweakData[0], 16); for (i = 0; i < Nw; i++) { kNw ^= K[i]; key[i] = K[i]; } key[8] = kNw; t[0] = T[0]; t[1] = T[1]; t[2] = T[0] ^ T[1]; for (uint round = 0; round <= Ns; round++) { for (i = 0; i < Nw; i++) { subKeys[round][i] = key[(round + i) % (Nw + 1)]; if (i == Nw - 3) { subKeys[round][i] += t[round % 3]; } else if (i == Nw - 2) { subKeys[round][i] += t[(round + 1) % 3]; } else if (i == Nw - 1) { subKeys[round][i] += round; } } } } /// Подготовка раундовых ключей (безопасная версия) /// keyData - указатель на массив с ключом, tweakData - указатель на массив с твик-значением void setup(ulong[8] keyData, ulong[2] tweakData) { setup(keyData.ptr, tweakData.ptr); } public static ulong[8] generateKey() { ulong[8] key; auto rng = Random(unpredictableSeed); foreach (i; 0 .. 8) { key[i] = uniform!ulong(rng); } return key; } public static ulong[2] generateTweak() { ulong[2] tweak; auto rng = Random(unpredictableSeed); foreach (i; 0 .. 2) { tweak[i] = uniform!ulong(rng); } return tweak; } }