Skip to content

Commit

Permalink
feat(block-ciphers): add 3DES algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
aykxt committed Apr 5, 2021
1 parent 15d408e commit c0877ae
Show file tree
Hide file tree
Showing 12 changed files with 257 additions and 99 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
- [Blowfish]
- [CAST5] (CAST-128)
- [DES]
- [3DES]
- ECB, CBC, CFB, OFB and CTR [block modes]

### [Message Authentication Code] algorithms (MACs)
Expand Down Expand Up @@ -66,6 +67,7 @@ reviews. **USE AT YOUR OWN RISK**
[Blowfish]: https://en.wikipedia.org/wiki/Blowfish_(cipher)
[CAST5]: https://en.wikipedia.org/wiki/CAST-128
[DES]: https://en.wikipedia.org/wiki/Data_Encryption_Standard
[3DES]: https://en.wikipedia.org/wiki/Triple_DES
[Message Authentication Code]: https://en.wikipedia.org/wiki/Message_authentication_code
[HMAC]: https://en.wikipedia.org/wiki/HMAC
[Key Derivation Functions]: https://en.wikipedia.org/wiki/Key_derivation_function
Expand Down
2 changes: 1 addition & 1 deletion aes.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { Aes } from "./src/aes/mod.ts";
export type { BlockCipher } from "./src/block-modes/mod.ts";
export type { BlockCipher } from "./src/block-modes/base.ts";
1 change: 1 addition & 0 deletions benchmarks/all.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import "./aes.ts";
import "./blowfish.ts";
import "./cast5.ts";
import "./des.ts";
import "./tdes.ts";

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

Expand Down
103 changes: 103 additions & 0 deletions benchmarks/tdes.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { bench, runBenchmarks } from "../dev_deps.ts";
import { TripleDes } from "../tdes.ts";
import { Cbc, Cfb, Ctr, Ecb, Ofb } from "../block-modes.ts";
import { args } from "./utils/benchmarkArgs.ts";

const { runs: _runs, ...opts } = args;
const runs = _runs || 25;

const key = new Uint8Array(24);
const iv = new Uint8Array(TripleDes.BLOCK_SIZE);
const data = new Uint8Array(1024 * 1024 * 2);

bench({
name: "3DES-ECB 2MiB Encrypt",
runs,
func(b) {
const cipher = new Ecb(TripleDes, key);
b.start();
cipher.encrypt(data);
b.stop();
},
});

bench({
name: "3DES-ECB 2MiB Decrypt",
runs,
func(b) {
const cipher = new Ecb(TripleDes, key);
b.start();
cipher.decrypt(data);
b.stop();
},
});

bench({
name: "3DES-CBC 2MiB Encrypt",
runs,
func(b) {
const cipher = new Cbc(TripleDes, key, iv);
b.start();
cipher.encrypt(data);
b.stop();
},
});

bench({
name: "3DES-CBC 2MiB Decrypt",
runs,
func(b) {
const cipher = new Cbc(TripleDes, key, iv);
b.start();
cipher.decrypt(data);
b.stop();
},
});

bench({
name: "3DES-CFB 2MiB Encrypt",
runs,
func(b) {
const cipher = new Cfb(TripleDes, key, iv);
b.start();
cipher.encrypt(data);
b.stop();
},
});

bench({
name: "3DES-CFB 2MiB Decrypt",
runs,
func(b) {
const cipher = new Cfb(TripleDes, key, iv);
b.start();
cipher.decrypt(data);
b.stop();
},
});

bench({
name: "3DES-OFB 2MiB Encrypt/Decrypt",
runs,
func(b) {
const cipher = new Ofb(TripleDes, key, iv);
b.start();
cipher.encrypt(data);
b.stop();
},
});

bench({
name: "3DES-CTR 2MiB Encrypt/Decrypt",
runs,
func(b) {
const cipher = new Ctr(TripleDes, key, iv);
b.start();
cipher.encrypt(data);
b.stop();
},
});

if (import.meta.main) {
runBenchmarks(opts);
}
2 changes: 1 addition & 1 deletion blowfish.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { Blowfish } from "./src/blowfish/mod.ts";
export type { BlockCipher } from "./src/block-modes/mod.ts";
export type { BlockCipher } from "./src/block-modes/base.ts";
2 changes: 1 addition & 1 deletion cast5.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { Cast5 } from "./src/cast5/mod.ts";
export type { BlockCipher } from "./src/block-modes/mod.ts";
export type { BlockCipher } from "./src/block-modes/base.ts";
4 changes: 2 additions & 2 deletions src/cast5/mod.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { S1, S2, S3, S4, S5, S6, S7, S8 } from "./consts.ts";
import { BlockCipher } from "../block-modes/mod.ts";
import { BlockCipher } from "../block-modes/base.ts";

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

Expand Down
4 changes: 1 addition & 3 deletions src/des/consts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,4 @@ export const PC2_9 = [0,268435456,8,268435464,0,268435456,8,268435464,1024,26843
export const PC2_10 = [0,32,0,32,1048576,1048608,1048576,1048608,8192,8224,8192,8224,1056768,1056800,1056768,1056800];
export const PC2_11 = [0,16777216,512,16777728,2097152,18874368,2097664,18874880,67108864,83886080,67109376,83886592,69206016,85983232,69206528,85983744];
export const PC2_12 = [0,4096,134217728,134221824,524288,528384,134742016,134746112,16,4112,134217744,134221840,524304,528400,134742032,134746128];
export const PC2_13 = [0,4,256,260,0,4,256,260,1,5,257,261,1,5,257,261];

export const SHIFTS = [0, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0];
export const PC2_13 = [0,4,256,260,0,4,256,260,1,5,257,261,1,5,257,261];
151 changes: 60 additions & 91 deletions src/des/mod.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import type { BlockCipher } from "../block-modes/base.ts";
// deno-fmt-ignore
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";
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";

/**
* Data Encryption Standard (DES) block cipher.
Expand All @@ -19,8 +19,7 @@ export class Des implements BlockCipher {
if (key.length != 8) {
throw new Error("Invalid key length (must be 8 bytes)");
}

const keyV = new DataView(key.buffer);
const keyV = new DataView(key.buffer, key.byteOffset, key.byteLength);
let l = keyV.getUint32(0);
let r = keyV.getUint32(4);
let t = (l >>> 4 ^ r) & 0x0f0f0f0f;
Expand All @@ -47,17 +46,14 @@ export class Des implements BlockCipher {
t = l << 8 | r >>> 20 & 0x000000f0;
l = r << 24 | r << 8 & 0xff0000 | r >>> 8 & 0xff00 | r >>> 24 & 0xf0;
r = t;

for (let i = 0; i < 32; i += 2) {
if (SHIFTS[i / 2]) {
l = l << 2 | l >>> 26;
r = r << 2 | r >>> 26;
if (i == 0 || i == 2 || i == 16 || i == 30) {
l = (l << 1 | l >>> 27) & 0xfffffff1;
r = (r << 1 | r >>> 27) & 0xfffffff1;
} else {
l = l << 1 | l >>> 27;
r = r << 1 | r >>> 27;
l = (l << 2 | l >>> 26) & 0xfffffff1;
r = (r << 2 | r >>> 26) & 0xfffffff1;
}
l &= -0xf;
r &= -0xf;
const lt = PC2_0[l >>> 28] | PC2_1[(l >>> 24) & 0xf] |
PC2_2[(l >>> 20) & 0xf] | PC2_3[(l >>> 16) & 0xf] |
PC2_4[(l >>> 12) & 0xf] | PC2_5[(l >>> 8) & 0xf] |
Expand All @@ -73,30 +69,13 @@ export class Des implements BlockCipher {
}

encryptBlock(data: DataView, offset: number) {
let l = data.getUint32(offset);
let r = data.getUint32(offset + 4);
let t = (l >>> 4 ^ r) & 0x0f0f0f0f;
r ^= t;
l ^= t << 4;
t = (l >>> 16 ^ r) & 0x0000ffff;
r ^= t;
l ^= t << 16;
t = (r >>> 2 ^ l) & 0x33333333;
l ^= t;
r ^= t << 2;
t = (r >>> 8 ^ l) & 0x00ff00ff;
l ^= t;
r ^= t << 8;
t = (l >>> 1 ^ r) & 0x55555555;
r ^= t;
l ^= t << 1;
l = l << 1 | l >>> 31;
r = r << 1 | r >>> 31;
let [l, r] = ip(data, offset);

for (let i = 0; i < 32; i += 2) {
const r1 = r ^ this.#keys[i];
const r2 = (r >>> 4 | r << 28) ^ this.#keys[i + 1];
t = l, l = r;
const t = l;
l = r;
r = t ^ (
SP2[r1 >>> 24 & 0x3f] |
SP4[r1 >>> 16 & 0x3f] |
Expand All @@ -109,53 +88,17 @@ export class Des implements BlockCipher {
);
}

t = l, l = r, r = t;
l = l >>> 1 | l << 31;
r = r >>> 1 | r << 31;
t = (l >>> 1 ^ r) & 0x55555555;
r ^= t;
l ^= t << 1;
t = (r >>> 8 ^ l) & 0x00ff00ff;
l ^= t;
r ^= t << 8;
t = (r >>> 2 ^ l) & 0x33333333;
l ^= t;
r ^= t << 2;
t = (l >>> 16 ^ r) & 0x0000ffff;
r ^= t;
l ^= t << 16;
t = (l >>> 4 ^ r) & 0x0f0f0f0f;
r ^= t;
l ^= t << 4;
data.setUint32(offset, l);
data.setUint32(offset + 4, r);
rip(r, l, data, offset);
}

decryptBlock(data: DataView, offset: number) {
let l = data.getUint32(offset);
let r = data.getUint32(offset + 4);
let t = (l >>> 4 ^ r) & 0x0f0f0f0f;
r ^= t;
l ^= t << 4;
t = (l >>> 16 ^ r) & 0x0000ffff;
r ^= t;
l ^= t << 16;
t = (r >>> 2 ^ l) & 0x33333333;
l ^= t;
r ^= t << 2;
t = (r >>> 8 ^ l) & 0x00ff00ff;
l ^= t;
r ^= t << 8;
t = (l >>> 1 ^ r) & 0x55555555;
r ^= t;
l ^= t << 1;
l = l << 1 | l >>> 31;
r = r << 1 | r >>> 31;
let [l, r] = ip(data, offset);

for (let i = 30; i >= 0; i -= 2) {
const r1 = r ^ this.#keys[i];
const r2 = (r >>> 4 | r << 28) ^ this.#keys[i + 1];
t = l, l = r;
const t = l;
l = r;
r = t ^ (
SP2[r1 >>> 24 & 0x3f] |
SP4[r1 >>> 16 & 0x3f] |
Expand All @@ -168,25 +111,51 @@ export class Des implements BlockCipher {
);
}

t = l, l = r, r = t;
l = l >>> 1 | l << 31;
r = r >>> 1 | r << 31;
t = (l >>> 1 ^ r) & 0x55555555;
r ^= t;
l ^= t << 1;
t = (r >>> 8 ^ l) & 0x00ff00ff;
l ^= t;
r ^= t << 8;
t = (r >>> 2 ^ l) & 0x33333333;
l ^= t;
r ^= t << 2;
t = (l >>> 16 ^ r) & 0x0000ffff;
r ^= t;
l ^= t << 16;
t = (l >>> 4 ^ r) & 0x0f0f0f0f;
r ^= t;
l ^= t << 4;
data.setInt32(offset, l);
data.setInt32(offset + 4, r);
rip(r, l, data, offset);
}
}

function ip(data: DataView, offset: number) {
let l = data.getUint32(offset);
let r = data.getUint32(offset + 4);
let t = (l >>> 4 ^ r) & 0x0f0f0f0f;
r ^= t;
l ^= t << 4;
t = (l >>> 16 ^ r) & 0x0000ffff;
r ^= t;
l ^= t << 16;
t = (r >>> 2 ^ l) & 0x33333333;
l ^= t;
r ^= t << 2;
t = (r >>> 8 ^ l) & 0x00ff00ff;
l ^= t;
r ^= t << 8;
t = (l >>> 1 ^ r) & 0x55555555;
r ^= t;
l ^= t << 1;
l = l << 1 | l >>> 31;
r = r << 1 | r >>> 31;
return [l, r];
}

function rip(l: number, r: number, data: DataView, offset: number) {
l = l >>> 1 | l << 31;
r = r >>> 1 | r << 31;
let t = (l >>> 1 ^ r) & 0x55555555;
r ^= t;
l ^= t << 1;
t = (r >>> 8 ^ l) & 0x00ff00ff;
l ^= t;
r ^= t << 8;
t = (r >>> 2 ^ l) & 0x33333333;
l ^= t;
r ^= t << 2;
t = (l >>> 16 ^ r) & 0x0000ffff;
r ^= t;
l ^= t << 16;
t = (l >>> 4 ^ r) & 0x0f0f0f0f;
r ^= t;
l ^= t << 4;
data.setInt32(offset, l);
data.setInt32(offset + 4, r);
}
Loading

0 comments on commit c0877ae

Please sign in to comment.