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 }