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 实现的密码学套件。目标是提供一个简单易用的密码学库。

140 lines (139 loc) 5.18 kB
import { createCipher } from '../../core/cipher'; import { KitError, U8, genBitMask, resizeBuffer, rotateL, rotateR } from '../../core/utils'; // const Eul = [0xB7, 0xE1, 0x51, 0x62, 0x8A, 0xED, 0x2A, 0x6A, 0xBF, 0x71, 0x58, 0x80, 0x9C, 0xF4, 0xF3, 0xC7, 0x62, 0xE7, 0x16, 0x0F, 0x38, 0xB4, 0xDA, 0x56, 0xA7, 0x84, 0xD9, 0x04, 0x51, 0x90, 0xCF, 0xEF] // const Phi = [0x9E, 0x37, 0x79, 0xB9, 0x7F, 0x4A, 0x7C, 0x15, 0xF3, 0x9C, 0xC0, 0x60, 0x5C, 0xED, 0xC8, 0x34, 0x10, 0x82, 0x27, 0x6B, 0xF3, 0xA2, 0x72, 0x51, 0xF8, 0x6C, 0x6A, 0x11, 0xD0, 0xC1, 0x8E, 0x95] // const P = Eul{0,...,w-1} - 2) | 1 // const Q = Phi{0,...,w-1} - 2) | 1 // * Functions function _setup(key, word_size, round, mask) { const word_bit = BigInt(word_size); const word_byte = word_size >> 3; const P = (0xb7e151628aed2a6abf7158809cf4f3c7n >> (128n - word_bit)) | 1n; const Q = (0x9e3779b97f4a7c15f39cc0605cedc835n >> (128n - word_bit)) | 1n; // Break the key into w-bit words const c = Math.ceil((key.length || 1) / word_byte); const L = resizeBuffer(key, c * word_byte); const LV = L.view(word_byte); // Initialize key-independent pseudorandom S array const t = (round + 1) << 1; const S = new U8(t * word_byte); const SV = S.view(word_byte); // S[0] = P let prv = P; SV.set(0, prv, true); for (let i = 1; i < t; i++) { // S[i] = S[i-1] + Q prv = (prv + Q) & mask; SV.set(i, prv, true); } // The main key scheduling loop let i = 0; let j = 0; let A = 0n; let B = 0n; const v = 3 * Math.max(c, t); for (let k = 0; k < v; k++) { // A = S[i] = (S[i] + A + B) <<< 3 const S = SV.get(i, true); A = rotateL(word_bit, S + A + B, 3n, mask); SV.set(i, A, true); // B = L[j] = (L[j] + A + B) <<< (A + B) const L = LV.get(j, true); B = rotateL(word_bit, L + A + B, A + B, mask); LV.set(j, B, true); // i = (i + 1) mod t i = (i + 1) % t; // j = (j + 1) mod c j = (j + 1) % c; } return S; } function _encrypt(M, S, word_size, round, mask) { if (M.byteLength !== word_size >> 2) { throw new KitError(`ARC5-${word_size}/${round} block must be ${word_size >> 3} byte`); } const word_bit = BigInt(word_size); const word_byte = word_size >> 3; const MV = U8.from(M).view(word_byte); const SV = U8.from(S).view(word_byte); // A = M[0] + S[0], B = M[1] + S[1] let A = MV.get(0, true) + SV.get(0, true); let B = MV.get(1, true) + SV.get(1, true); A &= mask; B &= mask; for (let i = 1; i <= round; i++) { // A = ((A ^ B) <<< B) + S[2 * i] A = rotateL(word_bit, A ^ B, B, mask); A += SV.get(i << 1, true); A &= mask; // B = ((B ^ A) <<< A) + S[2 * i + 1] B = rotateL(word_bit, B ^ A, A, mask); B += SV.get((i << 1) + 1, true); B &= mask; } return U8.fromBI(B << word_bit | A, word_byte << 1, true); } function _decrypt(C, S, word_size, round, mask) { if (C.byteLength !== word_size >> 2) { throw new KitError(`ARC5-${word_size}/${round} block must be ${word_size >> 3} byte`); } const word_bit = BigInt(word_size); const word_byte = word_size >> 3; const CV = U8.from(C).view(word_byte); const SV = U8.from(S).view(word_byte); // A = C[0], B = C[1] let A = CV.get(0, true); let B = CV.get(1, true); for (let i = round; i > 0; i--) { // B = ((B - S[2 * i + 1]) >>> A) ^ A const S1 = SV.get((i << 1) + 1, true); B = rotateR(word_bit, B - S1, A, mask); B = B ^ A; B &= mask; // A = ((A - S[2 * i]) >>> B) ^ B const S0 = SV.get(i << 1, true); A = rotateR(word_bit, A - S0, B, mask); A = A ^ B; A &= mask; } // A = A - S[0], B = B - S[1] A = A - SV.get(0, true); A &= mask; B = B - SV.get(1, true); B &= mask; return U8.fromBI(B << word_bit | A, word_byte << 1, true); } // * ARC5 Algorithm function _arc5(K, WORD_SIZE, round) { const mask = genBitMask(WORD_SIZE); const S = _setup(K, WORD_SIZE, round, mask); const encrypt = (M) => _encrypt(M, S, WORD_SIZE, round, mask); const decrypt = (C) => _decrypt(C, S, WORD_SIZE, round, mask); return { encrypt, decrypt }; } /** * ARC5 分组加密算法 / block cipher algorithm * * ```ts * const spec8 = arc5(8, 8) // ARC5-8/8 * const spec16 = arc5(16, 12) // ARC5-16/12 * const spec32 = arc5(32, 16) // ARC5-32/16 (default) * const spec64 = arc5(64, 20) // ARC5-64/20 * const spec128 = arc5(128, 24) // ARC5-128/24 * ``` * * @param {16 | 32 | 64} WORD_SIZE - 工作字长 / Word size (default: 32 bit) * @param {number} round - 轮数 / Rounds (default: 16) */ export function arc5(WORD_SIZE = 32, round = 16) { if (round <= 0 || round > 255) { throw new KitError('ARC5 round must be between 1 and 255'); } return createCipher((K) => _arc5(K, WORD_SIZE, round), { ALGORITHM: `ARC5-${WORD_SIZE}/${round}`, BLOCK_SIZE: WORD_SIZE >> 2, KEY_SIZE: 16, MIN_KEY_SIZE: 1, MAX_KEY_SIZE: 255, }); }