1 // Written in the D programming language.
2 
3 /**
4 This class provides an implementation of the DES cryptoalgorithm.
5 Implementation is based on the following code (by Daniel Huertas Gonzalez): https://github.com/dhuertas/DES/blob/master/des.c
6 
7 Copyright: LightHouse Software, 2023
8 License:   $(HTTP https://github.com/aquaratixc/ESL-License, Experimental Software License 1.0).
9 Authors:   Oleg Bakharev,
10 		   Daniel Huertas Gonzalez
11 
12 See also: 
13 	https://en.wikipedia.org/wiki/Data_Encryption_Standard, FIPS PUB 46-3	   
14 */
15 module styx2000.protosrv.des;
16 
17 /// DES functional mode: encryption or decryption
18 enum CRYPTOPERATION
19 {
20 	/// encrypt with DES
21 	ENCRYPT,
22 	/// decrypt with DES
23 	DECRYPT
24 }
25 
26 
27 /// DES algorith implementation
28 class DES 
29 {
30 	private {
31 		enum LB32_MASK = 0x00000001;
32 		enum LB64_MASK = 0x0000000000000001;
33 		enum L64_MASK = 0x00000000ffffffff;
34 		enum H64_MASK = 0xffffffff00000000;
35 	}
36 	
37 	private {
38 		/* Initial Permutation Table */
39 		static char[] IP = [
40 		    58, 50, 42, 34, 26, 18, 10, 2, 60, 52, 44, 36, 28, 20, 12, 4, 62, 54, 46,
41 		    38, 30, 22, 14, 6, 64, 56, 48, 40, 32, 24, 16, 8, 57, 49, 41, 33, 25, 17,
42 		    9, 1, 59, 51, 43, 35, 27, 19, 11, 3, 61, 53, 45, 37, 29, 21, 13, 5, 63,
43 		    55, 47, 39, 31, 23, 15, 7
44 		];
45 		
46 		/* Inverse Initial Permutation Table */
47 		static char[] PI = [
48 		    40, 8, 48, 16, 56, 24, 64, 32, 39, 7, 47, 15, 55, 23, 63, 31, 38, 6, 46,
49 		    14, 54, 22, 62, 30, 37, 5, 45, 13, 53, 21, 61, 29, 36, 4, 44, 12, 52, 20,
50 		    60, 28, 35, 3, 43, 11, 51, 19, 59, 27, 34, 2, 42, 10, 50, 18, 58, 26, 33,
51 		    1, 41, 9, 49, 17, 57, 25
52 		];
53 		
54 		/* Expansion table */
55 		static char[] E = [
56 		    32, 1, 2, 3, 4, 5, 4, 5, 6, 7, 8, 9, 8, 9, 10, 11, 12, 13, 12, 13, 14, 15,
57 		    16, 17, 16, 17, 18, 19, 20, 21, 20, 21, 22, 23, 24, 25, 24, 25, 26, 27,
58 		    28, 29, 28, 29, 30, 31, 32, 1
59 		];
60 		
61 		/* Post S-Box permutation */
62 		static char[] P = [
63 		    16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10, 2, 8, 24,
64 		    14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25
65 		];
66 		
67 		/* The S-Box tables */
68 		static char[64][8] S = [
69 		    [
70 		        /* S1 */
71 		        14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7, 0, 15, 7, 4,
72 		        14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8, 4, 1, 14, 8, 13, 6, 2, 11,
73 		        15, 12, 9, 7, 3, 10, 5, 0, 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0,
74 		        6, 13
75 		    ],
76 		    [
77 		        /* S2 */
78 		        15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10, 3, 13, 4, 7, 15,
79 		        2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5, 0, 14, 7, 11, 10, 4, 13, 1, 5, 8,
80 		        12, 6, 9, 3, 2, 15, 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9
81 		    ],
82 		    [
83 		        /* S3 */
84 		        10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8, 13, 7, 0, 9, 3,
85 		        4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1, 13, 6, 4, 9, 8, 15, 3, 0, 11,
86 		        1, 2, 12, 5, 10, 14, 7, 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2,
87 		        12
88 		    ],
89 		    [
90 		        /* S4 */
91 		        7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15, 13, 8, 11, 5, 6,
92 		        15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9, 10, 6, 9, 0, 12, 11, 7, 13, 15, 1,
93 		        3, 14, 5, 2, 8, 4, 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14
94 		    ],
95 		    [
96 		        /* S5 */
97 		        2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9, 14, 11, 2, 12,
98 		        4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6, 4, 2, 1, 11, 10, 13, 7, 8, 15,
99 		        9, 12, 5, 6, 3, 0, 14, 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5,
100 		        3
101 		    ],
102 		    [
103 		        /* S6 */
104 		        12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11, 10, 15, 4, 2, 7,
105 		        12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8, 9, 14, 15, 5, 2, 8, 12, 3, 7, 0,
106 		        4, 10, 1, 13, 11, 6, 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13
107 		    ],
108 		    [
109 		        /* S7 */
110 		        4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1, 13, 0, 11, 7, 4,
111 		        9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6, 1, 4, 11, 13, 12, 3, 7, 14, 10,
112 		        15, 6, 8, 0, 5, 9, 2, 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12
113 		    ],
114 		    [
115 		        /* S8 */
116 		        13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7, 1, 15, 13, 8, 10,
117 		        3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2, 7, 11, 4, 1, 9, 12, 14, 2, 0, 6,
118 		        10, 13, 15, 3, 5, 8, 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
119 		    ]
120 		];
121 		
122 		/* Permuted Choice 1 Table */
123 		static char[] PC1 = [
124 		    57, 49, 41, 33, 25, 17, 9, 1, 58, 50, 42, 34, 26, 18, 10, 2, 59, 51, 43,
125 		    35, 27, 19, 11, 3, 60, 52, 44, 36, 63, 55, 47, 39, 31, 23, 15, 7, 62, 54,
126 		    46, 38, 30, 22, 14, 6, 61, 53, 45, 37, 29, 21, 13, 5, 28, 20, 12, 4
127 		];
128 		
129 		/* Permuted Choice 2 Table */
130 		static char[] PC2 = [
131 		    14, 17, 11, 24, 1, 5, 3, 28, 15, 6, 21, 10, 23, 19, 12, 4, 26, 8, 16, 7,
132 		    27, 20, 13, 2, 41, 52, 31, 37, 47, 55, 30, 40, 51, 45, 33, 48, 44, 49, 39,
133 		    56, 34, 53, 46, 42, 50, 36, 29, 32
134 		];
135 		
136 		/* Iteration Shift Array */
137 		static char[] iteration_shift = [
138 		    /* 1   2   3   4   5   6   7   8   9  10  11  12  13  14  15  16 */
139 		    1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
140 		];
141 	}
142 	
143 	
144 	/**
145 	Method for encryption or decryption of one elementary block (block size - 64 bit, key size - 64 bit).
146 	Params:
147 	input = Elementary block for encrypt/decrypt with DES.
148 	key = Key for encrypt/decrypt
149 	mode = Type of operation mode of DES class - encryption or decryption
150 	*/
151 	static ulong des(ulong input, ulong key, CRYPTOPERATION mode)
152 	{
153 	    /* 8 bits */
154 	    char row, column;
155 	
156 	    /* 28 bits */
157 	    uint C = 0;
158 	    uint D = 0;
159 	
160 	    /* 32 bits */
161 	    uint L = 0;
162 	    uint R = 0;
163 	    uint s_output = 0;
164 	    uint f_function_res = 0;
165 	    uint temp = 0;
166 	
167 	    /* 48 bits */
168 	    ulong[16] sub_key = 0;
169 	    ulong s_input = 0;
170 	
171 	    /* 56 bits */
172 	    ulong permuted_choice_1 = 0;
173 	    ulong permuted_choice_2 = 0;
174 	
175 	    /* 64 bits */
176 	    ulong init_perm_res = 0;
177 	    ulong inv_init_perm_res = 0;
178 	    ulong pre_output = 0;
179 	
180 	    /* initial permutation */
181 	    for (int i = 0; i < 64; i++)
182 	    {
183 	        init_perm_res <<= 1;
184 	        init_perm_res |= (input >> (64 - IP[i])) & LB64_MASK;
185 	    }
186 	
187 	    L = cast(uint)(init_perm_res >> 32) & L64_MASK;
188 	    R = cast(uint) init_perm_res & L64_MASK;
189 	
190 	    /* initial key schedule calculation */
191 	    for (int i = 0; i < 56; i++)
192 	    {
193 	        permuted_choice_1 <<= 1;
194 	        permuted_choice_1 |= (key >> (64 - PC1[i])) & LB64_MASK;
195 	    }
196 	
197 	    C = cast(uint)((permuted_choice_1 >> 28) & 0x000000000fffffff);
198 	    D = cast(uint)(permuted_choice_1 & 0x000000000fffffff);
199 	
200 	    /* Calculation of the 16 keys */
201 	    for (int i = 0; i < 16; i++)
202 	    {
203 	        /* key schedule */
204 	        // shifting Ci and Di
205 	        for (int j = 0; j < iteration_shift[i]; j++)
206 	        {
207 	            C = 0x0fffffff & (C << 1) | 0x00000001 & (C >> 27);
208 	            D = 0x0fffffff & (D << 1) | 0x00000001 & (D >> 27);
209 	        }
210 	
211 	        permuted_choice_2 = 0;
212 	        permuted_choice_2 = ((cast(ulong) C) << 28) | cast(ulong) D;
213 	
214 	        sub_key[i] = 0;
215 	
216 	        for (int j = 0; j < 48; j++)
217 	        {
218 	            sub_key[i] <<= 1;
219 	            sub_key[i] |= (permuted_choice_2 >> (56 - PC2[j])) & LB64_MASK;
220 	        }
221 	
222 	    }
223 	
224 	    for (int i = 0; i < 16; i++)
225 	    {
226 	        /* f(R,k) function */
227 	        s_input = 0;
228 	
229 	        for (int j = 0; j < 48; j++)
230 	        {
231 	            s_input <<= 1;
232 	            s_input |= cast(ulong)((R >> (32 - E[j])) & LB32_MASK);
233 	        }
234 	
235 	        /* 
236 	         * Encryption/Decryption 
237 	         * XORing expanded Ri with Ki
238 	         */
239 	        final switch (mode) with (CRYPTOPERATION) 
240 	        {
241 				case ENCRYPT:
242 					s_input = s_input ^ sub_key[i];
243 					break;
244 				case DECRYPT:
245 					s_input = s_input ^ sub_key[15 - i];
246 					break;
247 			}
248 	
249 	        /* S-Box Tables */
250 	        for (int j = 0; j < 8; j++)
251 	        {
252 	            // 00 00 RCCC CR00 00 00 00 00 00 s_input
253 	            // 00 00 1000 0100 00 00 00 00 00 row mask
254 	            // 00 00 0111 1000 00 00 00 00 00 column mask
255 	
256 	            row = cast(char) ((s_input & (0x0000840000000000 >> 6 * j)) >> 42 - 6 * j);
257 	            row = (row >> 4) | row & 0x01;
258 	
259 	            column = cast(char) ((s_input & (0x0000780000000000 >> 6 * j)) >> 43 - 6 * j);
260 	
261 	            s_output <<= 4;
262 	            s_output |= cast(uint) (S[j][16 * row + column] & 0x0f);
263 	        }
264 	
265 	        f_function_res = 0;
266 	
267 	        for (int j = 0; j < 32; j++)
268 	        {
269 	            f_function_res <<= 1;
270 	            f_function_res |= (s_output >> (32 - P[j])) & LB32_MASK;
271 	        }
272 	
273 	        temp = R;
274 	        R = L ^ f_function_res;
275 	        L = temp;
276 	    }
277 	
278 	    pre_output = ((cast(ulong) R) << 32) | cast(ulong) L;
279 	
280 	    /* inverse initial permutation */
281 	    for (int i = 0; i < 64; i++)
282 	    {
283 	        inv_init_perm_res <<= 1;
284 	        inv_init_perm_res |= (pre_output >> (64 - PI[i])) & LB64_MASK;
285 	    }
286 	
287 	    return inv_init_perm_res;
288 	}
289 }
290 
291 unittest {
292 	enum ulong[16] TESTING_VECTOR = [
293 		0x8da744e0c94e5e17,
294 		0x0cdb25e3ba3c6d79,
295 		0x4784c4ba5006081f,
296 		0x1cf1fc126f2ef842,
297 		0xe4be250042098d13,
298 		0x7bfc5dc6adb5797c,
299 		0x1ab3b4d82082fb28,
300 		0xc1576a14de707097,
301 		0x739b68cd2e26782a,
302 		0x2a59f0c464506edb,
303 		0xa5c39d4251f0a81e,
304 		0x7239ac9a6107ddb1,
305 		0x070cac8590241233,
306 		0x78f87b6e3dfecf61,
307 		0x95ec2578c2c433f0,
308 		0x1b1a2ddb4c642438,
309 	];
310 	
311 	ulong input = 0x9474B8E8C73BCA7D;
312     ulong key = 0x0000000000000000;
313     ulong result = input;
314     
315     ulong[16] vector;
316     
317     for (int i = 0; i < 16; i++)
318     {
319         if (i % 2 == 0)
320         {
321             result = DES.des(result, result, CRYPTOPERATION.ENCRYPT);
322         }
323         else
324         {
325             result = DES.des(result, result, CRYPTOPERATION.DECRYPT);
326         }
327         
328         vector[i] = result;
329     }
330     
331     assert(vector == TESTING_VECTOR);
332     assert(result == 0x1b1a2ddb4c642438);
333 }