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 实现的密码学套件。目标是提供一个简单易用的密码学库。
471 lines (470 loc) • 11.9 kB
JavaScript
import { UTF8 } from './codec';
// * Math utility functions
/** 32-bit 循环左移 */
export function rotateL32(x, n) {
x >>>= 0;
n %= 32;
x = (x << n) | (x >>> (32 - n));
return x >>> 0;
}
/** 32-bit 循环右移 */
export function rotateR32(x, n) {
x >>>= 0;
n %= 32;
x = (x >>> n) | (x << (32 - n));
return x >>> 0;
}
/**
* 位循环左移 / Rotate Left
*
* @param {number} bit - 位数 / bit
* @param {number | bigint} x - 数值 / value
* @param {number | bigint} n - 位移 / shift
* @param {bigint} [mask] - 位掩码 / bit mask
*/
export function rotateL(bit, x, n, mask) {
bit = BigInt(bit);
mask ??= genBitMask(bit);
x = BigInt(x);
n = BigInt(n);
x &= mask;
n %= bit;
x = (x << n) | (x >> (bit - n));
return x & mask;
}
/**
* 位循环右移 / Rotate Right
*
* @param {number} bit - 位数 / bit
* @param {number | bigint} x - 数值 / value
* @param {number | bigint} n - 位移 / shift
* @param {bigint} [mask] - 位掩码 / bit mask
*/
export function rotateR(bit, x, n, mask) {
bit = BigInt(bit);
mask ??= genBitMask(bit);
x = BigInt(x);
n = BigInt(n);
x &= mask;
n %= bit;
x = (x >> n) | (x << (bit - n));
return x & mask;
}
/** 生成随机大整数 / Generate Random BigInt */
export function genRandomBI(max, byte) {
let result = 0n;
// 生成随机数
const buffer = new U8(byte);
do {
crypto.getRandomValues(buffer);
result = buffer.toBI();
} while (result >= max);
return { buffer, result };
}
/**
* 获取大整数的比特长度
*
* Get the bit length of a BigInt
*/
export function getBIBits(n) {
let bit = 0;
while (n > 0) {
bit++;
n >>= 1n;
}
return bit;
}
/**
* 生成位掩码 / Generate Bit Mask
*
* @param {number} w - 位数 / bit
*
* ```ts
* const mask = genBitMask(8) // 0xFFn
* ```
*/
export function genBitMask(w) {
w = BigInt(w);
let mask = 0x0n;
for (let i = 0; i < w; i++) {
mask = (mask << 1n) | 1n;
}
return mask;
}
/**
* 扩展欧几里得算法
*
* Extended Euclidean Algorithm
*
* - gcd: 最大公约数 / greatest common divisor
* - a_inv: a 的模逆 / modular inverse of a
* - b_inv: b 的模逆 / modular inverse of b
*/
function extendedEuclidean(a, b) {
let [s0, s1, t0, t1, r0, r1] = [1n, 0n, 0n, 1n, a, b];
if (b === 0n) {
return {
gcd: a,
a_inv: 1n,
b_inv: 0n,
};
}
while (r1 !== 0n) {
const q = r0 / r1;
[r0, r1] = [r1, r0 - q * r1];
[s0, s1] = [s1, s0 - q * s1];
[t0, t1] = [t1, t0 - q * t1];
}
return {
gcd: r0,
a_inv: s0,
b_inv: t0,
};
}
/**
* 勒让德符号
*
* Legendre Symbol
*/
function legendreSymbol(a, p) {
return modPow(a, (p - 1n) >> 1n, p);
}
/**
* 托内利-香克斯算法
*
* Tonelli-Shanks Algorithm
*/
function tonelliShanks(a, p) {
if (legendreSymbol(a, p) !== 1n) {
throw new KitError('There is no square root');
}
if (a === 0n) {
return 0n;
}
if (p === 2n) {
return a;
}
if (p % 4n === 3n) {
return modPow(a, (p + 1n) >> 2n, p);
}
let q = p - 1n;
let s = 0n;
while (mod(q, 2n) === 0n) {
q >>= 1n;
s++;
}
let z = 2n;
while (legendreSymbol(z, p) !== p - 1n) {
z++;
}
let m = s;
let c = modPow(z, q, p);
let t = modPow(a, q, p);
let r = modPow(a, (q + 1n) >> 1n, p);
while (t !== 0n && t !== 1n) {
let t2i = t;
let i = 1n;
for (; i < m; i++) {
t2i = modPow(t2i, 2n, p);
if (t2i === 1n) {
break;
}
}
const b = modPow(c, 1n << (m - i - 1n), p);
m = i;
c = modPow(b, 2n, p);
t = t * c % p;
r = r * b % p;
}
return r;
}
/**
* 最大公约数
*
* Greatest Common Divisor
*/
export function gcd(a, b) {
return extendedEuclidean(a, b).gcd;
}
/**
* 最小公倍数
*
* Least Common Multiple
*/
export function lcm(a, b) {
return a * b / gcd(a, b);
}
/**
* 求模: a mod b
*
* Modulo operation: a mod b
*
* @param {bigint} a - dividend
* @param {bigint} b - divisor
*/
export function mod(a, b) {
const r = a % b;
return r < 0n ? r + b : r;
}
/**
* 模幂运算: x ^ y mod n
*
* Modular exponentiation: x ^ y mod n
*
* @param {bigint} x - base
* @param {bigint} y - exponent
* @param {bigint} n - modulus
*/
export function modPow(x, y, n) {
x %= n;
let r = 1n;
while (y > 0n) {
if (y & 1n)
r = r * x % n;
x = x * x % n;
y >>= 1n;
}
return r;
}
/**
* 模逆运算: e ≡ x ^ -1 (mod n)
*
* Modular inverse operation: e ≡ x ^ -1 (mod n)
*
* @param {bigint} x - base
* @param {bigint} n - modulus
*/
export function modInverse(x, n) {
const { gcd, a_inv: inv } = extendedEuclidean(x, n);
if (gcd !== 1n) {
throw new KitError('Modular inverse does not exist');
}
return mod(inv, n);
}
/**
* 模素平方根运算: n ^ 0.5 (mod p)
*
* Modular prime square operation: n ^ 0.5 (mod p)
*/
export function modPrimeSquare(n, p) {
n = mod(n, p);
return tonelliShanks(n, p);
}
// * Buffer utility functions
/**
* @extends Uint8Array
*/
export class U8 extends Uint8Array {
/**
* 从 U8 中获取一个字 / Get a word from U8
*
* @param {number} word_byte - 字长 / word size
* @param {number} index - 字索引 / word index
* @param {boolean} [little_endian] - 是否为小端序 / little-endian (default: false)
*/
getWord(word_byte, index, little_endian = false) {
const offset = index * word_byte;
const buffer = this.subarray(offset, offset + word_byte);
return little_endian ? buffer.toBI(true) : buffer.toBI();
}
/**
* 将一个字写入 U8 / Set a word to U8
*
* @param {number} word_byte - 字长 / word size
* @param {number} index - 字索引 / word index
* @param {bigint | Uint8Array} word - 字 / word
* @param {boolean} [little_endian] - 是否为小端序 / little-endian (default: false)
*/
setWord(word_byte, index, word, little_endian = false) {
const offset = index * word_byte;
const buffer = typeof word === 'bigint' ? U8.fromBI(word, word_byte) : word;
this.set(little_endian ? buffer.toReversed() : buffer, offset);
}
/**
* U8 视图 / U8 view
*
* @param {number} word_byte - 字长 / word size
*/
view(word_byte) {
const length = Math.floor(this.length / word_byte);
const get = (index, little_endian = false) => this.getWord(word_byte, index, little_endian);
const set = (index, word, little_endian = false) => this.setWord(word_byte, index, word, little_endian);
return { get, set, length };
}
/**
* 将 U8 编码为字符串 / stringify U8 to encoded string
*/
to(codec) {
return codec(this);
}
/**
* 将 U8 转换为 BigInt / Convert U8 to BigInt
*
* @param {boolean} [little_endian] - 是否为小端序 / little-endian (default: false)
*/
toBI(little_endian = false) {
const buffer = little_endian ? this.toReversed() : this;
let bigint = 0n;
buffer.forEach(byte => bigint = (bigint << 8n) | BigInt(byte));
return bigint;
}
/**
* Convert U8 to Uint8Array
*
* 将 U8 转换为 Uint8Array
*/
toUint8Array() {
return new Uint8Array(this);
}
/**
* Convert string to U8 (default encoding: UTF-8)
*
* 将 字符串 转换为 U8 (默认编码: UTF-8)
*
*/
static fromString(input, codec = UTF8) {
return codec(input);
}
/**
* Convert BigInt to U8
*
* 将 BigInt 转换为 U8
*/
static fromBI(bigint, length, little_endian = false) {
length = length || (getBIBits(bigint) + 7) >> 3;
const buffer = new U8(length);
if (little_endian) {
for (let i = 0; i < buffer.length; i++) {
buffer[i] = Number(bigint & 0xffn);
bigint >>= 8n;
}
}
else {
for (let i = buffer.length - 1; i >= 0; i--) {
buffer[i] = Number(bigint & 0xffn);
bigint >>= 8n;
}
}
return buffer;
}
static from(arrayLike, mapfn, thisArg) {
return new U8(super.from(arrayLike, mapfn, thisArg));
}
filter(predicate, thisArg) {
return new U8(super.filter(predicate, thisArg));
}
map(callbackfn, thisArg) {
return new U8(super.map(callbackfn, thisArg));
}
static of(...items) {
return new U8(super.of(...items));
}
toReversed() {
return super.reverse();
}
toSorted(compareFn) {
return super.sort(compareFn);
}
reverse() {
return super.reverse();
}
slice(start, end) {
return new U8(super.slice(start, end));
}
subarray(begin, end) {
return new U8(super.subarray(begin, end));
}
with(index, value) {
return new U8(super.with(index, value));
}
}
/**
* Merging multiple ArrayBuffers
*
* 合并多个 ArrayBuffer
*/
export function joinBuffer(...buffers) {
const byteTotal = buffers.reduce((acc, cur) => acc + cur.byteLength, 0);
const result = new U8(byteTotal);
let offset = 0;
for (const buffer of buffers) {
result.set(new U8(buffer), offset);
offset += buffer.byteLength;
}
return result;
}
/**
* resize ArrayBuffer
*
* 调整 ArrayBuffer 大小
*
* @param {ArrayBuffer} buffer
* @param {number} size - byte
*/
export function resizeBuffer(buffer, size) {
const B = new U8(size);
B.set(new U8(buffer));
return B;
}
const nibbleReverseMap = [0x0n, 0x8n, 0x4n, 0xcn, 0x2n, 0xan, 0x6n, 0xen, 0x1n, 0x9n, 0x5n, 0xdn, 0x3n, 0xbn, 0x7n, 0xfn];
/**
* 快速翻转字节位序 / Fast Reverse Byte's Bit Order
*
* @param {number} byte - 字节 / byte
*/
export function reverseBit(byte) {
byte &= 0xFF;
const b_h = nibbleReverseMap[byte >> 4];
const b_l = nibbleReverseMap[byte & 0xF];
return (b_l << 4n) | b_h;
}
export class Counter extends U8 {
/**
* @param {number} offset - 计数器偏移 / counter offset
* @param {number} length - 计数器长度 / counter length
*/
inc(offset, length, little_endian = false) {
// 如果不提供偏移,则默认计数器从 0 开始
offset = offset || 0;
if (offset < 0 || offset >= this.length) {
throw new KitError('Invalid counter offset');
}
// 如果不提供长度,则默认计数器长度为剩余长度
length = length || this.length - offset;
if (length < 0 || offset + length > this.length) {
throw new KitError('Invalid counter length');
}
if (little_endian) {
for (let i = offset; i < offset + length; i++) {
if (this[i] < 0xFF) {
this[i] += 1;
break;
}
this[i] = 0;
}
}
else {
for (let i = offset + length - 1; i >= offset; i--) {
if (this[i] < 0xFF) {
this[i] += 1;
break;
}
this[i] = 0;
}
}
}
}
// * Other utility functions
export function wrap(...args) {
if (args.length === 0) {
return {};
}
// @ts-expect-error Object assign
return Object.assign(...args);
}
export class KitError extends Error {
constructor(message) {
super(message);
this.name = 'mima-kit Error';
}
}