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 实现的密码学套件。目标是提供一个简单易用的密码学库。
134 lines (133 loc) • 4.59 kB
JavaScript
import { createCipher } from '../../core/cipher';
import { KitError, U8, resizeBuffer, rotateL32 } from '../../core/utils';
// * Constants
const A = new Uint32Array([0x4D34D34D, 0xD34D34D3, 0x34D34D34, 0x4D34D34D, 0xD34D34D3, 0x34D34D34, 0x4D34D34D, 0xD34D34D3]);
// * Rabbit Algorithm
function _rabbit(key, iv) {
if (key.length !== 16) {
throw new KitError('Rabbit key must be 16 byte');
}
// 内部状态
let carray = 0;
const X = new Uint8Array(32);
const C = new Uint8Array(32);
const X32 = new Uint32Array(X.buffer);
const C32 = new Uint32Array(C.buffer);
const nextState = (skipExtract = false) => {
// Counter System
for (let i = 0; i < 8; i++) {
const T = C32[i] + A[i] + carray;
C32[i] = T | 0;
carray = T > 0xFFFFFFFF ? 1 : 0;
}
// G
const G = new Uint32Array(8);
for (let i = 0; i < 8; i++) {
const T = (BigInt(X32[i]) + BigInt(C32[i])) & 0xffffffffn;
const S = T * T;
G[i] = Number((S ^ (S >> 32n)) & 0xffffffffn);
}
// Next State
X32[0] = 0xFFFFFFFF & (G[0] + rotateL32(G[7], 16) + rotateL32(G[6], 16));
X32[1] = 0xFFFFFFFF & (G[1] + rotateL32(G[0], 8) + G[7]);
X32[2] = 0xFFFFFFFF & (G[2] + rotateL32(G[1], 16) + rotateL32(G[0], 16));
X32[3] = 0xFFFFFFFF & (G[3] + rotateL32(G[2], 8) + G[1]);
X32[4] = 0xFFFFFFFF & (G[4] + rotateL32(G[3], 16) + rotateL32(G[2], 16));
X32[5] = 0xFFFFFFFF & (G[5] + rotateL32(G[4], 8) + G[3]);
X32[6] = 0xFFFFFFFF & (G[6] + rotateL32(G[5], 16) + rotateL32(G[4], 16));
X32[7] = 0xFFFFFFFF & (G[7] + rotateL32(G[6], 8) + G[5]);
if (skipExtract) {
return new Uint8Array();
}
// Extract Output
const S = new Uint32Array(4);
S[0] = X32[0] ^ (X32[5] >>> 16) ^ (X32[3] << 16);
S[1] = X32[2] ^ (X32[7] >>> 16) ^ (X32[5] << 16);
S[2] = X32[4] ^ (X32[1] >>> 16) ^ (X32[7] << 16);
S[3] = X32[6] ^ (X32[3] >>> 16) ^ (X32[1] << 16);
return new Uint8Array(S.buffer);
};
// 初始化
(() => {
// 配置密钥
const K16 = new Uint16Array(key.buffer);
for (let i = 0; i < 8; i++) {
if ((i & 1) === 0) {
const KH = K16[(i + 1) % 8];
const KL = K16[i];
X32[i] = (KH << 16) | KL;
const CH = K16[(i + 4) % 8];
const CL = K16[(i + 5) % 8];
C32[i] = (CH << 16) | CL;
}
else {
const KH = K16[(i + 5) % 8];
const KL = K16[(i + 4) % 8];
X32[i] = (KH << 16) | KL;
const CH = K16[i];
const CL = K16[(i + 1) % 8];
C32[i] = (CH << 16) | CL;
}
}
for (let i = 0; i < 4; i++) {
nextState(true);
}
for (let i = 0; i < 8; i++) {
C32[i] ^= X32[(i + 4) % 8];
}
// 配置 IV
if (iv.length === 8) {
const iv32 = new Uint32Array(iv.buffer);
const iv16 = new Uint16Array(iv.buffer);
C32[0] ^= iv32[0];
C32[1] ^= (iv16[3] << 16) | iv16[1];
C32[2] ^= iv32[1];
C32[3] ^= (iv16[2] << 16) | iv16[0];
C32[4] ^= iv32[0];
C32[5] ^= (iv16[3] << 16) | iv16[1];
C32[6] ^= iv32[1];
C32[7] ^= (iv16[2] << 16) | iv16[0];
for (let i = 0; i < 4; i++) {
nextState(true);
}
}
else if (iv.length !== 0 && iv.length !== 8) {
throw new KitError('Rabbit iv must be 8 byte');
}
})();
// 密钥流
let S = nextState();
let current = 1;
const squeeze = (count) => {
if (current >= count) {
return S;
}
S = resizeBuffer(S, count << 4);
while (current < count) {
S.set(nextState(), current << 4);
current++;
}
return S;
};
const cipher = (M) => {
const BLOCK_TOTAL = Math.ceil(M.length >> 4) || 1;
S = squeeze(BLOCK_TOTAL);
return new U8(M.map((_, i) => _ ^ S[i]));
};
return {
encrypt: (M) => cipher(M),
decrypt: (C) => cipher(C),
};
}
/**
* Rabbit 流密码 / stream cipher
*/
export const rabbit = createCipher(_rabbit, {
ALGORITHM: 'rabbit',
KEY_SIZE: 16,
MIN_KEY_SIZE: 16,
MAX_KEY_SIZE: 16,
IV_SIZE: 8,
MIN_IV_SIZE: 0,
MAX_IV_SIZE: 8,
});