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 实现的密码学套件。目标是提供一个简单易用的密码学库。
277 lines (276 loc) • 6.96 kB
JavaScript
import { KitError, U8, genBitMask, joinBuffer, rotateL } from '../core/utils';
// * Constants
/**
* ρ(A) 位移表 / Shift Table
*/
const R = [
[0n, 36n, 3n, 41n, 18n],
[1n, 44n, 10n, 45n, 2n],
[62n, 6n, 43n, 15n, 61n],
[28n, 55n, 25n, 21n, 56n],
[27n, 20n, 39n, 8n, 14n],
];
/**
* https://datatracker.ietf.org/doc/draft-irtf-cfrg-kangarootwelve/
*/
const RC12 = [
0x000000008000808bn,
0x800000000000008bn,
0x8000000000008089n,
0x8000000000008003n,
0x8000000000008002n,
0x8000000000000080n,
0x000000000000800an,
0x800000008000000an,
0x8000000080008081n,
0x8000000000008080n,
0x0000000080000001n,
0x8000000080008008n,
];
/**
* `RCGen(6, 24)`
*/
const RC24 = [
0x0000000000000001n,
0x0000000000008082n,
0x800000000000808an,
0x8000000080008000n,
0x000000000000808bn,
0x0000000080000001n,
0x8000000080008081n,
0x8000000000008009n,
0x000000000000008an,
0x0000000000000088n,
0x0000000080008009n,
0x000000008000000an,
0x000000008000808bn,
0x800000000000008bn,
0x8000000000008089n,
0x8000000000008003n,
0x8000000000008002n,
0x8000000000000080n,
0x000000000000800an,
0x800000008000000an,
0x8000000080008081n,
0x8000000000008080n,
0x0000000080000001n,
0x8000000080008008n,
];
const mask64 = genBitMask(64);
const rotateL64 = (x, n) => rotateL(64, x, n, mask64);
// * Keccak Utils
/**
* RC Table Generation Function
*
* @param {number} l - log2(w)
* @param {number} [nr] - 轮数
*/
export function RCGen(l = 6, nr = 24) {
const RCTable = [];
for (let ir = 0; ir < nr; ir++) {
let RC = 0n;
for (let j = 0; j <= l; j++) {
const t = j + 7 * ir;
// rc(t)
let rc;
if (t % 255 === 0) {
rc = 1n;
}
else {
let R = 0x80n;
for (let i = 1; i <= t % 255; i++) {
const b = R & 1n;
R ^= (b << 8n) | (b << 4n) | (b << 3n) | (b << 2n);
R >>= 1n;
}
rc = R >> 7n;
}
// RC[2^j - 1] = rc(j + 7ir)
RC |= (rc << BigInt(2 ** j - 1));
}
RCTable.push(RC);
}
return RCTable;
}
/**
* @param {number} w - 工作字长度 / Word Size
*/
export function RGen(w) {
w = BigInt(w);
const R = [
[0n, 36n, 3n, 105n, 210n],
[1n, 300n, 10n, 45n, 66n],
[190n, 6n, 171n, 15n, 253n],
[28n, 276n, 120n, 136n, 55n],
[91n, 276n, 210n, 66n, 253n],
];
return R.map(x => x.map(y => y % w));
}
/**
* create a 5x5 `State Array`
*
* 创建一个 5x5 `状态矩阵`
*/
function createStateArray() {
return Array.from({ length: 5 }).map(() => new BigUint64Array(5));
}
/**
* Converting `State` to `State Arrays`
*
* 将 `状态` 转换为 `状态矩阵`
*/
function toStateArray(S) {
const A = createStateArray();
const view = new DataView(S.buffer);
for (let x = 0; x < 5; x++) {
for (let y = 0; y < 5; y++) {
A[x][y] = view.getBigUint64((y * 5 + x) << 3, true);
}
}
return A;
}
/**
* Converting `State Arrays` to `State`
*
* 将 `状态矩阵` 转换为 `状态`
*/
function toState(A) {
const S = new Uint8Array(200);
const view = new DataView(S.buffer);
for (let x = 0; x < 5; x++) {
for (let y = 0; y < 5; y++) {
view.setBigUint64((y * 5 + x) << 3, A[x][y], true);
}
}
return S;
}
// * Mapping Function
/** Algorithm 1: θ(A) */
function theta(A) {
const C = new BigUint64Array(5);
const D = new BigUint64Array(5);
for (let x = 0; x < 5; x++) {
C[x] = A[x][0] ^ A[x][1] ^ A[x][2] ^ A[x][3] ^ A[x][4];
}
for (let x = 0; x < 5; x++) {
D[x] = C[(x + 4) % 5] ^ rotateL64(C[(x + 1) % 5], 1n);
for (let y = 0; y < 5; y++) {
A[x][y] = A[x][y] ^ D[x];
}
}
return A;
}
/** Algorithm 2: ρ(A) */
// eslint-disable-next-line unused-imports/no-unused-vars
function rho(A) {
const _A = createStateArray();
for (let x = 0; x < 5; x++) {
for (let y = 0; y < 5; y++) {
_A[x][y] = rotateL64(A[x][y], R[x][y]);
}
}
return _A;
}
/** Algorithm 3: π(A) */
// eslint-disable-next-line unused-imports/no-unused-vars
function pi(A) {
const _A = createStateArray();
for (let x = 0; x < 5; x++) {
for (let y = 0; y < 5; y++) {
_A[x][y] = A[(x + 3 * y) % 5][x];
}
}
return _A;
}
/**
* Combining π(ρ(A))
*
* 合并执行 π(ρ(A))
*/
function rhoPi(A) {
const _A = createStateArray();
for (let x = 0; x < 5; x++) {
for (let y = 0; y < 5; y++) {
_A[y][(2 * x + 3 * y) % 5] = rotateL64(A[x][y], R[x][y]);
}
}
return _A;
}
/** Algorithm 4: χ(A) */
function chi(A) {
const _A = createStateArray();
for (let x = 0; x < 5; x++) {
for (let y = 0; y < 5; y++) {
_A[x][y] = A[x][y] ^ ((~A[(x + 1) % 5][y]) & A[(x + 2) % 5][y]);
}
}
return _A;
}
/** Algorithm 6: ι(A, ir) */
function iota(A, RC) {
A[0][0] = A[0][0] ^ RC;
return A;
}
/**
* `Keccak-p[1600, nr]` 置换函数 / Permutate Function
*
* @param {number} [nr] - 轮数 / Rounds (default: 24)
*/
export function keccak_p_1600(nr = 24) {
// b = 1600
const bByte = 200;
const l = 6;
// 当轮数非默认的情况下,重新生成 RC
let RC;
if (nr === 12) {
RC = RC12;
}
else if (nr === 24) {
RC = RC24;
}
else {
RC = RCGen(l, nr);
}
return (S) => {
if (S.byteLength !== bByte) {
throw new KitError('Invalid state size');
}
let A = toStateArray(S);
for (let i = 0; i < nr; i++) {
A = iota(chi(rhoPi(theta(A))), RC[i]);
}
return new U8(toState(A));
};
}
/**
* `SPONGE` & `Keccak-p[1600]`
*
* @param {number} r_byte - 处理速率 / Rate
* @param {number} d_byte - 输出长度 / Digest Size
* @param {SpongePadding} pad - 填充函数 / Padding Function
* @param {Keccak_p} f - Keccak-p 置换函数 / Permutate Function
*/
export function sponge_1600(r_byte, d_byte, pad, f = keccak_p_1600()) {
return (M) => {
// * 填充
const P = pad(M);
// * 吸收
let S = new Uint8Array(200);
let i = 0;
while (i < P.byteLength) {
const Pi = P.slice(i, i += r_byte);
S.forEach((byte, index) => S[index] = byte ^ Pi[index]);
S = f(S);
}
// * 挤出
const z = [S.slice(0, r_byte)];
let z_byte = r_byte;
while (z_byte < d_byte) {
S = f(S);
z.push(S.slice(0, r_byte));
z_byte += r_byte;
}
// * 截断输出
return joinBuffer(...z).slice(0, d_byte);
};
}