Skip to content

Commit c0877ae

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

File tree

12 files changed

+257
-99
lines changed

12 files changed

+257
-99
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
- [Blowfish]
2222
- [CAST5] (CAST-128)
2323
- [DES]
24+
- [3DES]
2425
- ECB, CBC, CFB, OFB and CTR [block modes]
2526

2627
### [Message Authentication Code] algorithms (MACs)
@@ -66,6 +67,7 @@ reviews. **USE AT YOUR OWN RISK**
6667
[Blowfish]: https://en.wikipedia.org/wiki/Blowfish_(cipher)
6768
[CAST5]: https://en.wikipedia.org/wiki/CAST-128
6869
[DES]: https://en.wikipedia.org/wiki/Data_Encryption_Standard
70+
[3DES]: https://en.wikipedia.org/wiki/Triple_DES
6971
[Message Authentication Code]: https://en.wikipedia.org/wiki/Message_authentication_code
7072
[HMAC]: https://en.wikipedia.org/wiki/HMAC
7173
[Key Derivation Functions]: https://en.wikipedia.org/wiki/Key_derivation_function

aes.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
export { Aes } from "./src/aes/mod.ts";
2-
export type { BlockCipher } from "./src/block-modes/mod.ts";
2+
export type { BlockCipher } from "./src/block-modes/base.ts";

benchmarks/all.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import "./aes.ts";
44
import "./blowfish.ts";
55
import "./cast5.ts";
66
import "./des.ts";
7+
import "./tdes.ts";
78

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

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

blowfish.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
export { Blowfish } from "./src/blowfish/mod.ts";
2-
export type { BlockCipher } from "./src/block-modes/mod.ts";
2+
export type { BlockCipher } from "./src/block-modes/base.ts";

cast5.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
11
export { Cast5 } from "./src/cast5/mod.ts";
2-
export type { BlockCipher } from "./src/block-modes/mod.ts";
2+
export type { BlockCipher } from "./src/block-modes/base.ts";

src/cast5/mod.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { S1, S2, S3, S4, S5, S6, S7, S8 } from "./consts.ts";
2-
import { BlockCipher } from "../block-modes/mod.ts";
2+
import { BlockCipher } from "../block-modes/base.ts";
33

44
/**
55
* CAST5 block cipher.
@@ -21,7 +21,7 @@ export class Cast5 implements BlockCipher {
2121
constructor(key: Uint8Array) {
2222
if (key.length < 5 || key.length > 16) {
2323
throw new Error(
24-
"Invalid key size (must be between 5 bytes to 16 bytes long)",
24+
"Invalid key size (must be between 5 and 16 bytes)",
2525
);
2626
}
2727

src/des/consts.ts

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,4 @@ export const PC2_9 = [0,268435456,8,268435464,0,268435456,8,268435464,1024,26843
2121
export const PC2_10 = [0,32,0,32,1048576,1048608,1048576,1048608,8192,8224,8192,8224,1056768,1056800,1056768,1056800];
2222
export const PC2_11 = [0,16777216,512,16777728,2097152,18874368,2097664,18874880,67108864,83886080,67109376,83886592,69206016,85983232,69206528,85983744];
2323
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];
24+
export const PC2_13 = [0,4,256,260,0,4,256,260,1,5,257,261,1,5,257,261];

src/des/mod.ts

Lines changed: 60 additions & 91 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import type { BlockCipher } from "../block-modes/base.ts";
22
// 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";
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, SP1, SP2, SP3, SP4, SP5, SP6, SP7, SP8 } from "./consts.ts";
44

55
/**
66
* Data Encryption Standard (DES) block cipher.
@@ -19,8 +19,7 @@ export class Des implements BlockCipher {
1919
if (key.length != 8) {
2020
throw new Error("Invalid key length (must be 8 bytes)");
2121
}
22-
23-
const keyV = new DataView(key.buffer);
22+
const keyV = new DataView(key.buffer, key.byteOffset, key.byteLength);
2423
let l = keyV.getUint32(0);
2524
let r = keyV.getUint32(4);
2625
let t = (l >>> 4 ^ r) & 0x0f0f0f0f;
@@ -47,17 +46,14 @@ export class Des implements BlockCipher {
4746
t = l << 8 | r >>> 20 & 0x000000f0;
4847
l = r << 24 | r << 8 & 0xff0000 | r >>> 8 & 0xff00 | r >>> 24 & 0xf0;
4948
r = t;
50-
5149
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;
50+
if (i == 0 || i == 2 || i == 16 || i == 30) {
51+
l = (l << 1 | l >>> 27) & 0xfffffff1;
52+
r = (r << 1 | r >>> 27) & 0xfffffff1;
5553
} else {
56-
l = l << 1 | l >>> 27;
57-
r = r << 1 | r >>> 27;
54+
l = (l << 2 | l >>> 26) & 0xfffffff1;
55+
r = (r << 2 | r >>> 26) & 0xfffffff1;
5856
}
59-
l &= -0xf;
60-
r &= -0xf;
6157
const lt = PC2_0[l >>> 28] | PC2_1[(l >>> 24) & 0xf] |
6258
PC2_2[(l >>> 20) & 0xf] | PC2_3[(l >>> 16) & 0xf] |
6359
PC2_4[(l >>> 12) & 0xf] | PC2_5[(l >>> 8) & 0xf] |
@@ -73,30 +69,13 @@ export class Des implements BlockCipher {
7369
}
7470

7571
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;
72+
let [l, r] = ip(data, offset);
9573

9674
for (let i = 0; i < 32; i += 2) {
9775
const r1 = r ^ this.#keys[i];
9876
const r2 = (r >>> 4 | r << 28) ^ this.#keys[i + 1];
99-
t = l, l = r;
77+
const t = l;
78+
l = r;
10079
r = t ^ (
10180
SP2[r1 >>> 24 & 0x3f] |
10281
SP4[r1 >>> 16 & 0x3f] |
@@ -109,53 +88,17 @@ export class Des implements BlockCipher {
10988
);
11089
}
11190

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);
91+
rip(r, l, data, offset);
13292
}
13393

13494
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;
95+
let [l, r] = ip(data, offset);
15496

15597
for (let i = 30; i >= 0; i -= 2) {
15698
const r1 = r ^ this.#keys[i];
15799
const r2 = (r >>> 4 | r << 28) ^ this.#keys[i + 1];
158-
t = l, l = r;
100+
const t = l;
101+
l = r;
159102
r = t ^ (
160103
SP2[r1 >>> 24 & 0x3f] |
161104
SP4[r1 >>> 16 & 0x3f] |
@@ -168,25 +111,51 @@ export class Des implements BlockCipher {
168111
);
169112
}
170113

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);
114+
rip(r, l, data, offset);
191115
}
192116
}
117+
118+
function ip(data: DataView, offset: number) {
119+
let l = data.getUint32(offset);
120+
let r = data.getUint32(offset + 4);
121+
let t = (l >>> 4 ^ r) & 0x0f0f0f0f;
122+
r ^= t;
123+
l ^= t << 4;
124+
t = (l >>> 16 ^ r) & 0x0000ffff;
125+
r ^= t;
126+
l ^= t << 16;
127+
t = (r >>> 2 ^ l) & 0x33333333;
128+
l ^= t;
129+
r ^= t << 2;
130+
t = (r >>> 8 ^ l) & 0x00ff00ff;
131+
l ^= t;
132+
r ^= t << 8;
133+
t = (l >>> 1 ^ r) & 0x55555555;
134+
r ^= t;
135+
l ^= t << 1;
136+
l = l << 1 | l >>> 31;
137+
r = r << 1 | r >>> 31;
138+
return [l, r];
139+
}
140+
141+
function rip(l: number, r: number, data: DataView, offset: number) {
142+
l = l >>> 1 | l << 31;
143+
r = r >>> 1 | r << 31;
144+
let t = (l >>> 1 ^ r) & 0x55555555;
145+
r ^= t;
146+
l ^= t << 1;
147+
t = (r >>> 8 ^ l) & 0x00ff00ff;
148+
l ^= t;
149+
r ^= t << 8;
150+
t = (r >>> 2 ^ l) & 0x33333333;
151+
l ^= t;
152+
r ^= t << 2;
153+
t = (l >>> 16 ^ r) & 0x0000ffff;
154+
r ^= t;
155+
l ^= t << 16;
156+
t = (l >>> 4 ^ r) & 0x0f0f0f0f;
157+
r ^= t;
158+
l ^= t << 4;
159+
data.setInt32(offset, l);
160+
data.setInt32(offset + 4, r);
161+
}

0 commit comments

Comments
 (0)