1 module threefish512;
2 
3 // memcpy
4 extern(C) nothrow @nogc void* memcpy(void* dst, const void* src, size_t n);
5 
6 class Threefish512
7 {
8 	private {
9 		// Размер блока шифра
10 		enum blockSize = 64;
11 		// Количество 64-битных слов в ключе (и в блоке)
12 		enum Nw = 8;
13 		// Количество раундов
14 		enum Nr = 72;
15 		// Количество раундов (за вычетом последнего)
16 		enum Ns = Nr / 4;
17 		// Функция перестановки
18 		uint[8] p   = [2, 1, 4, 7, 6, 5, 0, 3];
19 		uint[8] p_1 = [6, 1, 0, 7, 2, 5, 4, 3];
20 		
21 		// Функция смешивания и перестановки
22 		uint[4][8] r = [ 
23 			[46, 36, 19, 37], 
24 			[33, 27, 14, 42], 
25 			[17, 49, 36, 39], 
26 			[44, 9 , 54, 56],
27 			[39, 30, 34, 24], 
28 			[13, 50, 10, 17], 
29 			[25, 29, 39, 43],
30 			[8 , 35, 56, 22]
31 		];
32 		
33 		// Твик-значение (свободный параметр алгоритма)
34 		ulong[3] t;
35 		// Раундовые ключи
36 		ulong[8][Ns + 1] subKeys;
37 		
38 		auto _mix(ref ulong[2] x, ulong r, ref ulong[2] y)
39 		{
40 			y[0] = x[0] + x[1];
41 			y[1] = (x[1] << r) | (x[1] >> (64 - r));
42 			y[1] ^= y[0];
43 		}
44 		
45 		auto _demix(ref ulong[2] y, ulong r, ref ulong[2] x)
46 		{
47 			y[1] ^= y[0];
48 			x[1] = (y[1] << (64 - r)) | (y[1] >> r);
49 			x[0] = y[0] - x[1];
50 		}
51 		
52 		alias _mod8 = (ulong a) => a & 7UL;
53 	}
54 	
55 	/// Шифрование блока
56 	/// plain - указатель на блок для шифрования, c - массив-приемник результата
57 	void crypt(ulong* plainData, ulong* c) @system
58 	{
59 		ulong[8] f;
60 		ulong[8] e;
61 		ulong[2] y;
62 		ulong[2] x;
63 		ulong[8] v;
64 		uint i;
65 	
66 		memcpy (&v[0], plainData, 64);
67 	
68 		for (uint round = 0; round < Nr; round++)
69 		{
70 			if (round % 4 == 0)
71 			{
72 				uint s = round >> 2;
73 				
74 				for (i = 0; i < Nw; i++)
75 				{
76 					e[i] = v[i] + subKeys[s][i];
77 				}
78 			}
79 			else
80 			{
81 				for (i = 0; i < Nw; i++)
82 				{
83 					e[i] = v[i];
84 				}
85 			}
86 		
87 			for (i = 0; i < Nw / 2; i++)
88 			{
89 				x[0] = e[i * 2];
90 				x[1] = e[i * 2 + 1];
91 				
92 				_mix(x, r[_mod8(round)][i], y);
93 				
94 				f[i * 2] = y[0];
95 				f[i * 2 + 1] = y[1];
96 			}
97 		
98 			for (i = 0; i < Nw; i++)
99 			{
100 				v[i] = f[p[i]];
101 			}
102 		}
103 	
104 		for (i = 0; i < Nw; i++)
105 		{
106 			c[i] = v[i] + subKeys[Ns][i];
107 		}
108 	}
109 	
110 	/// Шифрование блока (безопасная версия)
111 	/// plain - массив с данными блока
112 	auto crypt(ulong[8] plainData)
113 	{
114 		ulong[8] c = 0;
115 		crypt(plainData.ptr, c.ptr);
116 		return c;
117 	}
118 	
119 	/// Дешифрование блока
120 	/// plain - указатель на блок для дешифрования, c - массив-приемник результата
121 	void decrypt(ulong* plainData, ulong* c) @system
122 	{
123 		ulong[8] f;
124 		ulong[8] e;
125 		ulong[2] y;
126 		ulong[2] x;
127 		ulong[8] v;
128 		uint i;
129 	
130 		memcpy(&v[0], plainData, 64);
131 	
132 		for (uint round = Nr; round > 0; round--)
133 		{
134 			if (round % 4 == 0)
135 			{
136 				uint s = round >> 2;
137 				for (i = 0; i < Nw; i++)
138 				{
139 					f[i] = v[i] - subKeys[s][i];
140 				}
141 			}
142 			else
143 			{
144 				for (i = 0; i < Nw; i++)
145 				{
146 					f[i] = v[i];
147 				}
148 			}
149 		
150 			for (i = 0; i < Nw; i++)
151 			{
152 				e[i] = f[p_1[i]];
153 			}
154 		
155 			for (i = 0; i < Nw / 2; i++)
156 			{
157 				y[0] = e[i * 2];
158 				y[1] = e[i * 2 + 1];
159 				
160 				_demix(y, r[_mod8(round - 1)][i], x);
161 				
162 				v[i * 2] = x[0];
163 				v[i * 2 + 1] = x[1];
164 			}
165 		}
166 	
167 		for (i = 0; i < Nw; i++)
168 		{
169 			c[i] = v[i] - subKeys[0][i];
170 		}
171 	}
172 	
173 	/// Дешифрование блока (безопасная версия)
174 	/// plain - массив с данными блока
175 	auto decrypt(ulong[8] plain)
176 	{
177 		ulong[8] c = 0;
178 		decrypt(plain.ptr, c.ptr);
179 		return c;
180 	}
181 	
182 	/// Подготовка раундовых ключей
183 	/// keyData - указатель на массив с ключом, tweakData - указатель на массив с твик-значением
184 	void setup(ulong* keyData, ulong* tweakData) @system
185 	{
186 		uint i;
187 		ulong[8] K;
188 		ulong[2] T;
189 		ulong[9] key;
190 
191 		// C240 constant
192 		ulong kNw =  0x1BD11BDAA9FC1A22;
193 		
194 		memcpy(&K[0], &keyData[0], 64);
195 		memcpy(&T[0], &tweakData[0], 16);
196 			
197 		for (i = 0; i < Nw; i++)
198 		{
199 			kNw ^= K[i];
200 			key[i] = K[i];
201 		}
202 		
203 		key[8] = kNw;
204 		
205 		t[0] = T[0];
206 		t[1] = T[1];
207 		t[2] = T[0] ^ T[1];
208 	
209 		for (uint round = 0; round <= Ns; round++)
210 		{
211 			for (i = 0; i < Nw; i++)
212 			{
213 				subKeys[round][i] = key[(round + i) % (Nw + 1)];
214 				
215 				if (i == Nw - 3)
216 				{
217 					subKeys[round][i] += t[round % 3];
218 				}
219 				else if (i == Nw - 2)
220 				{
221 					subKeys[round][i] += t[(round + 1) % 3];
222 				}
223 				else if (i == Nw - 1)
224 				{
225 					subKeys[round][i] += round;
226 				}
227 			}
228 		}
229 	}
230 	
231 	/// Подготовка раундовых ключей (безопасная версия)
232 	/// keyData - указатель на массив с ключом, tweakData - указатель на массив с твик-значением
233 	void setup(ulong[8] keyData, ulong[2] tweakData)
234 	{
235 		setup(keyData.ptr, tweakData.ptr);
236 	}
237 }