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 实现的密码学套件。目标是提供一个简单易用的密码学库。
596 lines (595 loc) • 21 kB
JavaScript
import { CoordinateSystem } from './coordinate_system';
import { GF, GF2 } from './galois_field';
import { getBIBits, joinBuffer, KitError, U8, u8 } from './utils';
function LadderMultiply(add, P, k) {
k = typeof k === 'bigint' ? k : u8(k).toBI();
let R0;
let R1;
switch (P.type) {
case 'affine':
R0 = { type: 'affine', isInfinity: true, x: 0n, y: 0n };
R1 = { type: 'affine', isInfinity: P.isInfinity, x: P.x, y: P.y };
break;
case 'jacobian':
R0 = { type: 'jacobian', isInfinity: true, x: 1n, y: 1n, z: 0n };
R1 = { type: 'jacobian', isInfinity: P.isInfinity, x: P.x, y: P.y, z: P.z };
break;
case 'ld':
R0 = { type: 'ld', isInfinity: true, x: 1n, y: 1n, z: 0n };
R1 = { type: 'ld', isInfinity: P.isInfinity, x: P.x, y: P.y, z: P.z };
break;
default:
throw new KitError('unknown coordinate system');
}
// MSb -> LSb
const bit_array = k.toString(2);
for (const bit of bit_array) {
if (bit === '1') {
R0 = add(R0, R1);
R1 = add(R1, R1);
}
else {
R1 = add(R0, R1);
R0 = add(R0, R0);
}
}
return R0;
}
// * FpEC Components
/**
* 素域 Weierstrass 椭圆曲线运算
*
* Prime Field Weierstrass Elliptic Curve Operations
*
* y^2 = x^3 + ax + b
*/
export function FpWEC(curve) {
const { p, n, a, b, G } = curve;
const p_bit = getBIBits(p);
const p_byte = (p_bit + 7) >> 3;
const field = GF(p);
const cs = CoordinateSystem(field);
const { add, sub, mul, div, root, include } = field;
const { toAffine, toJacobian } = cs;
const _addPoint = (A, B) => {
// O + P = P
if (A.isInfinity)
return B;
if (B.isInfinity)
return A;
const { x: X1, y: Y1 } = A;
const { x: X2, y: Y2 } = B;
const U = sub(X1, X2) === 0n;
// P + (-P) = O
if (U && add(Y1, Y2) === 0n)
return toAffine(undefined);
if (U && Y1 === 0n)
return toAffine(undefined);
let λ = 0n;
// P1 + P1
if (U) {
// λ = (3 * x1 * x1 + a) / 2 * y1
const numerator = add(mul(3n, X1, X1), a);
const denominator = mul(2n, Y1);
λ = div(numerator, denominator);
}
// P1 + P2
else {
// λ = (y2 - y1) / (x2 - x1)
const numerator = sub(Y2, Y1);
const denominator = sub(X2, X1);
λ = div(numerator, denominator);
}
// x3 = λ * λ - x1 - x2
const x3 = sub(mul(λ, λ), X1, X2);
// y3 = λ * (x1 - x3) - y1
const y3 = sub(mul(λ, sub(X1, x3)), Y1);
return { type: 'affine', isInfinity: false, x: x3, y: y3 };
};
const addPoint = (A, B) => {
// O + P = P
if (A.isInfinity)
return B;
if (B.isInfinity)
return A;
const [X1, Y1, Z1] = [A.x, A.y, A.z];
const [X2, Y2, Z2] = [B.x, B.y, B.z];
// 计算中间变量
const ZZ1 = mul(Z1, Z1);
const ZZ2 = mul(Z2, Z2);
const U1 = mul(X1, ZZ2);
const U2 = mul(X2, ZZ1);
const S1 = mul(Y1, ZZ2, Z2);
const S2 = mul(Y2, ZZ1, Z1);
// P + (-P) = O
if (U1 === U2 && S1 !== S2)
return { type: 'jacobian', isInfinity: true, x: 1n, y: 1n, z: 0n };
// P1 + P1
if (U1 === U2 && S1 === S2) {
const [X, Y, Z] = [X1, Y1, Z1];
// Z3 = 2 * Y1 * Z1
const Z3 = mul(2n, Y, Z);
if (Z3 === 0n)
return { type: 'jacobian', isInfinity: true, x: 1n, y: 1n, z: 0n };
const XX = mul(X, X);
const YY = mul(Y, Y);
const ZZ = ZZ1;
const S = mul(4n, X, YY);
// M = 3 * XX + a * ZZ^2
const M = add(mul(3n, XX), mul(a, ZZ, ZZ));
// X3 = M^2 - 2 * S
const X3 = sub(mul(M, M), mul(2n, S));
// Y3 = M * (S - X3) - 8 * YYYY
const Y3 = sub(mul(M, sub(S, X3)), mul(8n, YY, YY));
return { type: 'jacobian', isInfinity: false, x: X3, y: Y3, z: Z3 };
}
// P1 + P2
else {
const H = sub(U2, U1);
// Z3 = H * Z1 * Z2
const Z3 = mul(H, Z1, Z2);
if (Z3 === 0n)
return { type: 'jacobian', isInfinity: true, x: 1n, y: 1n, z: 0n };
const R = sub(S2, S1);
const HH = mul(H, H);
const HHH = mul(H, HH);
const U1HH = mul(U1, HH);
// X3 = R^2 - H^3 - 2 * U1 * H^2
const X3 = sub(mul(R, R), HHH, mul(2n, U1HH));
// Y3 = R * (U1 * H^2 - X3) - S1 * H^3
const Y3 = sub(mul(R, sub(U1HH, X3)), mul(S1, HHH));
return { type: 'jacobian', isInfinity: false, x: X3, y: Y3, z: Z3 };
}
};
const _mulPoint = (P, k) => LadderMultiply(_addPoint, P, k);
const mulPoint = (P, k) => LadderMultiply(addPoint, P, k);
const isLegalPK = (P) => {
// P != O
if (P.isInfinity)
return false;
// P(x, y) ∈ E
const { x, y } = toAffine(P);
if (!include(x) || !include(y))
return false;
// y^2 = x^3 + ax + b
const l = mul(y, y);
const r = add(mul(x, x, x), mul(a, x), b);
if (l !== r)
return false;
// nP = O
const nP = mulPoint(toJacobian(P), n);
return nP.isInfinity;
};
const isLegalSK = (k) => {
k = typeof k === 'bigint' ? k : u8(k).toBI();
if (k <= 0n || k >= n)
return false;
return !mulPoint(toJacobian(G), k).isInfinity;
};
const PointToU8 = (point, compress = false) => {
if (point.isInfinity)
return new U8([0x00]);
const { x, y } = point;
const sign_y = Number(y & 1n);
const PC = new U8([compress ? 0x02 | sign_y : 0x04]);
const X1 = U8.fromBI(x, p_byte);
const Y1 = compress ? new U8() : U8.fromBI(y, p_byte);
return joinBuffer(PC, X1, Y1);
};
const U8ToPoint = (buffer) => {
const point_buffer = U8.from(buffer);
const PC = point_buffer[0];
if (PC !== 0x00 && PC !== 0x02 && PC !== 0x03 && PC !== 0x04)
throw new KitError('Invalid Point');
// 无穷远点
if (PC === 0x00 && point_buffer.length === 1)
return toAffine(undefined);
// 无压缩
if (PC === 0x04 && point_buffer.length === (p_byte << 1) + 1) {
const x = point_buffer.slice(1, p_byte + 1).toBI();
const y = point_buffer.slice(p_byte + 1).toBI();
return { type: 'affine', isInfinity: false, x, y };
}
// 解压缩
if ((PC === 0x02 || PC === 0x03) && point_buffer.length === p_byte + 1) {
const x_buffer = point_buffer.slice(1);
const x = x_buffer.toBI();
const sign_y = BigInt(PC & 1);
let y;
y = add(mul(x, x, x), mul(a, x), b);
y = root(y);
y = (y & 1n) === sign_y ? y : sub(p, y);
return { type: 'affine', isInfinity: false, x, y };
}
throw new KitError('Invalid Point');
};
return {
field,
cs,
catalyst: 'jacobian',
addPoint,
mulPoint,
_addPoint,
_mulPoint,
isLegalPK,
isLegalSK,
PointToU8,
U8ToPoint,
};
}
/**
* 素域 Montgomery 椭圆曲线运算
*
* Prime Field Montgomery Elliptic Curve Operations
*
* b * y^2 = x^3 + a * x^2 + x
*/
export function FpMEC(curve) {
const { p, n, a, b, G } = curve;
const p_bit = getBIBits(p);
const p_byte = (p_bit + 7) >> 3;
const field = GF(p);
const cs = CoordinateSystem(field);
const { add, sub, mul, div, root, include } = field;
const { toAffine, toJacobian } = cs;
const _addPoint = (A, B) => {
// O + P = P
if (A.isInfinity)
return B;
if (B.isInfinity)
return A;
const { x: X1, y: Y1 } = A;
const { x: X2, y: Y2 } = B;
const U = sub(X1, X2) === 0n;
// P + (-P) = O
if (U && add(Y1, Y2) === 0n)
return toAffine(undefined);
if (U && Y1 === 0n)
return toAffine(undefined);
let λ = 0n;
// P1 + P1
if (U) {
// λ = (3 * x1 * x1 + 2 * a * x1 + 1) / 2 * b * y1
const numerator = add(mul(3n, X1, X1), mul(2n, a, X1), 1n);
const denominator = mul(2n, b, Y1);
λ = div(numerator, denominator);
}
// P1 + P2
else {
// λ = (y2 - y1) / (x2 - x1)
const numerator = sub(Y2, Y1);
const denominator = sub(X2, X1);
λ = div(numerator, denominator);
}
// x3 = b * λ * λ - a - x1 - x2
const X3 = sub(mul(λ, λ, b), a, X1, X2);
// y3 = (2 x1 + x2 + a) * λ - b * λ * λ * λ - y1
const Y3 = sub(mul(2n * X1 + X2 + a, λ), mul(λ, λ, λ, b), Y1);
return { type: 'affine', isInfinity: false, x: X3, y: Y3 };
};
const addPoint = (A, B) => {
// O + P = P
if (A.isInfinity)
return B;
if (B.isInfinity)
return A;
const [X1, Y1, Z1] = [A.x, A.y, A.z];
const [X2, Y2, Z2] = [B.x, B.y, B.z];
// 计算中间变量
const ZZ1 = mul(Z1, Z1); // Z1^2
const ZZ2 = mul(Z2, Z2); // Z2^2
const U1 = mul(X1, ZZ2);
const U2 = mul(X2, ZZ1);
const S1 = mul(Y1, ZZ2, Z2);
const S2 = mul(Y2, ZZ1, Z1);
const H = sub(U2, U1); // H = U2 - U1
const R = sub(S2, S1); // R = S2 - S1
// P + (-P) = O
if (H === 0n && R !== 0n)
return { type: 'jacobian', isInfinity: true, x: 1n, y: 1n, z: 0n };
// P1 + P1
if (H === 0n && R === 0n) {
const [X, Y, Z] = [X1, Y1, Z1];
// Z3 = 2 * b * Y1 * Z1 = λ 的分母
const Z3 = mul(2n, b, Y, Z);
if (Z3 === 0n)
return { type: 'jacobian', isInfinity: true, x: 1n, y: 1n, z: 0n };
const XX = mul(X, X);
const YY = mul(Y, Y);
const ZZ = ZZ1;
const ZZZZ = mul(ZZ, ZZ);
// N = 3 * X1^2 + 2 * a * X1 * Z1^2 + Z1^4 (λ 的分子,已清除分母)
const N = add(mul(3n, XX), mul(2n, a, X, ZZ), ZZZZ);
const NN = mul(N, N);
// X3 = b * N^2 - a * Z3^2 - 8 * b^2 * X1 * Y1^2
const X3 = sub(mul(b, NN), mul(a, Z3, Z3), mul(8n, b, b, X, YY));
// Y3 = 4 * b^2 * (3 * X1 + a * Z1^2) * N * Y1^2 - b * N^3 - 8 * b^3 * Y1^4
const Y3 = sub(mul(4n, b, b, add(mul(3n, X), mul(a, ZZ)), N, YY), mul(b, NN, N), mul(8n, b, b, b, YY, YY));
return { type: 'jacobian', isInfinity: false, x: X3, y: Y3, z: Z3 };
}
// P1 + P2
else {
const Z3 = mul(H, Z1, Z2);
if (Z3 === 0n)
return { type: 'jacobian', isInfinity: true, x: 1n, y: 1n, z: 0n };
const HH = mul(H, H); // HH = H^2
const X3 = sub(mul(b, R, R), mul(a, ZZ1, ZZ2, HH), mul(add(U1, U2), HH));
const Y3 = sub(mul(R, sub(mul(U1, HH), X3)), mul(S1, HH, H));
return { type: 'jacobian', isInfinity: false, x: X3, y: Y3, z: Z3 };
}
};
const _mulPoint = (P, k) => LadderMultiply(_addPoint, P, k);
const mulPoint = (P, k) => LadderMultiply(addPoint, P, k);
const isLegalPK = (P) => {
// P != O
if (P.isInfinity)
return false;
// P(x, y) ∈ E
const { x, y } = toAffine(P);
if (!include(x) || !include(y))
return false;
// b * y^2 = x^3 + a * x^2 + x
const l = mul(b, y, y);
const r = add(mul(x, x, x), mul(a, x, x), x);
if (l !== r)
return false;
// nP = O
const nP = mulPoint(toJacobian(P), n);
return nP.isInfinity;
};
const isLegalSK = (k) => {
k = typeof k === 'bigint' ? k : u8(k).toBI();
if (k <= 0n || k >= n)
return false;
return !mulPoint(toJacobian(G), k).isInfinity;
};
const PointToU8 = (point, compress = false) => {
if (point.isInfinity)
return new U8([0x00]);
const { x, y } = point;
const sign_y = Number(y & 1n);
const PC = new U8([compress ? 0x02 | sign_y : 0x04]);
const X1 = U8.fromBI(x, p_byte);
const Y1 = compress ? new U8() : U8.fromBI(y, p_byte);
return joinBuffer(PC, X1, Y1);
};
const U8ToPoint = (buffer) => {
const point_buffer = U8.from(buffer);
const PC = point_buffer[0];
if (PC !== 0x00 && PC !== 0x02 && PC !== 0x03 && PC !== 0x04)
throw new KitError('Invalid Point');
// 无穷远点
if (PC === 0x00 && point_buffer.length === 1)
return toAffine(undefined);
// 无压缩
if (PC === 0x04 && point_buffer.length === (p_byte << 1) + 1) {
const x = point_buffer.slice(1, p_byte + 1).toBI();
const y = point_buffer.slice(p_byte + 1).toBI();
return { type: 'affine', isInfinity: false, x, y };
}
// 解压缩
if ((PC === 0x02 || PC === 0x03) && point_buffer.length === p_byte + 1) {
const x_buffer = point_buffer.slice(1);
const x = x_buffer.toBI();
const sign_y = BigInt(PC & 1);
let y;
y = add(mul(x, x, x), mul(a, x, x), x);
y = div(y, b);
y = root(y);
y = (y & 1n) === sign_y ? y : sub(p, y);
return { type: 'affine', isInfinity: false, x, y };
}
throw new KitError('Invalid Point');
};
return {
field,
cs,
catalyst: 'jacobian',
addPoint,
mulPoint,
_addPoint,
_mulPoint,
isLegalPK,
isLegalSK,
PointToU8,
U8ToPoint,
};
}
// * FbEC Components
/**
* 二元扩域椭圆曲线运算
*
* Binary Field Elliptic Curve Operations
*
* y^2 + xy = x^3 + ax^2 + b
*/
export function FbEC(curve) {
const { m, IP, a, b, n, G } = curve;
const field = GF2(m, IP);
const cs = CoordinateSystem(field);
const m_byte = (Number(m) + 7) >> 3;
const { add, sub, mul, div, inv, include } = field;
const { toAffine, toLD } = cs;
const _addPoint = (A, B) => {
// O + P = P
if (A.isInfinity)
return B;
if (B.isInfinity)
return A;
const { x: X1, y: Y1 } = A;
const { x: X2, y: Y2 } = B;
// P + (-P) = O
const T = sub(X1, X2) === 0n;
if (T && add(X1, Y2) === Y1)
return toAffine(undefined);
if (T && X1 === 0n)
// 若 x1 为 0,则切线斜率不存在,结果为无穷远点
return toAffine(undefined);
let λ = 0n;
// P1 + P1
if (T) {
// λ = x1 + y1 / x1
λ = add(X1, div(Y1, X1));
}
// P1 + P2
else {
// λ = (y2 + y1) / (x2 + x1)
const numerator = add(Y2, Y1);
const denominator = add(X2, X1);
λ = div(numerator, denominator);
}
// x3 = λ * λ + λ + a + x1 + x2
const x3 = add(mul(λ, λ), λ, a, X1, X2);
// y3 = λ * (x1 + x3) + x3 + y1
const y3 = add(mul(λ, add(X1, x3)), x3, Y1);
return { type: 'affine', isInfinity: false, x: x3, y: y3 };
};
const addPoint = (P, Q) => {
// O + P = P
if (P.isInfinity)
return Q;
if (Q.isInfinity)
return P;
const [X1, Y1, Z1] = [P.x, P.y, P.z];
const [X2, Y2, Z2] = [Q.x, Q.y, Q.z];
// 计算中间变量
const A = add(mul(X1, Z2), mul(X2, Z1));
const B = add(mul(Y1, Z2, Z2), mul(Y2, Z1, Z1));
// P + (-P) = O
if (A === 0n && B !== 0n)
return { type: 'ld', isInfinity: true, x: 1n, y: 1n, z: 0n };
if (A === 0n && B === 0n && X1 === 0n)
return { type: 'ld', isInfinity: true, x: 1n, y: 1n, z: 0n };
// P1 + P1
if (A === 0n && B === 0n) {
const [X, Y, Z] = [X1, Y1, Z1];
const A = mul(X, X);
const B = mul(Z, Z);
const Z3 = mul(A, B);
const C = mul(A, A);
const D = mul(b, B, B);
const X3 = add(C, D);
const Y3 = add(mul(D, Z3), mul(X3, add(mul(a, Z3), mul(Y, Y), D)));
return { type: 'ld', isInfinity: false, x: X3, y: Y3, z: Z3 };
}
// P1 + P2
else {
const C = mul(Z1, A);
const D = mul(Z2, C);
const Z3 = mul(D, D);
const X3 = add(mul(D, add(mul(A, A), B)), mul(B, B), mul(a, Z3));
const E = mul(C, D);
const F = mul(E, E, Y2);
const G = add(X3, mul(X2, E));
const Y3 = add(mul(Z3, X3), F, mul(B, D, G));
return { type: 'ld', isInfinity: false, x: X3, y: Y3, z: Z3 };
}
};
const _mulPoint = (P, k) => LadderMultiply(_addPoint, P, k);
const mulPoint = (P, k) => LadderMultiply(addPoint, P, k);
const isLegalPK = (P) => {
// P != O
if (P.isInfinity)
return false;
// P(x, y) ∈ E
const { x, y } = toAffine(P);
if (!include(x) || !include(y))
return false;
// y^2 + xy = x^3 + ax^2 + b
const l = add(mul(y, y), mul(x, y));
const r = add(mul(x, x, x), mul(a, x, x), b);
if (l !== r)
return false;
return mulPoint(toLD(P), n).isInfinity;
};
const isLegalSK = (k) => {
k = typeof k === 'bigint' ? k : u8(k).toBI();
if (k <= 0n || k >= n - 2n)
return false;
return !mulPoint(toLD(G), k).isInfinity;
};
// 半迹函数
function half_trace(d) {
const k = (m - 1n) >> 1n;
let s = d;
let H = s;
for (let i = 1; i <= k; i++) {
s = mul(s, s, s, s); // s^4
H = add(H, s);
}
return H;
}
const PointToU8 = (point, compress = false) => {
if (point.isInfinity)
return new U8([0x00]);
const { x, y } = toAffine(point);
const sign_y = Number(div(y, x) & 1n);
const PC = new U8([compress ? 0x02 | sign_y : 0x04]);
const X1 = U8.fromBI(x, m_byte);
const Y1 = compress ? new U8() : U8.fromBI(y, m_byte);
return joinBuffer(PC, X1, Y1);
};
const U8ToPoint = (buffer) => {
const point_buffer = U8.from(buffer);
const PC = point_buffer[0];
if (PC !== 0x00 && PC !== 0x02 && PC !== 0x03 && PC !== 0x04)
throw new KitError('Invalid Point');
// 无穷远点
if (PC === 0x00 && point_buffer.length === 1)
return toAffine(undefined);
// 无压缩
if (PC === 0x04 && point_buffer.length === (m_byte << 1) + 1) {
const x = point_buffer.slice(1, m_byte + 1).toBI();
const y = point_buffer.slice(m_byte + 1).toBI();
return { type: 'affine', isInfinity: false, x, y };
}
// 解压缩
if ((PC === 0x02 || PC === 0x03) && point_buffer.length === m_byte + 1) {
const x_buffer = point_buffer.slice(1);
const x = x_buffer.toBI();
if (x === 0n && PC === 0x02)
throw new KitError('Invalid Point');
if (x === 0n && PC === 0x03)
return { type: 'affine', isInfinity: false, x: 0n, y: 1n };
const inv_x = inv(x);
const inv_x2 = mul(inv_x, inv_x);
const d = add(x, 1n, inv_x2);
// 计算半迹得到 z0
const z0 = half_trace(d);
// 计算 y0 = x * z0
const y0 = mul(x, z0);
// 获取 z0 的 LSB(常数项)
const z0_LSB = z0 & 1n;
// 根据前缀选择 y
const y = PC === 0x02 ? (z0_LSB === 0n ? y0 : add(y0, x)) : z0_LSB === 1n ? y0 : add(y0, x);
return { type: 'affine', isInfinity: false, x, y };
}
throw new KitError('Invalid Point');
};
return {
field,
cs,
catalyst: 'ld',
addPoint,
mulPoint,
_addPoint,
_mulPoint,
isLegalPK,
isLegalSK,
PointToU8,
U8ToPoint,
};
}
export function EC(curve) {
switch (curve.type) {
case 'Weierstrass':
return FpWEC(curve);
case 'Montgomery':
return FpMEC(curve);
case 'Pseudo-Random':
case 'Koblitz':
return FbEC(curve);
default:
throw new KitError('unknown curve type');
}
}