Skip to content

Commit 15d408e

Browse files
committed
feat(block-ciphers): add DES algorithm
1 parent 97a6e2a commit 15d408e

File tree

9 files changed

+354
-3
lines changed

9 files changed

+354
-3
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
- [AES] (Rijndael)
2121
- [Blowfish]
2222
- [CAST5] (CAST-128)
23+
- [DES]
2324
- ECB, CBC, CFB, OFB and CTR [block modes]
2425

2526
### [Message Authentication Code] algorithms (MACs)
@@ -64,6 +65,7 @@ reviews. **USE AT YOUR OWN RISK**
6465
[AES]: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
6566
[Blowfish]: https://en.wikipedia.org/wiki/Blowfish_(cipher)
6667
[CAST5]: https://en.wikipedia.org/wiki/CAST-128
68+
[DES]: https://en.wikipedia.org/wiki/Data_Encryption_Standard
6769
[Message Authentication Code]: https://en.wikipedia.org/wiki/Message_authentication_code
6870
[HMAC]: https://en.wikipedia.org/wiki/HMAC
6971
[Key Derivation Functions]: https://en.wikipedia.org/wiki/Key_derivation_function

benchmarks/aes.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ import { args } from "./utils/benchmarkArgs.ts";
77
const { runs: _runs, ...opts } = args;
88
const runs = _runs || 25;
99

10-
// deno-fmt-ignore
11-
const key = new Uint8Array([1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16])
10+
const key = new Uint8Array(16);
1211
const iv = new Uint8Array(Aes.BLOCK_SIZE);
1312
const data = new Uint8Array(1024 * 1024 * 2);
1413

benchmarks/all.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { args } from "./utils/benchmarkArgs.ts";
33
import "./aes.ts";
44
import "./blowfish.ts";
55
import "./cast5.ts";
6+
import "./des.ts";
67

78
const { runs: _, ...opts } = args;
89

benchmarks/blowfish.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { args } from "./utils/benchmarkArgs.ts";
66
const { runs: _runs, ...opts } = args;
77
const runs = _runs || 25;
88

9-
const key = new Uint8Array([1, 2, 3, 4, 5, 6, 7, 8]);
9+
const key = new Uint8Array(8);
1010
const iv = new Uint8Array(Blowfish.BLOCK_SIZE);
1111
const data = new Uint8Array(1024 * 1024 * 2);
1212

benchmarks/des.ts

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
import { bench, runBenchmarks } from "../dev_deps.ts";
2+
import { Des } from "../des.ts";
3+
import { Cbc, Cfb, Ctr, Ecb, Ofb } from "../block-modes.ts";
4+
import { args } from "./utils/benchmarkArgs.ts";
5+
6+
const { runs: _runs, ...opts } = args;
7+
const runs = _runs || 25;
8+
9+
const key = new Uint8Array(8);
10+
const iv = new Uint8Array(Des.BLOCK_SIZE);
11+
const data = new Uint8Array(1024 * 1024 * 2);
12+
13+
bench({
14+
name: "DES-ECB 2MiB Encrypt",
15+
runs,
16+
func(b) {
17+
const cipher = new Ecb(Des, key);
18+
b.start();
19+
cipher.encrypt(data);
20+
b.stop();
21+
},
22+
});
23+
24+
bench({
25+
name: "DES-ECB 2MiB Decrypt",
26+
runs,
27+
func(b) {
28+
const cipher = new Ecb(Des, key);
29+
b.start();
30+
cipher.decrypt(data);
31+
b.stop();
32+
},
33+
});
34+
35+
bench({
36+
name: "DES-CBC 2MiB Encrypt",
37+
runs,
38+
func(b) {
39+
const cipher = new Cbc(Des, key, iv);
40+
b.start();
41+
cipher.encrypt(data);
42+
b.stop();
43+
},
44+
});
45+
46+
bench({
47+
name: "DES-CBC 2MiB Decrypt",
48+
runs,
49+
func(b) {
50+
const cipher = new Cbc(Des, key, iv);
51+
b.start();
52+
cipher.decrypt(data);
53+
b.stop();
54+
},
55+
});
56+
57+
bench({
58+
name: "DES-CFB 2MiB Encrypt",
59+
runs,
60+
func(b) {
61+
const cipher = new Cfb(Des, key, iv);
62+
b.start();
63+
cipher.encrypt(data);
64+
b.stop();
65+
},
66+
});
67+
68+
bench({
69+
name: "DES-CFB 2MiB Decrypt",
70+
runs,
71+
func(b) {
72+
const cipher = new Cfb(Des, key, iv);
73+
b.start();
74+
cipher.decrypt(data);
75+
b.stop();
76+
},
77+
});
78+
79+
bench({
80+
name: "DES-OFB 2MiB Encrypt/Decrypt",
81+
runs,
82+
func(b) {
83+
const cipher = new Ofb(Des, key, iv);
84+
b.start();
85+
cipher.encrypt(data);
86+
b.stop();
87+
},
88+
});
89+
90+
bench({
91+
name: "DES-CTR 2MiB Encrypt/Decrypt",
92+
runs,
93+
func(b) {
94+
const cipher = new Ctr(Des, key, iv);
95+
b.start();
96+
cipher.encrypt(data);
97+
b.stop();
98+
},
99+
});
100+
101+
if (import.meta.main) {
102+
runBenchmarks(opts);
103+
}

des.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
export { Des } from "./src/des/mod.ts";
2+
export type { BlockCipher } from "./src/block-modes/mod.ts";

src/des/consts.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// deno-fmt-ignore-file
2+
export const SP1 = [16843776,0,65536,16843780,16842756,66564,4,65536,1024,16843776,16843780,1024,16778244,16842756,16777216,4,1028,16778240,16778240,66560,66560,16842752,16842752,16778244,65540,16777220,16777220,65540,0,1028,66564,16777216,65536,16843780,4,16842752,16843776,16777216,16777216,1024,16842756,65536,66560,16777220,1024,4,16778244,66564,16843780,65540,16842752,16778244,16777220,1028,66564,16843776,1028,16778240,16778240,0,65540,66560,0,16842756];
3+
export const SP2 = [2148565024,2147516416,32768,1081376,1048576,32,2148532256,2147516448,2147483680,2148565024,2148564992,2147483648,2147516416,1048576,32,2148532256,1081344,1048608,2147516448,0,2147483648,32768,1081376,2148532224,1048608,2147483680,0,1081344,32800,2148564992,2148532224,32800,0,1081376,2148532256,1048576,2147516448,2148532224,2148564992,32768,2148532224,2147516416,32,2148565024,1081376,32,32768,2147483648,32800,2148564992,1048576,2147483680,1048608,2147516448,2147483680,1048608,1081344,0,2147516416,32800,2147483648,2148532256,2148565024,1081344];
4+
export const SP3 = [520,134349312,0,134348808,134218240,0,131592,134218240,131080,134217736,134217736,131072,134349320,131080,134348800,520,134217728,8,134349312,512,131584,134348800,134348808,131592,134218248,131584,131072,134218248,8,134349320,512,134217728,134349312,134217728,131080,520,131072,134349312,134218240,0,512,131080,134349320,134218240,134217736,512,0,134348808,134218248,131072,134217728,134349320,8,131592,131584,134217736,134348800,134218248,520,134348800,131592,8,134348808,131584];
5+
export const SP4 = [8396801,8321,8321,128,8396928,8388737,8388609,8193,0,8396800,8396800,8396929,129,0,8388736,8388609,1,8192,8388608,8396801,128,8388608,8193,8320,8388737,1,8320,8388736,8192,8396928,8396929,129,8388736,8388609,8396800,8396929,129,0,0,8396800,8320,8388736,8388737,1,8396801,8321,8321,128,8396929,129,1,8192,8388609,8193,8396928,8388737,8193,8320,8388608,8396801,128,8388608,8192,8396928];
6+
export const SP5 = [256,34078976,34078720,1107296512,524288,256,1073741824,34078720,1074266368,524288,33554688,1074266368,1107296512,1107820544,524544,1073741824,33554432,1074266112,1074266112,0,1073742080,1107820800,1107820800,33554688,1107820544,1073742080,0,1107296256,34078976,33554432,1107296256,524544,524288,1107296512,256,33554432,1073741824,34078720,1107296512,1074266368,33554688,1073741824,1107820544,34078976,1074266368,256,33554432,1107820544,1107820800,524544,1107296256,1107820800,34078720,0,1074266112,1107296256,524544,33554688,1073742080,524288,0,1074266112,34078976,1073742080];
7+
export const SP6 = [536870928,541065216,16384,541081616,541065216,16,541081616,4194304,536887296,4210704,4194304,536870928,4194320,536887296,536870912,16400,0,4194320,536887312,16384,4210688,536887312,16,541065232,541065232,0,4210704,541081600,16400,4210688,541081600,536870912,536887296,16,541065232,4210688,541081616,4194304,16400,536870928,4194304,536887296,536870912,16400,536870928,541081616,4210688,541065216,4210704,541081600,0,541065232,16,16384,541065216,4210704,16384,4194320,536887312,0,541081600,536870912,4194320,536887312];
8+
export const SP7 = [2097152,69206018,67110914,0,2048,67110914,2099202,69208064,69208066,2097152,0,67108866,2,67108864,69206018,2050,67110912,2099202,2097154,67110912,67108866,69206016,69208064,2097154,69206016,2048,2050,69208066,2099200,2,67108864,2099200,67108864,2099200,2097152,67110914,67110914,69206018,69206018,2,2097154,67108864,67110912,2097152,69208064,2050,2099202,69208064,2050,67108866,69208066,69206016,2099200,0,2,69208066,0,2099202,69206016,2048,67108866,67110912,2048,2097154];
9+
export const SP8 = [268439616,4096,262144,268701760,268435456,268439616,64,268435456,262208,268697600,268701760,266240,268701696,266304,4096,64,268697600,268435520,268439552,4160,266240,262208,268697664,268701696,4160,0,0,268697664,268435520,268439552,266304,262144,266304,262144,268701696,4096,64,268697664,4096,266304,268439552,64,268435520,268697600,268697664,268435456,262144,268439616,0,268701760,262208,268435520,268697600,268439552,268439616,0,268701760,266240,266240,4160,4160,262208,268435456,268701696];
10+
11+
export const PC2_0 = [0,4,536870912,536870916,65536,65540,536936448,536936452,512,516,536871424,536871428,66048,66052,536936960,536936964];
12+
export const PC2_1 = [0,1,1048576,1048577,67108864,67108865,68157440,68157441,256,257,1048832,1048833,67109120,67109121,68157696,68157697];
13+
export const PC2_2 = [0,8,2048,2056,16777216,16777224,16779264,16779272,0,8,2048,2056,16777216,16777224,16779264,16779272];
14+
export const PC2_3 = [0,2097152,134217728,136314880,8192,2105344,134225920,136323072,131072,2228224,134348800,136445952,139264,2236416,134356992,136454144];
15+
export const PC2_4 = [0,262144,16,262160,0,262144,16,262160,4096,266240,4112,266256,4096,266240,4112,266256];
16+
export const PC2_5 = [0,1024,32,1056,0,1024,32,1056,33554432,33555456,33554464,33555488,33554432,33555456,33554464,33555488];
17+
export const PC2_6 = [0,268435456,524288,268959744,2,268435458,524290,268959746,0,268435456,524288,268959744,2,268435458,524290,268959746];
18+
export const PC2_7 = [0,65536,2048,67584,536870912,536936448,536872960,536938496,131072,196608,133120,198656,537001984,537067520,537004032,537069568];
19+
export const PC2_8 = [0,262144,0,262144,2,262146,2,262146,33554432,33816576,33554432,33816576,33554434,33816578,33554434,33816578];
20+
export const PC2_9 = [0,268435456,8,268435464,0,268435456,8,268435464,1024,268436480,1032,268436488,1024,268436480,1032,268436488];
21+
export const PC2_10 = [0,32,0,32,1048576,1048608,1048576,1048608,8192,8224,8192,8224,1056768,1056800,1056768,1056800];
22+
export const PC2_11 = [0,16777216,512,16777728,2097152,18874368,2097664,18874880,67108864,83886080,67109376,83886592,69206016,85983232,69206528,85983744];
23+
export const PC2_12 = [0,4096,134217728,134221824,524288,528384,134742016,134746112,16,4112,134217744,134221840,524304,528400,134742032,134746128];
24+
export const PC2_13 = [0,4,256,260,0,4,256,260,1,5,257,261,1,5,257,261];
25+
26+
export const SHIFTS = [0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0];

src/des/mod.ts

Lines changed: 192 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,192 @@
1+
import type { BlockCipher } from "../block-modes/base.ts";
2+
// deno-fmt-ignore
3+
import { PC2_0, PC2_1, PC2_10, PC2_11, PC2_12, PC2_13, PC2_2, PC2_3, PC2_4, PC2_5, PC2_6, PC2_7, PC2_8, PC2_9, SHIFTS, SP1, SP2, SP3, SP4, SP5, SP6, SP7, SP8 } from "./consts.ts";
4+
5+
/**
6+
* Data Encryption Standard (DES) block cipher.
7+
*
8+
* Note: This is a low level class. Use a block cipher mode to
9+
* encrypt and decrypt data.
10+
*/
11+
export class Des implements BlockCipher {
12+
/**
13+
* The block size of the block cipher in bytes
14+
*/
15+
static readonly BLOCK_SIZE = 8;
16+
#keys = new Uint32Array(32);
17+
18+
constructor(key: Uint8Array) {
19+
if (key.length != 8) {
20+
throw new Error("Invalid key length (must be 8 bytes)");
21+
}
22+
23+
const keyV = new DataView(key.buffer);
24+
let l = keyV.getUint32(0);
25+
let r = keyV.getUint32(4);
26+
let t = (l >>> 4 ^ r) & 0x0f0f0f0f;
27+
r ^= t;
28+
l ^= t << 4;
29+
t = (r >>> 16 ^ l) & 0x0000ffff;
30+
l ^= t;
31+
r ^= t << 16;
32+
t = (l >>> 2 ^ r) & 0x33333333;
33+
r ^= t;
34+
l ^= t << 2;
35+
t = (r >>> 16 ^ l) & 0x0000ffff;
36+
l ^= t;
37+
r ^= t << 16;
38+
t = (l >>> 1 ^ r) & 0x55555555;
39+
r ^= t;
40+
l ^= t << 1;
41+
t = (r >>> 8 ^ l) & 0x00ff00ff;
42+
l ^= t;
43+
r ^= t << 8;
44+
t = (l >>> 1 ^ r) & 0x55555555;
45+
r ^= t;
46+
l ^= t << 1;
47+
t = l << 8 | r >>> 20 & 0x000000f0;
48+
l = r << 24 | r << 8 & 0xff0000 | r >>> 8 & 0xff00 | r >>> 24 & 0xf0;
49+
r = t;
50+
51+
for (let i = 0; i < 32; i += 2) {
52+
if (SHIFTS[i / 2]) {
53+
l = l << 2 | l >>> 26;
54+
r = r << 2 | r >>> 26;
55+
} else {
56+
l = l << 1 | l >>> 27;
57+
r = r << 1 | r >>> 27;
58+
}
59+
l &= -0xf;
60+
r &= -0xf;
61+
const lt = PC2_0[l >>> 28] | PC2_1[(l >>> 24) & 0xf] |
62+
PC2_2[(l >>> 20) & 0xf] | PC2_3[(l >>> 16) & 0xf] |
63+
PC2_4[(l >>> 12) & 0xf] | PC2_5[(l >>> 8) & 0xf] |
64+
PC2_6[(l >>> 4) & 0xf];
65+
const rt = PC2_7[r >>> 28] | PC2_8[(r >>> 24) & 0xf] |
66+
PC2_9[(r >>> 20) & 0xf] | PC2_10[(r >>> 16) & 0xf] |
67+
PC2_11[(r >>> 12) & 0xf] | PC2_12[(r >>> 8) & 0xf] |
68+
PC2_13[(r >>> 4) & 0xf];
69+
t = ((rt >>> 16) ^ lt) & 0x0000ffff;
70+
this.#keys[i] = lt ^ t;
71+
this.#keys[i + 1] = rt ^ (t << 16);
72+
}
73+
}
74+
75+
encryptBlock(data: DataView, offset: number) {
76+
let l = data.getUint32(offset);
77+
let r = data.getUint32(offset + 4);
78+
let t = (l >>> 4 ^ r) & 0x0f0f0f0f;
79+
r ^= t;
80+
l ^= t << 4;
81+
t = (l >>> 16 ^ r) & 0x0000ffff;
82+
r ^= t;
83+
l ^= t << 16;
84+
t = (r >>> 2 ^ l) & 0x33333333;
85+
l ^= t;
86+
r ^= t << 2;
87+
t = (r >>> 8 ^ l) & 0x00ff00ff;
88+
l ^= t;
89+
r ^= t << 8;
90+
t = (l >>> 1 ^ r) & 0x55555555;
91+
r ^= t;
92+
l ^= t << 1;
93+
l = l << 1 | l >>> 31;
94+
r = r << 1 | r >>> 31;
95+
96+
for (let i = 0; i < 32; i += 2) {
97+
const r1 = r ^ this.#keys[i];
98+
const r2 = (r >>> 4 | r << 28) ^ this.#keys[i + 1];
99+
t = l, l = r;
100+
r = t ^ (
101+
SP2[r1 >>> 24 & 0x3f] |
102+
SP4[r1 >>> 16 & 0x3f] |
103+
SP6[r1 >>> 8 & 0x3f] |
104+
SP8[r1 & 0x3f] |
105+
SP1[r2 >>> 24 & 0x3f] |
106+
SP3[r2 >>> 16 & 0x3f] |
107+
SP5[r2 >>> 8 & 0x3f] |
108+
SP7[r2 & 0x3f]
109+
);
110+
}
111+
112+
t = l, l = r, r = t;
113+
l = l >>> 1 | l << 31;
114+
r = r >>> 1 | r << 31;
115+
t = (l >>> 1 ^ r) & 0x55555555;
116+
r ^= t;
117+
l ^= t << 1;
118+
t = (r >>> 8 ^ l) & 0x00ff00ff;
119+
l ^= t;
120+
r ^= t << 8;
121+
t = (r >>> 2 ^ l) & 0x33333333;
122+
l ^= t;
123+
r ^= t << 2;
124+
t = (l >>> 16 ^ r) & 0x0000ffff;
125+
r ^= t;
126+
l ^= t << 16;
127+
t = (l >>> 4 ^ r) & 0x0f0f0f0f;
128+
r ^= t;
129+
l ^= t << 4;
130+
data.setUint32(offset, l);
131+
data.setUint32(offset + 4, r);
132+
}
133+
134+
decryptBlock(data: DataView, offset: number) {
135+
let l = data.getUint32(offset);
136+
let r = data.getUint32(offset + 4);
137+
let t = (l >>> 4 ^ r) & 0x0f0f0f0f;
138+
r ^= t;
139+
l ^= t << 4;
140+
t = (l >>> 16 ^ r) & 0x0000ffff;
141+
r ^= t;
142+
l ^= t << 16;
143+
t = (r >>> 2 ^ l) & 0x33333333;
144+
l ^= t;
145+
r ^= t << 2;
146+
t = (r >>> 8 ^ l) & 0x00ff00ff;
147+
l ^= t;
148+
r ^= t << 8;
149+
t = (l >>> 1 ^ r) & 0x55555555;
150+
r ^= t;
151+
l ^= t << 1;
152+
l = l << 1 | l >>> 31;
153+
r = r << 1 | r >>> 31;
154+
155+
for (let i = 30; i >= 0; i -= 2) {
156+
const r1 = r ^ this.#keys[i];
157+
const r2 = (r >>> 4 | r << 28) ^ this.#keys[i + 1];
158+
t = l, l = r;
159+
r = t ^ (
160+
SP2[r1 >>> 24 & 0x3f] |
161+
SP4[r1 >>> 16 & 0x3f] |
162+
SP6[r1 >>> 8 & 0x3f] |
163+
SP8[r1 & 0x3f] |
164+
SP1[r2 >>> 24 & 0x3f] |
165+
SP3[r2 >>> 16 & 0x3f] |
166+
SP5[r2 >>> 8 & 0x3f] |
167+
SP7[r2 & 0x3f]
168+
);
169+
}
170+
171+
t = l, l = r, r = t;
172+
l = l >>> 1 | l << 31;
173+
r = r >>> 1 | r << 31;
174+
t = (l >>> 1 ^ r) & 0x55555555;
175+
r ^= t;
176+
l ^= t << 1;
177+
t = (r >>> 8 ^ l) & 0x00ff00ff;
178+
l ^= t;
179+
r ^= t << 8;
180+
t = (r >>> 2 ^ l) & 0x33333333;
181+
l ^= t;
182+
r ^= t << 2;
183+
t = (l >>> 16 ^ r) & 0x0000ffff;
184+
r ^= t;
185+
l ^= t << 16;
186+
t = (l >>> 4 ^ r) & 0x0f0f0f0f;
187+
r ^= t;
188+
l ^= t << 4;
189+
data.setInt32(offset, l);
190+
data.setInt32(offset + 4, r);
191+
}
192+
}

tests/des.test.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
import { assertEquals, decodeHex } from "../dev_deps.ts";
2+
import { Des } from "../des.ts";
3+
4+
Deno.test("[Block Cipher] DES", () => {
5+
const testData = [
6+
["0000000000000000", "0000000000000000", "8CA64DE9C1B123A7"],
7+
["FFFFFFFFFFFFFFFF", "FFFFFFFFFFFFFFFF", "7359B2163E4EDC58"],
8+
["3000000000000000", "1000000000000001", "958E6E627A05557B"],
9+
["1111111111111111", "1111111111111111", "F40379AB9E0EC533"],
10+
["0123456789ABCDEF", "1111111111111111", "17668DFC7292532D"],
11+
["1111111111111111", "0123456789ABCDEF", "8A5AE1F81AB8F2DD"],
12+
["FEDCBA9876543210", "0123456789ABCDEF", "ED39D950FA74BCC4"],
13+
];
14+
15+
for (const [key, plaintext, ciphertext] of testData) {
16+
const des = new Des(decodeHex(key));
17+
const data = decodeHex(plaintext);
18+
const dataView = new DataView(data.buffer);
19+
20+
des.encryptBlock(dataView, 0);
21+
assertEquals(data, decodeHex(ciphertext));
22+
23+
des.decryptBlock(dataView, 0);
24+
assertEquals(data, decodeHex(plaintext));
25+
}
26+
});

0 commit comments

Comments
 (0)