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 }