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

340 lines (339 loc) 12.4 kB
import { cbc, createCipher } from '../../core/cipher'; import { BIPoint, Fp, FpEC, U8Point } from '../../core/ec'; import { KitError, U8, genBitMask, genRandomBI, getBIBits, joinBuffer, mod, modInverse } from '../../core/utils'; import { x963kdf } from '../../core/kdf'; import { aes } from '../blockCipher/aes'; import { sha256 } from '../../hash/sha256'; import { hmac } from '../../hash/hmac'; // * Functions /** * 定义 ECIES 配置 * * Define ECIES Configuration * * @param {IVBlockCipher} [config.cipher] - 分组密码算法 / Block Cipher Algorithm (default: AES-256-GCM) * @param {KeyHash} [config.mac] - 密钥哈希函数 / Key Hash Function (default: HMAC-SHA-256) * @param {KDF} [config.kdf] - 密钥派生函数 / Key Derivation Function (default: ANSI-X9.63-KDF with SHA-256) * @param {Uint8Array} [config.S1] - 附加数据1 / Additional Data 1 (default: empty) * @param {Uint8Array} [config.S2] - 附加数据2 / Additional Data 2 (default: empty) * @param {Uint8Array} [config.iv] - 初始化向量 / Initialization Vector (default: Uint8Array(cipher.BLOCK_SIZE)) */ export function defineECIES(config) { config = config ?? {}; const { cipher = cbc(aes(256)), mac = hmac(sha256), kdf = x963kdf(sha256), S1 = new Uint8Array(0), S2 = new Uint8Array(0), iv = new Uint8Array(cipher.BLOCK_SIZE), } = config; return { cipher, mac, kdf, S1, S2, iv }; } // * EC Algorithms /** * 素域椭圆曲线密码学组件 * * Prime Field Elliptic Curve Cryptography Components */ export function FpECC(curve) { const { p, a, b, G, n, h } = curve; const p_bit = getBIBits(p); const p_byte = (p_bit + 7) >> 3; const n_bit = getBIBits(n); const n_mask = genBitMask(n_bit); const { addPoint, mulPoint } = FpEC(curve); const { plus, multiply, root } = Fp(p); const isLegalPK = (p_key) => { const { Q } = p_key; // P != O if (Q.isInfinity) { return false; } // P(x, y) ∈ E const P = BIPoint(Q); const { x, y } = P; if (x < 0n || x >= p || y < 0n || y >= p) { return false; } if (curve.type === 'Weierstrass') { // y^2 = x^3 + ax + b const l = multiply(y, y); const r = plus(multiply(x, x, x), multiply(a, x), b); if (l !== r) { return false; } // nP = O const nP = mulPoint(P, n); return nP.isInfinity; } if (curve.type === 'Montgomery') { // By^2 = x^3 + Ax^2 + x const l = multiply(b, y, y); const r = plus(multiply(x, x, x), multiply(a, x, x), x); if (l !== r) { return false; } // nP = O const nP = mulPoint(P, n); return nP.isInfinity; } // unknown curve type else { return false; } }; const isLegalSK = (s_key) => { const d = typeof s_key.d === 'bigint' ? s_key.d : U8.from(s_key.d).toBI(); if (d < 0n || d >= p) { return false; } return !mulPoint(G, d).isInfinity; }; const PointToU8 = (point, compress = false) => { if (point.isInfinity) { return new U8([0x00]); } const { x, y } = U8Point(point, p_byte); const sign_y = y[y.length - 1] & 1; const PC = new U8([compress ? 0x02 | sign_y : 0x04]); const X1 = x; const Y1 = compress ? new U8() : y; return joinBuffer(PC, X1, Y1); }; const U8ToPoint = (buffer) => { const point_buffer = U8.from(buffer); const PC = point_buffer[0]; if (PC === 0x00) { if (point_buffer.length !== 1) { throw new KitError('Invalid Point'); } return U8Point(); } if (PC !== 0x02 && PC !== 0x03 && PC !== 0x04) { throw new KitError('Invalid Point'); } // 无压缩 if (PC === 0x04) { if (point_buffer.length !== (p_byte << 1) + 1) { throw new KitError('Invalid Point'); } const x = point_buffer.slice(1, p_byte + 1); const y = point_buffer.slice(p_byte + 1); return { isInfinity: false, x, y }; } // 解压缩 else { if (point_buffer.length !== p_byte + 1) { throw new KitError('Invalid Point'); } const x_buffer = point_buffer.slice(1); const x = x_buffer.toBI(); const sign_y = BigInt(PC & 1); if (curve.type === 'Weierstrass') { let y = 0n; y = plus(multiply(x, x, x), multiply(a, x), b); y = root(y); y = (y & 1n) === sign_y ? y : p - y; return U8Point({ isInfinity: false, x: x_buffer, y }, p_byte); } else if (curve.type === 'Montgomery') { let y = 0n; y = plus(multiply(x, x, x), multiply(a, x, x), x); y = root(y / b); y = (y & 1n) === sign_y ? y : p - y; return U8Point({ isInfinity: false, x: x_buffer, y }, p_byte); } else { throw new KitError('Unknown curve type'); } } }; function gen(type = 'key_pair', s_key) { if (type === 'key_pair') { // private key const { buffer, result: d } = genRandomBI(n, p_byte); // public key const _ = mulPoint(G, d); const Q = U8Point(_, p_byte); return { Q, d: buffer }; } else if (type === 'private_key') { return { d: genRandomBI(n, p_byte).buffer }; } else if (type === 'public_key') { const d_buffer = typeof s_key.d === 'bigint' ? U8.fromBI(s_key.d) : U8.from(s_key.d); const d = typeof s_key.d === 'bigint' ? s_key.d : d_buffer.toBI(); if (d === 0n) { throw new KitError('Invalid private key'); } const _ = mulPoint(G, d); const Q = U8Point(_, p_byte); return { Q, d: d_buffer }; } } // Key agreement const ecdh = (s_key, p_key) => { if (!isLegalPK(p_key)) { throw new KitError('Invalid public key'); } if (!isLegalSK(s_key)) { throw new KitError('Invalid private key'); } const Q = p_key.Q; const d = s_key.d; const S = mulPoint(Q, d); if (S.isInfinity) { throw new KitError('the result of ECDH is the point at infinity'); } return U8Point(S, p_byte); }; const eccdh = (s_key, p_key) => { if (!isLegalPK(p_key)) { throw new KitError('Invalid public key'); } if (!isLegalSK(s_key)) { throw new KitError('Invalid private key'); } const Q = p_key.Q; const d = typeof s_key.d === 'bigint' ? s_key.d : U8.from(s_key.d).toBI(); const S = mulPoint(Q, d * h); if (S.isInfinity) { throw new KitError('the result of ECCDH is the point at infinity'); } return U8Point(S, p_byte); }; const ecmqv = (u1, u2, v1, v2) => { if (!isLegalPK(v1) || !isLegalPK(v2)) { throw new KitError('Invalid public key'); } const ceilLog2n = n_bit; const L = 1n << BigInt(Math.ceil(ceilLog2n / 2)); const u1d = typeof u1.d === 'bigint' ? u1.d : U8.from(u1.d).toBI(); const u2d = typeof u2.d === 'bigint' ? u2.d : U8.from(u2.d).toBI(); const u2Qx = typeof u2.Q.x === 'bigint' ? u2.Q.x : U8.from(u2.Q.x).toBI(); const v2Qx = typeof v2.Q.x === 'bigint' ? v2.Q.x : U8.from(v2.Q.x).toBI(); const Q2u = mod(u2Qx, L) + L; const Q2v = mod(v2Qx, L) + L; const s = mod(u2d + Q2u * u1d, n); const P = mulPoint(addPoint(v2.Q, mulPoint(v1.Q, Q2v)), s * h); if (P.isInfinity) { throw new KitError('Public key not available'); } return U8Point(P, p_byte); }; // Digital signature const ecdsa = (hash = sha256) => { const sign = (s_key, M) => { const d = typeof s_key.d === 'bigint' ? s_key.d : U8.from(s_key.d).toBI(); let r = 0n; let s = 0n; let z = hash(M).toBI(); while (z > n_mask) { z = z >> 1n; } do { const K = gen(); const k = K.d.toBI(); const x1 = K.Q.x.toBI(); r = mod(x1, n); if (r === 0n) continue; s = modInverse(k, n) * mod(z + r * d, n); s = mod(s, n); } while (s === 0n); const r_buffer = U8.fromBI(r); const s_buffer = U8.fromBI(s); return { r: r_buffer, s: s_buffer }; }; const verify = (p_key, M, signature) => { const { Q } = p_key; const r = typeof signature.r === 'bigint' ? signature.r : U8.from(signature.r).toBI(); const s = typeof signature.s === 'bigint' ? signature.s : U8.from(signature.s).toBI(); if (r <= 0n || r >= n || s <= 0n || s >= n) { return false; } let z = hash(M).toBI(); while (z > n_mask) { z = z >> 1n; } const w = modInverse(s, n); const u1 = mod(z * w, n); const u2 = mod(r * w, n); const P = addPoint(mulPoint(G, u1), mulPoint(Q, u2)); const v = mod(P.x, n); return v === r; }; return { sign, verify }; }; // Integrated encryption scheme const ecies = (config) => { const { cipher, mac, kdf, S1, S2, iv } = defineECIES(config); const encrypt = (p_key, M) => { if (!isLegalPK(p_key)) { throw new KitError('Invalid public key'); } let s_key; let deriveShare; do { s_key = gen(); deriveShare = ecdh(s_key, p_key); } while (deriveShare.isInfinity); const Z = deriveShare.x; const K = kdf((cipher.KEY_SIZE + mac.KEY_SIZE) << 3, joinBuffer(Z, S1)); const KE = K.slice(0, cipher.KEY_SIZE); const KM = K.slice(cipher.KEY_SIZE, cipher.KEY_SIZE + mac.KEY_SIZE); const _cipher = cipher(KE, iv); const R = { Q: s_key.Q }; const C = _cipher.encrypt(M); const D = mac(KM, joinBuffer(C, S2)); return { R, C, D }; }; const decrypt = (s_key, CT) => { const { R, C, D } = CT; // 密钥派生 const deriveShare = ecdh(s_key, R); if (deriveShare.isInfinity) { throw new KitError('ECIES Decryption failed'); } const Z = deriveShare.x; const K = kdf((cipher.KEY_SIZE + mac.KEY_SIZE) << 3, joinBuffer(Z, S1)); const KE = K.slice(0, cipher.KEY_SIZE); const KM = K.slice(cipher.KEY_SIZE, cipher.KEY_SIZE + mac.KEY_SIZE); const _cipher = cipher(KE, iv); // 校验 if (mac(KM, joinBuffer(C, S2)).some((v, i) => v !== D[i])) { throw new KitError('ECIES Decryption failed'); } const M = _cipher.decrypt(C); // 解密 return new U8(M); }; return { encrypt, decrypt }; }; return { utils: { addPoint, mulPoint, isLegalPK, isLegalSK, PointToU8, U8ToPoint, }, gen, dh: ecdh, cdh: eccdh, mqv: ecmqv, dsa: ecdsa, ies: ecies, }; } // * Algorithms for Test /** * ! 此加密算法仅用于测试 ECIES * ! This encryption algorithm is only used for testing ECIES */ export const es_xor = createCipher((K) => { const encrypt = (M) => new U8(M.map((v, i) => v ^ K[i])); const decrypt = (C) => new U8(C.map((v, i) => v ^ K[i])); return { encrypt, decrypt }; }, { ALGORITHM: 'ES-XOR', BLOCK_SIZE: 20, KEY_SIZE: 20, MIN_KEY_SIZE: 20, MAX_KEY_SIZE: 20, });