1 /**
2  * Implements the Data Encryption Standard (DES).
3  */
4 module zgrf.crypto.des;
5 
6 import std.bitmanip;
7 
8 import zgrf.bits;
9 
10 // Permuted choice 1
11 immutable ubyte[] pc1 = [
12     57, 49, 41, 33, 25, 17, 9,
13     1, 58, 50, 42, 34, 26, 18,
14     10, 2, 59, 51, 43, 35, 27,
15     19, 11, 3, 60, 52, 44, 36,
16     63, 55, 47, 39, 31, 23, 15,
17     7, 62, 54, 46, 38, 30, 22,
18     14, 6, 61, 53, 45, 37, 29,
19     21, 13, 5, 28, 20, 12, 4
20 ];
21 
22 // Permuted choice 2
23 immutable ubyte[] pc2 = [
24     14, 17, 11, 24, 1, 5,
25     3, 28, 15, 6, 21, 10,
26     23, 19, 12, 4, 26, 8,
27     16, 7, 27, 20, 13, 2,
28     41, 52, 31, 37, 47, 55,
29     30, 40, 51, 45, 33, 48,
30     44, 49, 39, 56, 34, 53,
31     46, 42, 50, 36, 29, 32
32 ];
33 
34 // Initial permutation
35 immutable ubyte[] ip = [
36     58, 50, 42, 34, 26, 18, 10, 2,
37     60, 52, 44, 36, 28, 20, 12, 4,
38     62, 54, 46, 38, 30, 22, 14, 6,
39     64, 56, 48, 40, 32, 24, 16, 8,
40     57, 49, 41, 33, 25, 17, 9, 1,
41     59, 51, 43, 35, 27, 19, 11, 3,
42     61, 53, 45, 37, 29, 21, 13, 5,
43     63, 55, 47, 39, 31, 23, 15, 7
44 ];
45 
46 // Expansion table (Bit-Selection table)
47 immutable ubyte[] e = [
48     32, 1, 2, 3, 4, 5,
49     4, 5, 6, 7, 8, 9,
50     8, 9, 10, 11, 12, 13,
51     12, 13, 14, 15, 16, 17,
52     16, 17, 18, 19, 20, 21,
53     20, 21, 22, 23, 24, 25,
54     24, 25, 26, 27, 28, 29,
55     28, 29, 30, 31, 32, 1
56 ];
57 
58 // Substition Boxes
59 immutable ubyte[][] sboxes = [
60     [
61         14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
62         0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
63         4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
64         15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13
65     ],
66     [
67         15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
68         3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
69         0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
70         13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9
71     ],
72     [
73         10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
74         13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
75         13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
76         1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12
77     ],
78     [
79         7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
80         13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
81         10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
82         3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14
83     ],
84     [
85         2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
86         14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
87         4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
88         11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3
89     ],
90     [
91         12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
92         10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
93         9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
94         4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13
95     ],
96     [
97         4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
98         13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
99         1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
100         6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12
101     ],
102     [
103         13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
104         1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
105         7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
106         2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
107     ]
108 ];
109 
110 // Permutation
111 immutable ubyte[] p = [
112     16, 7, 20, 21,
113     29, 12, 28, 17,
114     1, 15, 23, 26,
115     5, 18, 31, 10,
116     2, 8, 24, 14,
117     32, 27, 3, 9,
118     19, 13, 30, 6,
119     22, 11, 4, 25
120 ];
121 
122 // Final Permutation
123 immutable ubyte[] fp = [
124     40, 8, 48, 16, 56, 24, 64, 32,
125     39, 7, 47, 15, 55, 23, 63, 31,
126     38, 6, 46, 14, 54, 22, 62, 30,
127     37, 5, 45, 13, 53, 21, 61, 29,
128     36, 4, 44, 12, 52, 20, 60, 28,
129     35, 3, 43, 11, 51, 19, 59, 27,
130     34, 2, 42, 10, 50, 18, 58, 26,
131     33, 1, 41, 9, 49, 17, 57, 25
132 ];
133 
134 // Shift table
135 immutable ubyte[] shifts = [1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1];
136 
137 /**
138  * Pointer to a routine that generates the subkeys.
139  * The mixcrypt algorithm for example uses a custom routine.
140  */
141 alias CreateSubkeysFunc = BitArray[16]function(const(ubyte)[] key) pure;
142 /**
143  * Pointer to a routine that processes one block (8 byte).
144  * The mixcrypt algorithm for example uses a custom routine.
145  */
146 alias ProcessBlockFunc = ubyte[]function(const(ubyte)[] block, const BitArray[16] subkeys) pure;
147 
148 /**
149  * Encrypts data using a specific key.
150  *
151  * The input key must be exactly 8 bytes long.
152  *
153  * Params:
154  *  data = The data array to be encrypted
155  *  key = The key used to encrypt the data
156  *  ksFunc = The function responsible for creating the subkeys
157  *  processFunc = The function responsible for processing one block of data
158  *
159  * Returns:
160  *  An array of bytes containing the encrypted data
161  *
162  * See_Also:
163  *  [encrypt]
164 */
165 ubyte[] encrypt2(const(ubyte)[] data, const(ubyte)[] key, CreateSubkeysFunc ksFunc, ProcessBlockFunc processFunc) pure
166 in (key.length == 8, "Key must be 64 bits (8 bytes) long")
167 {
168 
169     size_t length = data.length;
170     const ubyte extraLength = length % 8;
171     const size_t paddedLength = (extraLength > 0) ? length + 8 - extraLength : length;
172     length -= extraLength;
173 
174     auto subkeys = ksFunc(key);
175 
176     ubyte[] encryptedData = new ubyte[paddedLength];
177 
178     for (auto i = 0; i < length; i += 8)
179     {
180         const processedBlock = processFunc(data[i .. i + 8], subkeys);
181         encryptedData[i .. i + 8] = processedBlock;
182     }
183 
184     if (extraLength > 0)
185     {
186         const ubyte padding = 8 - extraLength;
187         ubyte[] lastBlock = new ubyte[8];
188         lastBlock[0 .. extraLength] = data[length .. length + extraLength];
189         foreach (i; 0 .. padding)
190         {
191             lastBlock[extraLength + i] = padding;
192         }
193         const processedBlock = processFunc(lastBlock, subkeys);
194         encryptedData[length .. length + 8] = processedBlock;
195     }
196 
197     return encryptedData;
198 }
199 
200 /**
201  * Encrypts data using a specific key.
202  *
203  * The input key must be exactly 8 bytes long.
204  *
205  * Params:
206  *  data = The data array to be encrypted
207  *  key = The key used to encrypt the data
208  *
209  * Returns:
210  *  An array of bytes containing the encrypted data
211  *
212  * See_Also:
213  *  [encrypt2]
214  */
215 ubyte[] encrypt(const(ubyte)[] data, const(ubyte)[] key) pure
216 {
217     return encrypt2(data, key, &createSubkeys, &processBlock);
218 }
219 
220 /**
221  * Decrypts data using a specific key.
222  *
223  * The input data must be a multiple of 8 bytes and the input key
224  * must be exactly 8 bytes long.
225  *
226  * Params:
227  *  data = The data array to be decrypted
228  *  key =  The key used to decrypt the data
229  *  unencryptedSize = The size/length of the unencrypted data.
230  *                    The returning data will be truncated to this size if it has padding.
231  *  ksFunc = The function responsible for creating the subkeys
232  *  processFunc = The function responsible for processing one block of data
233  *
234  * Returns: An array of bytes containing the decrypted data
235  *
236  * See_Also:
237  *  [decrypt]
238 */
239 ubyte[] decrypt2(const(ubyte)[] data, const(ubyte)[] key, const size_t unencryptedSize, CreateSubkeysFunc ksFunc,
240         ProcessBlockFunc processFunc) pure
241 in (data.length % 8 == 0, "Data must be a multiple of 64 bits (8 bytes)")
242 in (key.length == 8, "Key must be 64 bits (8 bytes) long")
243 {
244 
245     const size_t length = data.length;
246 
247     auto subkeys = ksFunc(key);
248     BitArray temp;
249     foreach (i; 0 .. 8)
250     {
251         temp = subkeys[i];
252         subkeys[i] = subkeys[15 - i];
253         subkeys[15 - i] = temp;
254     }
255 
256     ubyte[] decryptedData = new ubyte[length];
257 
258     for (auto i = 0; i < length; i += 8)
259     {
260         const processedBlock = processFunc(data[i .. i + 8], subkeys);
261         decryptedData[i .. i + 8] = processedBlock;
262     }
263 
264     const long padding = decryptedData.length - unencryptedSize;
265 
266     if (padding > 0)
267     {
268         return decryptedData[0 .. $ - padding];
269     }
270     else
271     {
272         return decryptedData;
273     }
274 }
275 
276 /**
277  * Decrypts data using a specific key.
278  *
279  * Calls [decrypt2] with the subkeys function [createSubkeys] and
280  * the process function [processBlock].
281  *
282  * The input data must be a multiple of 8 bytes and the input key
283  * must be exactly 8 bytes long.
284  *
285  * Params:
286  *  data = The data array to be decrypted
287  *  key =  The key used to decrypt the data
288  *  unencryptedSize = The size/length of the unencrypted data.
289  *                    The returning data will be truncated to this size if it has padding.
290  *
291  * Returns: An array of bytes containing the decrypted data
292  *
293  * See_Also:
294  *  [decrypt2]
295 */
296 ubyte[] decrypt(const(ubyte)[] data, const(ubyte)[] key, const size_t unencryptedSize) pure
297 {
298     return decrypt2(data, key, unencryptedSize, &createSubkeys, &processBlock);
299 }
300 
301 /**
302  * Processes one block (8 bytes) of data.
303  *
304  * The rounds must be greater than 0 and equal or smaller than 16.
305  *
306  * Params:
307  *  block = The block to process (must be 8 bytes long)
308  *  subkeys = The subkeys to use for processing
309  *  rounds = The number of rounds applied
310  *
311  * Returns:
312  *  The processed block
313  *
314  */
315 ubyte[] processBlock(const(ubyte)[] block, const BitArray[16] subkeys, const int rounds) pure
316 in (rounds > 0 && rounds <= 16, "Rounds must be greater than 0 and equal or smaller than 16")
317 {
318     auto blockBits = toBitArray(block);
319 
320     auto blockAfterIP = bitArrayOfSize(64);
321     foreach (i; 0 .. 64)
322     {
323         blockAfterIP[i] = blockBits[ip[i] - 1];
324     }
325 
326     BitArray[] leftBlock;
327     BitArray[] rightBlock;
328 
329     const size_t reservedLeftBlockSize = leftBlock.reserve(rounds + 1);
330     const size_t reservedRightBlockSize = rightBlock.reserve(rounds + 1);
331     assert(reservedLeftBlockSize >= rounds + 1);
332     assert(reservedLeftBlockSize == leftBlock.capacity);
333     assert(reservedRightBlockSize >= rounds + 1);
334     assert(reservedRightBlockSize == rightBlock.capacity);
335 
336     foreach (i; 0 .. rounds + 1)
337     {
338         leftBlock ~= bitArrayOfSize(32);
339         rightBlock ~= bitArrayOfSize(32);
340     }
341 
342     foreach (i; 0 .. 32)
343     {
344         leftBlock[0][i] = blockAfterIP[i];
345         rightBlock[0][i] = blockAfterIP[i + 32];
346     }
347 
348     foreach (i; 1 .. rounds + 1)
349     {
350         leftBlock[i] = rightBlock[i - 1];
351         rightBlock[i] = leftBlock[i - 1] ^ feistel(rightBlock[i - 1], subkeys[i - 1]);
352     }
353 
354     auto concatenatedBlocks = bitArrayOfSize(64);
355     foreach (i; 0 .. 32)
356     {
357         concatenatedBlocks[i] = rightBlock[rounds][i];
358         concatenatedBlocks[i + 32] = leftBlock[rounds][i];
359     }
360 
361     auto finalBlock = bitArrayOfSize(64);
362     foreach (i; 0 .. 64)
363     {
364         finalBlock[i] = concatenatedBlocks[fp[i] - 1];
365     }
366 
367     return toByteArray(finalBlock);
368 }
369 
370 /**
371  * ditto, except that rounds is fixed at 16.
372  */
373 ubyte[] processBlock(const(ubyte)[] block, const BitArray[16] subkeys) pure
374 {
375     return processBlock(block, subkeys, 16);
376 }
377 
378 /**
379  * Creates 16 subkeys given the de-/encryption key.
380  *
381  * Params:
382  *  key = The de-/encryption key
383  *
384  * Returns:
385  *  16 BitArrays that contain the subkeys
386  */
387 BitArray[16] createSubkeys(const(ubyte)[] key) pure
388 {
389     auto keyBits = toBitArray(key);
390     auto keyAfterPC1 = bitArrayOfSize(56);
391 
392     foreach (i; 0 .. 56)
393     {
394         keyAfterPC1[i] = keyBits[pc1[i] - 1];
395     }
396 
397     BitArray[17] leftKey;
398     BitArray[17] rightKey;
399 
400     foreach (i; 0 .. 17)
401     {
402         leftKey[i] = bitArrayOfSize(56);
403         rightKey[i] = bitArrayOfSize(28);
404     }
405 
406     foreach (i; 0 .. 28)
407     {
408         leftKey[0][i] = keyAfterPC1[i];
409         rightKey[0][i] = keyAfterPC1[i + 28];
410     }
411 
412     foreach (i; 1 .. 17)
413     {
414         shiftLeft(leftKey[i - 1], 28, shifts[i - 1], leftKey[i]);
415         shiftLeft(rightKey[i - 1], 28, shifts[i - 1], rightKey[i]);
416     }
417 
418     BitArray[16] keysAfterPC2;
419 
420     foreach (i; 1 .. 17)
421     {
422         foreach (p; 0 .. 28)
423         {
424             leftKey[i][p + 28] = rightKey[i][p];
425         }
426 
427         keysAfterPC2[i - 1] = bitArrayOfSize(48);
428 
429         foreach (q; 0 .. 48)
430         {
431             keysAfterPC2[i - 1][q] = leftKey[i][pc2[q] - 1];
432         }
433     }
434 
435     return keysAfterPC2;
436 }
437 
438 /**
439  * Apply the feistel routine to a given block of data.
440  * Used by [processBlock].
441  *
442  * Params:
443  *  block = The block to apply the routine to
444  *  subkey = The subkey to use for this process
445  *
446  * Returns:
447  *  The processed block
448  */
449 BitArray feistel(const ref BitArray block, const ref BitArray subkey) pure
450 {
451     auto expandedBlock = bitArrayOfSize(48);
452 
453     foreach (i; 0 .. 48)
454     {
455         expandedBlock[i] = block[e[i] - 1];
456     }
457 
458     expandedBlock ^= subkey;
459 
460     auto substitutedBlock = bitArrayOfSize(32);
461     foreach (i; 0 .. 8)
462     {
463         const j = i * 6;
464         const row = expandedBlock[j] * 2 + expandedBlock[j + 5] * 1;
465         const col = expandedBlock[j + 1] * 8 +
466             expandedBlock[j + 2] * 4 +
467             expandedBlock[j + 3] * 2 +
468             expandedBlock[j + 4] * 1;
469 
470         const nibble = sboxes[i][row * 16 + col];
471         const k = j - i * 2;
472         substitutedBlock[k] = (nibble & 8) >> 3;
473         substitutedBlock[k + 1] = (nibble & 4) >> 2;
474         substitutedBlock[k + 2] = (nibble & 2) >> 1;
475         substitutedBlock[k + 3] = (nibble & 1);
476     }
477 
478     auto sBlockAfterP = bitArrayOfSize(32);
479     foreach (i; 0 .. 32)
480     {
481         sBlockAfterP[i] = substitutedBlock[p[i] - 1];
482     }
483 
484     return sBlockAfterP;
485 }
486 
487 ///
488 unittest
489 {
490     const ubyte[] message = [0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x87, 0x0A, 0x0B];
491     const ubyte[] key = [0x0e, 0x32, 0x92, 0x32, 0xea, 0x6d, 0x0d, 0x73];
492     const ubyte[] encoded = [0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
493         0xAF, 0x73, 0x75, 0x18, 0xB3, 0xB4, 0xD2, 0x9D];
494 
495     const ubyte[] actualEncoded = encrypt(message, key);
496     const ubyte[] actualDecoded = decrypt(encoded, key, message.length);
497 
498     assert(actualEncoded == encoded);
499     assert(actualDecoded == message);
500 }