1 /// Implementation of Anura digester.
2 module tern.digest.anura;
3 
4 // TODO: Fix?
5 import tern.digest;
6 import tern.digest.circe;
7 import tern.serialization;
8 import tern.algorithm;
9 
10 /**
11  * Implementation of Anura256 digester.
12  *
13  * Anura256 is a fast and efficient pseudo-fiestel block based block cipher 
14  * that primarily works by shuffling data around and works with blocks of 64 bits.
15  * It utilizes a 256-bit key for encryption and decryption.
16  *
17  * Example:
18  * ```d
19  * import tern.digest.anura;
20  * 
21  * ubyte[] data = [1, 2, 3, 4, 5];
22  * string key = "0123456789ABCDEF0123456789ABCDEF"; // Must be 32 bytes (256 bits)
23  * Anura256.encrypt(data, key);
24  * Anura256.decrypt(data, key);
25  * ```
26  */
27 public static @digester class Anura256
28 {
29 public:
30 static:
31 pure:
32     /**
33      * Encrypts the given data using Anura256 algorithm.
34      *
35      * Params:
36      *  data = The data to be encrypted.
37      *  key = The encryption key, must be 256 bits (32 bytes).
38      */
39     void encrypt(ref ubyte[] data, string key)
40     {
41         if (key.length != 32)
42             throw new Throwable("Key is not 256 bits!");
43 
44         key = cast(string)digest!Circe(cast(ubyte[])key[0..32]);
45         ulong rola = (cast(ulong*)key.ptr)[1];
46         ulong rolb = (cast(ulong*)key.ptr)[2];
47         ulong rolc = (cast(ulong*)key.ptr)[3];
48         ulong rold = (cast(ulong*)key.ptr)[4];
49 
50         ulong[8] set = [
51             rola,
52             rolb,
53             rolc,
54             rold,
55             rola << rolb,
56             rolb << rolc,
57             rolc << rold,
58             rold << rola,
59         ];
60 
61         foreach (i; 0..(rola % 128))
62         {
63             size_t factor = ((set[i % 8] * i) % ((data.length / 16_384) | 2)) | 1;  
64             for (size_t j = factor; j < data.length; j += factor)
65                 data.swap(j, j - factor);
66         }
67 
68         void swap(ref ulong block)
69         {
70             uint left = (cast(uint*)&block)[0];
71             (cast(uint*)&block)[0] = (cast(uint*)&block)[1];
72             (cast(uint*)&block)[1] = left;
73         }
74 
75         foreach (i; 0..4)
76         {
77             foreach (j, ref block; data.portionTo!(ulong))
78             {
79                 size_t ri = ~j;
80                 size_t si = j % 8;
81                 block += (rola << si) ^ ri;
82                 block ^= (rolb << si) ^ ri;
83                 swap(block);
84                 block -= (rolc << si) ^ ri;
85                 block ^= (rold << si) ^ ri;
86             }
87 
88             foreach (j, ref block; (cast(ulong[])data)[1..$])
89                 block ^= data[j];
90         }
91     }
92 
93     /**
94      * Decrypts the given data using Anura256 algorithm.
95      *
96      * Params:
97      *  data = The data to be decrypted.
98      *  key = The decryption key, must be 256 bits (32 bytes).
99      */
100     void decrypt(ref ubyte[] data, string key)
101     {
102         if (key.length != 32)
103             throw new Throwable("Key is not 256 bits!");
104 
105         key = cast(string)digest!Circe(cast(ubyte[])key[0..32]);
106         ulong rola = (cast(ulong*)key.ptr)[1];
107         ulong rolb = (cast(ulong*)key.ptr)[2];
108         ulong rolc = (cast(ulong*)key.ptr)[3];
109         ulong rold = (cast(ulong*)key.ptr)[4];
110 
111         ulong[8] set = [
112             rola,
113             rolb,
114             rolc,
115             rold,
116             rola << rolb,
117             rolb << rolc,
118             rolc << rold,
119             rold << rola,
120         ];
121 
122         if (data.length % 8 != 0)
123             vacpp(data, 8);
124 
125         void swap(ref ulong block)
126         {
127             uint left = (cast(uint*)&block)[0];
128             (cast(uint*)&block)[0] = (cast(uint*)&block)[1];
129             (cast(uint*)&block)[1] = left;
130         }
131 
132         foreach_reverse (i; 0..4)
133         {
134             foreach_reverse (j, ref block; (cast(ulong[])data)[1..$])
135                 block ^= data[j];
136 
137             foreach_reverse (j, ref block; cast(ulong[])data)
138             {
139                 size_t ri = ~j;
140                 size_t si = j % 8;
141                 block ^= (rold << si) ^ ri;
142                 block += (rolc << si) ^ ri;
143                 swap(block);
144                 block ^= (rolb << si) ^ ri;
145                 block -= (rola << si) ^ ri;
146             }
147         }
148 
149         foreach_reverse (i; 0..(rola % 128))
150         {
151             size_t factor = ((set[i % 8] * i) % ((data.length / 16_384) | 2)) | 1;  
152             for (size_t j = factor; j < data.length; j += factor)
153                 data.swap(j, j - factor);
154         }
155 
156         unvacpp(data);
157     }
158 }
159 
160 /**
161  * Implementation of Anura1024 digester.
162  *
163  * Anura1024 is a variant of Anura cipher that utilizes a 1024-bit key for encryption
164  * and decryption.
165  *
166  * Example:
167  * ```d
168  * import tern.digest.anura;
169  * 
170  * ubyte[] data = [1, 2, 3, 4, 5];
171  * string key = "0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF"; // Must be 128 bytes (1024 bits)
172  * Anura1024.encrypt(data, key);
173  * Anura1024.decrypt(data, key);
174  * ```
175  */
176 public static @digester class Anura1024
177 {
178 public:
179 static:
180 pure:
181     /**
182      * Encrypts the given data using Anura1024 algorithm.
183      *
184      * Params:
185      *  data = The data to be encrypted.
186      *  key = The encryption key, must be 1024 bits (128 bytes).
187      */
188     void encrypt(ref ubyte[] data, string key)
189     {
190         if (key.length != 128)
191             throw new Throwable("Key is not 1024 bits!");
192 
193         key = cast(string)digest!Circe(cast(ubyte[])key[0..32], 0xFC2AB8FFFFF)~
194             cast(string)digest!Circe(cast(ubyte[])key[32..64], 0xCCABB72DA)~
195             cast(string)digest!Circe(cast(ubyte[])key[64..96], 0xABC0001700)~
196             cast(string)digest!Circe(cast(ubyte[])key[96..128], 0x0000D7FFF);
197         ulong rola = (cast(ulong*)key.ptr)[0] ^ (cast(ulong*)key.ptr)[1] | 32;
198         ulong rolb = (cast(ulong*)key.ptr)[2] ^ (cast(ulong*)key.ptr)[3] | 32;
199         ulong rolc = (cast(ulong*)key.ptr)[4] ^ (cast(ulong*)key.ptr)[5] | 32;
200         ulong rold = (cast(ulong*)key.ptr)[6] ^ (cast(ulong*)key.ptr)[7] | 32;
201         ulong roba = (cast(ulong*)key.ptr)[12] ^ (cast(ulong*)key.ptr)[8] | rola;
202         ulong robb = (cast(ulong*)key.ptr)[13] ^ (cast(ulong*)key.ptr)[9] | rolb;
203         ulong robc = (cast(ulong*)key.ptr)[14] ^ (cast(ulong*)key.ptr)[10] | rolc;
204         ulong robd = (cast(ulong*)key.ptr)[15] ^ (cast(ulong*)key.ptr)[11] | rold;
205 
206         ulong[16] set = [
207             rola,
208             rolb,
209             rolc,
210             rold,
211             roba,
212             robb,
213             robc,
214             robd,
215             rola ^ roba,
216             rolb ^ robb,
217             rolc ^ robc,
218             rold ^ robd,
219             rola * roba,
220             rolb * robb,
221             rolc * robc,
222             rold * robd,
223         ];
224 
225         vacpp(data, 8);
226 
227         foreach (i; 0..(roba % 128))
228         {
229             size_t factor = ((set[i % 16] * i) % ((data.length / 16_384) | 2)) | 1;  
230             for (size_t j = factor; j < data.length; j += factor)
231                 data.swap(j, j - factor);
232         }
233 
234         void swap(ref ulong block)
235         {
236             uint left = (cast(uint*)&block)[0];
237             (cast(uint*)&block)[0] = (cast(uint*)&block)[1];
238             (cast(uint*)&block)[1] = left;
239         }
240 
241         foreach (i; 0..2)
242         {
243             foreach (j, ref block; cast(ulong[])data)
244             {
245                 size_t ri = ~i;
246                 size_t si = i % 8;
247                 block += (rola << si) ^ ri;
248                 block ^= (robb << si) ^ ri;
249                 swap(block);
250                 block -= (rolc << si) ^ ri;
251                 block ^= (robd << si) ^ ri;
252             }
253 
254             foreach (j, ref block; (cast(ulong[])data)[1..$])
255                 block ^= data.ptr[j - 1];
256         }
257     }
258 
259     /**
260      * Decrypts the given data using Anura1024 algorithm.
261      *
262      * Params:
263      *  data = The data to be decrypted.
264      *  key = The decryption key, must be 1024 bits (128 bytes).
265      */
266     void decrypt(ref ubyte[] data, string key)
267     {
268         if (key.length != 128)
269             throw new Throwable("Key is not 1024 bits!");
270 
271         key = cast(string)digest!Circe(cast(ubyte[])key[0..32], 0xFC2AB8FFFFF)~
272             cast(string)digest!Circe(cast(ubyte[])key[32..64], 0xCCABB72DA)~
273             cast(string)digest!Circe(cast(ubyte[])key[64..96], 0xABC0001700)~
274             cast(string)digest!Circe(cast(ubyte[])key[96..128], 0x0000D7FFF);
275         ulong rola = (cast(ulong*)key.ptr)[0] ^ (cast(ulong*)key.ptr)[1] | 32;
276         ulong rolb = (cast(ulong*)key.ptr)[2] ^ (cast(ulong*)key.ptr)[3] | 32;
277         ulong rolc = (cast(ulong*)key.ptr)[4] ^ (cast(ulong*)key.ptr)[5] | 32;
278         ulong rold = (cast(ulong*)key.ptr)[6] ^ (cast(ulong*)key.ptr)[7] | 32;
279         ulong roba = (cast(ulong*)key.ptr)[12] ^ (cast(ulong*)key.ptr)[8] | rola;
280         ulong robb = (cast(ulong*)key.ptr)[13] ^ (cast(ulong*)key.ptr)[9] | rolb;
281         ulong robc = (cast(ulong*)key.ptr)[14] ^ (cast(ulong*)key.ptr)[10] | rolc;
282         ulong robd = (cast(ulong*)key.ptr)[15] ^ (cast(ulong*)key.ptr)[11] | rold;
283 
284         ulong[16] set = [
285             rola,
286             rolb,
287             rolc,
288             rold,
289             roba,
290             robb,
291             robc,
292             robd,
293             rola ^ roba,
294             rolb ^ robb,
295             rolc ^ robc,
296             rold ^ robd,
297             rola * roba,
298             rolb * robb,
299             rolc * robc,
300             rold * robd,
301         ];
302 
303         if (data.length % 8 != 0)
304             vacpp(data, 8);
305 
306         void swap(ref ulong block)
307         {
308             uint left = (cast(uint*)&block)[0];
309             (cast(uint*)&block)[0] = (cast(uint*)&block)[1];
310             (cast(uint*)&block)[1] = left;
311         }
312 
313         foreach_reverse (i; 0..2)
314         {
315             foreach_reverse (j, ref b; (cast(ulong[])data)[1..$])
316                 b ^= data.ptr[j - 1];
317 
318             foreach_reverse (j, ref block; cast(ulong[])data)
319             {
320                 size_t ri = ~i;
321                 size_t si = i % 8;
322                 block ^= (robd << si) ^ ri;
323                 block += (rolc << si) ^ ri;
324                 swap(block);
325                 block ^= (robb << si) ^ ri;
326                 block -= (rola << si) ^ ri;
327             }
328         }
329 
330         foreach_reverse (i; 0..(roba % 128))
331         {
332             size_t factor = ((set[i % 16] * i) % ((data.length / 16_384) | 2)) | 1;  
333             size_t s = factor;
334             for (; s < data.length; s += factor) { }
335             s -= factor;
336             for (; s >= factor; s -= factor)
337                 data.swap(s, s - factor);
338         }
339 
340         unvacpp(data);
341     }
342 }