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

438 lines (437 loc) 15.9 kB
import { UTF8 } from '../core/codec'; import { KitError, joinBuffer } from '../core/utils'; import { createHash, createKeyHash, createTupleHash } from '../core/hash'; import { Keccak_c, shake128, shake256 } from './sha3'; // * Encode and Padding Function /** * SP.800-185 2.3.1: * * 左侧整数编码 / left_encode * * Inspired by https://github.com/paulmillr/noble-hashes * * ```ts * leftEncode(0) // Uint8Array(2) [ 1, 0 ] * leftEncode(18446744073709551615n) // Uint8Array(9) [ 8, 255, 255, 255, 255, 255, 255, 255, 255 ] * ``` * * @param {number} x - 输入 / input */ function leftEncode(x) { const result = []; do { result.unshift(x & 0xFF); x = x >> 8; } while (x > 0); result.unshift(result.length); return Uint8Array.from(result); } /** * SP.800-185 2.3.1: * * 右侧整数编码 / right_encode * * Inspired by https://github.com/paulmillr/noble-hashes * * ```ts * rightEncode(0) // Uint8Array(2) [ 0, 1 ] * rightEncode(18446744073709551615n) // Uint8Array(9) [ 255, 255, 255, 255, 255, 255, 255, 255, 8 ] * ``` * * @param {number | bigint} x - 输入 / input */ function rightEncode(x) { const result = []; do { result.unshift(x & 0xFF); x = x >> 8; } while (x > 0); result.push(result.length); return Uint8Array.from(result); } /** * SP.800-185 2.3.2: * * 字符编码 / encode_string * * 与规范文档不同, 这个实现不会进行串接操作, 而是返回一个数组, 串接操作在外部进行. 详细见 `bytepad` 函数. * * Unlike the specification document, this implementation does not perform concatenation operations, but returns an array, and the concatenation operation is performed externally. See `bytepad` function for details. * * ```ts * encodeString(K) // [left_encode(len(K)), K] * ``` * * @param {string | Uint8Array} input - 输入 / input */ function encodeString(input) { input = typeof input === 'string' ? UTF8(input) : input; return [leftEncode(input.byteLength << 3), input]; } /** * SP.800-185 2.3.3: * * 在算法中 `bytePad` 涉及很多串接操作, 但对 `Javascript` 实现来说, 每次串接都意味着创建 `Uint8Array` 进行合并. 频繁地创建 `Uint8Array` 有可能导致性能问题. * 这是一个优化后的实现. 将输入 `X` 改为数组, 最后也返回数组, 将合并操作移动到外部. * * The `bytePad` is used by many algorithms which involves many concatenation operations, but for `Javascript` implementation, each concatenation means creating a `Uint8Array` for merging. Frequent creation of `Uint8Array` may cause performance issues. * This is an optimized implementation. The input `X` is changed to an array, and the final return is also an array. The merge operation is moved to the outside. * * * ```ts * bytepad(X, w) = left_encode(w) || X0 || ... || Xn || 0^z * ``` * * @param {Uint8Array} X - 输入数组 / input array * @param {number} w - 字节倍数 / byte multiple */ function bytepad(X, w) { if (w <= 0) { throw new KitError('w must be greater than 0'); } // 使用 leftEncode 函数编码 w const left_encoded_w = leftEncode(w); // z = left_encode(w) || X0 || ... || Xn // 计算 z 的有效字节长度总和, 用于计算填充零字节的数量 let z_byte = left_encoded_w.length; X.forEach(x => z_byte += x.length); // 计算需要填充的零字节的数量 const zero_byte = w - z_byte % w; X.unshift(left_encoded_w); X.push(new Uint8Array(zero_byte)); return X; } /** * `cSHAKE` 填充函数 / Padding Function * * ```ts * M || 00 || 10*1 * ``` * * @param {number} r_byte - 处理速率 / Rate */ const cshakePadding = (r_byte) => { return (M) => { const sig_byte = M.length; const pad_byte = r_byte - (sig_byte % r_byte); const P = new Uint8Array(sig_byte + pad_byte); P.set(M); if (pad_byte === 1) { P[sig_byte] = 0x84; } P[sig_byte] = 0x04; P[P.length - 1] |= 0x80; return P; }; }; // * cSHAKE /** * @param {number} d - 输出长度 / Digest Size (bit) * @param {Uint8Array} N - 函数名 / Function name * @param {Uint8Array} S - 自定义参数 / Customization * @param {number} c - 安全容量 / capacity (bit) * @param {number} r_byte - 处理速率 / Rate (byte) * @param {Hash} SHAKE - SHAKE 函数 */ function cshake(d, N, S, c, r_byte, SHAKE) { if (N.byteLength === 0 && S.byteLength === 0) { return (M) => SHAKE(d)(M); } return (M) => { const P = bytepad([...encodeString(N), ...encodeString(S)], r_byte); P.push(M); return Keccak_c(c, d, cshakePadding)(joinBuffer(...P)); }; } /** * `cSHAKE128` 是 `SHAKE128` 的可定制变体 * * `cSHAKE128` is a customizable variant of `SHAKE128` * * @param {number} d - 输出长度 / Digest Size (bit) * @param {Uint8Array} [N] - 函数名 / Function name * @param {Uint8Array} [S] - 自定义参数 / Customization */ export function cshake128(d, N = new Uint8Array(), S = new Uint8Array()) { return createHash(cshake(d, N, S, 256, 168, shake128), { ALGORITHM: `cSHAKE128/${d}`, BLOCK_SIZE: 168, DIGEST_SIZE: d >> 3, }); } /** * `cSHAKE256` 是 `SHAKE256` 的可定制变体 * * `cSHAKE256` is a customizable variant of `SHAKE256` * * @param {number} d - 输出长度 / Digest Size (bit) * @param {Uint8Array} [N] - 函数名 / Function name * @param {Uint8Array} [S] - 自定义参数 / Customization */ export function cshake256(d, N = new Uint8Array(), S = new Uint8Array()) { return createHash(cshake(d, N, S, 512, 136, shake256), { ALGORITHM: `cSHAKE256/${d}`, BLOCK_SIZE: 136, DIGEST_SIZE: d >> 3, }); } // * KMAC /** * @param {number} d - 输出长度 / Digest Size (bit) * @param {Uint8Array} S - 自定义参数 / Customization * @param {number} c - 安全容量 / capacity (bit) * @param {number} r_byte - 处理速率 / Rate (byte) * @param {boolean} XOF - 是否为 XOF 模式 / XOF mode */ function kmac(d, S, c, r_byte, XOF) { return (K, M) => { const X = bytepad([...encodeString('KMAC'), ...encodeString(S)], r_byte); X.push(...bytepad(encodeString(K), r_byte)); X.push(M); X.push(rightEncode(XOF ? 0 : d)); return Keccak_c(c, d, cshakePadding)(joinBuffer(...X)); }; } /** * Keccak 消息认证码 (KMAC) 算法 * `KMAC128` 是 `KMAC` 的变体, 由 `cSHAKE128` 构建 * * The Keccak Message Authentication Code (KMAC) algorithm * `KMAC128` is a variant of `KMAC`, build from `cSHAKE128` * * @param {number} d - 输出长度 / Digest Size (bit) * @param {Uint8Array} S - 自定义参数 / Customization * @param {number} k_size - 推荐密钥大小 / Recommended key size (bit) */ export function kmac128(d, S = new Uint8Array(0), k_size = 128) { return createKeyHash(kmac(d, S, 256, 168, false), { ALGORITHM: `KMAC128/${d}`, BLOCK_SIZE: 168, DIGEST_SIZE: d >> 3, KEY_SIZE: k_size >> 3, }); } /** * Keccak 消息认证码 (KMAC) 算法 * `KMAC256` 是 `KMAC` 的变体, 由 `cSHAKE256` 构建 * * The Keccak Message Authentication Code (KMAC) algorithm * `KMAC256` is a variant of `KMAC`, build from `cSHAKE256` * * @param {number} d - 输出长度 / Digest Size (bit) * @param {Uint8Array} S - 自定义参数 / Customization * @param {number} k_size - 推荐密钥大小 / Recommended key size (bit) */ export function kmac256(d, S = new Uint8Array(0), k_size = 256) { return createKeyHash(kmac(d, S, 512, 136, false), { ALGORITHM: `KMAC256/${d}`, BLOCK_SIZE: 136, DIGEST_SIZE: d >> 3, KEY_SIZE: k_size >> 3, }); } /** * 可变长度输出的 `KMAC` * `KMAC128XOF` 是 `KMAC128` 的 XOF 模式, 由 `cSHAKE128` 构建 * * `KMAC` with Arbitrary-Length Output * `KMAC128XOF` is a XOF mode of `KMAC128`, build from `cSHAKE128` * * @param {number} d - 输出长度 / Digest Size (bit) * @param {Uint8Array} S - 自定义参数 / Customization * @param {number} k_size - 推荐密钥大小 / Recommended key size (bit) */ export function kmac128XOF(d, S = new Uint8Array(0), k_size = 128) { return createKeyHash(kmac(d, S, 256, 168, true), { ALGORITHM: `KMAC128XOF/${d}`, BLOCK_SIZE: 168, DIGEST_SIZE: d >> 3, KEY_SIZE: k_size >> 3, }); } /** * 可变长度输出的 `KMAC` * `KMAC256XOF` 是 `KMAC256` 的 XOF 模式, 由 `cSHAKE256` 构建 * * `KMAC` with Arbitrary-Length Output * `KMAC256XOF` is a XOF mode of `KMAC256`, build from `cSHAKE256` * * @param {number} d - 输出长度 / Digest Size (bit) * @param {Uint8Array} S - 自定义参数 / Customization * @param {number} k_size - 推荐密钥大小 / recommended key size (bit) */ export function kmac256XOF(d, S = new Uint8Array(0), k_size = 256) { return createKeyHash(kmac(d, S, 512, 136, true), { ALGORITHM: `KMAC256XOF/${d}`, BLOCK_SIZE: 136, DIGEST_SIZE: d >> 3, KEY_SIZE: k_size >> 3, }); } // * TupleHash /** * @param {number} d - 输出长度 / Digest Size (bit) * @param {Uint8Array} S - 自定义参数 / Customization * @param {number} c - 安全容量 / capacity (bit) * @param {number} r_byte - 处理速率 / Rate (byte) * @param {boolean} XOF - 是否为 XOF 模式 / XOF mode */ function tuplehash(d, S, c, r_byte, XOF) { return (M) => { const X = bytepad([...encodeString('TupleHash'), ...encodeString(S)], r_byte); M.forEach(m => X.push(...encodeString(m))); X.push(rightEncode(XOF ? 0 : d)); return Keccak_c(c, d, cshakePadding)(joinBuffer(...X)); }; } /** * `TupleHash` 是一个具有可变长度输出的 `SHA3` 派生散列函数, 旨在以一种明确的方式简单地散列输入字符串的元组, 这些字符串中的任何一个或全部都可以是空字符串. * * `TupleHash` is a `SHA3` derived hash function with variable-length output that is designed to simply hash a tuple of input strings, any or all of which may be empty strings, in an unambiguous way. * * @param {number} d - 输出长度 / Digest Size (bit) * @param {Uint8Array} S - 自定义参数 / Customization */ export function tuplehash128(d, S = new Uint8Array()) { return createTupleHash(tuplehash(d, S, 256, 168, false), { ALGORITHM: `TupleHash128/${d}`, BLOCK_SIZE: 168, DIGEST_SIZE: d >> 3, }); } /** * `TupleHash` 是一个具有可变长度输出的 `SHA3` 派生散列函数, 旨在以一种明确的方式简单地散列输入字符串的元组, 这些字符串中的任何一个或全部都可以是空字符串. * * `TupleHash` is a `SHA3` derived hash function with variable-length output that is designed to simply hash a tuple of input strings, any or all of which may be empty strings, in an unambiguous way. * * @param {number} d - 输出长度 / Digest Size (bit) * @param {Uint8Array} S - 自定义参数 / Customization */ export function tuplehash256(d, S = new Uint8Array()) { return createTupleHash(tuplehash(d, S, 512, 136, false), { ALGORITHM: `TupleHash256/${d}`, BLOCK_SIZE: 136, DIGEST_SIZE: d >> 3, }); } /** * 可变长度输出的 `TupleHash` * * `TupleHash` with Arbitrary-Length Output * * @param {number} d - 输出长度 / Digest Size (bit) * @param {Uint8Array} S - 自定义参数 / Customization */ export function tuplehash128XOF(d, S = new Uint8Array()) { return createTupleHash(tuplehash(d, S, 256, 168, true), { ALGORITHM: `TupleHash128XOF/${d}`, BLOCK_SIZE: 168, DIGEST_SIZE: d >> 3, }); } /** * 可变长度输出的 `TupleHash` * * `TupleHash` with Arbitrary-Length Output * * @param {number} d - 输出长度 / Digest Size (bit) * @param {Uint8Array} S - 自定义参数 / Customization */ export function tuplehash256XOF(d, S = new Uint8Array()) { return createTupleHash(tuplehash(d, S, 512, 136, true), { ALGORITHM: `TupleHash256XOF/${d}`, BLOCK_SIZE: 136, DIGEST_SIZE: d >> 3, }); } // * ParallelHash // ! Note: This ParallelHash does not actually perform parallel computation, because writing multi-threaded in JavaScript is not easy. // ! 注意: 此 ParallelHash 实际上并不执行并行计算, 因为在 JavaScript 写多线程并不轻松. // TODO 计划引入 `multithreading` 依赖, 实现真正的并行计算 /** * @param {number} b - 状态大小 / State size (bit) * @param {number} d - 输出长度 / Digest Size (bit) * @param {Uint8Array} S - 自定义参数 / Customization * @param {number} c - 安全容量 / capacity (bit) * @param {number} r_byte - 处理速率 / Rate (byte) * @param {boolean} XOF - 是否为 XOF 模式 / XOF mode */ function parallelhash(b, d, S, c, r_byte, XOF, SHAKE) { const bByte = b >> 3; return (M) => { const n = Math.ceil(M.byteLength / bByte); const X = bytepad([...encodeString('ParallelHash'), ...encodeString(S)], r_byte); X.push(leftEncode(b)); for (let i = 0; i < n; i++) { const B = M.slice(i * (b << 3), (i + 1) * (b << 3)); X.push(SHAKE(B)); } X.push(rightEncode(n)); X.push(rightEncode(XOF ? 0 : d)); return Keccak_c(c, d, cshakePadding)(joinBuffer(...X)); }; } /** * `ParallelHash` 的目的是利用现代处理器中可用的并行性, 支持对非常长的字符串进行高效散列. * * The purpose of `ParallelHash` is to support the efficient hashing of very long strings, by taking advantage of the parallelism available in modern processors. * * @param {number} b - 状态大小 / State size (bit) * @param {number} d - 输出长度 / Digest Size (bit) * @param {Uint8Array} S - 自定义参数 / Customization */ export function parallelhash128(b, d, S = new Uint8Array()) { return createHash(parallelhash(b, d, S, 256, 168, false, shake128(256)), { ALGORITHM: `ParallelHash128/${d}`, BLOCK_SIZE: 168, DIGEST_SIZE: d >> 3, }); } /** * `ParallelHash` 的目的是利用现代处理器中可用的并行性, 支持对非常长的字符串进行高效散列. * * The purpose of `ParallelHash` is to support the efficient hashing of very long strings, by taking advantage of the parallelism available in modern processors. * * @param {number} b - 状态大小 / State size (bit) * @param {number} d - 输出长度 / Digest Size (bit) * @param {Uint8Array} S - 自定义参数 / Customization */ export function parallelhash256(b, d, S = new Uint8Array()) { return createHash(parallelhash(b, d, S, 512, 136, false, shake256(512)), { ALGORITHM: `ParallelHash256/${d}`, BLOCK_SIZE: 136, DIGEST_SIZE: d >> 3, }); } /** * 可变长度输出的 `ParallelHash` * * `ParallelHash` with Arbitrary-Length Output * * @param {number} b - 状态大小 / State size (bit) * @param {number} d - 输出长度 / Digest Size (bit) * @param {Uint8Array} S - 自定义参数 / Customization */ export function parallelhash128XOF(b, d, S = new Uint8Array()) { return createHash(parallelhash(b, d, S, 256, 168, true, shake128(256)), { ALGORITHM: `ParallelHash128XOF`, BLOCK_SIZE: 168, DIGEST_SIZE: d >> 3, }); } /** * 可变长度输出的 `ParallelHash` * * `ParallelHash` with Arbitrary-Length Output * * @param {number} b - 状态大小 / State size (bit) * @param {number} d - 输出长度 / Digest Size (bit) * @param {Uint8Array} S - 自定义参数 / Customization */ export function parallelhash256XOF(b, d, S = new Uint8Array()) { return createHash(parallelhash(b, d, S, 512, 136, true, shake256(512)), { ALGORITHM: `ParallelHash256XOF`, BLOCK_SIZE: 136, DIGEST_SIZE: d >> 3, }); }