Files
2025-12-22 16:23:48 +01:00

261 lines
5.4 KiB
D
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}
}