2 * Copyright (c) 2017 Thomas Pornin <pornin@bolet.org>
4 * Permission is hereby granted, free of charge, to any person obtaining
5 * a copy of this software and associated documentation files (the
6 * "Software"), to deal in the Software without restriction, including
7 * without limitation the rights to use, copy, modify, merge, publish,
8 * distribute, sublicense, and/or sell copies of the Software, and to
9 * permit persons to whom the Software is furnished to do so, subject to
10 * the following conditions:
12 * The above copyright notice and this permission notice shall be
13 * included in all copies or substantial portions of the Software.
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
19 * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
20 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
21 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 * Perfunctory implementation of the Triple-DES block cipher (also works
31 * as "single DES" when used with an 8-byte DES key). It only processes
32 * single blocks, and does it "in place" (the input block is replaced
33 * with the output block in the same array). This code is a direct
34 * translation of the specification and is not optimized for speed.
36 * Supported key sizes are 8, 16 and 24 bytes. When an 8-byte key is
37 * used, this is the original DES (64-bit key, out of which 8 are
38 * unused, so the "true key size" is 56 bits). When a 16-byte or 24-byte
39 * key is used, this is Triple-DES (with a 16-byte key, the key is
40 * expanded to 24 bytes by reusing bytes 0..7 as bytes 16..23 of the
43 * Instances are not thread-safe; however, distinct threads may use
44 * distinct instances concurrently.
46 * Since instances are pure managed code, there is no need for explicit
47 * disposal after usage.
50 public sealed class DES : BlockCipherCore {
56 * Initialize a new instance.
63 /* see IBlockCipher */
64 public override IBlockCipher Dup()
67 Array.Copy(skey, 0, d.skey, 0, skey.Length);
73 * Get the block size in bytes (always 8).
75 public override int BlockSize {
82 * Set the key (8, 16 or 24 bytes).
84 public override void SetKey(byte[] key, int off, int len)
88 KeySchedule(key, off, 0);
92 KeySchedule(key, off, 0);
93 KeySchedule(key, off + 8, 16);
94 KeySchedule(key, off, 32);
98 KeySchedule(key, off, 0);
99 KeySchedule(key, off + 8, 16);
100 KeySchedule(key, off + 16, 32);
104 throw new ArgumentException(
105 "bad DES/3DES key length: " + len);
109 * Inverse order of subkeys for the middle-DES (3DES
110 * uses the "EDE" configuration).
112 for (int j = 16; j < 24; j ++) {
114 skey[j] = skey[47 - j];
119 void KeySchedule(byte[] key, int off, int skeyOff)
121 ulong k = Dec64be(key, off);
124 ulong kr = k & 0x0FFFFFFF;
125 for (int i = 0; i < 16; i ++) {
127 kl = ((kl << r) | (kl >> (28 - r))) & 0x0FFFFFFF;
128 kr = ((kr << r) | (kr >> (28 - r))) & 0x0FFFFFFF;
129 skey[skeyOff + i] = Perm(tabPC2, (kl << 28) | kr);
134 * Encrypt one block; the block consists in the 8 bytes beginning
135 * at offset 'off'. Other bytes in the array are unaltered.
137 public override void BlockEncrypt(byte[] buf, int off)
140 throw new Exception("no key provided");
142 ulong x = Dec64be(buf, off);
144 uint xl = (uint)(x >> 32);
146 for (int i = 0, k = 0; i < rounds; i ++) {
147 xl ^= FConf(xr, skey[k ++]);
148 xr ^= FConf(xl, skey[k ++]);
149 xl ^= FConf(xr, skey[k ++]);
150 xr ^= FConf(xl, skey[k ++]);
151 xl ^= FConf(xr, skey[k ++]);
152 xr ^= FConf(xl, skey[k ++]);
153 xl ^= FConf(xr, skey[k ++]);
154 xr ^= FConf(xl, skey[k ++]);
155 xl ^= FConf(xr, skey[k ++]);
156 xr ^= FConf(xl, skey[k ++]);
157 xl ^= FConf(xr, skey[k ++]);
158 xr ^= FConf(xl, skey[k ++]);
159 xl ^= FConf(xr, skey[k ++]);
160 xr ^= FConf(xl, skey[k ++]);
161 xl ^= FConf(xr, skey[k ++]);
162 uint tmp = xr ^ FConf(xl, skey[k ++]);
166 x = ((ulong)xl << 32) | (ulong)xr;
168 Enc64be(x, buf, off);
172 * Decrypt one block; the block consists in the 8 bytes beginning
173 * at offset 'off'. Other bytes in the array are unaltered.
175 public override void BlockDecrypt(byte[] buf, int off)
178 throw new Exception("no key provided");
180 ulong x = Dec64be(buf, off);
182 uint xl = (uint)(x >> 32);
184 for (int i = 0, k = rounds << 4; i < rounds; i ++) {
185 xl ^= FConf(xr, skey[-- k]);
186 xr ^= FConf(xl, skey[-- k]);
187 xl ^= FConf(xr, skey[-- k]);
188 xr ^= FConf(xl, skey[-- k]);
189 xl ^= FConf(xr, skey[-- k]);
190 xr ^= FConf(xl, skey[-- k]);
191 xl ^= FConf(xr, skey[-- k]);
192 xr ^= FConf(xl, skey[-- k]);
193 xl ^= FConf(xr, skey[-- k]);
194 xr ^= FConf(xl, skey[-- k]);
195 xl ^= FConf(xr, skey[-- k]);
196 xr ^= FConf(xl, skey[-- k]);
197 xl ^= FConf(xr, skey[-- k]);
198 xr ^= FConf(xl, skey[-- k]);
199 xl ^= FConf(xr, skey[-- k]);
200 uint tmp = xr ^ FConf(xl, skey[-- k]);
204 x = ((ulong)xl << 32) | (ulong)xr;
206 Enc64be(x, buf, off);
210 * Arrays below are extracted exactly from FIPS 46-3. They use
211 * the conventions defined in that document:
213 * -- Bits are numbered from 1, in the left-to-right order; in
214 * a 64-bit integer, the most significant (leftmost) bit is 1,
215 * while the least significant (rightmost) bit is 64.
217 * -- For each permutation (or extraction), the defined array
218 * lists the source index of each bit.
220 * -- For the S-boxes, bits 1 (leftmost) and 6 (rightmost) select
221 * the row, and bits 2 to 5 select the index within the row.
224 static uint[,] Sdef = {
226 14, 4, 13, 1, 2, 15, 11, 8, 3, 10, 6, 12, 5, 9, 0, 7,
227 0, 15, 7, 4, 14, 2, 13, 1, 10, 6, 12, 11, 9, 5, 3, 8,
228 4, 1, 14, 8, 13, 6, 2, 11, 15, 12, 9, 7, 3, 10, 5, 0,
229 15, 12, 8, 2, 4, 9, 1, 7, 5, 11, 3, 14, 10, 0, 6, 13
231 15, 1, 8, 14, 6, 11, 3, 4, 9, 7, 2, 13, 12, 0, 5, 10,
232 3, 13, 4, 7, 15, 2, 8, 14, 12, 0, 1, 10, 6, 9, 11, 5,
233 0, 14, 7, 11, 10, 4, 13, 1, 5, 8, 12, 6, 9, 3, 2, 15,
234 13, 8, 10, 1, 3, 15, 4, 2, 11, 6, 7, 12, 0, 5, 14, 9
236 10, 0, 9, 14, 6, 3, 15, 5, 1, 13, 12, 7, 11, 4, 2, 8,
237 13, 7, 0, 9, 3, 4, 6, 10, 2, 8, 5, 14, 12, 11, 15, 1,
238 13, 6, 4, 9, 8, 15, 3, 0, 11, 1, 2, 12, 5, 10, 14, 7,
239 1, 10, 13, 0, 6, 9, 8, 7, 4, 15, 14, 3, 11, 5, 2, 12
241 7, 13, 14, 3, 0, 6, 9, 10, 1, 2, 8, 5, 11, 12, 4, 15,
242 13, 8, 11, 5, 6, 15, 0, 3, 4, 7, 2, 12, 1, 10, 14, 9,
243 10, 6, 9, 0, 12, 11, 7, 13, 15, 1, 3, 14, 5, 2, 8, 4,
244 3, 15, 0, 6, 10, 1, 13, 8, 9, 4, 5, 11, 12, 7, 2, 14
246 2, 12, 4, 1, 7, 10, 11, 6, 8, 5, 3, 15, 13, 0, 14, 9,
247 14, 11, 2, 12, 4, 7, 13, 1, 5, 0, 15, 10, 3, 9, 8, 6,
248 4, 2, 1, 11, 10, 13, 7, 8, 15, 9, 12, 5, 6, 3, 0, 14,
249 11, 8, 12, 7, 1, 14, 2, 13, 6, 15, 0, 9, 10, 4, 5, 3
251 12, 1, 10, 15, 9, 2, 6, 8, 0, 13, 3, 4, 14, 7, 5, 11,
252 10, 15, 4, 2, 7, 12, 9, 5, 6, 1, 13, 14, 0, 11, 3, 8,
253 9, 14, 15, 5, 2, 8, 12, 3, 7, 0, 4, 10, 1, 13, 11, 6,
254 4, 3, 2, 12, 9, 5, 15, 10, 11, 14, 1, 7, 6, 0, 8, 13
256 4, 11, 2, 14, 15, 0, 8, 13, 3, 12, 9, 7, 5, 10, 6, 1,
257 13, 0, 11, 7, 4, 9, 1, 10, 14, 3, 5, 12, 2, 15, 8, 6,
258 1, 4, 11, 13, 12, 3, 7, 14, 10, 15, 6, 8, 0, 5, 9, 2,
259 6, 11, 13, 8, 1, 4, 10, 7, 9, 5, 0, 15, 14, 2, 3, 12
261 13, 2, 8, 4, 6, 15, 11, 1, 10, 9, 3, 14, 5, 0, 12, 7,
262 1, 15, 13, 8, 10, 3, 7, 4, 12, 5, 6, 11, 0, 14, 9, 2,
263 7, 11, 4, 1, 9, 12, 14, 2, 0, 6, 10, 13, 15, 3, 5, 8,
264 2, 1, 14, 7, 4, 10, 8, 13, 15, 12, 9, 0, 3, 5, 6, 11
268 static int[] defP = {
269 16, 7, 20, 21, 29, 12, 28, 17, 1, 15, 23, 26, 5, 18, 31, 10,
270 2, 8, 24, 14, 32, 27, 3, 9, 19, 13, 30, 6, 22, 11, 4, 25
273 static int[] defPC1 = {
274 57, 49, 41, 33, 25, 17, 9,
275 1, 58, 50, 42, 34, 26, 18,
276 10, 2, 59, 51, 43, 35, 27,
277 19, 11, 3, 60, 52, 44, 36,
278 63, 55, 47, 39, 31, 23, 15,
279 7, 62, 54, 46, 38, 30, 22,
280 14, 6, 61, 53, 45, 37, 29,
281 21, 13, 5, 28, 20, 12, 4
284 static int[] defPC2 = {
285 14, 17, 11, 24, 1, 5,
286 3, 28, 15, 6, 21, 10,
287 23, 19, 12, 4, 26, 8,
288 16, 7, 27, 20, 13, 2,
289 41, 52, 31, 37, 47, 55,
290 30, 40, 51, 45, 33, 48,
291 44, 49, 39, 56, 34, 53,
292 46, 42, 50, 36, 29, 32
295 static int[] rotK = {
296 1, 1, 2, 2, 2, 2, 2, 2,
297 1, 2, 2, 2, 2, 2, 2, 1
301 * Permutations (and extractions) are implemented with the
302 * following tables, that are initialized from the class
303 * initialization code. The representation of a permutation is
304 * an array of words, where word at index k is a one-bit mask
305 * that identifies the source bit that should go at position k
306 * in the output. The "k" index here is in right-to-left
307 * convention: least significant bit (rightmost) is numbered 0.
309 * The Perm() method applies a permutation, using one of the
312 * The S*[] arrays contain the S-boxes, with the permutation P
313 * effect merged in, and expecting their 6-bit input "as is".
315 static ulong[] tabPC1;
316 static ulong[] tabPC2;
318 static uint[] S1, S2, S3, S4, S5, S6, S7, S8;
322 tabPC1 = new ulong[defPC1.Length];
323 for (int i = 0; i < defPC1.Length; i ++) {
324 tabPC1[55 - i] = (ulong)1 << (64 - defPC1[i]);
326 tabPC2 = new ulong[defPC2.Length];
327 for (int i = 0; i < defPC2.Length; i ++) {
328 tabPC2[47 - i] = (ulong)1 << (56 - defPC2[i]);
331 int[] PInv = new int[32];
332 for (int i = 0; i < defP.Length; i ++) {
333 PInv[32 - defP[i]] = 31 - i;
335 S1 = MakeSbox(PInv, 0);
336 S2 = MakeSbox(PInv, 1);
337 S3 = MakeSbox(PInv, 2);
338 S4 = MakeSbox(PInv, 3);
339 S5 = MakeSbox(PInv, 4);
340 S6 = MakeSbox(PInv, 5);
341 S7 = MakeSbox(PInv, 6);
342 S8 = MakeSbox(PInv, 7);
345 static uint[] MakeSbox(int[] PInv, int i)
347 uint[] S = new uint[64];
348 for (int j = 0; j < 64; j ++) {
349 int idx = ((j & 0x01) << 4) | (j & 0x20)
351 uint zb = Sdef[i, idx];
353 for (int k = 0; k < 4; k ++) {
354 za |= ((zb >> k) & 1)
355 << PInv[((7 - i) << 2) + k];
362 static ulong IPStep(ulong x, int size, ulong mask)
364 uint left = (uint)(x >> 32);
365 uint right = (uint)x;
366 uint tmp = ((left >> size) ^ right) & (uint)mask;
369 return ((ulong)left << 32) | (ulong)right;
372 static ulong DoIP(ulong x)
375 * Permutation algorithm is initially from Richard
376 * Outerbridge; this implementation has been adapted
377 * from Crypto++ "des.cpp" file (which is in public
380 uint l = (uint)(x >> 32);
383 t = ((l >> 4) ^ r) & 0x0F0F0F0F;
386 t = ((l >> 16) ^ r) & 0x0000FFFF;
389 t = ((r >> 2) ^ l) & 0x33333333;
392 t = ((r >> 8) ^ l) & 0x00FF00FF;
395 t = ((l >> 1) ^ r) & 0x55555555;
398 x = ((ulong)l << 32) | (ulong)r;
402 static ulong DoIPInv(ulong x)
407 uint l = (uint)(x >> 32);
410 t = ((l >> 1) ^ r) & 0x55555555;
413 t = ((r >> 8) ^ l) & 0x00FF00FF;
416 t = ((r >> 2) ^ l) & 0x33333333;
419 t = ((l >> 16) ^ r) & 0x0000FFFF;
422 t = ((l >> 4) ^ r) & 0x0F0F0F0F;
425 x = ((ulong)l << 32) | (ulong)r;
430 * Apply a permutation or extraction. For all k, bit k of the
431 * output (right-to-left numbering) is set if and only if the
432 * source bit in x defined by the tab[k] mask is set.
434 static ulong Perm(ulong[] tab, ulong x)
437 for (int i = 0; i < tab.Length; i ++) {
438 if ((x & tab[i]) != 0) {
445 static uint FConf(uint r0, ulong sk)
447 uint skhi = (uint)(sk >> 24);
448 uint sklo = (uint)sk;
449 uint r1 = (r0 >> 16) | (r0 << 16);
451 S1[((r1 >> 11) ^ (skhi >> 18)) & 0x3F]
452 | S2[((r0 >> 23) ^ (skhi >> 12)) & 0x3F]
453 | S3[((r0 >> 19) ^ (skhi >> 6)) & 0x3F]
454 | S4[((r0 >> 15) ^ (skhi )) & 0x3F]
455 | S5[((r0 >> 11) ^ (sklo >> 18)) & 0x3F]
456 | S6[((r0 >> 7) ^ (sklo >> 12)) & 0x3F]
457 | S7[((r0 >> 3) ^ (sklo >> 6)) & 0x3F]
458 | S8[((r1 >> 15) ^ (sklo )) & 0x3F];
462 * 64-bit big-endian decoding.
464 static ulong Dec64be(byte[] buf, int off)
466 return ((ulong)buf[off] << 56)
467 | ((ulong)buf[off + 1] << 48)
468 | ((ulong)buf[off + 2] << 40)
469 | ((ulong)buf[off + 3] << 32)
470 | ((ulong)buf[off + 4] << 24)
471 | ((ulong)buf[off + 5] << 16)
472 | ((ulong)buf[off + 6] << 8)
473 | (ulong)buf[off + 7];
477 * 64-bit big-endian encoding.
479 static void Enc64be(ulong v, byte[] buf, int off)
481 buf[off + 0] = (byte)(v >> 56);
482 buf[off + 1] = (byte)(v >> 48);
483 buf[off + 2] = (byte)(v >> 40);
484 buf[off + 3] = (byte)(v >> 32);
485 buf[off + 4] = (byte)(v >> 24);
486 buf[off + 5] = (byte)(v >> 16);
487 buf[off + 6] = (byte)(v >> 8);
488 buf[off + 7] = (byte)v;