2 // System.Security.Cryptography.DES
5 // Sergey Chaban (serge@wildwestsoftware.com)
9 using System.Security.Cryptography;
11 // References: FIPS PUB 46-3
13 namespace System.Security.Cryptography {
15 internal sealed class DESCore {
17 internal static readonly int KEY_BIT_SIZE = 64;
18 internal static readonly int KEY_BYTE_SIZE = KEY_BIT_SIZE / 8;
19 internal static readonly int BLOCK_BIT_SIZE = 64;
20 internal static readonly int BLOCK_BYTE_SIZE = BLOCK_BIT_SIZE / 8;
22 private byte [] keySchedule;
23 private byte [] byteBuff;
24 private uint [] dwordBuff;
26 internal delegate void DESCall (byte [] block, byte [] output);
30 internal static ulong [] weakKeys = {
31 0x0101010101010101, /* 0000000 0000000 */
32 0xFEFEFEFEFEFEFEFE, /* FFFFFFF FFFFFFF */
33 0x1F1F1F1FE0E0E0E0, /* 0000000 FFFFFFF */
34 0xE0E0E0E01F1F1F1F /* FFFFFFF 0000000 */
38 internal static ulong [] semiweakKeys = {
39 0x01FE01FE01FE01FE, 0xFE01FE01FE01FE01,
40 0x1FE01FE00EF10EF1, 0xE01FE01FF10EF10E,
41 0x01E001E001F101F1, 0xE001E001F101F101,
42 0x1FFE1FFE0EFE0EFE, 0xFE1FFE1FFE0EFE0E,
43 0x011F011F010E010E, 0x1F011F010E010E01,
44 0xE0FEE0FEF1FEF1FE, 0xFEE0FEE0FEF1FEF1
49 // S-boxes from FIPS 46-3, Appendix 1, page 17
50 private static byte [] sBoxes = {
52 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
53 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
54 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
55 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13,
58 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
59 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
60 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
61 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9,
64 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
65 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
66 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
67 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12,
70 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
71 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
72 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
73 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14,
76 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
77 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
78 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
79 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3,
82 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
83 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
84 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
85 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13,
88 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
89 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
90 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
91 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12,
94 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
95 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
96 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
97 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
101 // P table from page 15, also in Appendix 1, page 18
102 private static byte [] pTab = {
103 16-1, 7-1, 20-1, 21-1,
104 29-1, 12-1, 28-1, 17-1,
105 1-1, 15-1, 23-1, 26-1,
106 5-1, 18-1, 31-1, 10-1,
107 2-1, 8-1, 24-1, 14-1,
108 32-1, 27-1, 3-1, 9-1,
109 19-1, 13-1, 30-1, 6-1,
110 22-1, 11-1, 4-1, 25-1
114 // Permuted choice 1 table, PC-1, page 19
115 // Translated to zero-based format.
116 private static byte [] PC1 = {
117 57-1, 49-1, 41-1, 33-1, 25-1, 17-1, 9-1,
118 1-1, 58-1, 50-1, 42-1, 34-1, 26-1, 18-1,
119 10-1, 2-1, 59-1, 51-1, 43-1, 35-1, 27-1,
120 19-1, 11-1, 3-1, 60-1, 52-1, 44-1, 36-1,
122 63-1, 55-1, 47-1, 39-1, 31-1, 23-1, 15-1,
123 7-1, 62-1, 54-1, 46-1, 38-1, 30-1, 22-1,
124 14-1, 6-1, 61-1, 53-1, 45-1, 37-1, 29-1,
125 21-1, 13-1, 5-1, 28-1, 20-1, 12-1, 4-1
129 private static byte [] leftRot = {
130 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
133 private static byte [] leftRotTotal;
137 // Permuted choice 2 table, PC-2, page 21
138 // Translated to zero-based format.
139 private static byte [] PC2 = {
140 14-1, 17-1, 11-1, 24-1, 1-1, 5-1,
141 3-1, 28-1, 15-1, 6-1, 21-1, 10-1,
142 23-1, 19-1, 12-1, 4-1, 26-1, 8-1,
143 16-1, 7-1, 27-1, 20-1, 13-1, 2-1,
144 41-1, 52-1, 31-1, 37-1, 47-1, 55-1,
145 30-1, 40-1, 51-1, 45-1, 33-1, 48-1,
146 44-1, 49-1, 39-1, 56-1, 34-1, 53-1,
147 46-1, 42-1, 50-1, 36-1, 29-1, 32-1
151 // Initial permutation IP, page 10.
152 // Transposed to 0-based format.
153 private static byte [] ipBits = {
154 58-1, 50-1, 42-1, 34-1, 26-1, 18-1, 10-1, 2-1,
155 60-1, 52-1, 44-1, 36-1, 28-1, 20-1, 12-1, 4-1,
156 62-1, 54-1, 46-1, 38-1, 30-1, 22-1, 14-1, 6-1,
157 64-1, 56-1, 48-1, 40-1, 32-1, 24-1, 16-1, 8-1,
158 57-1, 49-1, 41-1, 33-1, 25-1, 17-1, 9-1, 1-1,
159 59-1, 51-1, 43-1, 35-1, 27-1, 19-1, 11-1, 3-1,
160 61-1, 53-1, 45-1, 37-1, 29-1, 21-1, 13-1, 5-1,
161 63-1, 55-1, 47-1, 39-1, 31-1, 23-1, 15-1, 7-1
165 // Final permutation FP = IP^(-1), page 10.
166 // Transposed to 0-based format.
167 private static byte [] fpBits = {
168 40-1, 8-1, 48-1, 16-1, 56-1, 24-1, 64-1, 32-1,
169 39-1, 7-1, 47-1, 15-1, 55-1, 23-1, 63-1, 31-1,
170 38-1, 6-1, 46-1, 14-1, 54-1, 22-1, 62-1, 30-1,
171 37-1, 5-1, 45-1, 13-1, 53-1, 21-1, 61-1, 29-1,
172 36-1, 4-1, 44-1, 12-1, 52-1, 20-1, 60-1, 28-1,
173 35-1, 3-1, 43-1, 11-1, 51-1, 19-1, 59-1, 27-1,
174 34-1, 2-1, 42-1, 10-1, 50-1, 18-1, 58-1, 26-1,
175 33-1, 1-1, 41-1, 9-1, 49-1, 17-1, 57-1, 25-1
180 private static uint [] spBoxes;
181 private static int [] ipTab;
182 private static int [] fpTab;
187 spBoxes = new uint [64 * 8];
189 int [] pBox = new int [32];
191 for (int p = 0; p < 32; p++) {
192 for (int i = 0; i < 32; i++) {
201 for (int s = 0; s < 8; s++) { // for each S-box
204 for (int i = 0; i < 64; i++) { // inputs
207 int indx = (i & 0x20) | ((i & 1) << 4) | ((i >> 1) & 0xF);
209 for (int j = 0; j < 4; j++) { // for each bit in the output
210 if ((sBoxes [sOff + indx] & (8 >> j)) != 0) {
211 sp |= (uint) (1 << (31 - pBox [(s << 2) + j]));
215 spBoxes [sOff + i] = sp;
220 leftRotTotal = new byte [leftRot.Length];
223 for (int i = 0; i < leftRot.Length; i++) {
225 for (int j = 0; j <= i; r += leftRot [j++]);
226 leftRotTotal [i] = (byte) r;
230 InitPermutationTable (ipBits, out ipTab);
231 InitPermutationTable (fpBits, out fpTab);
233 } // class constructor
237 // Default constructor.
240 keySchedule = new byte [KEY_BYTE_SIZE * 16];
241 byteBuff = new byte [BLOCK_BYTE_SIZE];
242 dwordBuff = new uint [BLOCK_BYTE_SIZE / 4];
248 private static void InitPermutationTable (byte [] pBits, out int [] permTab)
250 permTab = new int [8*2 * 8*2 * (64/32)];
252 for (int i = 0; i < 16; i++) {
253 for (int j = 0; j < 16; j++) {
254 int offs = (i << 5) + (j << 1);
255 for (int n = 0; n < 64; n++) {
256 int bitNum = (int) pBits [n];
257 if ((bitNum >> 2 == i) &&
258 0 != (j & (8 >> (bitNum & 3)))) {
259 permTab [offs + (n >> (3+2))] |= (int) ((0x80808080 & (0xFF << (n & (3 << 3)))) >> (n & 7));
268 internal static ulong PackKey (byte [] key)
271 for (int i = 0, sh = 8*KEY_BYTE_SIZE; (sh = sh - 8) >= 0; i++) {
272 res |= (ulong) key [i] << sh;
279 internal static byte [] UnpackKey (ulong key)
281 byte [] res = new byte [KEY_BYTE_SIZE];
282 for (int i = 0, sh = 8*KEY_BYTE_SIZE; (sh = sh - 8) >= 0; i++) {
283 res [i] = (byte) (key >> sh);
290 internal static bool IsValidKeySize (byte [] key)
292 return (key.Length == KEY_BYTE_SIZE);
297 private uint CipherFunct(uint r, int n)
300 byte [] subkey = keySchedule;
303 uint rt = (r >> 1) | (r << 31); // ROR32(r)
304 res |= spBoxes [0*64 + (((rt >> 26) ^ subkey [i++]) & 0x3F)];
305 res |= spBoxes [1*64 + (((rt >> 22) ^ subkey [i++]) & 0x3F)];
306 res |= spBoxes [2*64 + (((rt >> 18) ^ subkey [i++]) & 0x3F)];
307 res |= spBoxes [3*64 + (((rt >> 14) ^ subkey [i++]) & 0x3F)];
308 res |= spBoxes [4*64 + (((rt >> 10) ^ subkey [i++]) & 0x3F)];
309 res |= spBoxes [5*64 + (((rt >> 6) ^ subkey [i++]) & 0x3F)];
310 res |= spBoxes [6*64 + (((rt >> 2) ^ subkey [i++]) & 0x3F)];
311 rt = (r << 1) | (r >> 31); // ROL32(r)
312 res |= spBoxes [7*64 + ((rt ^ subkey [i]) & 0x3F)];
318 private static void Permutation (byte [] input, byte [] _output, int [] permTab, bool preSwap)
321 if (preSwap) BSwap (input);
323 byte [] output = _output;
325 int offs1 = (((int)(input [0]) >> 4)) << 1;
326 int offs2 = (1 << 5) + ((((int)input [0]) & 0xF) << 1);
328 int d1 = permTab [offs1++] | permTab [offs2++];
329 int d2 = permTab [offs1] | permTab [offs2];
332 int max = BLOCK_BYTE_SIZE << 1;
333 for (int i = 2, indx = 1; i < max; i += 2, indx++) {
334 int ii = (int) input [indx];
335 offs1 = (i << 5) + ((ii >> 4) << 1);
336 offs2 = ((i + 1) << 5) + ((ii & 0xF) << 1);
338 d1 |= permTab [offs1++] | permTab [offs2++];
339 d2 |= permTab [offs1] | permTab [offs2];
343 output [0] = (byte) (d1);
344 output [1] = (byte) (d1 >> 8);
345 output [2] = (byte) (d1 >> 16);
346 output [3] = (byte) (d1 >> 24);
347 output [4] = (byte) (d2);
348 output [5] = (byte) (d2 >> 8);
349 output [6] = (byte) (d2 >> 16);
350 output [7] = (byte) (d2 >> 24);
352 output [0] = (byte) (d1 >> 24);
353 output [1] = (byte) (d1 >> 16);
354 output [2] = (byte) (d1 >> 8);
355 output [3] = (byte) (d1);
356 output [4] = (byte) (d2 >> 24);
357 output [5] = (byte) (d2 >> 16);
358 output [6] = (byte) (d2 >> 8);
359 output [7] = (byte) (d2);
366 private static void BSwap (byte [] byteBuff)
371 byteBuff [0] = byteBuff [3];
375 byteBuff [1] = byteBuff [2];
379 byteBuff [4] = byteBuff [7];
383 byteBuff [5] = byteBuff [6];
389 internal void SetKey (byte [] key)
391 // NOTE: see Fig. 3, Key schedule calculation, at page 20.
393 Array.Clear (keySchedule, 0, keySchedule.Length);
395 int keyBitSize = PC1.Length;
397 byte [] keyPC1 = new byte [keyBitSize]; // PC1-permuted key
398 byte [] keyRot = new byte [keyBitSize]; // PC1 & rotated
402 foreach (byte bitPos in PC1) {
403 keyPC1 [indx++] = (byte)((key [(int)bitPos >> 3] >> (7 ^ (bitPos & 7))) & 1);
410 for (int i = 0; i < KEY_BYTE_SIZE*2; i++) {
411 int b = keyBitSize >> 1;
413 for (j = 0; j < b; j++) {
414 int s = j + (int) leftRotTotal [i];
415 keyRot [j] = keyPC1 [s < b ? s : s - b];
418 for (j = b; j < keyBitSize; j++) {
419 int s = j + (int) leftRotTotal [i];
420 keyRot [j] = keyPC1 [s < keyBitSize ? s : s - b];
423 int keyOffs = i * KEY_BYTE_SIZE;
426 foreach (byte bitPos in PC2) {
427 if (keyRot [(int)bitPos] != 0) {
428 keySchedule [keyOffs + (j/6)] |= (byte) (0x80 >> ((j % 6) + 2));
438 internal void Encrypt (byte [] block, byte [] output)
440 byte [] dest = (output == null ? block : output);
442 byte [] byteBuff = this.byteBuff;
443 uint [] dwordBuff = this.dwordBuff;
445 Permutation (block, byteBuff, ipTab, false);
447 Buffer.BlockCopy (byteBuff, 0, dwordBuff, 0, BLOCK_BYTE_SIZE);
449 uint d0 = dwordBuff[0];
450 uint d1 = dwordBuff[1];
453 d0 ^= CipherFunct (d1, 0);
454 d1 ^= CipherFunct (d0, 1);
455 d0 ^= CipherFunct (d1, 2);
456 d1 ^= CipherFunct (d0, 3);
457 d0 ^= CipherFunct (d1, 4);
458 d1 ^= CipherFunct (d0, 5);
459 d0 ^= CipherFunct (d1, 6);
460 d1 ^= CipherFunct (d0, 7);
461 d0 ^= CipherFunct (d1, 8);
462 d1 ^= CipherFunct (d0, 9);
463 d0 ^= CipherFunct (d1, 10);
464 d1 ^= CipherFunct (d0, 11);
465 d0 ^= CipherFunct (d1, 12);
466 d1 ^= CipherFunct (d0, 13);
467 d0 ^= CipherFunct (d1, 14);
468 d1 ^= CipherFunct (d0, 15);
473 Buffer.BlockCopy (dwordBuff, 0, byteBuff, 0, BLOCK_BYTE_SIZE);
475 Permutation (byteBuff, dest, fpTab, true);
479 internal void Decrypt (byte [] block, byte [] output)
481 byte [] dest = (output == null ? block : output);
483 byte [] byteBuff = this.byteBuff;
484 uint [] dwordBuff = this.dwordBuff;
486 Permutation (block, byteBuff, ipTab, false);
488 Buffer.BlockCopy (byteBuff, 0, dwordBuff, 0, BLOCK_BYTE_SIZE);
490 uint d1 = dwordBuff [0];
491 uint d0 = dwordBuff [1];
493 // 16 rounds in reverse order
494 d1 ^= CipherFunct (d0, 15);
495 d0 ^= CipherFunct (d1, 14);
496 d1 ^= CipherFunct (d0, 13);
497 d0 ^= CipherFunct (d1, 12);
498 d1 ^= CipherFunct (d0, 11);
499 d0 ^= CipherFunct (d1, 10);
500 d1 ^= CipherFunct (d0, 9);
501 d0 ^= CipherFunct (d1, 8);
502 d1 ^= CipherFunct (d0, 7);
503 d0 ^= CipherFunct (d1, 6);
504 d1 ^= CipherFunct (d0, 5);
505 d0 ^= CipherFunct (d1, 4);
506 d1 ^= CipherFunct (d0, 3);
507 d0 ^= CipherFunct (d1, 2);
508 d1 ^= CipherFunct (d0, 1);
509 d0 ^= CipherFunct (d1, 0);
515 Buffer.BlockCopy (dwordBuff, 0, byteBuff, 0, BLOCK_BYTE_SIZE);
517 Permutation (byteBuff, dest, fpTab, true);
526 public abstract class DES : SymmetricAlgorithm {
533 public static new DES Create()
539 public static new DES Create(string name)
546 public static bool IsWeakKey (byte [] rgbKey)
548 if (!DESCore.IsValidKeySize (rgbKey)) {
549 throw new CryptographicException ();
552 ulong lk = DESCore.PackKey (rgbKey);
553 foreach (ulong wk in DESCore.weakKeys) {
554 if (lk == wk) return true;
560 public static bool IsSemiWeakKey (byte [] rgbKey)
562 if (!DESCore.IsValidKeySize (rgbKey)) {
563 throw new CryptographicException ();
566 ulong lk = DESCore.PackKey (rgbKey);
567 foreach (ulong swk in DESCore.semiweakKeys) {
568 if (lk == swk) return true;
573 public override byte[] Key {
578 this.key = new byte [DESCore.KEY_BYTE_SIZE];
579 Array.Copy (value, 0, this.key, 0, DESCore.KEY_BYTE_SIZE);
585 } // System.Security.Cryptography