diff --git a/source/styx2000/protosrv/des.d b/source/styx2000/protosrv/des.d new file mode 100644 index 0000000..657dc60 --- /dev/null +++ b/source/styx2000/protosrv/des.d @@ -0,0 +1,333 @@ +// Written in the D programming language. + +/** +This class provides an implementation of the DES cryptoalgorithm. +Implementation is based on the following code (by Daniel Huertas Gonzalez): https://github.com/dhuertas/DES/blob/master/des.c + +Copyright: LightHouse Software, 2023 +License: $(HTTP https://github.com/aquaratixc/ESL-License, Experimental Software License 1.0). +Authors: Oleg Bakharev, + Daniel Huertas Gonzalez + +See also: + https://en.wikipedia.org/wiki/Data_Encryption_Standard, FIPS PUB 46-3 +*/ +module styx2000.protosrv.des; + +/// DES functional mode: encryption or decryption +enum CRYPTOPERATION +{ + /// encrypt with DES + ENCRYPT, + /// decrypt with DES + DECRYPT +} + + +/// DES algorith implementation +class DES +{ + private { + enum LB32_MASK = 0x00000001; + enum LB64_MASK = 0x0000000000000001; + enum L64_MASK = 0x00000000ffffffff; + enum H64_MASK = 0xffffffff00000000; + } + + private { + /* Initial Permutation Table */ + static char[] IP = [ + 58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46, + 38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, 57, 49, 41, 33, 25, 17, + 9, 1, 59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63, + 55, 47, 39, 31, 23, 15, 7 + ]; + + /* Inverse Initial Permutation Table */ + static char[] PI = [ + 40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46, + 14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29, 36, 4, 44, 12, 52, 20, + 60, 28, 35, 3, 43, 11, 51, 19, 59, 27, 34, 2, 42, 10, 50, 18, 58, 26, 33, + 1, 41, 9, 49, 17, 57, 25 + ]; + + /* Expansion table */ + static char[] E = [ + 32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, 8, 9, 10, 11, 12, 13, 12, 13, 14, 15, + 16, 17, 16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25, 24, 25, 26, 27, + 28, 29, 28, 29, 30, 31, 32, 1 + ]; + + /* Post S-Box permutation */ + static char[] P = [ + 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, 2, 8, 24, + 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25 + ]; + + /* The S-Box tables */ + static char[64][8] S = [ + [ + /* S1 */ + 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, 0, 15, 7, 4, + 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, 4, 1, 14, 8, 13, 6, 2, 11, + 15, 12, 9, 7, 3, 10, 5, 0, 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, + 6, 13 + ], + [ + /* S2 */ + 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, 3, 13, 4, 7, 15, + 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, + 12, 6, 9, 3, 2, 15, 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9 + ], + [ + /* S3 */ + 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, 13, 7, 0, 9, 3, + 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, 13, 6, 4, 9, 8, 15, 3, 0, 11, + 1, 2, 12, 5, 10, 14, 7, 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, + 12 + ], + [ + /* S4 */ + 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, 13, 8, 11, 5, 6, + 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, + 3, 14, 5, 2, 8, 4, 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14 + ], + [ + /* S5 */ + 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, 14, 11, 2, 12, + 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, 4, 2, 1, 11, 10, 13, 7, 8, 15, + 9, 12, 5, 6, 3, 0, 14, 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, + 3 + ], + [ + /* S6 */ + 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, 10, 15, 4, 2, 7, + 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, + 4, 10, 1, 13, 11, 6, 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13 + ], + [ + /* S7 */ + 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, 13, 0, 11, 7, 4, + 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, 1, 4, 11, 13, 12, 3, 7, 14, 10, + 15, 6, 8, 0, 5, 9, 2, 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12 + ], + [ + /* S8 */ + 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, 1, 15, 13, 8, 10, + 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, + 10, 13, 15, 3, 5, 8, 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11 + ] + ]; + + /* Permuted Choice 1 Table */ + static char[] PC1 = [ + 57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43, + 35, 27, 19, 11, 3, 60, 52, 44, 36, 63, 55, 47, 39, 31, 23, 15, 7, 62, 54, + 46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4 + ]; + + /* Permuted Choice 2 Table */ + static char[] PC2 = [ + 14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8, 16, 7, + 27, 20, 13, 2, 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, 44, 49, 39, + 56, 34, 53, 46, 42, 50, 36, 29, 32 + ]; + + /* Iteration Shift Array */ + static char[] iteration_shift = [ + /* 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 */ + 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1 + ]; + } + + + /** + Method for encryption or decryption of one elementary block (block size - 64 bit, key size - 64 bit). + Params: + input = Elementary block for encrypt/decrypt with DES. + key = Key for encrypt/decrypt + mode = Type of operation mode of DES class - encryption or decryption + */ + static ulong des(ulong input, ulong key, CRYPTOPERATION mode) + { + /* 8 bits */ + char row, column; + + /* 28 bits */ + uint C = 0; + uint D = 0; + + /* 32 bits */ + uint L = 0; + uint R = 0; + uint s_output = 0; + uint f_function_res = 0; + uint temp = 0; + + /* 48 bits */ + ulong[16] sub_key = 0; + ulong s_input = 0; + + /* 56 bits */ + ulong permuted_choice_1 = 0; + ulong permuted_choice_2 = 0; + + /* 64 bits */ + ulong init_perm_res = 0; + ulong inv_init_perm_res = 0; + ulong pre_output = 0; + + /* initial permutation */ + for (int i = 0; i < 64; i++) + { + init_perm_res <<= 1; + init_perm_res |= (input >> (64 - IP[i])) & LB64_MASK; + } + + L = cast(uint)(init_perm_res >> 32) & L64_MASK; + R = cast(uint) init_perm_res & L64_MASK; + + /* initial key schedule calculation */ + for (int i = 0; i < 56; i++) + { + permuted_choice_1 <<= 1; + permuted_choice_1 |= (key >> (64 - PC1[i])) & LB64_MASK; + } + + C = cast(uint)((permuted_choice_1 >> 28) & 0x000000000fffffff); + D = cast(uint)(permuted_choice_1 & 0x000000000fffffff); + + /* Calculation of the 16 keys */ + for (int i = 0; i < 16; i++) + { + /* key schedule */ + // shifting Ci and Di + for (int j = 0; j < iteration_shift[i]; j++) + { + C = 0x0fffffff & (C << 1) | 0x00000001 & (C >> 27); + D = 0x0fffffff & (D << 1) | 0x00000001 & (D >> 27); + } + + permuted_choice_2 = 0; + permuted_choice_2 = ((cast(ulong) C) << 28) | cast(ulong) D; + + sub_key[i] = 0; + + for (int j = 0; j < 48; j++) + { + sub_key[i] <<= 1; + sub_key[i] |= (permuted_choice_2 >> (56 - PC2[j])) & LB64_MASK; + } + + } + + for (int i = 0; i < 16; i++) + { + /* f(R,k) function */ + s_input = 0; + + for (int j = 0; j < 48; j++) + { + s_input <<= 1; + s_input |= cast(ulong)((R >> (32 - E[j])) & LB32_MASK); + } + + /* + * Encryption/Decryption + * XORing expanded Ri with Ki + */ + final switch (mode) with (CRYPTOPERATION) + { + case ENCRYPT: + s_input = s_input ^ sub_key[i]; + break; + case DECRYPT: + s_input = s_input ^ sub_key[15 - i]; + break; + } + + /* S-Box Tables */ + for (int j = 0; j < 8; j++) + { + // 00 00 RCCC CR00 00 00 00 00 00 s_input + // 00 00 1000 0100 00 00 00 00 00 row mask + // 00 00 0111 1000 00 00 00 00 00 column mask + + row = cast(char) ((s_input & (0x0000840000000000 >> 6 * j)) >> 42 - 6 * j); + row = (row >> 4) | row & 0x01; + + column = cast(char) ((s_input & (0x0000780000000000 >> 6 * j)) >> 43 - 6 * j); + + s_output <<= 4; + s_output |= cast(uint) (S[j][16 * row + column] & 0x0f); + } + + f_function_res = 0; + + for (int j = 0; j < 32; j++) + { + f_function_res <<= 1; + f_function_res |= (s_output >> (32 - P[j])) & LB32_MASK; + } + + temp = R; + R = L ^ f_function_res; + L = temp; + } + + pre_output = ((cast(ulong) R) << 32) | cast(ulong) L; + + /* inverse initial permutation */ + for (int i = 0; i < 64; i++) + { + inv_init_perm_res <<= 1; + inv_init_perm_res |= (pre_output >> (64 - PI[i])) & LB64_MASK; + } + + return inv_init_perm_res; + } +} + +unittest { + enum ulong[16] TESTING_VECTOR = [ + 0x8da744e0c94e5e17, + 0x0cdb25e3ba3c6d79, + 0x4784c4ba5006081f, + 0x1cf1fc126f2ef842, + 0xe4be250042098d13, + 0x7bfc5dc6adb5797c, + 0x1ab3b4d82082fb28, + 0xc1576a14de707097, + 0x739b68cd2e26782a, + 0x2a59f0c464506edb, + 0xa5c39d4251f0a81e, + 0x7239ac9a6107ddb1, + 0x070cac8590241233, + 0x78f87b6e3dfecf61, + 0x95ec2578c2c433f0, + 0x1b1a2ddb4c642438, + ]; + + ulong input = 0x9474B8E8C73BCA7D; + ulong key = 0x0000000000000000; + ulong result = input; + + ulong[16] vector; + + for (int i = 0; i < 16; i++) + { + if (i % 2 == 0) + { + result = DES.des(result, result, CRYPTOPERATION.ENCRYPT); + } + else + { + result = DES.des(result, result, CRYPTOPERATION.DECRYPT); + } + + vector[i] = result; + } + + assert(vector == TESTING_VECTOR); + assert(result == 0x1b1a2ddb4c642438); +}