1 module tagion.crypto.aes.tiny_aes.tiny_aes;
2 /*
3 
4   This is an implementation of the AES algorithm, specifically ECB, CTR and CBC mode.
5   Block size can be chosen in aes.h - available choices are AES128, AES192, AES256.
6 
7   The implementation is verified against the test vectors in:
8   National Institute of Standards and Technology Special Publication 800-38A 2001 ED
9 
10   ECB-AES128
11   ----------
12 
13   plain-text:
14   6bc1bee22e409f96e93d7e117393172a
15   ae2d8a571e03ac9c9eb76fac45af8e51
16   30c81c46a35ce411e5fbc1191a0a52ef
17   f69f2445df4f9b17ad2b417be66c3710
18 
19   key:
20   2b7e151628aed2a6abf7158809cf4f3c
21 
22   resulting cipher
23   3ad77bb40d7a3660a89ecaf32466ef97
24   f5d3d58503b9699de785895a96fdbaaf
25   43b1cd7f598ece23881b00e3ed030688
26   7b0c785e27e8ad3f8223207104725dd4
27 
28 
29   NOTE:   String length must be evenly divisible by 16byte (str_len % 16 == 0)
30   You should pad the end of the string with zeros if this is not the case.
31   For AES192/256 the key size is proportionally larger.
32 
33 */
34 
35 //version = PRINT;
36 
37 /+
38 The different modes is explained in
39 https://www.highgo.ca/2019/08/08/the-difference-in-five-modes-in-the-aes-encryption-algorithm/
40 +/
41 enum Mode {
42     ECB, /// mode: Electronic Code Book mode
43     CBC, /// mode: Cipher Block Chaining mode
44     CFB, /// mode: Cipher FeedBack mode
45     //    OFB, /// mode: Output FeedBack mode
46     CTR /// mode: Counter mode
47 
48 }
49 
50 @safe @nogc
51 struct Tiny_AES(int KEY_LENGTH, Mode mode = Mode.CBC) {
52     pure nothrow {
53         enum KEY_SIZE = KEY_LENGTH >> 3;
54         enum BLOCK_SIZE = 16; // Block length in bytes - AES is 128b block only
55 
56         enum Nb = 4;
57         static if (KEY_LENGTH is 256) {
58             enum Nk = 8;
59             enum Nr = 14;
60             enum keyExpSize = 240;
61 
62         }
63         else static if (KEY_LENGTH is 192) {
64             enum Nk = 6;
65             enum Nr = 12;
66             enum keyExpSize = 208;
67 
68         }
69         else static if (KEY_LENGTH is 128) {
70             enum Nk = 4; // The number of 32 bit words in a key.
71             enum Nr = 10; // The number of rounds in AES Cipher.
72             enum keyExpSize = 176;
73         }
74 
75         struct Context {
76             ubyte[keyExpSize] round_key;
77             static if (mode !is Mode.ECB) {
78                 ubyte[BLOCK_SIZE] Iv;
79             }
80         }
81 
82         // jcallan@github points out that declaring Multiply as a function
83         // reduces code size considerably with the Keil ARM compiler.
84         // See this link for more information: https://github.com/kokke/tiny-AES-C/pull/3
85         // #ifndef MULTIPLY_AS_A_FUNCTION
86         //   #define MULTIPLY_AS_A_FUNCTION 0
87         // #endif
88 
89         /*****************************************************************************/
90         /* Private variables:                                                        */
91         /*****************************************************************************/
92         // state - array holding the intermediate results during decryption.
93         alias state_t = ubyte[4][4];
94         union State {
95             state_t* state_p;
96             protected ubyte* buf_p;
97             @trusted
98             static ref state_t opCall(ref return scope ubyte[] buf)
99             in {
100                 assert(buf.length >= state_t.sizeof);
101             }
102             do {
103                 State state;
104                 state.buf_p = buf.ptr;
105                 return *state.state_p;
106             }
107 
108             @trusted
109             static ref state_t opCall(ref return scope ubyte[BLOCK_SIZE] buf) {
110                 State state;
111                 state.buf_p = cast(ubyte*)&buf;
112                 return *state.state_p;
113             }
114 
115             static assert(state_t.sizeof is BLOCK_SIZE);
116         }
117 
118         // The lookup-tables are marked const so they can be placed in read-only storage instead of RAM
119         // The numbers below can be computed dynamically trading ROM for RAM -
120         // This can be useful in (embedded) bootloader applications, where ROM is often limited.
121         shared static immutable(ubyte[256]) sbox;
122         shared static immutable(ubyte[256]) rsbox;
123         shared static this() {
124             // The sbox was changed to from enum to immutable becuase of some odd segment fault
125             sbox = [
126                 //0     1    2      3     4    5     6     7      8    9     A      B    C     D     E     F
127                 0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5, 0x30, 0x01, 0x67,
128                 0x2b, 0xfe, 0xd7, 0xab, 0x76,
129                 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0, 0xad, 0xd4, 0xa2,
130                 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
131                 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc, 0x34, 0xa5, 0xe5,
132                 0xf1, 0x71, 0xd8, 0x31, 0x15,
133                 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a, 0x07, 0x12, 0x80,
134                 0xe2, 0xeb, 0x27, 0xb2, 0x75,
135                 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0, 0x52, 0x3b, 0xd6,
136                 0xb3, 0x29, 0xe3, 0x2f, 0x84,
137                 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b, 0x6a, 0xcb, 0xbe,
138                 0x39, 0x4a, 0x4c, 0x58, 0xcf,
139                 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85, 0x45, 0xf9, 0x02,
140                 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
141                 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5, 0xbc, 0xb6, 0xda,
142                 0x21, 0x10, 0xff, 0xf3, 0xd2,
143                 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17, 0xc4, 0xa7, 0x7e,
144                 0x3d, 0x64, 0x5d, 0x19, 0x73,
145                 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88, 0x46, 0xee, 0xb8,
146                 0x14, 0xde, 0x5e, 0x0b, 0xdb,
147                 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c, 0xc2, 0xd3, 0xac,
148                 0x62, 0x91, 0x95, 0xe4, 0x79,
149                 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9, 0x6c, 0x56, 0xf4,
150                 0xea, 0x65, 0x7a, 0xae, 0x08,
151                 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6, 0xe8, 0xdd, 0x74,
152                 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
153                 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e, 0x61, 0x35, 0x57,
154                 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
155                 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94, 0x9b, 0x1e, 0x87,
156                 0xe9, 0xce, 0x55, 0x28, 0xdf,
157                 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68, 0x41, 0x99, 0x2d,
158                 0x0f, 0xb0, 0x54, 0xbb, 0x16
159             ];
160             static immutable(ubyte[256]) _reverse_sbox() {
161                 ubyte[256] result;
162                 static foreach (i; 0 .. 256) {
163                     result[sbox[i]] = i;
164                 }
165                 return result;
166             }
167             // Generate the reverse sbox
168             rsbox = _reverse_sbox();
169         }
170 
171         // The round constant word array, Rcon[i], contains the values given by
172         // x to the power (i-1) being powers of x (x is denoted as {02}) in the field GF(2^8)
173         enum Rcon = cast(ubyte[11])[
174                 0x8d, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36
175             ];
176 
177         /*
178  * Jordan Goulder points out in PR #12 (https://github.com/kokke/tiny-AES-C/pull/12),
179  * that you can remove most of the elements in the Rcon array, because they are unused.
180  *
181  * From Wikipedia's article on the Rijndael key schedule @ https://en.wikipedia.org/wiki/Rijndael_key_schedule#Rcon
182  *
183  * "Only the first some of these constants are actually used – up to rcon[10] for AES-128 (as 11 round keys are needed),
184  *  up to rcon[8] for AES-192, up to rcon[7] for AES-256. rcon[0] is not used in AES algorithm."
185  */
186 
187         // This function produces Nb(Nr+1) round keys. The round keys are used in each round to decrypt the states.
188         private void keyExpansion(ref const(ubyte[KEY_SIZE]) Key) {
189             ubyte[4] tempa; // Used for the column/row operations
190             foreach (i; 0 .. Nk) {
191                 static foreach (j; 0 .. 4) {
192                     ctx.round_key[(i * 4) + j] = Key[(i * 4) + j];
193                 }
194             }
195             // All other round keys are found from the previous round keys.
196             static foreach (i; Nk .. Nb * (Nr + 1)) {
197                 static foreach (j; 0 .. 4) {
198                     {
199                         const k = (i - 1) * 4;
200                         tempa[j] = ctx.round_key[k + j];
201                     }
202                 }
203 
204                 if (i % Nk == 0) {
205                     // This function shifts the 4 bytes in a word to the left once.
206                     // [a0,a1,a2,a3] becomes [a1,a2,a3,a0]
207 
208                     // Function RotWord()
209                     {
210                         const ubyte u8tmp = tempa[0];
211                         tempa[0] = tempa[1];
212                         tempa[1] = tempa[2];
213                         tempa[2] = tempa[3];
214                         tempa[3] = u8tmp;
215                     }
216 
217                     // SubWord() is a function that takes a four-byte input word and
218                     // applies the S-box to each of the four bytes to produce an output word.
219 
220                     // Function Subword()
221                     static foreach (j; 0 .. 4) {
222                         tempa[j] = sbox[tempa[j]];
223                     }
224 
225                     tempa[0] = tempa[0] ^ Rcon[i / Nk];
226                 }
227                 static if (KEY_LENGTH == 256) {
228                     if (i % Nk is 4) {
229                         // Function Subword()
230                         static foreach (j; 0 .. 4) {
231                             tempa[j] = sbox[tempa[j]];
232                         }
233                     }
234                 }
235                 {
236                     const j = i * 4;
237                     const k = (i - Nk) * 4;
238                     static foreach (l; 0 .. 4) {
239                         ctx.round_key[j + l] = ctx.round_key[k + l] ^ tempa[l];
240                     }
241                 }
242             }
243         }
244 
245         Context ctx;
246         static if (mode is Mode.ECB) {
247             this(ref const(ubyte[KEY_SIZE]) key) {
248                 keyExpansion(key);
249             }
250         }
251         else {
252             this(ref const(ubyte[KEY_SIZE]) key, ref const(ubyte[BLOCK_SIZE]) iv) {
253                 keyExpansion(key);
254                 ctx.Iv = iv;
255             }
256 
257             void iv(ref const(ubyte[BLOCK_SIZE]) iv) {
258                 ctx.Iv = iv;
259             }
260         }
261 
262         private {
263             // This function adds the round key to state.
264             // The round key is added to the state by an XOR function.
265             void addRoundKey(ubyte round, ref scope state_t state) const {
266                 static foreach (i; 0 .. 4) {
267                     static foreach (j; 0 .. 4) {
268                         state[i][j] ^= ctx.round_key[(round * Nb * 4) + (i * Nb) + j];
269                     }
270                 }
271             }
272 
273             // The SubBytes Function Substitutes the values in the
274             // state matrix with values in an S-box.
275             static void SubBytes(ref scope state_t state) {
276                 static foreach (i; 0 .. 4) {
277                     static foreach (j; 0 .. 4) {
278                         state[j][i] = sbox[state[j][i]];
279                     }
280                 }
281             }
282 
283             // The ShiftRows() function shifts the rows in the state to the left.
284             // Each row is shifted with different offset.
285             // Offset = Row number. So the first row is not shifted.
286             static void ShiftRows(ref scope state_t state) {
287                 ubyte temp;
288                 // Rotate first row 1 columns to left
289                 temp = state[0][1];
290                 state[0][1] = state[1][1];
291                 state[1][1] = state[2][1];
292                 state[2][1] = state[3][1];
293                 state[3][1] = temp;
294 
295                 // Rotate second row 2 columns to left
296                 temp = state[0][2];
297                 state[0][2] = state[2][2];
298                 state[2][2] = temp;
299 
300                 temp = state[1][2];
301                 state[1][2] = state[3][2];
302                 state[3][2] = temp;
303 
304                 // Rotate third row 3 columns to left
305                 temp = state[0][3];
306                 state[0][3] = state[3][3];
307                 state[3][3] = state[2][3];
308                 state[2][3] = state[1][3];
309                 state[1][3] = temp;
310             }
311 
312             static ubyte xtime(ubyte x) {
313                 return ((x << 1) ^ (((x >> 7) & 1) * 0x1b)) & ubyte.max;
314             }
315 
316             // MixColumns function mixes the columns of the state matrix
317             static void MixColumns(ref scope state_t state) {
318                 ubyte Tmp, Tm;
319                 static foreach (i; 0 .. 4) {
320                     {
321                         const t = state[i][0];
322                         Tmp = state[i][0] ^ state[i][1] ^ state[i][2] ^ state[i][3];
323                         Tm = state[i][0] ^ state[i][1];
324                         Tm = xtime(Tm);
325                         state[i][0] ^= Tm ^ Tmp;
326                         Tm = state[i][1] ^ state[i][2];
327                         Tm = xtime(Tm);
328                         state[i][1] ^= Tm ^ Tmp;
329                         Tm = state[i][2] ^ state[i][3];
330                         Tm = xtime(Tm);
331                         state[i][2] ^= Tm ^ Tmp;
332                         Tm = state[i][3] ^ t;
333                         Tm = xtime(Tm);
334                         state[i][3] ^= Tm ^ Tmp;
335                     }
336                 }
337             }
338 
339             // Multiply is used to multiply numbers in the field GF(2^8)
340             // Note: The last call to xtime() is unneeded, but often ends up generating a smaller binary
341             //       The compiler seems to be able to vectorize the operation better this way.
342             //       See https://github.com/kokke/tiny-AES-c/pull/34
343             static ubyte Multiply(ubyte x, ubyte y) {
344                 return (((y & 1) * x) ^
345                         ((y >> 1 & 1) * xtime(x)) ^
346                         ((y >> 2 & 1) * xtime(xtime(x))) ^
347                         ((y >> 3 & 1) * xtime(xtime(xtime(x)))) ^
348                         ((y >> 4 & 1) * xtime(xtime(xtime(xtime(x))))));
349                 /* this last call to xtime() can be omitted */
350             }
351 
352             //    static ubyte getSBoxInvert(ubyte num) {return rsbox[num];};
353 
354             // MixColumns function mixes the columns of the state matrix.
355             // The method used to multiply may be difficult to understand for the inexperienced.
356             // Please use the references to gain more information.
357             static void InvMixColumns(ref scope state_t state) {
358                 static foreach (i; 0 .. 4) {
359                     {
360                         const a = state[i][0];
361                         const b = state[i][1];
362                         const c = state[i][2];
363                         const d = state[i][3];
364 
365                         state[i][0] = Multiply(a, 0x0e) ^ Multiply(b, 0x0b) ^ Multiply(c, 0x0d) ^ Multiply(d, 0x09);
366                         state[i][1] = Multiply(a, 0x09) ^ Multiply(b, 0x0e) ^ Multiply(c, 0x0b) ^ Multiply(d, 0x0d);
367                         state[i][2] = Multiply(a, 0x0d) ^ Multiply(b, 0x09) ^ Multiply(c, 0x0e) ^ Multiply(d, 0x0b);
368                         state[i][3] = Multiply(a, 0x0b) ^ Multiply(b, 0x0d) ^ Multiply(c, 0x09) ^ Multiply(d, 0x0e);
369                     }
370                 }
371             }
372 
373             // The SubBytes Function Substitutes the values in the
374             // state matrix with values in an S-box.
375             static void InvSubBytes(ref scope state_t state) {
376                 static foreach (i; 0 .. 4) {
377                     static foreach (j; 0 .. 4) {
378                         state[j][i] = rsbox[state[j][i]];
379                     }
380                 }
381             }
382 
383             static void InvShiftRows(ref scope state_t state) {
384                 ubyte temp;
385 
386                 // Rotate first row 1 columns to right
387                 temp = state[3][1];
388                 state[3][1] = state[2][1];
389                 state[2][1] = state[1][1];
390                 state[1][1] = state[0][1];
391                 state[0][1] = temp;
392 
393                 // Rotate second row 2 columns to right
394                 temp = state[0][2];
395                 state[0][2] = state[2][2];
396                 state[2][2] = temp;
397 
398                 temp = state[1][2];
399                 state[1][2] = state[3][2];
400                 state[3][2] = temp;
401 
402                 // Rotate third row 3 columns to right
403                 temp = state[0][3];
404                 state[0][3] = state[1][3];
405                 state[1][3] = state[2][3];
406                 state[2][3] = state[3][3];
407                 state[3][3] = temp;
408             }
409 
410             // Cipher is the main function that encrypts the PlainText.
411             void cipher(ref scope state_t state) {
412                 ubyte round = 0;
413 
414                 // Add the First round key to the state before starting the rounds.
415                 addRoundKey(0, state);
416 
417                 // There will be Nr rounds.
418                 // The first Nr-1 rounds are identical.
419                 // These Nr rounds are executed in the loop below.
420                 // Last one without MixColumns()
421                 for (round = 1;; ++round) {
422                     SubBytes(state);
423                     ShiftRows(state);
424                     if (round == Nr) {
425                         break;
426                     }
427                     MixColumns(state);
428                     addRoundKey(round, state);
429                 }
430                 // Add round key to last round
431                 addRoundKey(Nr, state);
432             }
433 
434             void InvCipher(ref scope state_t state) {
435                 ubyte round = 0;
436 
437                 // Add the First round key to the state before starting the rounds.
438                 addRoundKey(Nr, state);
439 
440                 // There will be Nr rounds.
441                 // The first Nr-1 rounds are identical.
442                 // These Nr rounds are executed in the loop below.
443                 // Last one without InvMixColumn()
444                 for (round = (Nr - 1);; --round) {
445                     InvShiftRows(state);
446                     InvSubBytes(state);
447                     addRoundKey(round, state);
448                     if (round == 0) {
449                         break;
450                     }
451                     InvMixColumns(state);
452                 }
453 
454             }
455 
456             static void xorWithIv(ubyte[] buf, ref const(ubyte[BLOCK_SIZE]) Iv) {
457                 static foreach (i; 0 .. BLOCK_SIZE) {
458                     buf[i] ^= Iv[i];
459                 }
460             }
461 
462         }
463         /*****************************************************************************/
464         /* Public functions:                                                         */
465         /*****************************************************************************/
466 
467         //        version(unittest) {
468         static if (mode is Mode.ECB) {
469             void encrypt(scope ubyte[] buf) {
470                 // The next function call encrypts the PlainText with the Key using AES algorithm.
471                 while (buf.length) {
472                     cipher(State(buf));
473                     buf = buf[BLOCK_SIZE .. $];
474                 }
475             }
476 
477             void decrypt(scope ubyte[] buf) {
478                 // The next function call decrypts the PlainText with the Key using AES algorithm.
479                 while (buf.length) {
480                     InvCipher(State(buf));
481                     buf = buf[BLOCK_SIZE .. $];
482                 }
483             }
484         }
485 
486         static if (mode is Mode.CBC) {
487             void encrypt(scope ubyte[] buf) {
488                 auto Iv = ctx.Iv;
489                 while (buf.length) {
490                     xorWithIv(buf, Iv);
491                     cipher(State(buf));
492                     Iv = buf[0 .. BLOCK_SIZE];
493                     buf = buf[BLOCK_SIZE .. $];
494                 }
495                 /* store Iv in ctx for next call */
496                 ctx.Iv = Iv;
497             }
498 
499             void decrypt(scope ubyte[] buf) {
500                 ubyte[BLOCK_SIZE] storeNextIv;
501                 while (buf.length) {
502                     storeNextIv = buf[0 .. BLOCK_SIZE];
503                     InvCipher(State(buf));
504                     xorWithIv(buf, ctx.Iv);
505                     ctx.Iv = storeNextIv;
506                     buf = buf[BLOCK_SIZE .. $];
507                 }
508             }
509         }
510 
511         static if (mode is Mode.CFB) {
512             void encrypt(scope ubyte[] buf) {
513                 scope storeNextIv = ctx.Iv;
514                 while (buf.length) {
515                     cipher(State(storeNextIv));
516                     xorWithIv(buf, storeNextIv);
517                     storeNextIv = buf[0 .. BLOCK_SIZE];
518                     buf = buf[BLOCK_SIZE .. $];
519                 }
520             }
521 
522             void decrypt(scope ubyte[] buf) {
523                 scope storeNextIv = ctx.Iv;
524                 while (buf.length) {
525                     cipher(State(storeNextIv));
526                     const xor = storeNextIv;
527                     storeNextIv = buf[0 .. BLOCK_SIZE];
528                     xorWithIv(buf, xor);
529                     buf = buf[BLOCK_SIZE .. $];
530                 }
531             }
532         }
533 
534         /* Symmetrical operation: same function for encrypting as for decrypting. Note any IV/nonce should never be reused with the same key */
535         static if (mode is Mode.CTR) {
536             alias encrypt = xcrypt;
537             alias decrypt = xcrypt;
538             void xcrypt(scope ubyte[] buf) {
539                 ubyte[BLOCK_SIZE] buffer;
540                 size_t bi; //=BLOCK_SIZE;
541                 foreach (i; 0 .. buf.length) {
542                     if (i % BLOCK_SIZE == 0) { //bi == BLOCK_SIZE) { /* we need to regen xor compliment in buffer */
543                         buffer[0 .. BLOCK_SIZE] = ctx.Iv;
544                         cipher(State(buffer));
545 
546                         /* Increment Iv and handle overflow */
547                         foreach_reverse (j; 0 .. BLOCK_SIZE) {
548                             /* inc will overflow */
549                             if (ctx.Iv[j] == 255) {
550                                 ctx.Iv[j] = 0;
551                                 continue;
552                             }
553                             ctx.Iv[j] += 1;
554                             break;
555                         }
556                         bi = 0;
557                     }
558 
559                     buf[i] ^= buffer[bi];
560                     bi++;
561                 }
562             }
563         }
564 
565     }
566     unittest {
567         version (PRINT) import std.stdio;
568 
569         version (PRINT)
570             writefln("Start unittest %s %s", KEY_LENGTH, mode);
571         // prints string as hex
572         static void phex(ubyte[] str) {
573             version (PRINT) {
574                 ubyte len = KEY_SIZE;
575                 ubyte i;
576                 for (i = 0; i < len; ++i)
577                     writef("%.2x", str[i]);
578                 writeln;
579             }
580         }
581 
582         version (PRINT)
583             static if (mode is Mode.ECB && KEY_LENGTH is 128) {
584                 {
585                     // Example of more verbose verification
586 
587                     // 128bit key
588                     ubyte[KEY_SIZE] key = [
589                         0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7,
590                         0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
591                     ];
592                     // 512bit text
593                     ubyte[64] plain_text = [
594                         0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d,
595                         0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
596                         0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7,
597                         0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
598                         0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb,
599                         0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
600                         0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b,
601                         0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
602                     ];
603 
604                     // print text to encrypt, key and IV
605                     writeln("ECB encrypt verbose:\n");
606                     writeln("plain text:\n");
607                     foreach (i; 0 .. 4) {
608                         phex(plain_text[i * 16 .. $]);
609                     }
610                     writeln();
611 
612                     write("key:");
613                     phex(key);
614                     writeln();
615 
616                     // print the resulting cipher as 4 x 16 byte strings
617                     writeln("ciphertext:");
618                     //ctx ctx;
619                     auto aes = Tiny_AES(key);
620 
621                     foreach (i; 0 .. 4) {
622                         aes.encrypt(plain_text[i * 16 .. $]);
623                         phex(plain_text[i * 16 .. $]);
624                     }
625                     writeln();
626                 }
627             }
628 
629         static if (mode is Mode.ECB) {
630             { // test_encrypt_ecb
631                 static if (KEY_LENGTH is 256) {
632                     ubyte[KEY_SIZE] key = [
633                         0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73,
634                         0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
635                         0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98,
636                         0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4
637                     ];
638                     ubyte[] outdata = [
639                         0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, 0x06, 0x4b,
640                         0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8
641                     ];
642                 }
643                 else static if (KEY_LENGTH is 192) {
644                     ubyte[KEY_SIZE] key = [
645                         0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10,
646                         0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5,
647                         0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b
648                     ];
649                     ubyte[] outdata = [
650                         0xbd, 0x33, 0x4f, 0x1d, 0x6e, 0x45, 0xf2, 0x5f, 0xf7, 0x12,
651                         0xa2, 0x14, 0x57, 0x1f, 0xa5, 0xcc
652                     ];
653                 }
654                 else static if (KEY_LENGTH is 128) {
655                     ubyte[KEY_SIZE] key = [
656                         0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7,
657                         0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
658                     ];
659                     ubyte[] outdata = [
660                         0x3a, 0xd7, 0x7b, 0xb4, 0x0d, 0x7a, 0x36, 0x60, 0xa8, 0x9e,
661                         0xca, 0xf3, 0x24, 0x66, 0xef, 0x97
662                     ];
663                 }
664 
665                 ubyte[] indata = [
666                     0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d,
667                     0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a
668                 ];
669                 //Tiny_AES aes;
670                 auto aes = Tiny_AES(key);
671                 aes.encrypt(indata);
672 
673                 version (PRINT)
674                     writeln("ECB encrypt: ");
675 
676                 assert(outdata == indata);
677             }
678         }
679 
680         static if (mode is Mode.CBC) {
681             { // test_decrypt_cbc
682                 static if (KEY_LENGTH is 256) {
683                     ubyte[KEY_SIZE] key = [
684                         0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73,
685                         0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
686                         0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98,
687                         0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4
688                     ];
689                     ubyte[] indata = [
690                         0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba, 0x77, 0x9e,
691                         0xab, 0xfb, 0x5f, 0x7b, 0xfb, 0xd6,
692                         0x9c, 0xfc, 0x4e, 0x96, 0x7e, 0xdb, 0x80, 0x8d, 0x67, 0x9f,
693                         0x77, 0x7b, 0xc6, 0x70, 0x2c, 0x7d,
694                         0x39, 0xf2, 0x33, 0x69, 0xa9, 0xd9, 0xba, 0xcf, 0xa5, 0x30,
695                         0xe2, 0x63, 0x04, 0x23, 0x14, 0x61,
696                         0xb2, 0xeb, 0x05, 0xe2, 0xc3, 0x9b, 0xe9, 0xfc, 0xda, 0x6c,
697                         0x19, 0x07, 0x8c, 0x6a, 0x9d, 0x1b
698                     ];
699                 }
700                 else static if (KEY_LENGTH is 192) {
701                     ubyte[KEY_SIZE] key = [
702                         0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10,
703                         0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5,
704                         0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b
705                     ];
706                     ubyte[] indata = [
707                         0x4f, 0x02, 0x1d, 0xb2, 0x43, 0xbc, 0x63, 0x3d, 0x71, 0x78,
708                         0x18, 0x3a, 0x9f, 0xa0, 0x71, 0xe8,
709                         0xb4, 0xd9, 0xad, 0xa9, 0xad, 0x7d, 0xed, 0xf4, 0xe5, 0xe7,
710                         0x38, 0x76, 0x3f, 0x69, 0x14, 0x5a,
711                         0x57, 0x1b, 0x24, 0x20, 0x12, 0xfb, 0x7a, 0xe0, 0x7f, 0xa9,
712                         0xba, 0xac, 0x3d, 0xf1, 0x02, 0xe0,
713                         0x08, 0xb0, 0xe2, 0x79, 0x88, 0x59, 0x88, 0x81, 0xd9, 0x20,
714                         0xa9, 0xe6, 0x4f, 0x56, 0x15, 0xcd
715                     ];
716                 }
717                 else static if (KEY_LENGTH is 128) {
718                     ubyte[KEY_SIZE] key = [
719                         0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7,
720                         0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
721                     ];
722                     ubyte[] indata = [
723                         0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9,
724                         0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d,
725                         0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb,
726                         0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2,
727                         0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16,
728                         0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16,
729                         0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, 0x12, 0x0e,
730                         0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7
731                     ];
732                 }
733 
734                 ubyte[BLOCK_SIZE] iv = [
735                     0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
736                     0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
737                 ];
738                 ubyte[] outdata = [
739                     0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d,
740                     0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
741                     0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7,
742                     0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
743                     0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb,
744                     0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
745                     0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b,
746                     0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
747                 ];
748 
749                 //Tiny_AES aes;
750                 auto aes = Tiny_AES(key, iv);
751                 aes.decrypt(indata);
752 
753                 version (PRINT)
754                     writeln("CBC decrypt: ");
755 
756                 assert(outdata == indata);
757             }
758         }
759 
760         static if (mode is Mode.CBC) {
761             { // test_encrypt_cbc
762                 static if (KEY_LENGTH is 256) {
763                     ubyte[KEY_SIZE] key = [
764                         0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73,
765                         0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
766                         0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98,
767                         0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4
768                     ];
769                     ubyte[] outdata = [
770                         0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba, 0x77, 0x9e,
771                         0xab, 0xfb, 0x5f, 0x7b, 0xfb, 0xd6,
772                         0x9c, 0xfc, 0x4e, 0x96, 0x7e, 0xdb, 0x80, 0x8d, 0x67, 0x9f,
773                         0x77, 0x7b, 0xc6, 0x70, 0x2c, 0x7d,
774                         0x39, 0xf2, 0x33, 0x69, 0xa9, 0xd9, 0xba, 0xcf, 0xa5, 0x30,
775                         0xe2, 0x63, 0x04, 0x23, 0x14, 0x61,
776                         0xb2, 0xeb, 0x05, 0xe2, 0xc3, 0x9b, 0xe9, 0xfc, 0xda, 0x6c,
777                         0x19, 0x07, 0x8c, 0x6a, 0x9d, 0x1b
778                     ];
779                 }
780                 else static if (KEY_LENGTH is 192) {
781 
782                     ubyte[KEY_SIZE] key = [
783                         0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10,
784                         0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5,
785                         0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b
786                     ];
787                     ubyte[] outdata = [
788                         0x4f, 0x02, 0x1d, 0xb2, 0x43, 0xbc, 0x63, 0x3d, 0x71, 0x78,
789                         0x18, 0x3a, 0x9f, 0xa0, 0x71, 0xe8,
790                         0xb4, 0xd9, 0xad, 0xa9, 0xad, 0x7d, 0xed, 0xf4, 0xe5, 0xe7,
791                         0x38, 0x76, 0x3f, 0x69, 0x14, 0x5a,
792                         0x57, 0x1b, 0x24, 0x20, 0x12, 0xfb, 0x7a, 0xe0, 0x7f, 0xa9,
793                         0xba, 0xac, 0x3d, 0xf1, 0x02, 0xe0,
794                         0x08, 0xb0, 0xe2, 0x79, 0x88, 0x59, 0x88, 0x81, 0xd9, 0x20,
795                         0xa9, 0xe6, 0x4f, 0x56, 0x15, 0xcd
796                     ];
797                 }
798                 else static if (KEY_LENGTH is 128) {
799                     ubyte[KEY_SIZE] key = [
800                         0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7,
801                         0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
802                     ];
803                     ubyte[] outdata = [
804                         0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9,
805                         0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d,
806                         0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb,
807                         0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2,
808                         0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16,
809                         0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16,
810                         0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, 0x12, 0x0e,
811                         0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7
812                     ];
813                 }
814 
815                 ubyte[BLOCK_SIZE] iv = [
816                     0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
817                     0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
818                 ];
819                 ubyte[] indata = [
820                     0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d,
821                     0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
822                     0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7,
823                     0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
824                     0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb,
825                     0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
826                     0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b,
827                     0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
828                 ];
829                 //            ctx ctx;
830                 //Tiny_AES aes;
831                 auto aes = Tiny_AES(key, iv);
832                 aes.encrypt(indata);
833 
834                 version (PRINT)
835                     writeln("CBC encrypt: ");
836 
837                 assert(outdata == indata);
838             }
839         }
840 
841         static if (mode is Mode.CTR) {
842             { // test ctr
843                 static if (KEY_LENGTH is 256) {
844                     ubyte[KEY_SIZE] key = [
845                         0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73,
846                         0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
847                         0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98,
848                         0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4
849                     ];
850                     ubyte[64] indata = [
851                         0x60, 0x1e, 0xc3, 0x13, 0x77, 0x57, 0x89, 0xa5, 0xb7, 0xa7,
852                         0xf5, 0x04, 0xbb, 0xf3, 0xd2, 0x28,
853                         0xf4, 0x43, 0xe3, 0xca, 0x4d, 0x62, 0xb5, 0x9a, 0xca, 0x84,
854                         0xe9, 0x90, 0xca, 0xca, 0xf5, 0xc5,
855                         0x2b, 0x09, 0x30, 0xda, 0xa2, 0x3d, 0xe9, 0x4c, 0xe8, 0x70,
856                         0x17, 0xba, 0x2d, 0x84, 0x98, 0x8d,
857                         0xdf, 0xc9, 0xc5, 0x8d, 0xb6, 0x7a, 0xad, 0xa6, 0x13, 0xc2,
858                         0xdd, 0x08, 0x45, 0x79, 0x41, 0xa6
859                     ];
860                 }
861                 else static if (KEY_LENGTH is 192) {
862                     ubyte[KEY_SIZE] key = [
863                         0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10,
864                         0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5,
865                         0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b
866                     ];
867                     ubyte[64] indata = [
868                         0x1a, 0xbc, 0x93, 0x24, 0x17, 0x52, 0x1c, 0xa2, 0x4f, 0x2b,
869                         0x04, 0x59, 0xfe, 0x7e, 0x6e, 0x0b,
870                         0x09, 0x03, 0x39, 0xec, 0x0a, 0xa6, 0xfa, 0xef, 0xd5, 0xcc,
871                         0xc2, 0xc6, 0xf4, 0xce, 0x8e, 0x94,
872                         0x1e, 0x36, 0xb2, 0x6b, 0xd1, 0xeb, 0xc6, 0x70, 0xd1, 0xbd,
873                         0x1d, 0x66, 0x56, 0x20, 0xab, 0xf7,
874                         0x4f, 0x78, 0xa7, 0xf6, 0xd2, 0x98, 0x09, 0x58, 0x5a, 0x97,
875                         0xda, 0xec, 0x58, 0xc6, 0xb0, 0x50
876                     ];
877                 }
878                 else static if (KEY_LENGTH is 128) {
879                     ubyte[KEY_SIZE] key = [
880                         0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7,
881                         0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
882                     ];
883                     ubyte[64] indata = [
884                         0x87, 0x4d, 0x61, 0x91, 0xb6, 0x20, 0xe3, 0x26, 0x1b, 0xef,
885                         0x68, 0x64, 0x99, 0x0d, 0xb6, 0xce,
886                         0x98, 0x06, 0xf6, 0x6b, 0x79, 0x70, 0xfd, 0xff, 0x86, 0x17,
887                         0x18, 0x7b, 0xb9, 0xff, 0xfd, 0xff,
888                         0x5a, 0xe4, 0xdf, 0x3e, 0xdb, 0xd5, 0xd3, 0x5e, 0x5b, 0x4f,
889                         0x09, 0x02, 0x0d, 0xb0, 0x3e, 0xab,
890                         0x1e, 0x03, 0x1d, 0xda, 0x2f, 0xbe, 0x03, 0xd1, 0x79, 0x21,
891                         0x70, 0xa0, 0xf3, 0x00, 0x9c, 0xee
892                     ];
893                 }
894                 ubyte[16] iv = [
895                     0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,
896                     0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
897                 ];
898                 ubyte[64] outdata = [
899                     0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d,
900                     0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a,
901                     0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7,
902                     0x6f, 0xac, 0x45, 0xaf, 0x8e, 0x51,
903                     0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb,
904                     0xc1, 0x19, 0x1a, 0x0a, 0x52, 0xef,
905                     0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b,
906                     0x41, 0x7b, 0xe6, 0x6c, 0x37, 0x10
907                 ];
908                 //Tiny_AES aes;
909                 auto aes = Tiny_AES(key, iv);
910                 aes.xcrypt(indata);
911 
912                 assert(outdata == indata);
913             }
914         }
915 
916         static if (mode is Mode.ECB) {
917             { // test_decrypt_ecb
918                 static if (KEY_LENGTH is 256) {
919                     ubyte[KEY_SIZE] key = [
920                         0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73,
921                         0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
922                         0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98,
923                         0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4
924                     ];
925                     ubyte[] indata = [
926                         0xf3, 0xee, 0xd1, 0xbd, 0xb5, 0xd2, 0xa0, 0x3c, 0x06, 0x4b,
927                         0x5a, 0x7e, 0x3d, 0xb1, 0x81, 0xf8
928                     ];
929                 }
930                 else static if (KEY_LENGTH is 192) {
931                     ubyte[KEY_SIZE] key = [
932                         0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10,
933                         0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5,
934                         0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b
935                     ];
936                     ubyte[] indata = [
937                         0xbd, 0x33, 0x4f, 0x1d, 0x6e, 0x45, 0xf2, 0x5f, 0xf7, 0x12,
938                         0xa2, 0x14, 0x57, 0x1f, 0xa5, 0xcc
939                     ];
940                 }
941                 else static if (KEY_LENGTH is 128) {
942                     ubyte[KEY_SIZE] key = [
943                         0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7,
944                         0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
945                     ];
946                     ubyte[] indata = [
947                         0x3a, 0xd7, 0x7b, 0xb4, 0x0d, 0x7a, 0x36, 0x60, 0xa8, 0x9e,
948                         0xca, 0xf3, 0x24, 0x66, 0xef, 0x97
949                     ];
950                 }
951 
952                 ubyte[] outdata = [
953                     0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d,
954                     0x7e, 0x11, 0x73, 0x93, 0x17, 0x2a
955                 ];
956                 //Tiny_AES aes;
957                 auto aes = Tiny_AES(key);
958                 aes.decrypt(indata);
959 
960                 version (PRINT)
961                     writeln("ECB decrypt: ");
962 
963                 assert(outdata == indata);
964             }
965         }
966     }
967 }
968 
969 unittest {
970     version (PRINT) import std.stdio;
971     import std.traits : EnumMembers;
972 
973     static foreach (key_size; [128, 192, 256]) {
974         static foreach (mode; EnumMembers!Mode) {
975             {
976                 alias AES = Tiny_AES!(key_size, mode);
977                 AES aes;
978                 version (PRINT)
979                     writefln("%s", AES.stringof);
980             }
981         }
982     }
983 }