@zlattice/lattice-js
Version:
Lattice blockchain TypeScript SDK with dual module support (CJS + ESM)
255 lines • 9.38 kB
JavaScript
import { bytesToHex } from '../../crypto/sm3/utils.js';
import * as utils from '@noble/curves/abstract/utils';
/* eslint-disable no-use-before-define */
import { decodeDer, decodeEnc, encodeDer, encodeEnc } from './asn1.js';
import { ONE, ZERO } from './bn.js';
import { field, sm2Curve } from './ec.js';
import { sm3 } from './sm3.js';
import { arrayToHex, arrayToUtf8, generateKeyPairHex, hexToArray, leftPad, utf8ToHex } from './utils.js';
export * from "./utils.js";
export { initRNGPool } from './rng.js';
export { calculateSharedKey } from './kx.js';
const C1C2C3 = 0;
// a empty array, just make tsc happy
export const EmptyArray = new Uint8Array();
/**
* 加密
*/
export function doEncrypt(msg, publicKey, cipherMode = 1, options) {
const msgArr = typeof msg === 'string' ? hexToArray(utf8ToHex(msg)) : Uint8Array.from(msg);
const publicKeyPoint = typeof publicKey === 'string' ? sm2Curve.ProjectivePoint.fromHex(publicKey) :
publicKey;
const keypair = generateKeyPairHex();
const k = utils.hexToNumber(keypair.privateKey);
// c1 = k * G
let c1 = keypair.publicKey;
if (c1.length > 128)
c1 = c1.substring(c1.length - 128);
const p = publicKeyPoint.multiply(k);
// (x2, y2) = k * publicKey
const x2 = hexToArray(leftPad(utils.numberToHexUnpadded(p.x), 64));
const y2 = hexToArray(leftPad(utils.numberToHexUnpadded(p.y), 64));
// c3 = hash(x2 || msg || y2)
const c3 = bytesToHex(sm3(utils.concatBytes(x2, msgArr, y2)));
xorCipherStream(x2, y2, msgArr);
const c2 = bytesToHex(msgArr);
if (options?.asn1) {
const point = sm2Curve.ProjectivePoint.fromHex(keypair.publicKey);
const encode = cipherMode === C1C2C3 ?
encodeEnc(point.x, point.y, c2, c3) :
encodeEnc(point.x, point.y, c3, c2);
return encode;
}
return cipherMode === C1C2C3 ? c1 + c2 + c3 : c1 + c3 + c2;
}
function xorCipherStream(x2, y2, msg) {
let ct = 1;
let offset = 0;
let t = EmptyArray;
const ctShift = new Uint8Array(4);
const nextT = () => {
// (1) Hai = hash(z || ct)
// (2) ct++
ctShift[0] = ct >> 24 & 0x00ff;
ctShift[1] = ct >> 16 & 0x00ff;
ctShift[2] = ct >> 8 & 0x00ff;
ctShift[3] = ct & 0x00ff;
t = sm3(utils.concatBytes(x2, y2, ctShift));
ct++;
offset = 0;
};
nextT(); // 先生成 Ha1
for (let i = 0, len = msg.length; i < len; i++) {
// t = Ha1 || Ha2 || Ha3 || Ha4
if (offset === t.length)
nextT();
// c2 = msg ^ t
msg[i] ^= t[offset++] & 0xff;
}
}
export function doDecrypt(encryptData, privateKey, cipherMode = 1, options) {
const { output = 'string', asn1 = false } = options || {};
const privateKeyInteger = utils.hexToNumber(privateKey);
let c1;
let c2;
let c3;
if (asn1) {
const { x, y, cipher, hash } = decodeEnc(encryptData);
c1 = sm2Curve.ProjectivePoint.fromAffine({ x, y });
c3 = hash;
c2 = cipher;
if (cipherMode === C1C2C3) {
[c2, c3] = [c3, c2];
}
}
else {
// c1c3c2
c1 = sm2Curve.ProjectivePoint.fromHex(`04${encryptData.substring(0, 128)}`);
c3 = encryptData.substring(128, 128 + 64);
c2 = encryptData.substring(128 + 64);
if (cipherMode === C1C2C3) {
c3 = encryptData.substring(encryptData.length - 64);
c2 = encryptData.substring(128, encryptData.length - 64);
}
}
const msg = hexToArray(c2);
const p = c1.multiply(privateKeyInteger);
const x2 = hexToArray(leftPad(utils.numberToHexUnpadded(p.x), 64));
const y2 = hexToArray(leftPad(utils.numberToHexUnpadded(p.y), 64));
xorCipherStream(x2, y2, msg);
// c3 = hash(x2 || msg || y2)
const checkC3 = arrayToHex(Array.from(sm3(utils.concatBytes(x2, msg, y2))));
if (checkC3 === c3.toLowerCase()) {
return output === 'array' ? msg : arrayToUtf8(msg);
}
return output === 'array' ? [] : '';
}
/**
* 签名
*/
export function doSignature(msg, privateKey, options = {}) {
let { pointPool, der, hash, publicKey, userId } = options;
let hashHex = typeof msg === 'string' ? utf8ToHex(msg) : arrayToHex(Array.from(msg));
if (hash) {
// sm3杂凑
publicKey = publicKey || getPublicKeyFromPrivateKey(privateKey);
hashHex = getHash(hashHex, publicKey, userId);
}
const dA = utils.hexToNumber(privateKey);
const e = utils.hexToNumber(hashHex);
// k
let k = null;
let r = null;
let s = null;
do {
do {
let point;
if (pointPool?.length) {
point = pointPool.pop();
}
else {
point = getPoint();
}
k = point.k;
// r = (e + x1) mod n
r = field.add(e, point.x1);
} while (r === ZERO || (r + k) === sm2Curve.CURVE.n);
// s = ((1 + dA)^-1 * (k - r * dA)) mod n
s = field.mul(field.inv(field.addN(dA, ONE)), field.subN(k, field.mulN(r, dA)));
} while (s === ZERO);
if (der)
return encodeDer(r, s); // asn.1 der 编码
return leftPad(utils.numberToHexUnpadded(r), 64) + leftPad(utils.numberToHexUnpadded(s), 64);
}
/**
* 验签
*/
export function doVerifySignature(msg, signHex, publicKey, options = {}) {
let hashHex;
const { hash, der, userId, } = options;
const publicKeyHex = typeof publicKey === 'string' ? publicKey : publicKey.toHex(false);
if (hash) {
// sm3杂凑
hashHex = getHash(typeof msg === 'string' ? utf8ToHex(msg) : msg, publicKeyHex, userId);
}
else {
hashHex = typeof msg === 'string' ? utf8ToHex(msg) : arrayToHex(Array.from(msg));
}
let r;
let s;
if (der) {
const decodeDerObj = decodeDer(signHex); // asn.1 der 解码
r = decodeDerObj.r;
s = decodeDerObj.s;
}
else {
r = utils.hexToNumber(signHex.substring(0, 64));
s = utils.hexToNumber(signHex.substring(64));
}
const PA = typeof publicKey === 'string' ? sm2Curve.ProjectivePoint.fromHex(publicKey) : publicKey;
const e = utils.hexToNumber(hashHex);
// t = (r + s) mod n
const t = field.add(r, s);
if (t === ZERO)
return false;
// x1y1 = s * G + t * PA
const x1y1 = sm2Curve.ProjectivePoint.BASE.multiply(s).add(PA.multiply(t));
// R = (e + x1) mod n
// const R = e.add(x1y1.getX().toBigInteger()).mod(n)
const R = field.add(e, x1y1.x);
// return r.equals(R)
return r === R;
}
export function getZ(publicKey, userId = '1234567812345678') {
// z = hash(entl || userId || a || b || gx || gy || px || py)
userId = utf8ToHex(userId);
const a = leftPad(utils.numberToHexUnpadded(sm2Curve.CURVE.a), 64);
// const b = leftPad(G.curve.b.toBigInteger().toRadix(16), 64)
const b = leftPad(utils.numberToHexUnpadded(sm2Curve.CURVE.b), 64);
// const gx = leftPad(G.getX().toBigInteger().toRadix(16), 64)
const gx = leftPad(utils.numberToHexUnpadded(sm2Curve.ProjectivePoint.BASE.x), 64);
// const gy = leftPad(G.getY().toBigInteger().toRadix(16), 64)
const gy = leftPad(utils.numberToHexUnpadded(sm2Curve.ProjectivePoint.BASE.y), 64);
let px;
let py;
if (publicKey.length === 128) {
px = publicKey.substring(0, 64);
py = publicKey.substring(64, 128);
}
else {
// const point = G.curve.decodePointHex(publicKey)!
const point = sm2Curve.ProjectivePoint.fromHex(publicKey);
// px = leftPad(point.getX().toBigInteger().toRadix(16), 64)
px = leftPad(utils.numberToHexUnpadded(point.x), 64);
// py = leftPad(point.getY().toBigInteger().toRadix(16), 64)
py = leftPad(utils.numberToHexUnpadded(point.y), 64);
}
const data = hexToArray(userId + a + b + gx + gy + px + py);
const entl = userId.length * 4;
const z = sm3(utils.concatBytes(new Uint8Array([entl >> 8 & 0x00ff, entl & 0x00ff]), data));
return z;
}
/**
* sm3杂凑算法
*/
export function getHash(hashHex, publicKey, userId = '1234567812345678') {
const z = getZ(publicKey, userId);
// e = hash(z || msg)
return bytesToHex(sm3(utils.concatBytes(z, typeof hashHex === 'string' ? hexToArray(hashHex) : hashHex)));
}
/**
* 预计算公钥点,可用于提升加密性能
* @export
* @param {string} publicKey 公钥
* @param windowSize 计算窗口大小,默认为 8
* @returns {ProjPointType<bigint>} 预计算的点
*/
export function precomputePublicKey(publicKey, windowSize) {
const point = sm2Curve.ProjectivePoint.fromHex(publicKey);
return sm2Curve.utils.precompute(windowSize, point);
}
/**
* 计算公钥
*/
export function getPublicKeyFromPrivateKey(privateKey, compressed = false) {
const pubKey = sm2Curve.getPublicKey(privateKey, compressed);
const pubPad = leftPad(utils.bytesToHex(pubKey), 64);
return pubPad;
}
/**
* 获取椭圆曲线点
*/
export function getPoint() {
const keypair = generateKeyPairHex();
const PA = sm2Curve.ProjectivePoint.fromHex(keypair.publicKey);
const k = utils.hexToNumber(keypair.privateKey);
if (!PA) {
throw new Error('Invalid public key');
}
return {
...keypair,
k,
x1: PA.x,
};
}
//# sourceMappingURL=index.js.map