Skip to content

Commit

Permalink
feat(block-ciphers): add CAST5 algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
aykxt committed Apr 2, 2021
1 parent fc5857e commit 840c419
Show file tree
Hide file tree
Showing 8 changed files with 410 additions and 7 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ A collection of useful cryptographic algorithms written in Typescript.

- [AES] (Rijndael)
- [Blowfish]
- [CAST5]
- ECB, CBC, CFB, OFB and CTR [block modes]

### [Message Authentication Code] algorithms (MACs)
Expand Down Expand Up @@ -60,6 +61,7 @@ reviews. **USE AT YOUR OWN RISK**
[block modes]: https://en.wikipedia.org/wiki/Block_cipher_mode_of_operation
[AES]: https://en.wikipedia.org/wiki/Advanced_Encryption_Standard
[Blowfish]: https://en.wikipedia.org/wiki/Blowfish_(cipher)
[CAST5]: https://en.wikipedia.org/wiki/CAST-128
[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
106 changes: 106 additions & 0 deletions benchmarks/cast5.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
import { bench, runBenchmarks } from "../dev_deps.ts";
import { Cast5 } from "../cast5.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(16);
const iv = new Uint8Array(Cast5.BLOCK_SIZE);
const data = new Uint8Array(1024 * 1024 * 2);

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

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

bench({
name: "CAST5-CBC 2MiB Encrypt",
runs,
func(b) {
const cipher = new Cbc(Cast5, key, iv);

b.start();
cipher.encrypt(data);
b.stop();
},
});

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

bench({
name: "CAST5-CFB 2MiB Encrypt",
runs,
func(b) {
const cipher = new Cfb(Cast5, key, iv);

b.start();
cipher.encrypt(data);
b.stop();
},
});

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

bench({
// Encryption and decryption are the same
name: "CAST5-OFB 2MiB Encrypt/Decrypt",
runs,
func(b) {
const cipher = new Ofb(Cast5, key, iv);
b.start();
cipher.encrypt(data);
b.stop();
},
});

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

if (import.meta.main) {
runBenchmarks(opts);
}
2 changes: 2 additions & 0 deletions cast5.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
export { Cast5 } from "./src/cast5/mod.ts";
export type { BlockCipher } from "./src/block-modes/mod.ts";
10 changes: 4 additions & 6 deletions src/aes/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { BlockCipher } from "../block-modes/base.ts";
* Advanced Encryption Standard (AES) block cipher.
*
* Note: This is a low level class. Use a block cipher mode to
* encrypt and decrypt data
* encrypt and decrypt data.
*/
export class Aes implements BlockCipher {
/**
Expand Down Expand Up @@ -42,7 +42,7 @@ export class Aes implements BlockCipher {
S[(tmp >> 8) & 0xff] << 8 ^ S[tmp & 0xff];

if (i % keyLen === 0) {
tmp = tmp << 8 ^ tmp >>> 24 ^ (rcon << 24);
tmp = tmp << 8 ^ tmp >>> 24 ^ rcon << 24;
rcon = rcon << 1 ^ (rcon >> 7) * 0x11b;
}
}
Expand All @@ -55,12 +55,10 @@ export class Aes implements BlockCipher {
if (i <= 4 || j < 4) {
this.#kd[j] = tmp;
} else {
this.#kd[j] = (
T5[S[tmp >>> 24]] ^
this.#kd[j] = T5[S[tmp >>> 24]] ^
T6[S[(tmp >> 16) & 0xff]] ^
T7[S[(tmp >> 8) & 0xff]] ^
T8[S[tmp & 0xff]]
);
T8[S[tmp & 0xff]];
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/blowfish/mod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { P, S } from "./consts.ts";
* Blowfish block cipher.
*
* Note: This is a low level class. Use a block cipher mode to
* encrypt and decrypt data
* encrypt and decrypt data.
*/
export class Blowfish implements BlockCipher {
/**
Expand Down
9 changes: 9 additions & 0 deletions src/cast5/consts.ts

Large diffs are not rendered by default.

231 changes: 231 additions & 0 deletions src/cast5/mod.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,231 @@
import { S1, S2, S3, S4, S5, S6, S7, S8 } from "./consts.ts";
import { BlockCipher } from "../block-modes/mod.ts";

/**
* CAST5 block cipher.
*
* Note: This is a low level class. Use a block cipher mode to
* encrypt and decrypt data.
*/
// https://tools.ietf.org/html/rfc2144
export class Cast5 implements BlockCipher {
/**
* The block size of the block cipher in bytes
*/
static readonly BLOCK_SIZE = 8;

#km = new Uint32Array(16);
#kr = new Uint8Array(16);
#shortKey: boolean;

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)",
);
}

// https://tools.ietf.org/html/rfc2144#section-2.5
this.#shortKey = key.length <= 10;

const x = new Uint8Array(16);
x.set(key);
const xV = new DataView(x.buffer, x.byteOffset, x.byteLength);
const k = new Uint32Array(32);

const z = new Uint8Array(16);
const zV = new DataView(z.buffer);

// https://tools.ietf.org/html/rfc2144#section-2.4
// deno-fmt-ignore
for (let i = 0; i <= 16; i += 16) {
zV.setUint32(0, xV.getUint32(0) ^ S5[x[13]] ^ S6[x[15]] ^ S7[x[12]] ^ S8[x[14]] ^ S7[x[8]]);
zV.setUint32(4, xV.getUint32(8) ^ S5[z[0]] ^ S6[z[2]] ^ S7[z[1]] ^ S8[z[3]] ^ S8[x[10]]);
zV.setUint32(8, xV.getUint32(12) ^ S5[z[7]] ^ S6[z[6]] ^ S7[z[5]] ^ S8[z[4]] ^ S5[x[9]]),
zV.setUint32(12, xV.getUint32(4) ^ S5[z[10]] ^ S6[z[9]] ^ S7[z[11]] ^ S8[z[8]] ^ S6[x[11]]);
k[i + 0] = S5[z[8]] ^ S6[z[9]] ^ S7[z[7]] ^ S8[z[6]] ^ S5[z[2]];
k[i + 1] = S5[z[10]] ^ S6[z[11]] ^ S7[z[5]] ^ S8[z[4]] ^ S6[z[6]];
k[i + 2] = S5[z[12]] ^ S6[z[13]] ^ S7[z[3]] ^ S8[z[2]] ^ S7[z[9]];
k[i + 3] = S5[z[14]] ^ S6[z[15]] ^ S7[z[1]] ^ S8[z[0]] ^ S8[z[12]];
xV.setUint32(0, zV.getUint32(8) ^ S5[z[5]] ^ S6[z[7]] ^ S7[z[4]] ^ S8[z[6]] ^ S7[z[0]]);
xV.setUint32(4, zV.getUint32(0) ^ S5[x[0]] ^ S6[x[2]] ^ S7[x[1]] ^ S8[x[3]] ^ S8[z[2]]);
xV.setUint32(8, zV.getUint32(4) ^ S5[x[7]] ^ S6[x[6]] ^ S7[x[5]] ^ S8[x[4]] ^ S5[z[1]]);
xV.setUint32(12, zV.getUint32(12) ^ S5[x[10]] ^ S6[x[9]] ^ S7[x[11]] ^ S8[x[8]] ^ S6[z[3]]);
k[i + 4] = S5[x[3]] ^ S6[x[2]] ^ S7[x[12]] ^ S8[x[13]] ^ S5[x[8]];
k[i + 5] = S5[x[1]] ^ S6[x[0]] ^ S7[x[14]] ^ S8[x[15]] ^ S6[x[13]];
k[i + 6] = S5[x[7]] ^ S6[x[6]] ^ S7[x[8]] ^ S8[x[9]] ^ S7[x[3]];
k[i + 7] = S5[x[5]] ^ S6[x[4]] ^ S7[x[10]] ^ S8[x[11]] ^ S8[x[7]];
zV.setUint32(0, xV.getUint32(0) ^ S5[x[13]] ^ S6[x[15]] ^ S7[x[12]] ^ S8[x[14]] ^ S7[x[8]]);
zV.setUint32(4, xV.getUint32(8) ^ S5[z[0]] ^ S6[z[2]] ^ S7[z[1]] ^ S8[z[3]] ^ S8[x[10]]);
zV.setUint32(8, xV.getUint32(12) ^ S5[z[7]] ^ S6[z[6]] ^ S7[z[5]] ^ S8[z[4]] ^ S5[x[9]]);
zV.setUint32(12, xV.getUint32(4) ^ S5[z[10]] ^ S6[z[9]] ^ S7[z[11]] ^ S8[z[8]] ^ S6[x[11]]);
k[i + 8] = S5[z[3]] ^ S6[z[2]] ^ S7[z[12]] ^ S8[z[13]] ^ S5[z[9]];
k[i + 9] = S5[z[1]] ^ S6[z[0]] ^ S7[z[14]] ^ S8[z[15]] ^ S6[z[12]];
k[i + 10] = S5[z[7]] ^ S6[z[6]] ^ S7[z[8]] ^ S8[z[9]] ^ S7[z[2]];
k[i + 11] = S5[z[5]] ^ S6[z[4]] ^ S7[z[10]] ^ S8[z[11]] ^ S8[z[6]];
xV.setUint32(0, zV.getUint32(8) ^ S5[z[5]] ^ S6[z[7]] ^ S7[z[4]] ^ S8[z[6]] ^ S7[z[0]]);
xV.setUint32(4, zV.getUint32(0) ^ S5[x[0]] ^ S6[x[2]] ^ S7[x[1]] ^ S8[x[3]] ^ S8[z[2]]);
xV.setUint32(8, zV.getUint32(4) ^ S5[x[7]] ^ S6[x[6]] ^ S7[x[5]] ^ S8[x[4]] ^ S5[z[1]]);
xV.setUint32(12, zV.getUint32(12) ^ S5[x[10]] ^ S6[x[9]] ^ S7[x[11]] ^ S8[x[8]] ^ S6[z[3]]);
k[i + 12] = S5[x[8]] ^ S6[x[9]] ^ S7[x[7]] ^ S8[x[6]] ^ S5[x[3]];
k[i + 13] = S5[x[10]] ^ S6[x[11]] ^ S7[x[5]] ^ S8[x[4]] ^ S6[x[7]];
k[i + 14] = S5[x[12]] ^ S6[x[13]] ^ S7[x[3]] ^ S8[x[2]] ^ S7[x[8]];
k[i + 15] = S5[x[14]] ^ S6[x[15]] ^ S7[x[1]] ^ S8[x[0]] ^ S8[x[13]];
}

for (let i = 0; i < 16; i++) {
this.#km[i] = k[i];
this.#kr[i] = k[16 + i] & 0x1f;
}
}

encryptBlock(data: DataView, offset: number) {
let l = data.getUint32(offset);
let r = data.getUint32(offset + 4);
let t;

t = r;
r = l ^ this.f1(r, 0);
l = t;
t = r;
r = l ^ this.f2(r, 1);
l = t;
t = r;
r = l ^ this.f3(r, 2);
l = t;
t = r;
r = l ^ this.f1(r, 3);
l = t;

t = r;
r = l ^ this.f2(r, 4);
l = t;
t = r;
r = l ^ this.f3(r, 5);
l = t;
t = r;
r = l ^ this.f1(r, 6);
l = t;
t = r;
r = l ^ this.f2(r, 7);
l = t;

t = r;
r = l ^ this.f3(r, 8);
l = t;
t = r;
r = l ^ this.f1(r, 9);
l = t;
t = r;
r = l ^ this.f2(r, 10);
l = t;
t = r;
r = l ^ this.f3(r, 11);
l = t;

if (!this.#shortKey) {
t = r;
r = l ^ this.f1(r, 12);
l = t;
t = r;
r = l ^ this.f2(r, 13);
l = t;
t = r;
r = l ^ this.f3(r, 14);
l = t;
t = r;
r = l ^ this.f1(r, 15);
l = t;
}

data.setUint32(offset, r);
data.setUint32(offset + 4, l);
}

decryptBlock(data: DataView, offset: number) {
let l = data.getUint32(offset);
let r = data.getUint32(offset + 4);
let t;

if (!this.#shortKey) {
t = r;
r = l ^ this.f1(r, 15);
l = t;
t = r;
r = l ^ this.f3(r, 14);
l = t;
t = r;
r = l ^ this.f2(r, 13);
l = t;
t = r;
r = l ^ this.f1(r, 12);
l = t;
}

t = r;
r = l ^ this.f3(r, 11);
l = t;
t = r;
r = l ^ this.f2(r, 10);
l = t;
t = r;
r = l ^ this.f1(r, 9);
l = t;
t = r;
r = l ^ this.f3(r, 8);
l = t;

t = r;
r = l ^ this.f2(r, 7);
l = t;
t = r;
r = l ^ this.f1(r, 6);
l = t;
t = r;
r = l ^ this.f3(r, 5);
l = t;
t = r;
r = l ^ this.f2(r, 4);
l = t;

t = r;
r = l ^ this.f1(r, 3);
l = t;
t = r;
r = l ^ this.f3(r, 2);
l = t;
t = r;
r = l ^ this.f2(r, 1);
l = t;
t = r;
r = l ^ this.f1(r, 0);
l = t;

data.setUint32(offset, r);
data.setUint32(offset + 4, l);
}

private f1(d: number, n: number) {
const r = this.#kr[n];
const t = this.#km[n] + d;
const i = t << r | t >>> (32 - r);
return ((S1[i >>> 24] ^ S2[(i >>> 16) & 0xff]) -
S3[(i >>> 8) & 0xff]) + S4[i & 0xff];
}

private f2(d: number, n: number) {
const r = this.#kr[n];
const t = this.#km[n] ^ d;
const i = t << r | t >>> (32 - r);
return ((S1[i >>> 24] - S2[(i >>> 16) & 0xff]) +
S3[(i >>> 8) & 0xff]) ^ S4[i & 0xff];
}

private f3(d: number, n: number) {
const r = this.#kr[n];
const t = this.#km[n] - d;
const i = t << r | t >>> (32 - r);
return ((S1[i >>> 24] + S2[(i >>> 16) & 0xff]) ^
S3[(i >>> 8) & 0xff]) - S4[i & 0xff];
}
}
Loading

0 comments on commit 840c419

Please sign in to comment.