UNPKG

node-ice

Version:

NodeJS Information Concealment Engine (ICE) algorithm

300 lines (239 loc) 7.92 kB
export class IceKey { private size: number; private rounds: number; private keySchedule: number[][]; private static spBox: number[][]; private static spBoxInitialised = false; private static sMod = [ [333, 313, 505, 369], [379, 375, 319, 391], [361, 445, 451, 397], [397, 425, 395, 505]]; private static sXor = [ [0x83, 0x85, 0x9b, 0xcd], [0xcc, 0xa7, 0xad, 0x41], [0x4b, 0x2e, 0xd4, 0x33], [0xea, 0xcb, 0x2e, 0x04]]; private static pBox = [ 0x00000001, 0x00000080, 0x00000400, 0x00002000, 0x00080000, 0x00200000, 0x01000000, 0x40000000, 0x00000008, 0x00000020, 0x00000100, 0x00004000, 0x00010000, 0x00800000, 0x04000000, 0x20000000, 0x00000004, 0x00000010, 0x00000200, 0x00008000, 0x00020000, 0x00400000, 0x08000000, 0x10000000, 0x00000002, 0x00000040, 0x00000800, 0x00001000, 0x00040000, 0x00100000, 0x02000000, 0x80000000]; private static keyrot = [ 0, 1, 2, 3, 2, 1, 3, 0, 1, 3, 2, 0, 3, 1, 0, 2]; // 8-bit Galois Field multiplication of a by b, modulo m. // Just like arithmetic multiplication, except that // additions and subtractions are replaced by XOR. private gf_mult(a: number, b: number, m: number): number { let res = 0; while (b != 0) { if ((b & 1) != 0) res ^= a; a <<= 1; b >>>= 1; if (a >= 256) a ^= m; } return (res); } // 8-bit Galois Field exponentiation. // Raise the base to the power of 7, modulo m. private gf_exp7(b: number, m: number): number { let x: number; if (b == 0) return (0); x = this.gf_mult(b, b, m); x = this.gf_mult(b, x, m); x = this.gf_mult(x, x, m); return (this.gf_mult(b, x, m)); } // Carry out the ICE 32-bit permutation. private perm32(x: number): number { let res = 0; let i = 0; while (x != 0) { if ((x & 1) != 0) res |= IceKey.pBox[i]; i++; x >>>= 1; } return (res); } // Initialise the substitution/permutation boxes. private spBoxInit() { IceKey.spBox = []; for (let i: number = 0; i < 4; i++) { IceKey.spBox[i] = []; for (let j: number = 0; j < 1024; j++) { IceKey.spBox[i][j] = 0; } } for (let i = 0; i < 1024; i++) { let col = (i >>> 1) & 0xff; let row = (i & 0x1) | ((i & 0x200) >>> 8); let x; x = this.gf_exp7(col ^ IceKey.sXor[0][row], IceKey.sMod[0][row]) << 24; IceKey.spBox[0][i] = this.perm32(x); x = this.gf_exp7(col ^ IceKey.sXor[1][row], IceKey.sMod[1][row]) << 16; IceKey.spBox[1][i] = this.perm32(x); x = this.gf_exp7(col ^ IceKey.sXor[2][row], IceKey.sMod[2][row]) << 8; IceKey.spBox[2][i] = this.perm32(x); x = this.gf_exp7(col ^ IceKey.sXor[3][row], IceKey.sMod[3][row]); IceKey.spBox[3][i] = this.perm32(x); } } // Create a new ICE key with the specified level. constructor(level: number) { if (!IceKey.spBoxInitialised) { this.spBoxInit(); IceKey.spBoxInitialised = true; } if (level < 1) { this.size = 1; this.rounds = 8; } else { this.size = level; this.rounds = level * 16; } this.keySchedule = []; for (let i: number = 0; i < this.rounds; i++) { this.keySchedule[i] = []; for (let j: number = 0; j < 3; j++) { this.keySchedule[i][j] = 0; } } } // Return the key size, in bytes. public keySize(): number { return (this.size * 8); } // Return the block size, in bytes. public blockSize(): number { return (8); } // Set 8 rounds [n, n+7] of the key schedule of an ICE key. private scheduleBuild(kb: Uint16Array, n: number, krot_idx: number) { let i; for (i = 0; i < 8; i++) { let j; let kr = IceKey.keyrot[krot_idx + i]; let subkey = this.keySchedule[n + i]; for (j = 0; j < 3; j++) this.keySchedule[n + i][j] = 0; for (j = 0; j < 15; j++) { let k; let curr_sk = j % 3; for (k = 0; k < 4; k++) { let curr_kb = kb[(kr + k) & 3]; let bit = curr_kb & 1; subkey[curr_sk] = (subkey[curr_sk] << 1) | bit; kb[(kr + k) & 3] = (curr_kb >>> 1) | ((bit ^ 1) << 15); } } } } // Set the key schedule of an ICE key. public set(key: Uint8Array) { let kb = new Uint16Array(4); for (let i = 0; i < 4; i++) kb[i] = 0; if (this.rounds == 8) { for (let i = 0; i < 4; i++) kb[3 - i] = ((key[i * 2] & 0xff) << 8) | (key[i * 2 + 1] & 0xff); this.scheduleBuild(kb, 0, 0); return; } for (let i = 0; i < this.size; i++) { for (let j = 0; j < 4; j++) kb[3 - j] = ((key[i * 8 + j * 2] & 0xff) << 8) | (key[i * 8 + j * 2 + 1] & 0xff); this.scheduleBuild(kb, i * 8, 0); this.scheduleBuild(kb, this.rounds - 8 - i * 8, 8); } } // Clear the key schedule to prevent memory snooping. public clear() { for (let i = 0; i < this.rounds; i++) for (let j = 0; j < 3; j++) this.keySchedule[i][j] = 0; } // The single round ICE f function. private roundFunc(p: number, subkey: number[]): number { let tl = ((p >>> 16) & 0x3ff) | (((p >>> 14) | (p << 18)) & 0xffc00); let tr = (p & 0x3ff) | ((p << 2) & 0xffc00); let al = subkey[2] & (tl ^ tr); let ar = al ^ tr; al ^= tl; al ^= subkey[0]; ar ^= subkey[1]; return (IceKey.spBox[0][al >>> 10] | IceKey.spBox[1][al & 0x3ff] | IceKey.spBox[2][ar >>> 10] | IceKey.spBox[3][ar & 0x3ff]); } // Encrypt a block of 8 bytes of data. public encrypt(plaintext: Uint8Array, ciphertext: Uint8Array) { let i; let l = 0, r = 0; for (i = 0; i < 4; i++) { l |= (plaintext[i] & 0xff) << (24 - i * 8); r |= (plaintext[i + 4] & 0xff) << (24 - i * 8); } for (i = 0; i < this.rounds; i += 2) { l ^= this.roundFunc(r, this.keySchedule[i]); r ^= this.roundFunc(l, this.keySchedule[i + 1]); } for (i = 0; i < 4; i++) { ciphertext[3 - i] = (Number)(r & 0xff); ciphertext[7 - i] = (Number)(l & 0xff); r >>>= 8; l >>>= 8; } } // Decrypt a block of 8 bytes of data. public decrypt(ciphertext: Uint8Array, plaintext: Uint8Array) { let i; let l = 0; let r = 0; for (i = 0; i < 4; i++) { l |= (ciphertext[i] & 0xff) << (24 - i * 8); r |= (ciphertext[i + 4] & 0xff) << (24 - i * 8); } for (i = this.rounds - 1; i > 0; i -= 2) { l ^= this.roundFunc(r, this.keySchedule[i]); r ^= this.roundFunc(l, this.keySchedule[i - 1]); } for (i = 0; i < 4; i++) { plaintext[3 - i] = (Number)(r & 0xff); plaintext[7 - i] = (Number)(l & 0xff); r >>>= 8; l >>>= 8; } } private cipherHolder = new Uint8Array(8); private plainHolder = new Uint8Array(8); public decryptUint8Array(cipherArray: Uint8Array, plainArray: Uint8Array, from?: number, to?: number) { if (from == null) from = 0; if (to == null) to = cipherArray.length; let k = 0; // decrypt full blocks while (from + 8 <= to) { for (let i = 0; i < 8; i++) { this.cipherHolder[i] = cipherArray[from + i]; } this.decrypt(this.cipherHolder, this.plainHolder); for (let i = 0; i < 8; i++) { plainArray[k + i] = this.plainHolder[i]; } k += 8; from += 8; } // remaining bytes do not get encryption, just copy them if (((to - from) & 0x7) != 0) { for (let i = from; i < to; i++) { plainArray[k++] = cipherArray[i]; } } } }