1 /// Implementation of Mira digesters.
2 module tern.digest.mira;
3 
4 import core.simd;
5 import tern.digest;
6 import tern.digest.circe;
7 import tern.algorithm;
8 
9 /**
10  * Implementation of Mira256 digester, internally backed by `tern.digest.circe`.
11  *
12  * Mira is an incredibly fast stream encryption algorithm based on shuffling and vector
13  * xor operations on data.
14  *
15  * Example:
16  * ```d
17  * string key = "SpWc5m7uednxBqV2YrKk83tZ6UayFEPRSpWc5m7uednxBqV2YrKk83tZ6UayFEPR";
18  * ubyte[] data = cast(ubyte[])"Hello World!";
19  * Mira256.encrypt(data, key);
20  * Mira256.decrypt(data, key);
21  */
22 public static @digester class Mira256
23 {
24 public:
25 static:
26 pure:
27     /**
28      * Encrypts the given byte array `data`.
29      *
30      * This method encrypts the data using the Mira algorithm with the specified encryption `key`.
31      * and an optional `seed` value. The encryption is done in place.
32      *
33      * Params:
34      *  data = Reference to the byte array to be encrypted.
35      *  key = The encryption key as a string. Must be either 256 or 512 bits.
36      *  seed = An optional seed value used for encryption. Defaults to 0.
37      */
38     void encrypt(ref ubyte[] data, string key, ulong seed = 0)
39     {
40         assert(key.length == 32, "Key must be 256 bits!");
41 
42         ubyte[] keyFront = digest!Circe(cast(ubyte[])key, seed);
43         ulong a = (cast(ulong*)keyFront.ptr)[0];
44         ulong b = (cast(ulong*)keyFront.ptr)[1];
45         ulong c = (cast(ulong*)keyFront.ptr)[2];
46         ulong d = (cast(ulong*)keyFront.ptr)[3];
47 
48         size_t rlen = data.length - (data.length % 16);
49         size_t factor = ((a + b + c + d) % ((data.length / 16_384) | 2)) | 1;
50 
51         for (size_t i = factor; i < data.length; i += factor)
52         {
53             ubyte b0 = data[i];
54             data[i] = data[i - factor];
55             data[i - factor] = b0;
56         }
57 
58         foreach (i, ref vec; data.portionTo!ulong2)
59         {
60             size_t ri = ~i;
61             size_t si = i % 8;
62             vec += factor;
63             vec ^= (a << si) ^ i; 
64             vec ^= (b << si) + ri;
65             vec ^= (c << si) * ri;
66             vec ^= (d << si) - ri;
67             vec[1] ^= vec[0] << 8;
68             import std.stdio;
69         }
70 
71         foreach (i, ref _b; data[rlen..$])
72         {
73             size_t ri = ~i;
74             size_t si = i % 8;
75             _b += factor;
76             _b ^= (a << si) ^ i;
77             _b ^= (b << si) + ri;
78             _b ^= (c << si) * ri;
79             _b ^= (d << si) - ri;
80         }
81 
82         for (size_t i = factor; i < data.length; i += factor)
83         {
84             ubyte b0 = data[i];
85             data[i] = data[i - factor];
86             data[i - factor] = b0;
87         }
88     }
89 
90     /**
91      * Decrypts the given byte array `data`.
92      *
93      * This method decrypts the data using the Mira algorithm with the specified decryption `key`.
94      * and an optional `seed` value. The decryption is done in place.
95      *
96      * Params:
97      *  data = Reference to the byte array to be decrypted.
98      *  key = The decryption key as a string. Must be either 256 or 512 bits.
99      *  seed = An optional seed value used for decryption. Defaults to 0.
100      */
101     void decrypt(ref ubyte[] data, string key, ulong seed = 0)
102     {
103         assert(key.length == 32, "Key must be 256 bits!");
104             
105         ubyte[] keyFront = digest!Circe(cast(ubyte[])key, seed);
106         ulong a = (cast(ulong*)keyFront.ptr)[0];
107         ulong b = (cast(ulong*)keyFront.ptr)[1];
108         ulong c = (cast(ulong*)keyFront.ptr)[2];
109         ulong d = (cast(ulong*)keyFront.ptr)[3];
110 
111         size_t factor = ((a + b + c + d) % ((data.length / 16_384) | 2)) | 1;
112         size_t rlen = data.length - (data.length % 16);
113 
114         size_t s = factor;
115         for (; s < data.length; s += factor) { }
116         s -= factor;
117         for (; s >= factor; s -= factor)
118         {
119             ubyte b0 = data[s];
120             data[s] = data[s - factor];
121             data[s - factor] = b0;
122         }
123 
124         ulong2* vptr = cast(ulong2*)data.ptr;
125         foreach (i; 0..(data.length / 16))
126         {
127             size_t ri = ~i;
128             size_t si = i % 8;
129      *vptr ^= (a << si) ^ ri; 
130      *vptr ^= (b << si) ^ ri;
131      *vptr ^= (c << si) ^ ri; 
132      *vptr ^= (d << si) ^ ri;
133      *vptr -= factor;
134             vptr += 1;
135         }
136 
137         foreach (i, ref _b; data[rlen..$])
138         {
139             size_t ri = ~i;
140             size_t si = i % 8;
141             _b ^= (d << si) - ri;
142             _b ^= (c << si) * ri;
143             _b ^= (b << si) + ri;
144             _b ^= (a << si) ^ i;
145             _b -= factor;
146         }
147 
148         s = factor;
149         for (; s < data.length; s += factor) { }
150         s -= factor;
151         for (; s >= factor; s -= factor)
152         {
153             ubyte b0 = data[s];
154             data[s] = data[s - factor];
155             data[s - factor] = b0;
156         }
157     }
158 }
159 
160 /**
161  * Implementation of Mira512 digester, internally backed by `tern.digest.circe`.
162  *
163  * Mira is an incredibly fast stream encryption algorithm based on shuffling and vector
164  * xor operations on data.
165  *
166  * Example:
167  * ```d
168  * string key = "SpWc5m7uednxBqV2YrKk83tZ6UayFEPRSpWc5m7uednxBqV2YrKk83tZ6UayFEPR";
169  * ubyte[] data = cast(ubyte[])"Hello World!";
170  * Mira512.encrypt(data, key);
171  * Mira512.decrypt(data, key);
172  */
173 public static @digester class Mira512
174 {
175 public:
176 static:
177 pure:
178     /**
179      * Encrypts the given byte array `data`.
180      *
181      * This method encrypts the data using the Mira algorithm with the specified encryption `key`.
182      * and an optional `seed` value. The encryption is done in place.
183      *
184      * Params:
185      *  data = Reference to the byte array to be encrypted.
186      *  key = The encryption key as a string. Must be either 256 or 512 bits.
187      *  seed = An optional seed value used for encryption. Defaults to 0.
188      */
189     void encrypt(ref ubyte[] data, string key, ulong seed = 0)
190     {
191         if (key.length != 64)
192             throw new Throwable("Key is not 512 bits!");
193 
194         ubyte[] keyFront = digest!Circe(cast(ubyte[])key[0..32], seed);
195         ubyte[] keyBack = digest!Circe(cast(ubyte[])key[32..64], seed);
196 
197         ulong a = (cast(ulong*)keyFront.ptr)[0];
198         ulong b = (cast(ulong*)keyFront.ptr)[1];
199         ulong c = (cast(ulong*)keyFront.ptr)[2];
200         ulong d = (cast(ulong*)keyFront.ptr)[3];
201         ulong ap = (cast(ulong*)keyBack.ptr)[0];
202         ulong bp = (cast(ulong*)keyBack.ptr)[1];
203         ulong cp = (cast(ulong*)keyBack.ptr)[2];
204         ulong dp = (cast(ulong*)keyBack.ptr)[3];
205 
206         size_t rlen = data.length - (data.length % 16);
207         size_t factor = ((ap + bp + cp + dp) % ((data.length / 16_384) | 2)) | 1;
208 
209         for (size_t i = factor; i < data.length; i += factor)
210         {
211             ubyte b0 = data[i];
212             data[i] = data[i - factor];
213             data[i - factor] = b0;
214         }
215 
216         ulong2* vptr = cast(ulong2*)data.ptr;
217         foreach (i; 0..(data.length / 16))
218         {
219             size_t ri = ~i;
220             size_t si = i % 8;
221      *vptr += factor;
222      *vptr ^= (a << si) ^ ri; 
223      *vptr ^= (b << si) ^ ri;
224      *vptr ^= (c << si) ^ ri; 
225      *vptr ^= (d << si) ^ ri;
226             vptr += 1;
227         }
228 
229         foreach (i, ref _b; data[rlen..$])
230         {
231             size_t ri = ~i;
232             size_t si = i % 8;
233             _b += factor;
234             _b ^= (a << si) ^ ri;
235             _b ^= (b << si) ^ ri;
236             _b ^= (c << si) ^ ri;
237             _b ^= (d << si) ^ ri;
238         }
239     }
240 
241     /**
242      * Decrypts the given byte array `data`.
243      *
244      * This method decrypts the data using the Mira algorithm with the specified decryption `key`.
245      * and an optional `seed` value. The decryption is done in place.
246      *
247      * Params:
248      *  data = Reference to the byte array to be decrypted.
249      *  key = The decryption key as a string. Must be either 256 or 512 bits.
250      *  seed = An optional seed value used for decryption. Defaults to 0.
251      */
252     void decrypt(ref ubyte[] data, string key, ulong seed = 0)
253     {
254         if (key.length != 64)
255             throw new Throwable("Key is not 512 bits!");
256         
257         ubyte[] keyFront = digest!Circe(cast(ubyte[])key[0..32], seed);
258         ubyte[] keyBack = digest!Circe(cast(ubyte[])key[32..64], seed);
259         
260         ulong a = (cast(ulong*)keyFront.ptr)[0];
261         ulong b = (cast(ulong*)keyFront.ptr)[1];
262         ulong c = (cast(ulong*)keyFront.ptr)[2];
263         ulong d = (cast(ulong*)keyFront.ptr)[3];
264         ulong ap = (cast(ulong*)keyBack.ptr)[0];
265         ulong bp = (cast(ulong*)keyBack.ptr)[1];
266         ulong cp = (cast(ulong*)keyBack.ptr)[2];
267         ulong dp = (cast(ulong*)keyBack.ptr)[3];
268 
269         size_t rlen = data.length - (data.length % 16);
270         size_t factor = ((ap + bp + cp + dp) % ((data.length / 16_384) | 2)) | 1;
271 
272         ulong2* vptr = cast(ulong2*)data.ptr;
273         foreach (i; 0..(data.length / 16))
274         {
275             size_t ri = ~i;
276             size_t si = i % 8;
277      *vptr ^= (a << si) ^ ri; 
278      *vptr ^= (b << si) ^ ri;
279      *vptr ^= (c << si) ^ ri; 
280      *vptr ^= (d << si) ^ ri;
281      *vptr -= factor;
282             vptr += 1;
283         }
284 
285         foreach (i, ref _b; data[rlen..$])
286         {
287             size_t ri = ~i;
288             size_t si = i % 8;
289             _b ^= (a << si) ^ ri;
290             _b ^= (b << si) ^ ri;
291             _b ^= (c << si) ^ ri;
292             _b ^= (d << si) ^ ri;
293             _b -= factor;
294         }
295 
296         size_t s = factor;
297         for (; s < data.length; s += factor) { }
298         s -= factor;
299         for (; s >= factor; s -= factor)
300         {
301             ubyte b0 = data[s];
302             data[s] = data[s - factor];
303             data[s - factor] = b0;
304         }
305     }
306 }