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 实现的密码学套件。目标是提供一个简单易用的密码学库。
233 lines (232 loc) • 8.09 kB
JavaScript
import { aes } from '../../cipher/blockCipher/aes';
import { cbc, createCipher } from '../../core/cipher';
import { EC } from '../../core/ec';
import { x963kdf } from '../../core/kdf';
import { genBitMask, genRandomBI, getBIBits, joinBuffer, KitError, mod, modInverse, U8 } from '../../core/utils';
import { hmac } from '../../hash/hmac';
import { sha256 } from '../../hash/sha256';
// * Functions
/**
* 定义 ECIES 配置
*
* Define ECIES Configuration
*/
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 };
}
export function ECC(curve) {
let ec;
switch (curve.type) {
case 'Weierstrass':
case 'Montgomery':
ec = EC(curve);
break;
case 'Pseudo-Random':
case 'Koblitz':
ec = EC(curve);
break;
default:
throw new KitError('unsupported curve type');
}
let toCatalyst;
switch (ec.catalyst) {
case 'jacobian':
toCatalyst = ec.cs.toJacobian;
break;
case 'ld':
toCatalyst = ec.cs.toLD;
break;
default:
throw new KitError('unsupported catalyst type');
}
const { G, n, h } = curve;
/** 优化基点 */
const CG = toCatalyst(G);
const n_bit = getBIBits(n);
const n_mask = genBitMask(n_bit);
const p = 'p' in curve ? curve.p : undefined;
const p_bit = p ? getBIBits(p) : undefined;
const p_byte = p ? (p_bit + 7) >> 3 : undefined;
const m = 'm' in curve ? curve.m : undefined;
const m_bit = m ? getBIBits(m) : undefined;
const m_byte = m ? (m_bit + 7) >> 3 : undefined;
const ele_byte = p_byte ?? m_byte;
const { addPoint, mulPoint, isLegalSK, isLegalPK } = ec;
const toAffine = ec.cs.toAffine;
function gen(type = 'key_pair', s_key) {
if (type === 'key_pair') {
// private key
const { result: d } = genRandomBI(n, ele_byte);
// public key
const _ = mulPoint(CG, d);
const Q = toAffine(_);
return { Q, d };
}
else if (type === 'private_key') {
const { result: d } = genRandomBI(n, ele_byte);
return { d };
}
else if (type === 'public_key') {
const d = s_key.d;
if (d === 0n)
throw new KitError('Invalid private key');
const _ = mulPoint(CG, d);
const Q = toAffine(_);
return { Q, d };
}
throw new KitError('Invalid type');
}
const dh = (s_key, p_key) => {
if (!isLegalPK(p_key.Q))
throw new KitError('Invalid public key');
if (!isLegalSK(s_key.d))
throw new KitError('Invalid private key');
const Q = toCatalyst(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 toAffine(S);
};
const cdh = (s_key, p_key) => {
if (!isLegalPK(p_key.Q))
throw new KitError('Invalid public key');
if (!isLegalSK(s_key.d))
throw new KitError('Invalid private key');
const Q = toCatalyst(p_key.Q);
const d = s_key.d;
const S = mulPoint(Q, d * h);
if (S.isInfinity)
throw new KitError('the result of ECDH is the point at infinity');
return toAffine(S);
};
const mqv = (u1, u2, v1, v2) => {
if (!isLegalPK(v1.Q) || !isLegalPK(v2.Q))
throw new KitError('Invalid public key');
const ceilLog2n = n_bit;
const L = 1n << BigInt(Math.ceil(ceilLog2n / 2));
const u1d = u1.d;
const u2d = u2.d;
const u2Qx = u2.Q.x;
const v2Qx = v2.Q.x;
const Q2u = mod(u2Qx, L) + L;
const Q2v = mod(v2Qx, L) + L;
const s = mod(u2d + Q2u * u1d, n);
const v2Q = toCatalyst(v2.Q);
const v1Q = toCatalyst(v1.Q);
const P = mulPoint(addPoint(v2Q, mulPoint(v1Q, Q2v)), s * h);
if (P.isInfinity)
throw new KitError('Public key not available');
return toAffine(P);
};
const dsa = (hash = sha256) => {
const sign = (s_key, M) => {
const d = s_key.d;
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;
const x1 = K.Q.x;
r = mod(x1, n);
if (r === 0n)
continue;
s = modInverse(k, n) * mod(z + r * d, n);
s = mod(s, n);
} while (s === 0n);
return { r, s };
};
const verify = (p_key, M, signature) => {
const Q = toCatalyst(p_key.Q);
const r = signature.r;
const s = signature.s;
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_j = addPoint(mulPoint(CG, u1), mulPoint(Q, u2));
const P = toAffine(P_j);
const v = mod(P.x, n);
return v === r;
};
return { sign, verify };
};
const ies = (config) => {
const { cipher, mac, kdf, S1, S2, iv } = defineECIES(config);
const encrypt = (p_key, M) => {
if (!isLegalPK(p_key.Q))
throw new KitError('Invalid public key');
let s_key;
let deriveShare;
do {
s_key = gen();
deriveShare = dh(s_key, p_key);
} while (deriveShare.isInfinity);
const Z = U8.fromBI(deriveShare.x);
const K = kdf(cipher.KEY_SIZE + mac.KEY_SIZE, 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 = dh(s_key, R);
if (deriveShare.isInfinity)
throw new KitError('ECIES Decryption failed');
const Z = U8.fromBI(deriveShare.x);
const K = kdf(cipher.KEY_SIZE + mac.KEY_SIZE, 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 {
parameters: curve,
utils: ec,
gen,
dh,
cdh,
mqv,
dsa,
ies,
};
}
// * 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,
});