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

151 lines (150 loc) 4.98 kB
import { createCipher } from '../../core/cipher'; import { Counter, KitError, resizeBuffer, rotateL32, u8, u32 } from '../../core/utils'; // * Functions // biome-ignore lint/correctness/noUnusedVariables: <tips> function QR(a, b, c, d) { b ^= rotateL32(a + d, 7); c ^= rotateL32(b + a, 9); d ^= rotateL32(c + b, 13); a ^= rotateL32(d + c, 18); return [a, b, c, d]; } function hash(x, rounds = 20) { // to word const X = u32(x); const W = X.slice(0); // main loop for (let i = 0; i < rounds; i += 2) { // ODD Rounds // [W[0], W[4], W[8], W[12]] = QR(W[0], W[4], W[8], W[12]); // [W[5], W[9], W[13], W[1]] = QR(W[5], W[9], W[13], W[1]); // [W[10], W[14], W[2], W[6]] = QR(W[10], W[14], W[2], W[6]); // [W[15], W[3], W[7], W[11]] = QR(W[15], W[3], W[7], W[11]); // EVEN Rounds // [W[0], W[1], W[2], W[3]] = QR(W[0], W[1], W[2], W[3]); // [W[5], W[6], W[7], W[4]] = QR(W[5], W[6], W[7], W[4]); // [W[10], W[11], W[8], W[9]] = QR(W[10], W[11], W[8], W[9]); // [W[15], W[12], W[13], W[14]] = QR(W[15], W[12], W[13], W[14]) W[4] ^= rotateL32(W[0] + W[12], 7); W[8] ^= rotateL32(W[4] + W[0], 9); W[12] ^= rotateL32(W[8] + W[4], 13); W[0] ^= rotateL32(W[12] + W[8], 18); W[9] ^= rotateL32(W[5] + W[1], 7); W[13] ^= rotateL32(W[9] + W[5], 9); W[1] ^= rotateL32(W[13] + W[9], 13); W[5] ^= rotateL32(W[1] + W[13], 18); W[14] ^= rotateL32(W[10] + W[6], 7); W[2] ^= rotateL32(W[14] + W[10], 9); W[6] ^= rotateL32(W[2] + W[14], 13); W[10] ^= rotateL32(W[6] + W[2], 18); W[3] ^= rotateL32(W[15] + W[11], 7); W[7] ^= rotateL32(W[3] + W[15], 9); W[11] ^= rotateL32(W[7] + W[3], 13); W[15] ^= rotateL32(W[11] + W[7], 18); W[1] ^= rotateL32(W[0] + W[3], 7); W[2] ^= rotateL32(W[1] + W[0], 9); W[3] ^= rotateL32(W[2] + W[1], 13); W[0] ^= rotateL32(W[3] + W[2], 18); W[6] ^= rotateL32(W[5] + W[4], 7); W[7] ^= rotateL32(W[6] + W[5], 9); W[4] ^= rotateL32(W[7] + W[6], 13); W[5] ^= rotateL32(W[4] + W[7], 18); W[11] ^= rotateL32(W[10] + W[9], 7); W[8] ^= rotateL32(W[11] + W[10], 9); W[9] ^= rotateL32(W[8] + W[11], 13); W[10] ^= rotateL32(W[9] + W[8], 18); W[12] ^= rotateL32(W[15] + W[14], 7); W[13] ^= rotateL32(W[12] + W[15], 9); W[14] ^= rotateL32(W[13] + W[12], 13); W[15] ^= rotateL32(W[14] + W[13], 18); } // mix for (let i = 0; i < 16; i++) { W[i] += X[i]; } return u8(W); } function expand(K, iv) { if (iv.byteLength !== 8) { throw new KitError(`Salsa20 iv must be 8 byte`); } const S = new Counter(64); const S32 = u32(S); const K32 = u32(K); const N32 = u32(iv); switch (K.byteLength) { case 16: // use tau S32[0] = 0x61707865; S32[1] = K32[0]; S32[2] = K32[1]; S32[3] = K32[2]; S32[4] = K32[3]; S32[5] = 0x3120646e; S32[6] = N32[0]; S32[7] = N32[1]; S32[10] = 0x79622d36; S32[11] = K32[0]; S32[12] = K32[1]; S32[13] = K32[2]; S32[14] = K32[3]; S32[15] = 0x6b206574; break; case 32: // use sigma S32[0] = 0x61707865; S32[1] = K32[0]; S32[2] = K32[1]; S32[3] = K32[2]; S32[4] = K32[3]; S32[5] = 0x3320646e; S32[6] = N32[0]; S32[7] = N32[1]; S32[10] = 0x79622d32; S32[11] = K32[4]; S32[12] = K32[5]; S32[13] = K32[6]; S32[14] = K32[7]; S32[15] = 0x6b206574; break; default: throw new KitError(`Salsa20 key must be 16 or 32 byte`); } return S; } // * Salsa20 Algorithm function _salsa20(key, iv) { /** Counter Block */ const E = expand(key, iv); /** Pseudo Random Byte Stream */ let S = hash(E); let current = 1; const cipher = (M) => { const BLOCK_TOTAL = (M.length >> 6) + 1; if (current > BLOCK_TOTAL) { return u8(M).map((byte, i) => byte ^ S[i]); } // Squeeze S = resizeBuffer(S, BLOCK_TOTAL << 6); while (BLOCK_TOTAL > current) { E.inc(32, 8, true); S.set(hash(E), current << 6); current++; } return u8(M).map((byte, i) => byte ^ S[i]); }; return { encrypt: (M) => cipher(M), decrypt: (C) => cipher(C), }; } /** * Salsa20 流密码 / Stream Cipher */ export const salsa20 = createCipher(_salsa20, { ALGORITHM: 'Salsa20', KEY_SIZE: 32, MIN_KEY_SIZE: 16, MAX_KEY_SIZE: 32, IV_SIZE: 8, MIN_IV_SIZE: 8, MAX_IV_SIZE: 8, });