1 module tagion.crypto.aes.AESCrypto;
2 
3 import tagion.basic.Debug : __format;
4 
5 alias AES128 = AESCrypto!128;
6 alias AES196 = AESCrypto!192;
7 alias AES256 = AESCrypto!256;
8 
9 struct AESCrypto(int KEY_LENGTH) {
10     static assert((KEY_LENGTH is 128) || (KEY_LENGTH is 192) || (KEY_LENGTH is 256),
11             format("The KEYLENGTH of the %s must be 128, 196 or 256 not %d", AESCrypto.stringof, KEY_LENGTH));
12 
13     @disable this();
14 
15     static size_t enclength(const size_t inputlength) pure nothrow {
16         return ((inputlength / BLOCK_SIZE) + ((inputlength % BLOCK_SIZE == 0) ? 0 : 1)) * BLOCK_SIZE;
17     }
18 
19     import tagion.crypto.aes.tiny_aes.tiny_aes;
20 
21     alias AES = Tiny_AES!(KEY_LENGTH, Mode.CBC);
22     enum BLOCK_SIZE = AES.BLOCK_SIZE;
23     enum KEY_SIZE = AES.KEY_SIZE;
24     static void crypt_parse(bool ENCRYPT = true)(
25             const(ubyte[]) key,
26     ubyte[BLOCK_SIZE] iv,
27     ref ubyte[] data) nothrow
28     in (data)
29     in (data.length % BLOCK_SIZE == 0,
30         __format("Data must be an equal number of %d bytes but is %d", BLOCK_SIZE, data.length))
31     in (key.length is KEY_SIZE,
32         __format("The key size must be %d bytes not %d", KEY_SIZE, key.length))
33     do {
34         scope aes = AES(key[0 .. KEY_SIZE], iv);
35         scope (exit) {
36             aes = aes.init;
37         }
38         static if (ENCRYPT) {
39             aes.encrypt(data);
40         }
41         else {
42             aes.decrypt(data);
43         }
44     }
45 
46     static void crypt(bool ENCRYPT = true)(
47             scope const(ubyte[]) key,
48     scope const(ubyte[]) iv,
49     return scope const(ubyte[]) indata, ref ubyte[] outdata) pure nothrow @safe
50     in {
51         if (outdata.length) {
52             assert(enclength(indata.length) == outdata.length,
53                     __format("Output data must be an equal number of %d bytes", BLOCK_SIZE));
54             assert(iv.length is BLOCK_SIZE,
55                     __format("The iv size must be %d bytes not %d", BLOCK_SIZE, iv.length));
56         }
57     }
58     do {
59         if (outdata.length < indata.length) {
60             outdata = indata.dup;
61         }
62         else if (&outdata[0]!is &indata[0]) {
63             outdata[0 .. $] = indata[0 .. $];
64         }
65         size_t old_length;
66         if (outdata.length % BLOCK_SIZE !is 0) {
67             old_length = outdata.length;
68             outdata.length = enclength(outdata.length);
69         }
70         scope (exit) {
71             if (old_length) {
72                 outdata.length = old_length;
73             }
74         }
75         ubyte[BLOCK_SIZE] temp_iv = iv[0 .. BLOCK_SIZE];
76         crypt_parse!ENCRYPT(key, temp_iv, outdata);
77     }
78 
79     alias encrypt = crypt!true;
80     alias decrypt = crypt!false;
81 
82     unittest {
83         import std.algorithm.iteration : map;
84         import std.array : array;
85         import std.range : iota;
86         import tagion.utils.Random;
87 
88         { // Encrypt
89             immutable(ubyte[]) indata = [
90                 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e,
91                 0x11, 0x73, 0x93, 0x17, 0x2a,
92                 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f,
93                 0xac, 0x45, 0xaf, 0x8e, 0x51,
94                 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1,
95                 0x19, 0x1a, 0x0a, 0x52, 0xef,
96                 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41,
97                 0x7b, 0xe6, 0x6c, 0x37, 0x10
98             ];
99             static if (KEY_LENGTH is 256) {
100                 immutable(ubyte[KEY_SIZE]) key = [
101                     0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73,
102                     0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
103                     0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98,
104                     0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4
105                 ];
106                 immutable(ubyte[64]) outdata = [
107                     0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba, 0x77, 0x9e,
108                     0xab, 0xfb, 0x5f, 0x7b, 0xfb, 0xd6,
109                     0x9c, 0xfc, 0x4e, 0x96, 0x7e, 0xdb, 0x80, 0x8d, 0x67, 0x9f,
110                     0x77, 0x7b, 0xc6, 0x70, 0x2c, 0x7d,
111                     0x39, 0xf2, 0x33, 0x69, 0xa9, 0xd9, 0xba, 0xcf, 0xa5, 0x30,
112                     0xe2, 0x63, 0x04, 0x23, 0x14, 0x61,
113                     0xb2, 0xeb, 0x05, 0xe2, 0xc3, 0x9b, 0xe9, 0xfc, 0xda, 0x6c,
114                     0x19, 0x07, 0x8c, 0x6a, 0x9d, 0x1b
115                 ];
116             }
117             else static if (KEY_LENGTH is 192) {
118                 immutable(ubyte[KEY_SIZE]) key = [
119                     0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10,
120                     0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5,
121                     0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b
122                 ];
123                 immutable(ubyte[64]) outdata = [
124                     0x4f, 0x02, 0x1d, 0xb2, 0x43, 0xbc, 0x63, 0x3d, 0x71, 0x78,
125                     0x18, 0x3a, 0x9f, 0xa0, 0x71, 0xe8,
126                     0xb4, 0xd9, 0xad, 0xa9, 0xad, 0x7d, 0xed, 0xf4, 0xe5, 0xe7,
127                     0x38, 0x76, 0x3f, 0x69, 0x14, 0x5a,
128                     0x57, 0x1b, 0x24, 0x20, 0x12, 0xfb, 0x7a, 0xe0, 0x7f, 0xa9,
129                     0xba, 0xac, 0x3d, 0xf1, 0x02, 0xe0,
130                     0x08, 0xb0, 0xe2, 0x79, 0x88, 0x59, 0x88, 0x81, 0xd9, 0x20,
131                     0xa9, 0xe6, 0x4f, 0x56, 0x15, 0xcd
132                 ];
133             }
134             else static if (KEY_LENGTH is 128) {
135                 immutable(ubyte[KEY_SIZE]) key = [
136                     0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7,
137                     0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
138                 ];
139                 immutable(ubyte[64]) outdata = [
140                     0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9,
141                     0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d,
142                     0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb,
143                     0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2,
144                     0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16,
145                     0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16,
146                     0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, 0x12, 0x0e,
147                     0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7
148                 ];
149             }
150             ubyte[BLOCK_SIZE] iv = [
151                 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
152                 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
153             ];
154 
155             ubyte[] enc_output;
156             AESCrypto.encrypt(key, iv, indata, enc_output);
157         }
158 
159         { // Decrypt
160             static if (KEY_LENGTH is 256) {
161                 immutable(ubyte[KEY_SIZE]) key = [
162                     0x60, 0x3d, 0xeb, 0x10, 0x15, 0xca, 0x71, 0xbe, 0x2b, 0x73,
163                     0xae, 0xf0, 0x85, 0x7d, 0x77, 0x81,
164                     0x1f, 0x35, 0x2c, 0x07, 0x3b, 0x61, 0x08, 0xd7, 0x2d, 0x98,
165                     0x10, 0xa3, 0x09, 0x14, 0xdf, 0xf4
166                 ];
167                 immutable(ubyte[64]) indata = [
168                     0xf5, 0x8c, 0x4c, 0x04, 0xd6, 0xe5, 0xf1, 0xba, 0x77, 0x9e,
169                     0xab, 0xfb, 0x5f, 0x7b, 0xfb, 0xd6,
170                     0x9c, 0xfc, 0x4e, 0x96, 0x7e, 0xdb, 0x80, 0x8d, 0x67, 0x9f,
171                     0x77, 0x7b, 0xc6, 0x70, 0x2c, 0x7d,
172                     0x39, 0xf2, 0x33, 0x69, 0xa9, 0xd9, 0xba, 0xcf, 0xa5, 0x30,
173                     0xe2, 0x63, 0x04, 0x23, 0x14, 0x61,
174                     0xb2, 0xeb, 0x05, 0xe2, 0xc3, 0x9b, 0xe9, 0xfc, 0xda, 0x6c,
175                     0x19, 0x07, 0x8c, 0x6a, 0x9d, 0x1b
176                 ];
177             }
178             else static if (KEY_LENGTH is 192) {
179                 immutable(ubyte[KEY_SIZE]) key = [
180                     0x8e, 0x73, 0xb0, 0xf7, 0xda, 0x0e, 0x64, 0x52, 0xc8, 0x10,
181                     0xf3, 0x2b, 0x80, 0x90, 0x79, 0xe5,
182                     0x62, 0xf8, 0xea, 0xd2, 0x52, 0x2c, 0x6b, 0x7b
183                 ];
184                 immutable(ubyte[64]) indata = [
185                     0x4f, 0x02, 0x1d, 0xb2, 0x43, 0xbc, 0x63, 0x3d, 0x71, 0x78,
186                     0x18, 0x3a, 0x9f, 0xa0, 0x71, 0xe8,
187                     0xb4, 0xd9, 0xad, 0xa9, 0xad, 0x7d, 0xed, 0xf4, 0xe5, 0xe7,
188                     0x38, 0x76, 0x3f, 0x69, 0x14, 0x5a,
189                     0x57, 0x1b, 0x24, 0x20, 0x12, 0xfb, 0x7a, 0xe0, 0x7f, 0xa9,
190                     0xba, 0xac, 0x3d, 0xf1, 0x02, 0xe0,
191                     0x08, 0xb0, 0xe2, 0x79, 0x88, 0x59, 0x88, 0x81, 0xd9, 0x20,
192                     0xa9, 0xe6, 0x4f, 0x56, 0x15, 0xcd
193                 ];
194             }
195             else static if (KEY_LENGTH is 128) {
196                 immutable(ubyte[KEY_SIZE]) key = [
197                     0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6, 0xab, 0xf7,
198                     0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
199                 ];
200                 immutable(ubyte[64]) indata = [
201                     0x76, 0x49, 0xab, 0xac, 0x81, 0x19, 0xb2, 0x46, 0xce, 0xe9,
202                     0x8e, 0x9b, 0x12, 0xe9, 0x19, 0x7d,
203                     0x50, 0x86, 0xcb, 0x9b, 0x50, 0x72, 0x19, 0xee, 0x95, 0xdb,
204                     0x11, 0x3a, 0x91, 0x76, 0x78, 0xb2,
205                     0x73, 0xbe, 0xd6, 0xb8, 0xe3, 0xc1, 0x74, 0x3b, 0x71, 0x16,
206                     0xe6, 0x9e, 0x22, 0x22, 0x95, 0x16,
207                     0x3f, 0xf1, 0xca, 0xa1, 0x68, 0x1f, 0xac, 0x09, 0x12, 0x0e,
208                     0xca, 0x30, 0x75, 0x86, 0xe1, 0xa7
209                 ];
210             }
211             ubyte[BLOCK_SIZE] iv = [
212                 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a,
213                 0x0b, 0x0c, 0x0d, 0x0e, 0x0f
214             ];
215             immutable(ubyte[]) outdata = [
216                 0x6b, 0xc1, 0xbe, 0xe2, 0x2e, 0x40, 0x9f, 0x96, 0xe9, 0x3d, 0x7e,
217                 0x11, 0x73, 0x93, 0x17, 0x2a,
218                 0xae, 0x2d, 0x8a, 0x57, 0x1e, 0x03, 0xac, 0x9c, 0x9e, 0xb7, 0x6f,
219                 0xac, 0x45, 0xaf, 0x8e, 0x51,
220                 0x30, 0xc8, 0x1c, 0x46, 0xa3, 0x5c, 0xe4, 0x11, 0xe5, 0xfb, 0xc1,
221                 0x19, 0x1a, 0x0a, 0x52, 0xef,
222                 0xf6, 0x9f, 0x24, 0x45, 0xdf, 0x4f, 0x9b, 0x17, 0xad, 0x2b, 0x41,
223                 0x7b, 0xe6, 0x6c, 0x37, 0x10
224             ];
225 
226             ubyte[] dec_output;
227             auto temp_iv = iv;
228             AESCrypto.decrypt(key, temp_iv, indata, dec_output);
229             assert(dec_output == outdata);
230         }
231 
232     }
233 }
234 
235 unittest {
236     static foreach (key_size; [128, 192, 256]) {
237         {
238             alias AES = AESCrypto!key_size;
239         }
240     }
241 }