Реализации алгоритмов/RC5
В данной реализации используется размер слова W = 64 бита, что соответствует размеру блока в 128 бит. Число раундов R = 16. Ключ K определяется пользователем самостоятельно.
using System;
namespace RC5Alg
{
public class RC5
{
const int W = 64; // половина длины блока в битах.
// Возможные значения 16, 32 и 64.
// Для эффективной реализации величину W
// рекомендуют брать равным машинному слову.
// Например, для 64-битных платформ оптимальным будет
// выбор W=64, что соответствует размеру блока в 128 бит.
const int R = 16; // число раундов. Возможные значения 0…255.
// Увеличение числа раундов обеспечивает увеличение
// уровня безопасности шифра. Так, если R = 0,
// то информация шифроваться не будет.
const UInt64 PW = 0xB7E151628AED2A6B; // 64-битная константа
const UInt64 QW = 0x9E3779B97F4A7C15; // 64-битная константа
UInt64[] L; // массив слов для секретного ключа пользователя
UInt64[] S; // таблица расширенных ключей
int t; // размер таблицы
int b; // длина ключа в байтах. Возможные значения 0…255.
int u; // кол-во байтов в одном машинном слове
int c; // размер массива слов L
public RC5(byte[] key)
{
/*
* Перед непосредственно шифрованием или расшифровкой данных выполняется процедура расширения ключа.
* Процедура генерации ключа состоит из четырех этапов:
* 1. Генерация констант
* 2. Разбиение ключа на слова
* 3. Построение таблицы расширенных ключей
* 4. Перемешивание
*/
// основные переменные
UInt64 x, y;
int i, j, n;
/*
* Этап 1. Генерация констант
* Для заданного параметра W генерируются две псевдослучайные величины,
* используя две математические константы: e (экспонента) и f (Golden ratio).
* Qw = Odd((e - 2) * 2^W;
* Pw = Odd((f - 1) * 2^W;
* где Odd() - это округление до ближайшего нечетного целого.
*
* Для оптимизации алгоритмы эти 2 величины определены заранее (см. константы выше).
*/
/*
* Этап 2. Разбиение ключа на слова
* На этом этапе происходит копирование ключа K[0]..K[255] в массив слов L[0]..L[c-1], где
* c = b/u, а u = W/8. Если b не кратен W/8, то L[i] дополняется нулевыми битами до ближайшего
* большего размера c, при котором длина ключа b будет кратна W/8.
*/
u = W >> 3;
b = key.Length;
c = b % u > 0 ? b / u + 1 : b / u;
L = new UInt64[c];
for (i = b - 1; i >= 0; i--)
{
L[i / u] = ROL(L[i / u], 8) + key[i];
}
/* Этап 3. Построение таблицы расширенных ключей
* На этом этапе происходит построение таблицы расширенных ключей S[0]..S[2(R + 1)],
* которая выполняется следующим образом:
*/
t = 2 * (R + 1);
S = new UInt64[t];
S[0] = PW;
for (i = 1; i < t; i++)
{
S[i] = S[i - 1] + QW;
}
/* Этап 4. Перемешивание
* Циклически выполняются следующие действия:
*/
x = y = 0;
i = j = 0;
n = 3 * Math.Max(t, c);
for (int k = 0; k < n; k++)
{
x = S[i] = ROL((S[i] + x + y), 3);
y = L[j] = ROL((L[j] + x + y), (int)(x + y));
i = (i + 1) % t;
j = (j + 1) % c;
}
}
/// <summary>
/// Циклический сдвиг битов слова влево
/// </summary>
/// <param name="a">машинное слово: 64 бита</param>
/// <param name="offset">смещение</param>
/// <returns>машинное слово: 64 бита</returns>
private UInt64 ROL(UInt64 a, int offset)
{
UInt64 r1, r2;
r1 = a << offset;
r2 = a >> (W - offset);
return (r1 | r2);
}
/// <summary>
/// Циклический сдвиг битов слова вправо
/// </summary>
/// <param name="a">машинное слово: 64 бита</param>
/// <param name="offset">смещение</param>
/// <returns>машинное слово: 64 бита</returns>
private UInt64 ROR(UInt64 a, int offset)
{
UInt64 r1, r2;
r1 = a >> offset;
r2 = a << (W - offset);
return (r1 | r2);
}
/// <summary>
/// Свертка слова (64 бит) по 8-ми байтам
/// </summary>
/// <param name="b">массив байтов</param>
/// <param name="p">позиция</param>
/// <returns></returns>
private static UInt64 BytesToUInt64(byte[] b, int p)
{
UInt64 r = 0;
for (int i = p + 7; i > p; i--)
{
r |= (UInt64)b[i];
r <<= 8;
}
r |= (UInt64)b[p];
return r;
}
/// <summary>
/// Развертка слова (64 бит) по 8-ми байтам
/// </summary>
/// <param name="a">64-битное слово</param>
/// <param name="b">массив байтов</param>
/// <param name="p">позиция</param>
private static void UInt64ToBytes(UInt64 a, byte[] b, int p)
{
for (int i = 0; i < 7; i++)
{
b[p + i] = (byte)(a & 0xFF);
a >>= 8;
}
b[p + 7] = (byte)(a & 0xFF);
}
/// <summary>
/// Операция шифрования
/// </summary>
/// <param name="inBuf">входной буфер для шифруемых данных (64 бита)</param>
/// <param name="outBuf">выходной буфер (64 бита)</param>
public void Cipher(byte[] inBuf, byte[] outBuf)
{
UInt64 a = BytesToUInt64(inBuf, 0);
UInt64 b = BytesToUInt64(inBuf, 8);
a = a + S[0];
b = b + S[1];
for (int i = 1; i < R + 1; i++)
{
a = ROL((a ^ b), (int)b) + S[2 * i];
b = ROL((b ^ a), (int)a) + S[2 * i + 1];
}
UInt64ToBytes(a, outBuf, 0);
UInt64ToBytes(b, outBuf, 8);
}
/// <summary>
/// Операция расшифрования
/// </summary>
/// <param name="inBuf">входной буфер для шифруемых данных (64 бита)</param>
/// <param name="outBuf">выходной буфер (64 бита)</param>
public void Decipher(byte[] inBuf, byte[] outBuf)
{
UInt64 a = BytesToUInt64(inBuf, 0);
UInt64 b = BytesToUInt64(inBuf, 8);
for (int i = R; i > 0; i--)
{
b = ROR((b - S[2 * i + 1]), (int)a) ^ a;
a = ROR((a - S[2 * i]), (int)b) ^ b;
}
b = b - S[1];
a = a - S[0];
UInt64ToBytes(a, outBuf, 0);
UInt64ToBytes(b, outBuf, 8);
}
}
}