UNPKG

mima-kit

Version:

mima-kit is a cryptographic suite implemented in TypeScript. The goal is to provide an easy-to-use cryptographic library. mima-kit 是一个使用 TypeScript 实现的密码学套件。目标是提供一个简单易用的密码学库。

315 lines (314 loc) 13.4 kB
import { createCipher } from '../../core/cipher'; import { KitError, U8, rotateL32, rotateR32 } from '../../core/utils'; // * Constants const P0 = new Uint8Array([0xA9, 0x67, 0xB3, 0xE8, 0x04, 0xFD, 0xA3, 0x76, 0x9A, 0x92, 0x80, 0x78, 0xE4, 0xDD, 0xD1, 0x38, 0x0D, 0xC6, 0x35, 0x98, 0x18, 0xF7, 0xEC, 0x6C, 0x43, 0x75, 0x37, 0x26, 0xFA, 0x13, 0x94, 0x48, 0xF2, 0xD0, 0x8B, 0x30, 0x84, 0x54, 0xDF, 0x23, 0x19, 0x5B, 0x3D, 0x59, 0xF3, 0xAE, 0xA2, 0x82, 0x63, 0x01, 0x83, 0x2E, 0xD9, 0x51, 0x9B, 0x7C, 0xA6, 0xEB, 0xA5, 0xBE, 0x16, 0x0C, 0xE3, 0x61, 0xC0, 0x8C, 0x3A, 0xF5, 0x73, 0x2C, 0x25, 0x0B, 0xBB, 0x4E, 0x89, 0x6B, 0x53, 0x6A, 0xB4, 0xF1, 0xE1, 0xE6, 0xBD, 0x45, 0xE2, 0xF4, 0xB6, 0x66, 0xCC, 0x95, 0x03, 0x56, 0xD4, 0x1C, 0x1E, 0xD7, 0xFB, 0xC3, 0x8E, 0xB5, 0xE9, 0xCF, 0xBF, 0xBA, 0xEA, 0x77, 0x39, 0xAF, 0x33, 0xC9, 0x62, 0x71, 0x81, 0x79, 0x09, 0xAD, 0x24, 0xCD, 0xF9, 0xD8, 0xE5, 0xC5, 0xB9, 0x4D, 0x44, 0x08, 0x86, 0xE7, 0xA1, 0x1D, 0xAA, 0xED, 0x06, 0x70, 0xB2, 0xD2, 0x41, 0x7B, 0xA0, 0x11, 0x31, 0xC2, 0x27, 0x90, 0x20, 0xF6, 0x60, 0xFF, 0x96, 0x5C, 0xB1, 0xAB, 0x9E, 0x9C, 0x52, 0x1B, 0x5F, 0x93, 0x0A, 0xEF, 0x91, 0x85, 0x49, 0xEE, 0x2D, 0x4F, 0x8F, 0x3B, 0x47, 0x87, 0x6D, 0x46, 0xD6, 0x3E, 0x69, 0x64, 0x2A, 0xCE, 0xCB, 0x2F, 0xFC, 0x97, 0x05, 0x7A, 0xAC, 0x7F, 0xD5, 0x1A, 0x4B, 0x0E, 0xA7, 0x5A, 0x28, 0x14, 0x3F, 0x29, 0x88, 0x3C, 0x4C, 0x02, 0xB8, 0xDA, 0xB0, 0x17, 0x55, 0x1F, 0x8A, 0x7D, 0x57, 0xC7, 0x8D, 0x74, 0xB7, 0xC4, 0x9F, 0x72, 0x7E, 0x15, 0x22, 0x12, 0x58, 0x07, 0x99, 0x34, 0x6E, 0x50, 0xDE, 0x68, 0x65, 0xBC, 0xDB, 0xF8, 0xC8, 0xA8, 0x2B, 0x40, 0xDC, 0xFE, 0x32, 0xA4, 0xCA, 0x10, 0x21, 0xF0, 0xD3, 0x5D, 0x0F, 0x00, 0x6F, 0x9D, 0x36, 0x42, 0x4A, 0x5E, 0xC1, 0xE0]); const P1 = new Uint8Array([0x75, 0xF3, 0xC6, 0xF4, 0xDB, 0x7B, 0xFB, 0xC8, 0x4A, 0xD3, 0xE6, 0x6B, 0x45, 0x7D, 0xE8, 0x4B, 0xD6, 0x32, 0xD8, 0xFD, 0x37, 0x71, 0xF1, 0xE1, 0x30, 0x0F, 0xF8, 0x1B, 0x87, 0xFA, 0x06, 0x3F, 0x5E, 0xBA, 0xAE, 0x5B, 0x8A, 0x00, 0xBC, 0x9D, 0x6D, 0xC1, 0xB1, 0x0E, 0x80, 0x5D, 0xD2, 0xD5, 0xA0, 0x84, 0x07, 0x14, 0xB5, 0x90, 0x2C, 0xA3, 0xB2, 0x73, 0x4C, 0x54, 0x92, 0x74, 0x36, 0x51, 0x38, 0xB0, 0xBD, 0x5A, 0xFC, 0x60, 0x62, 0x96, 0x6C, 0x42, 0xF7, 0x10, 0x7C, 0x28, 0x27, 0x8C, 0x13, 0x95, 0x9C, 0xC7, 0x24, 0x46, 0x3B, 0x70, 0xCA, 0xE3, 0x85, 0xCB, 0x11, 0xD0, 0x93, 0xB8, 0xA6, 0x83, 0x20, 0xFF, 0x9F, 0x77, 0xC3, 0xCC, 0x03, 0x6F, 0x08, 0xBF, 0x40, 0xE7, 0x2B, 0xE2, 0x79, 0x0C, 0xAA, 0x82, 0x41, 0x3A, 0xEA, 0xB9, 0xE4, 0x9A, 0xA4, 0x97, 0x7E, 0xDA, 0x7A, 0x17, 0x66, 0x94, 0xA1, 0x1D, 0x3D, 0xF0, 0xDE, 0xB3, 0x0B, 0x72, 0xA7, 0x1C, 0xEF, 0xD1, 0x53, 0x3E, 0x8F, 0x33, 0x26, 0x5F, 0xEC, 0x76, 0x2A, 0x49, 0x81, 0x88, 0xEE, 0x21, 0xC4, 0x1A, 0xEB, 0xD9, 0xC5, 0x39, 0x99, 0xCD, 0xAD, 0x31, 0x8B, 0x01, 0x18, 0x23, 0xDD, 0x1F, 0x4E, 0x2D, 0xF9, 0x48, 0x4F, 0xF2, 0x65, 0x8E, 0x78, 0x5C, 0x58, 0x19, 0x8D, 0xE5, 0x98, 0x57, 0x67, 0x7F, 0x05, 0x64, 0xAF, 0x63, 0xB6, 0xFE, 0xF5, 0xB7, 0x3C, 0xA5, 0xCE, 0xE9, 0x68, 0x44, 0xE0, 0x4D, 0x43, 0x69, 0x29, 0x2E, 0xAC, 0x15, 0x59, 0xA8, 0x0A, 0x9E, 0x6E, 0x47, 0xDF, 0x34, 0x35, 0x6A, 0xCF, 0xDC, 0x22, 0xC9, 0xC0, 0x9B, 0x89, 0xD4, 0xED, 0xAB, 0x12, 0xA2, 0x0D, 0x52, 0xBB, 0x02, 0x2F, 0xA9, 0xD7, 0x61, 0x1E, 0xB4, 0x50, 0x04, 0xF6, 0xC2, 0x16, 0x25, 0x86, 0x56, 0x55, 0x09, 0xBE, 0x91]); const P = [P0, P1]; const P_00 = 1; const P_01 = 0; const P_02 = 0; const P_03 = P_01 ^ 1; const P_04 = 1; const P_10 = 0; const P_11 = 0; const P_12 = 1; const P_13 = P_11 ^ 1; const P_14 = 0; const P_20 = 1; const P_21 = 1; const P_22 = 0; const P_23 = P_21 ^ 1; const P_24 = 0; const P_30 = 0; const P_31 = 1; const P_32 = 1; const P_33 = P_31 ^ 1; const P_34 = 1; const GF256_FDBK_2 = 0x169 >> 1; const GF256_FDBK_4 = 0x169 >> 2; const RS_GF_FDBK = 0x14D; const MDS = [ new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), new Uint32Array(256), ]; // * Functions const LFSR1 = (x) => (x >> 1) ^ ((x & 0x01) !== 0 ? GF256_FDBK_2 : 0); const LFSR2 = (x) => (x >> 2) ^ ((x & 0x02) !== 0 ? GF256_FDBK_2 : 0) ^ ((x & 0x01) !== 0 ? GF256_FDBK_4 : 0); const Mx_X = (x) => x ^ LFSR2(x); const Mx_Y = (x) => x ^ LFSR1(x) ^ LFSR2(x); (function initMDS() { // precompute the MDS matrix const m1 = new Uint32Array(2); const mX = new Uint32Array(2); const mY = new Uint32Array(2); for (let i = 0; i < 256; i++) { const j0 = P[0][i] & 0xFF; m1[0] = j0; mX[0] = Mx_X(j0) & 0xFF; mY[0] = Mx_Y(j0) & 0xFF; const j1 = P[1][i] & 0xFF; m1[1] = j1; mX[1] = Mx_X(j1) & 0xFF; mY[1] = Mx_Y(j1) & 0xFF; MDS[0][i] = (m1[P_00] << 0) | (mX[P_00] << 8) | (mY[P_00] << 16) | (mY[P_00] << 24); MDS[1][i] = (mY[P_10] << 0) | (mY[P_10] << 8) | (mX[P_10] << 16) | (m1[P_10] << 24); MDS[2][i] = (mX[P_20] << 0) | (mY[P_20] << 8) | (m1[P_20] << 16) | (mY[P_20] << 24); MDS[3][i] = (mX[P_30] << 0) | (m1[P_30] << 8) | (mY[P_30] << 16) | (mX[P_30] << 24); } })(); const b0 = (x) => x & 0xFF; const b1 = (x) => (x >>> 8) & 0xFF; const b2 = (x) => (x >>> 16) & 0xFF; const b3 = (x) => (x >>> 24) & 0xFF; function chooseB(x, N) { switch (N & 3) { case 0: return b0(x); case 1: return b1(x); case 2: return b2(x); case 3: return b3(x); default: return 0; } } function RS_REM(x) { const b = (x >>> 24) & 0xFF; const g2 = ((b << 1) ^ ((b & 0x80) !== 0 ? RS_GF_FDBK : 0)) & 0xFF; const g3 = (b >>> 1) ^ ((b & 0x01) !== 0 ? (RS_GF_FDBK >>> 1) : 0) ^ g2; return (x << 8) ^ (g3 << 24) ^ (g2 << 16) ^ (g3 << 8) ^ b; } function RS_MDS_Encode(k0, k1) { for (let i = 0; i < 4; i++) { k1 = RS_REM(k1); } k1 ^= k0; for (let i = 0; i < 4; i++) { k1 = RS_REM(k1); } return k1; } function F32(K64SigByte, x, k32) { let lB0 = b0(x); let lB1 = b1(x); let lB2 = b2(x); let lB3 = b3(x); const k0 = k32[0] || 0; const k1 = k32[1] || 0; const k2 = k32[2] || 0; const k3 = k32[3] || 0; let result = 0; let K64LSB = K64SigByte & 0x3; if (K64LSB === 1) { result = MDS[0][P[P_01][lB0] & 0xFF ^ b0(k0)] ^ MDS[1][P[P_11][lB1] & 0xFF ^ b1(k0)] ^ MDS[2][P[P_21][lB2] & 0xFF ^ b2(k0)] ^ MDS[3][P[P_31][lB3] & 0xFF ^ b3(k0)]; return result; } if (K64LSB === 0) { lB0 = P[P_04][lB0] & 0xFF ^ b0(k3); lB1 = P[P_14][lB1] & 0xFF ^ b1(k3); lB2 = P[P_24][lB2] & 0xFF ^ b2(k3); lB3 = P[P_34][lB3] & 0xFF ^ b3(k3); K64LSB = 3; } if (K64LSB === 3) { lB0 = P[P_03][lB0] & 0xFF ^ b0(k2); lB1 = P[P_13][lB1] & 0xFF ^ b1(k2); lB2 = P[P_23][lB2] & 0xFF ^ b2(k2); lB3 = P[P_33][lB3] & 0xFF ^ b3(k2); K64LSB = 2; } if (K64LSB === 2) { result = MDS[0][P[P_01][P[P_02][lB0] & 0xFF ^ b0(k1)] & 0xFF ^ b0(k0)] ^ MDS[1][P[P_11][P[P_12][lB1] & 0xFF ^ b1(k1)] & 0xFF ^ b1(k0)] ^ MDS[2][P[P_21][P[P_22][lB2] & 0xFF ^ b2(k1)] & 0xFF ^ b2(k0)] ^ MDS[3][P[P_31][P[P_32][lB3] & 0xFF ^ b3(k1)] & 0xFF ^ b3(k0)]; return result; } return 0; } function Fe32(SBox, x, R) { const result = SBox[0x000 + 2 * chooseB(x, R + 0) + 0] ^ SBox[0x000 + 2 * chooseB(x, R + 1) + 1] ^ SBox[0x200 + 2 * chooseB(x, R + 2) + 0] ^ SBox[0x200 + 2 * chooseB(x, R + 3) + 1]; return result; } // * Blowfish Algorithm function initKeySchedule(K) { const K32 = new Uint32Array(K.buffer, K.byteOffset); const K32e = new Uint32Array([K32[0], K32[2], K32[4], K32[6]]); const K32o = new Uint32Array([K32[1], K32[3], K32[5], K32[7]]); const K64Count = K32.length >> 1; // compute S-box keys using (12, 8) Reed-Solomon code over GF(256) const SBoxKeys = new Uint32Array(4); for (let i = 0; i < 4; i++) { const j = K64Count - 1 - i; SBoxKeys[j] = RS_MDS_Encode(K32e[i], K32o[i]); } // compute the round decryption subkeys for PHT. these same subkeys // will be used in encryption but will be applied in reverse order. let q = 0; const Subkeys = new Uint32Array(40); for (let i = 0; i < 20; i++) { let A = F32(K64Count, q, K32e); let B = F32(K64Count, q + 0x01010101, K32o); B = rotateL32(B, 8); A += B; Subkeys[2 * i] = A; A += B; Subkeys[2 * i + 1] = rotateL32(A, 9); q += 0x02020202; } // fully expand the table for speed const k0 = SBoxKeys[0]; const k1 = SBoxKeys[1]; const k2 = SBoxKeys[2]; const k3 = SBoxKeys[3]; const SBox = new Uint32Array(4 * 256); for (let i = 0; i < 256; i++) { let lb0 = i; let lb1 = i; let lb2 = i; let lb3 = i; let K64CountLSB = K64Count & 3; if (K64CountLSB === 1) { SBox[0x000 + 2 * i + 0] = MDS[0][P[P_01][lb0] & 0xFF ^ b0(k0)]; SBox[0x000 + 2 * i + 1] = MDS[1][P[P_11][lb1] & 0xFF ^ b1(k0)]; SBox[0x200 + 2 * i + 0] = MDS[2][P[P_21][lb2] & 0xFF ^ b2(k0)]; SBox[0x200 + 2 * i + 1] = MDS[3][P[P_31][lb3] & 0xFF ^ b3(k0)]; continue; } if (K64CountLSB === 0) { lb0 = P[P_04][lb0] & 0xFF ^ b0(k3); lb1 = P[P_14][lb1] & 0xFF ^ b1(k3); lb2 = P[P_24][lb2] & 0xFF ^ b2(k3); lb3 = P[P_34][lb3] & 0xFF ^ b3(k3); K64CountLSB = 3; } if (K64CountLSB === 3) { lb0 = P[P_03][lb0] & 0xFF ^ b0(k2); lb1 = P[P_13][lb1] & 0xFF ^ b1(k2); lb2 = P[P_23][lb2] & 0xFF ^ b2(k2); lb3 = P[P_33][lb3] & 0xFF ^ b3(k2); K64CountLSB = 2; } if (K64CountLSB === 2) { SBox[0x000 + 2 * i + 0] = MDS[0][P[P_01][P[P_02][lb0] & 0xFF ^ b0(k1)] & 0xFF ^ b0(k0)]; SBox[0x000 + 2 * i + 1] = MDS[1][P[P_11][P[P_12][lb1] & 0xFF ^ b1(k1)] & 0xFF ^ b1(k0)]; SBox[0x200 + 2 * i + 0] = MDS[2][P[P_21][P[P_22][lb2] & 0xFF ^ b2(k1)] & 0xFF ^ b2(k0)]; SBox[0x200 + 2 * i + 1] = MDS[3][P[P_31][P[P_32][lb3] & 0xFF ^ b3(k1)] & 0xFF ^ b3(k0)]; } } return { Subkeys, SBox }; } function _twofish(K, b) { if (K.byteLength !== b >> 3) { throw new KitError(`Twofish key must be ${b >> 3} byte`); } const { Subkeys, SBox } = initKeySchedule(K); const encrypt = (M) => { if (M.byteLength !== 16) { throw new KitError('Twofish block must be 16 byte'); } const M32 = new Uint32Array(M.buffer, M.byteOffset); // input whitening let x0 = M32[0] ^ Subkeys[0]; let x1 = M32[1] ^ Subkeys[1]; let x2 = M32[2] ^ Subkeys[2]; let x3 = M32[3] ^ Subkeys[3]; let k = 8; // 16 rounds of encryption for (let i = 0; i < 16; i += 2) { let t0 = Fe32(SBox, x0, 0); let t1 = Fe32(SBox, x1, 3); x2 ^= t0 + t1 + Subkeys[k++]; x2 = rotateR32(x2, 1); x3 = rotateL32(x3, 1); x3 ^= t0 + 2 * t1 + Subkeys[k++]; t0 = Fe32(SBox, x2, 0); t1 = Fe32(SBox, x3, 3); x0 ^= t0 + t1 + Subkeys[k++]; x0 = rotateR32(x0, 1); x1 = rotateL32(x1, 1); x1 ^= t0 + 2 * t1 + Subkeys[k++]; } // output whitening x2 ^= Subkeys[4]; x3 ^= Subkeys[5]; x0 ^= Subkeys[6]; x1 ^= Subkeys[7]; return new U8(new Uint32Array([x2, x3, x0, x1]).buffer); }; const decrypt = (C) => { if (C.byteLength !== 16) { throw new KitError('Twofish block must be 16 byte'); } const M32 = new Uint32Array(C.buffer, C.byteOffset); // input whitening let x2 = M32[0] ^ Subkeys[4]; let x3 = M32[1] ^ Subkeys[5]; let x0 = M32[2] ^ Subkeys[6]; let x1 = M32[3] ^ Subkeys[7]; let k = 39; // 16 rounds of decryption for (let i = 0; i < 16; i += 2) { let t0 = Fe32(SBox, x2, 0); let t1 = Fe32(SBox, x3, 3); x1 ^= t0 + 2 * t1 + Subkeys[k--]; x1 = rotateR32(x1, 1); x0 = rotateL32(x0, 1); x0 ^= t0 + t1 + Subkeys[k--]; t0 = Fe32(SBox, x0, 0); t1 = Fe32(SBox, x1, 3); x3 ^= t0 + 2 * t1 + Subkeys[k--]; x3 = rotateR32(x3, 1); x2 = rotateL32(x2, 1); x2 ^= t0 + t1 + Subkeys[k--]; } // output whitening x0 ^= Subkeys[0]; x1 ^= Subkeys[1]; x2 ^= Subkeys[2]; x3 ^= Subkeys[3]; return new U8(new Uint32Array([x0, x1, x2, x3]).buffer); }; return { encrypt, decrypt }; } /** * Twofish 分组密码算法 / block cipher algorithm * * @param {128 | 192 | 256} b - 密钥长度 / Key size (bit) */ export function twofish(b) { return createCipher((K) => _twofish(K, b), { ALGORITHM: 'Twofish', BLOCK_SIZE: 16, KEY_SIZE: b >> 3, MIN_KEY_SIZE: b >> 3, MAX_KEY_SIZE: b >> 3, }); }