UNPKG

kuznyechik_multi

Version:

Шифрование ГОСТ 34.12-2018 Кузнечик. Encryption GOST 34.12-2018 Kuznyechik || Grasshopper. Support || Поддержка ESM/CommonJS/ChildProcess/browser

383 lines (382 loc) 15.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.decryptSync = exports.encryptSync = exports.decryptChilds = exports.encryptChilds = exports.FN_TRIM_START = exports.EXPAND_KEYS = exports.FEISTEL_NETWORK = exports.REV_L_TRANSFORM = exports.L_TRANSFORM = exports.REV_S_TRANSFORM = exports.REV_R_TRANSFORM = exports.R_TRANFORM = exports.S_TRANSFORM = exports.GALOIS_MULT = exports.XOR = void 0; exports.toBuffer = toBuffer; exports.ROUNDS_10 = ROUNDS_10; exports.REV_ROUNDS_10 = REV_ROUNDS_10; exports.countChild = countChild; exports.encryptAsync = encryptAsync; exports.decryptAsync = decryptAsync; const os_1 = require("os"); const constants_cjs_1 = require("./constants.cjs"); const child_process_1 = require("child_process"); const path_1 = require("path"); /** * Сложение/вычетание в поле Галуа эквивалент XOR * * XOR (исключающее или) для буферов, возвращает сам буфер */ const XOR = (a, b) => { if (a.length !== b.length) throw "XOR a.length !== b.length"; return Buffer.from(a.map((x, i) => b[i] ^ x)); }; exports.XOR = XOR; /** * Умножение в поле Галуа по модулю неприводимого многочлена x^{8}+x^{7}+x^{6}+x+1} */ const GALOIS_MULT = (a, b) => { let ret = 0; let hiBbit = 0; for (let i = 0; i < 8; i++) { let _tmp = b & 1; if (_tmp === 1) ret ^= a; hiBbit = a & 0x80; a <<= 1; if (hiBbit === 0x80) a ^= 0x1c3; //0x1c3 = x^{8}+x^{7}+x^{6}+x+1} || 0xc3 = x^{7}+x^{6}+x+1 b >>= 1; } return ret; }; exports.GALOIS_MULT = GALOIS_MULT; /** * S преобразование * * Операция замены байтов путем применения нелинейной биективного преобразования */ const S_TRANSFORM = (buf) => { let ret = Buffer.from(constants_cjs_1.ARR_16_ZEROS.slice()); buf.forEach((x, i) => ret[i] = constants_cjs_1.PI[x]); return ret; }; exports.S_TRANSFORM = S_TRANSFORM; /** * R преобразование * * Умножение + сдвиг */ const R_TRANFORM = (buf) => { let ret = Buffer.from(buf); let a15 = 0; for (let i = 0; i < 16; i++) a15 ^= (0, exports.GALOIS_MULT)(buf[i], constants_cjs_1.L_CONSTANT[i]); for (let i = 15; i >= 1; i--) ret[i] = buf[i - 1]; ret[0] = a15; return ret; }; exports.R_TRANFORM = R_TRANFORM; /** * Обратное R преобразование */ const REV_R_TRANSFORM = (buf) => { let a0 = buf[0]; let ret = Buffer.from(constants_cjs_1.ARR_16_ZEROS.slice()); for (let i = 0; i < 15; i++) { ret[i] = buf[i + 1]; a0 ^= (0, exports.GALOIS_MULT)(buf[i + 1], constants_cjs_1.L_CONSTANT[i]); } ret[15] = a0; return ret; }; exports.REV_R_TRANSFORM = REV_R_TRANSFORM; /** * Обратное S преобразование * * Обратная операция замены байтов путем применения нелинейной биективного преобразования */ const REV_S_TRANSFORM = (buf) => { let ret = Buffer.from(constants_cjs_1.ARR_16_ZEROS.slice()); buf.forEach((x, i) => ret[i] = constants_cjs_1.PI.indexOf(x)); return ret; }; exports.REV_S_TRANSFORM = REV_S_TRANSFORM; /**L (линейное) преобразование */ const L_TRANSFORM = (buf) => { let ret = Buffer.from(buf); for (let i = 0; i < 16; i++) ret = (0, exports.R_TRANFORM)(ret); return ret; }; exports.L_TRANSFORM = L_TRANSFORM; /**Обратное L (линейное) преобразование */ const REV_L_TRANSFORM = (buf) => { let ret = Buffer.from(buf); for (let i = 0; i < 16; i++) ret = (0, exports.REV_R_TRANSFORM)(ret); return ret; }; exports.REV_L_TRANSFORM = REV_L_TRANSFORM; /** Преобразования ячейки Фейстеля */ const FEISTEL_NETWORK = (key1, key2, iterC) => { let internal = (0, exports.XOR)(key1, iterC); internal = (0, exports.S_TRANSFORM)(internal); internal = (0, exports.L_TRANSFORM)(internal); return [(0, exports.XOR)(internal, key2), key1]; }; exports.FEISTEL_NETWORK = FEISTEL_NETWORK; /** Расчет раундовых ключей */ const EXPAND_KEYS = (masterkey) => { if (masterkey.length !== 32) throw "Ключ должен быть 32 байта или 256 бит\n" + "Получил " + masterkey.length + " байт"; let iterC = []; let keyL = Buffer.from(constants_cjs_1.ARR_16_ZEROS.slice()); let keyR = Buffer.from(constants_cjs_1.ARR_16_ZEROS.slice()); for (let i = 0; i < masterkey.length; i++) { let j = i % 16; let m = Buffer.from(constants_cjs_1.ARR_16_ZEROS.slice()); m[15] = i; iterC.push((0, exports.L_TRANSFORM)(m)); if (i < 16) keyL[j] = masterkey[j]; else keyR[j] = masterkey[j]; } let ret = [Buffer.from(keyR), Buffer.from(keyL)]; for (let i = 0; i < 4; i++) { for (let j = 0; j < 8; j++) { let temp = (0, exports.FEISTEL_NETWORK)(keyL, keyR, iterC[j + 8 * i]); keyR = Buffer.from(temp[0]); keyL = Buffer.from(temp[1]); } ret[2 * i + 2] = Buffer.from(keyL); ret[2 * i + 3] = Buffer.from(keyR); } return ret; }; exports.EXPAND_KEYS = EXPAND_KEYS; function toBuffer(data) { if (typeof data === 'string') { if (/[^0123456789abcdef]/gi.test(data)) throw "Строка не HEX"; let arr = data.match(/.?./g); if (!arr) throw "match errore FN toBuffer"; return Buffer.from(arr.map(x => parseInt(x, 16))); } else return data; } function ROUNDS_10(block, keys) { let temp = Buffer.from(block); for (let i = 0; i < 9; i++) { temp = (0, exports.XOR)(temp, keys[i]); temp = (0, exports.S_TRANSFORM)(temp); temp = (0, exports.L_TRANSFORM)(temp); } return (0, exports.XOR)(temp, keys[9]); } function REV_ROUNDS_10(block, keys) { let temp = (0, exports.XOR)(block, keys[9]); for (let i = 8; i >= 0; i--) { temp = (0, exports.REV_L_TRANSFORM)(temp); temp = (0, exports.REV_S_TRANSFORM)(temp); temp = (0, exports.XOR)(temp, keys[i]); } return temp; } const FN_TRIM_START = (b) => { let index = b.findIndex((v) => v !== 0); if (~index) return b.subarray(index, b.length); return b; }; exports.FN_TRIM_START = FN_TRIM_START; function countChild(data, MAX_BLOCK) { let max = (0, os_1.cpus)().length; if (MAX_BLOCK < max) max = MAX_BLOCK; let r = 0; if (data === 'all') r = max; else r = data; return r > max ? max : r; } async function CHILD_E_D_METHOD(i, child) { let sizeOf = i * this.BLOCKS_FOR_CHILD * this.BITES_FROM_CHILD; let MAX_SIZE = this.BLOCKS_FOR_CHILD * this.BITES_FROM_CHILD + sizeOf; if (MAX_SIZE > this.data.length) MAX_SIZE = this.data.length; let END_OF_DATA = this.BITES_FROM_CHILD + 0; for (let j = 0; j < this.BLOCKS_FOR_CHILD; j++) { if ((END_OF_DATA + sizeOf) > MAX_SIZE) END_OF_DATA = MAX_SIZE - sizeOf; await new Promise((res, rej) => { child.send({ type: this.method, data: this.data.subarray(sizeOf, sizeOf + END_OF_DATA), masterkey: this.MASTER_KEY_BUFF }); child.once("message", (resp) => { let bf = Buffer.from(resp); if (bf.length !== END_OF_DATA) throw "Получил данных больше чем нужно"; bf.copy(this.data, sizeOf, 0, bf.length); res(true); }); }); sizeOf += END_OF_DATA; } return this.data; } function SEARCHER_BITES_FROM_CHILD(size, BITES_FROM_CHILD) { if (!!BITES_FROM_CHILD && BITES_FROM_CHILD > 0) { let n = Math.ceil(BITES_FROM_CHILD); return n % 16 + n; } let i = 0; for (; i < 2; i++) if (Math.floor(size / ((1024 ** i))) === 0) break; if (!i) return 16; return 1024 * 0.5; } /**Многопоточное шифрование по методу Кузнечик */ const encryptChilds = async (data, masterkey, childs, BITES_FROM_CHILD = 0) => { if (data.length > 2147483647) console.warn("encryptChilds не был расчитан на данные более 2 гб"); if (!!(data.length % 16)) data = Buffer.concat([Buffer.from(Array.from({ length: 16 - data.length % 16 }).map(x => 0)), data]); const C_BITES_FROM_CHILD = SEARCHER_BITES_FROM_CHILD(data.length, BITES_FROM_CHILD); const CHILD_MAX = countChild(childs, Math.ceil(data.length / C_BITES_FROM_CHILD)); const BLOCKS_FOR_CHILD = Math.ceil((data.length / C_BITES_FROM_CHILD) / CHILD_MAX); const MASTER_KEY_BUFF = toBuffer(masterkey); const CHILD_PATH = (0, path_1.join)(__dirname, 'child.cjs'); const CHILDS = Array.from({ length: CHILD_MAX }).map(x => (0, child_process_1.fork)(CHILD_PATH)); const MAIN_ENCRYPT_FN = CHILD_E_D_METHOD.bind({ method: "encrypt", BITES_FROM_CHILD: C_BITES_FROM_CHILD, BLOCKS_FOR_CHILD: BLOCKS_FOR_CHILD, data: data, MASTER_KEY_BUFF: MASTER_KEY_BUFF }); let ARR_PROMISE_INIT = CHILDS.map(child => { return new Promise(function (res, rej) { let timer = setTimeout(() => { child.kill(0); res(false); }, 5 * 1000); child.once("message", (data) => { clearTimeout(timer); res(true); }); }); }); await Promise.all(ARR_PROMISE_INIT); ARR_PROMISE_INIT = CHILDS.map((child, i) => MAIN_ENCRYPT_FN(i, child)); await Promise.all(ARR_PROMISE_INIT); await Promise.all(CHILDS.map((child, i) => { return new Promise(function (res, rej) { child.on('exit', () => res(true)); child.kill('SIGKILL'); }); })); return data; }; exports.encryptChilds = encryptChilds; /**Многопоточное дешифрование по методу Кузнечик */ const decryptChilds = async (data, masterkey, childs, BITES_FROM_CHILD = 0, trimStart = true) => { if (data.length > 2147483647) console.warn("decryptChilds не был расчитан на данные более 2 гб"); if (!!(data.length % 16)) throw "Не кратно 16 байтам"; const C_BITES_FROM_CHILD = SEARCHER_BITES_FROM_CHILD(data.length, BITES_FROM_CHILD); const CHILD_MAX = countChild(childs, Math.ceil(data.length / C_BITES_FROM_CHILD)); const BLOCKS_FOR_CHILD = Math.ceil((data.length / C_BITES_FROM_CHILD) / CHILD_MAX); const MASTER_KEY_BUFF = toBuffer(masterkey); const CHILD_PATH = (0, path_1.join)(__dirname, 'child.cjs'); const CHILDS = Array.from({ length: CHILD_MAX }).map(x => (0, child_process_1.fork)(CHILD_PATH)); const MAIN_DECRYPT_FN = CHILD_E_D_METHOD.bind({ method: "decrypt", BITES_FROM_CHILD: C_BITES_FROM_CHILD, BLOCKS_FOR_CHILD: BLOCKS_FOR_CHILD, data: data, MASTER_KEY_BUFF: MASTER_KEY_BUFF }); let ARR_PROMISE_INIT = CHILDS.map(child => { return new Promise(function (res, rej) { let timer = setTimeout(() => { child.kill(0); res(false); }, 5 * 1000); child.once("message", (data) => { clearTimeout(timer); res(true); }); }); }); await Promise.all(ARR_PROMISE_INIT); ARR_PROMISE_INIT = CHILDS.map((child, i) => { return MAIN_DECRYPT_FN(i, child); }); await Promise.all(ARR_PROMISE_INIT); await Promise.all(CHILDS.map((child, i) => { return new Promise(function (res, rej) { child.on('exit', () => res(true)); child.kill('SIGKILL'); }); })); return trimStart ? (0, exports.FN_TRIM_START)(data) : data; }; exports.decryptChilds = decryptChilds; /**Асинхронное шифрование по методу Кузнечик */ async function encryptAsync(data, masterkey) { let keys = this?.EXPAND_KEYS || (0, exports.EXPAND_KEYS)(toBuffer(masterkey)); if (!!(data.length % 16)) data = Buffer.concat([Buffer.from(Array.from({ length: 16 - data.length % 16 }).map(x => 0)), data]); let MAX_ITER = data.length / 16; let ArrPromise = []; for (let i = 0; i < MAX_ITER; i++) { ArrPromise.push(new Promise(function (resolve, reject) { resolve(ROUNDS_10(Buffer.from(data.subarray(i * 16, (i + 1) * 16)), keys)); })); } return Buffer.concat(await Promise.all(ArrPromise)); } /**Асинхронное дешифрование по методу Кузнечик */ async function decryptAsync(data, masterkey, trimStart = true) { let trs = trimStart; if (this?.trimStart !== undefined) trs = this?.trimStart; let keys = this?.EXPAND_KEYS || (0, exports.EXPAND_KEYS)(toBuffer(masterkey)); if (!!(data.length % 16)) data = Buffer.concat([Buffer.from(Array.from({ length: 16 - data.length % 16 }).map(x => 0)), data]); let MAX_ITER = data.length / 16; let ArrPromise = []; for (let i = 0; i < MAX_ITER; i++) { ArrPromise.push(new Promise(function (resolve, reject) { resolve(REV_ROUNDS_10(Buffer.from(data.subarray(i * 16, (i + 1) * 16)), keys)); })); } return trs ? (0, exports.FN_TRIM_START)(Buffer.concat(await Promise.all(ArrPromise))) : Buffer.concat(await Promise.all(ArrPromise)); } /**Синхронное шифрование по методу Кузнечик */ const encryptSync = (data, masterkey) => { let keys = (0, exports.EXPAND_KEYS)(toBuffer(masterkey)); let ret = []; let block = []; if (!!(data.length % 16)) data = Buffer.concat([Buffer.from(Array.from({ length: 16 - data.length % 16 }).map(x => 0)), data]); for (let i = 0; i < data.length; i++) { block.push(data[i]); if (block.length === 16) { ret.push(...ROUNDS_10(block, keys)); block = []; } } if (block.length > 0) ret.push(...ROUNDS_10(block, keys)); return Buffer.from(ret); }; exports.encryptSync = encryptSync; /**Синхронное дешифрование по методу Кузнечик */ const decryptSync = (data, masterkey, trimStart = true) => { let keys = (0, exports.EXPAND_KEYS)(toBuffer(masterkey)); let ret = []; let block = []; if (!!(data.length % 16)) data = Buffer.concat([Buffer.from(Array.from({ length: 16 - data.length % 16 }).map(x => 0)), data]); for (let i = 0; i < data.length; i++) { block.push(data[i]); if (block.length === 16) { ret.push(...REV_ROUNDS_10(block, keys)); block = []; } } if (block.length !== 0) ret.push(...REV_ROUNDS_10(block, keys)); return trimStart ? (0, exports.FN_TRIM_START)(Buffer.from(ret)) : Buffer.from(ret); }; exports.decryptSync = decryptSync;