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 {
16 internal sealed class DESCore {
18 internal static readonly int KEY_BIT_SIZE = 64;
19 internal static readonly int KEY_BYTE_SIZE = KEY_BIT_SIZE / 8;
20 internal static readonly int BLOCK_BIT_SIZE = 64;
21 internal static readonly int BLOCK_BYTE_SIZE = BLOCK_BIT_SIZE / 8;
23 private byte [] keySchedule;
24 private byte [] byteBuff;
25 private uint [] dwordBuff;
27 internal delegate void DESCall (byte [] block, byte [] output);
31 internal static ulong [] weakKeys = {
32 0x0101010101010101, /* 0000000 0000000 */
33 0xFEFEFEFEFEFEFEFE, /* FFFFFFF FFFFFFF */
34 0x1F1F1F1FE0E0E0E0, /* 0000000 FFFFFFF */
35 0xE0E0E0E01F1F1F1F /* FFFFFFF 0000000 */
39 internal static ulong [] semiweakKeys = {
40 0x01FE01FE01FE01FE, 0xFE01FE01FE01FE01,
41 0x1FE01FE00EF10EF1, 0xE01FE01FF10EF10E,
42 0x01E001E001F101F1, 0xE001E001F101F101,
43 0x1FFE1FFE0EFE0EFE, 0xFE1FFE1FFE0EFE0E,
44 0x011F011F010E010E, 0x1F011F010E010E01,
45 0xE0FEE0FEF1FEF1FE, 0xFEE0FEE0FEF1FEF1
50 // S-boxes from FIPS 46-3, Appendix 1, page 17
51 private static byte [] sBoxes = {
53 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
54 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
55 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
56 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13,
59 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
60 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
61 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
62 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9,
65 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
66 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
67 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
68 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12,
71 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
72 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
73 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
74 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14,
77 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
78 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
79 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
80 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3,
83 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
84 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
85 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
86 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13,
89 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
90 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
91 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
92 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12,
95 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
96 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
97 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
98 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
102 // P table from page 15, also in Appendix 1, page 18
103 private static byte [] pTab = {
104 16-1, 7-1, 20-1, 21-1,
105 29-1, 12-1, 28-1, 17-1,
106 1-1, 15-1, 23-1, 26-1,
107 5-1, 18-1, 31-1, 10-1,
108 2-1, 8-1, 24-1, 14-1,
109 32-1, 27-1, 3-1, 9-1,
110 19-1, 13-1, 30-1, 6-1,
111 22-1, 11-1, 4-1, 25-1
115 // Permuted choice 1 table, PC-1, page 19
116 // Translated to zero-based format.
117 private static byte [] PC1 = {
118 57-1, 49-1, 41-1, 33-1, 25-1, 17-1, 9-1,
119 1-1, 58-1, 50-1, 42-1, 34-1, 26-1, 18-1,
120 10-1, 2-1, 59-1, 51-1, 43-1, 35-1, 27-1,
121 19-1, 11-1, 3-1, 60-1, 52-1, 44-1, 36-1,
123 63-1, 55-1, 47-1, 39-1, 31-1, 23-1, 15-1,
124 7-1, 62-1, 54-1, 46-1, 38-1, 30-1, 22-1,
125 14-1, 6-1, 61-1, 53-1, 45-1, 37-1, 29-1,
126 21-1, 13-1, 5-1, 28-1, 20-1, 12-1, 4-1
130 private static byte [] leftRot = {
131 1, 1, 2, 2, 2, 2, 2, 2, 1, 2, 2, 2, 2, 2, 2, 1
134 private static byte [] leftRotTotal;
138 // Permuted choice 2 table, PC-2, page 21
139 // Translated to zero-based format.
140 private static byte [] PC2 = {
141 14-1, 17-1, 11-1, 24-1, 1-1, 5-1,
142 3-1, 28-1, 15-1, 6-1, 21-1, 10-1,
143 23-1, 19-1, 12-1, 4-1, 26-1, 8-1,
144 16-1, 7-1, 27-1, 20-1, 13-1, 2-1,
145 41-1, 52-1, 31-1, 37-1, 47-1, 55-1,
146 30-1, 40-1, 51-1, 45-1, 33-1, 48-1,
147 44-1, 49-1, 39-1, 56-1, 34-1, 53-1,
148 46-1, 42-1, 50-1, 36-1, 29-1, 32-1
152 // Initial permutation IP, page 10.
153 // Transposed to 0-based format.
154 private static byte [] ipBits = {
155 58-1, 50-1, 42-1, 34-1, 26-1, 18-1, 10-1, 2-1,
156 60-1, 52-1, 44-1, 36-1, 28-1, 20-1, 12-1, 4-1,
157 62-1, 54-1, 46-1, 38-1, 30-1, 22-1, 14-1, 6-1,
158 64-1, 56-1, 48-1, 40-1, 32-1, 24-1, 16-1, 8-1,
159 57-1, 49-1, 41-1, 33-1, 25-1, 17-1, 9-1, 1-1,
160 59-1, 51-1, 43-1, 35-1, 27-1, 19-1, 11-1, 3-1,
161 61-1, 53-1, 45-1, 37-1, 29-1, 21-1, 13-1, 5-1,
162 63-1, 55-1, 47-1, 39-1, 31-1, 23-1, 15-1, 7-1
166 // Final permutation FP = IP^(-1), page 10.
167 // Transposed to 0-based format.
168 private static byte [] fpBits = {
169 40-1, 8-1, 48-1, 16-1, 56-1, 24-1, 64-1, 32-1,
170 39-1, 7-1, 47-1, 15-1, 55-1, 23-1, 63-1, 31-1,
171 38-1, 6-1, 46-1, 14-1, 54-1, 22-1, 62-1, 30-1,
172 37-1, 5-1, 45-1, 13-1, 53-1, 21-1, 61-1, 29-1,
173 36-1, 4-1, 44-1, 12-1, 52-1, 20-1, 60-1, 28-1,
174 35-1, 3-1, 43-1, 11-1, 51-1, 19-1, 59-1, 27-1,
175 34-1, 2-1, 42-1, 10-1, 50-1, 18-1, 58-1, 26-1,
176 33-1, 1-1, 41-1, 9-1, 49-1, 17-1, 57-1, 25-1
181 private static uint [] spBoxes;
182 private static byte [] ipTab;
183 private static byte [] fpTab;
188 spBoxes = new uint [64 * 8];
190 int [] pBox = new int [32];
192 for (int p = 0; p < 32; p++) {
193 for (int i = 0; i < 32; i++) {
202 for (int s = 0; s < 8; s++) { // for each S-box
205 for (int i = 0; i < 64; i++) { // inputs
208 int indx = (i & 0x20) | ((i & 1) << 4) | ((i >> 1) & 0xF);
210 for (int j = 0; j < 4; j++) { // for each bit in the output
211 if ((sBoxes [sOff + indx] & (8 >> j)) != 0) {
212 sp |= (uint) (1 << (31 - pBox [(s << 2) + j]));
216 spBoxes [sOff + i] = sp;
221 leftRotTotal = new byte [leftRot.Length];
224 for (int i = 0; i < leftRot.Length; i++) {
226 for (int j = 0; j <= i; r += leftRot [j++]);
227 leftRotTotal [i] = (byte) r;
231 InitPermutationTable (ipBits, out ipTab);
232 InitPermutationTable (fpBits, out fpTab);
234 } // class constructor
238 // Default constructor.
241 keySchedule = new byte [KEY_BYTE_SIZE * 16];
242 byteBuff = new byte [BLOCK_BYTE_SIZE];
243 dwordBuff = new uint [BLOCK_BYTE_SIZE / 4];
249 private static void InitPermutationTable (byte [] pBits, out byte [] permTab)
251 permTab = new byte [8*2 * 8*2 * (64/8)];
253 for (int i = 0; i < 16; i++) {
254 for (int j = 0; j < 16; j++) {
255 int offs = (i << 7) + (j << 3);
256 for (int n = 0; n < 64; n++) {
257 int bitNum = (int) pBits [n];
258 if ((bitNum >> 2 == i) &&
259 0 != (j & (8 >> (bitNum & 3)))) {
260 permTab [offs + (n >> 3)] |= (byte) (0x80 >> (n & 7));
269 internal static ulong PackKey (byte [] key)
272 for (int i = 0, sh = 8*KEY_BYTE_SIZE; (sh = sh - 8) >= 0; i++) {
273 res |= (ulong) key [i] << sh;
280 internal static byte [] UnpackKey (ulong key)
282 byte [] res = new byte [KEY_BYTE_SIZE];
283 for (int i = 0, sh = 8*KEY_BYTE_SIZE; (sh = sh - 8) >= 0; i++) {
284 res [i] = (byte) (key >> sh);
291 internal static bool IsValidKeySize (byte [] key)
293 return (key.Length == KEY_BYTE_SIZE);
298 private uint CipherFunct(uint r, int n)
301 byte [] subkey = keySchedule;
304 uint rt = (r >> 1) | (r << 31); // ROR32(r)
305 res |= spBoxes [0*64 + (((rt >> 26) ^ subkey [i++]) & 0x3F)];
306 res |= spBoxes [1*64 + (((rt >> 22) ^ subkey [i++]) & 0x3F)];
307 res |= spBoxes [2*64 + (((rt >> 18) ^ subkey [i++]) & 0x3F)];
308 res |= spBoxes [3*64 + (((rt >> 14) ^ subkey [i++]) & 0x3F)];
309 res |= spBoxes [4*64 + (((rt >> 10) ^ subkey [i++]) & 0x3F)];
310 res |= spBoxes [5*64 + (((rt >> 6) ^ subkey [i++]) & 0x3F)];
311 res |= spBoxes [6*64 + (((rt >> 2) ^ subkey [i++]) & 0x3F)];
312 rt = (r << 1) | (r >> 31); // ROL32(r)
313 res |= spBoxes [7*64 + ((rt ^ subkey [i]) & 0x3F)];
320 private static void Permutation (byte [] input, byte [] output, byte [] permTab)
322 Array.Clear (output, 0, BLOCK_BYTE_SIZE);
324 for (int i = 0, indx = 0; i < BLOCK_BYTE_SIZE*2; i += 2, indx++) {
325 int offs1 = (i << 7) + ((((int)(input [indx]) >> 4)) << 3);
326 int offs2 = ((i + 1) << 7) + ((((int)input [indx]) & 0xF) << 3);
328 for (int j = 0; j < 8; j++) {
329 output [j] |= (byte) (permTab [offs1++] | permTab [offs2++]);
335 private static void Permutation (byte [] input, byte [] _output, byte [] permTab)
337 byte [] output = _output;
338 Array.Clear (output, 0, BLOCK_BYTE_SIZE);
340 int offs1 = (((int)(input [0]) >> 4)) << 3;
341 int offs2 = (1 << 7) + ((((int)input [0]) & 0xF) << 3);
343 output [0] |= (byte) (permTab [offs1++] | permTab [offs2++]);
344 output [1] |= (byte) (permTab [offs1++] | permTab [offs2++]);
345 output [2] |= (byte) (permTab [offs1++] | permTab [offs2++]);
346 output [3] |= (byte) (permTab [offs1++] | permTab [offs2++]);
347 output [4] |= (byte) (permTab [offs1++] | permTab [offs2++]);
348 output [5] |= (byte) (permTab [offs1++] | permTab [offs2++]);
349 output [6] |= (byte) (permTab [offs1++] | permTab [offs2++]);
350 output [7] |= (byte) (permTab [offs1] | permTab [offs2]);
352 for (int i = 2, indx = 1; i < BLOCK_BYTE_SIZE*2; i += 2, indx++) {
353 offs1 = (i << 7) + ((((int)(input [indx]) >> 4)) << 3);
354 offs2 = ((i + 1) << 7) + ((((int)input [indx]) & 0xF) << 3);
356 output [0] |= (byte) (permTab [offs1++] | permTab [offs2++]);
357 output [1] |= (byte) (permTab [offs1++] | permTab [offs2++]);
358 output [2] |= (byte) (permTab [offs1++] | permTab [offs2++]);
359 output [3] |= (byte) (permTab [offs1++] | permTab [offs2++]);
360 output [4] |= (byte) (permTab [offs1++] | permTab [offs2++]);
361 output [5] |= (byte) (permTab [offs1++] | permTab [offs2++]);
362 output [6] |= (byte) (permTab [offs1++] | permTab [offs2++]);
363 output [7] |= (byte) (permTab [offs1] | permTab [offs2]);
370 private void BSwap ()
374 // byte [] byteBuff = this.byteBuff;
377 byteBuff [0] = byteBuff [3];
381 byteBuff [1] = byteBuff [2];
385 byteBuff [4] = byteBuff [7];
389 byteBuff [5] = byteBuff [6];
395 internal void SetKey (byte [] key)
397 // NOTE: see Fig. 3, Key schedule calculation, at page 20.
399 Array.Clear (keySchedule, 0, keySchedule.Length);
401 int keyBitSize = PC1.Length;
403 byte [] keyPC1 = new byte [keyBitSize]; // PC1-permuted key
404 byte [] keyRot = new byte [keyBitSize]; // PC1 & rotated
408 foreach (byte bitPos in PC1) {
409 keyPC1 [indx++] = (byte)((key [(int)bitPos >> 3] >> (7 ^ (bitPos & 7))) & 1);
416 for (int i = 0; i < KEY_BYTE_SIZE*2; i++) {
417 int b = keyBitSize >> 1;
419 for (j = 0; j < b; j++) {
420 int s = j + (int) leftRotTotal [i];
421 keyRot [j] = keyPC1 [s < b ? s : s - b];
424 for (j = b; j < keyBitSize; j++) {
425 int s = j + (int) leftRotTotal [i];
426 keyRot [j] = keyPC1 [s < keyBitSize ? s : s - b];
429 int keyOffs = i * KEY_BYTE_SIZE;
432 foreach (byte bitPos in PC2) {
433 if (keyRot [(int)bitPos] != 0) {
434 keySchedule [keyOffs + (j/6)] |= (byte) (0x80 >> ((j % 6) + 2));
444 internal void Encrypt (byte [] block, byte [] output)
446 byte [] dest = (output == null ? block : output);
448 byte [] byteBuff = this.byteBuff;
449 uint [] dwordBuff = this.dwordBuff;
451 Permutation (block, byteBuff, ipTab);
455 Buffer.BlockCopy (byteBuff, 0, dwordBuff, 0, BLOCK_BYTE_SIZE);
458 dwordBuff[0] ^= CipherFunct (dwordBuff [1], 0);
459 dwordBuff[1] ^= CipherFunct (dwordBuff [0], 1);
460 dwordBuff[0] ^= CipherFunct (dwordBuff [1], 2);
461 dwordBuff[1] ^= CipherFunct (dwordBuff [0], 3);
462 dwordBuff[0] ^= CipherFunct (dwordBuff [1], 4);
463 dwordBuff[1] ^= CipherFunct (dwordBuff [0], 5);
464 dwordBuff[0] ^= CipherFunct (dwordBuff [1], 6);
465 dwordBuff[1] ^= CipherFunct (dwordBuff [0], 7);
466 dwordBuff[0] ^= CipherFunct (dwordBuff [1], 8);
467 dwordBuff[1] ^= CipherFunct (dwordBuff [0], 9);
468 dwordBuff[0] ^= CipherFunct (dwordBuff [1], 10);
469 dwordBuff[1] ^= CipherFunct (dwordBuff [0], 11);
470 dwordBuff[0] ^= CipherFunct (dwordBuff [1], 12);
471 dwordBuff[1] ^= CipherFunct (dwordBuff [0], 13);
472 dwordBuff[0] ^= CipherFunct (dwordBuff [1], 14);
473 dwordBuff[1] ^= CipherFunct (dwordBuff [0], 15);
476 uint t = dwordBuff [0];
477 dwordBuff [0] = dwordBuff [1];
479 Buffer.BlockCopy (dwordBuff, 0, byteBuff, 0, BLOCK_BYTE_SIZE);
482 Permutation (byteBuff, dest, fpTab);
486 internal void Decrypt (byte [] block, byte [] output)
488 byte [] dest = (output == null ? block : output);
490 byte [] byteBuff = this.byteBuff;
491 uint [] dwordBuff = this.dwordBuff;
493 Permutation (block, byteBuff, ipTab);
496 Buffer.BlockCopy (byteBuff, 0, dwordBuff, 0, BLOCK_BYTE_SIZE);
498 uint t = dwordBuff [0];
499 dwordBuff [0] = dwordBuff [1];
502 // 16 rounds in reverse order
503 dwordBuff[1] ^= CipherFunct (dwordBuff [0], 15);
504 dwordBuff[0] ^= CipherFunct (dwordBuff [1], 14);
505 dwordBuff[1] ^= CipherFunct (dwordBuff [0], 13);
506 dwordBuff[0] ^= CipherFunct (dwordBuff [1], 12);
507 dwordBuff[1] ^= CipherFunct (dwordBuff [0], 11);
508 dwordBuff[0] ^= CipherFunct (dwordBuff [1], 10);
509 dwordBuff[1] ^= CipherFunct (dwordBuff [0], 9);
510 dwordBuff[0] ^= CipherFunct (dwordBuff [1], 8);
511 dwordBuff[1] ^= CipherFunct (dwordBuff [0], 7);
512 dwordBuff[0] ^= CipherFunct (dwordBuff [1], 6);
513 dwordBuff[1] ^= CipherFunct (dwordBuff [0], 5);
514 dwordBuff[0] ^= CipherFunct (dwordBuff [1], 4);
515 dwordBuff[1] ^= CipherFunct (dwordBuff [0], 3);
516 dwordBuff[0] ^= CipherFunct (dwordBuff [1], 2);
517 dwordBuff[1] ^= CipherFunct (dwordBuff [0], 1);
518 dwordBuff[0] ^= CipherFunct (dwordBuff [1], 0);
522 Buffer.BlockCopy (dwordBuff, 0, byteBuff, 0, BLOCK_BYTE_SIZE);
525 Permutation (byteBuff, dest, fpTab);
534 public abstract class DES : SymmetricAlgorithm {
541 public static new DES Create()
547 public static new DES Create(string name)
554 public static bool IsWeakKey (byte [] rgbKey)
556 if (!DESCore.IsValidKeySize (rgbKey)) {
557 throw new CryptographicException ();
560 ulong lk = DESCore.PackKey (rgbKey);
561 foreach (ulong wk in DESCore.weakKeys) {
562 if (lk == wk) return true;
568 public static bool IsSemiWeakKey (byte [] rgbKey)
570 if (!DESCore.IsValidKeySize (rgbKey)) {
571 throw new CryptographicException ();
574 ulong lk = DESCore.PackKey (rgbKey);
575 foreach (ulong swk in DESCore.semiweakKeys) {
576 if (lk == swk) return true;
581 public override byte[] Key {
586 this.key = new byte [DESCore.KEY_BYTE_SIZE];
587 Array.Copy (value, 0, this.key, 0, DESCore.KEY_BYTE_SIZE);
593 } // System.Security.Cryptography