diff --git a/benchmark/index.js b/benchmark/index.js index f9f544c..9912e18 100644 --- a/benchmark/index.js +++ b/benchmark/index.js @@ -1,6 +1,6 @@ import { run, mark, utils } from 'micro-bmark'; import * as sm from 'sm-crypto' -import * as smV2 from '../dist/index.js' +import * as smV2 from 'sm-crypto-v2' const msg = 'Hello world~!' const longMsg = msg.repeat(10000) diff --git a/src/sm2/globalThis.ts b/src/sm2/globalThis.ts new file mode 100644 index 0000000..be5a199 --- /dev/null +++ b/src/sm2/globalThis.ts @@ -0,0 +1,21 @@ + +(function (Object) { + typeof globalThis !== 'object' && ( + // @ts-ignore + this ? + get() : + (Object.defineProperty(Object.prototype, '_T_', { + configurable: true, + get: get + // @ts-ignore + }), _T_) + ); + function get() { + // @ts-ignore + var global = this || self; + global.globalThis = global; + // @ts-ignore + delete Object.prototype._T_; + } +}(Object)); +export default globalThis as any; \ No newline at end of file diff --git a/src/sm2/index.ts b/src/sm2/index.ts index 9a5f87a..3ae0453 100644 --- a/src/sm2/index.ts +++ b/src/sm2/index.ts @@ -19,14 +19,39 @@ export const EmptyArray = new Uint8Array() /** * 加密 */ +class LRUMap extends Map { + length: number + constructor(length: number) { + super(); + this.length = length; + } + set(key: K, value: V) { + super.delete(this.size < this.length ? key : super.keys().next().value); + return super.set(key, value); + } +} +// 保存最后 3 个加密的公钥,用来保证在这个情况下,性能不退化的太厉害 +const precomputedPublicKey = new LRUMap>(3) export function doEncrypt(msg: string | Uint8Array, publicKey: string | ProjPointType, cipherMode = 1, options?: { asn1?: boolean // 使用 ASN.1 对 C1 编码 }) { - const msgArr = typeof msg === 'string' ? hexToArray(utf8ToHex(msg)) : Uint8Array.from(msg) - const publicKeyPoint = typeof publicKey === 'string' ? sm2Curve.ProjectivePoint.fromHex(publicKey) : - publicKey - + let publicKeyPoint: ProjPointType + if (typeof publicKey === 'string') { + const cached = precomputedPublicKey.get(publicKey) + if (cached) { + publicKeyPoint = cached + } else { + const point = sm2Curve.ProjectivePoint.fromHex(publicKey) + sm2Curve.utils.precompute(undefined, point) + precomputedPublicKey.set(publicKey, point) + publicKeyPoint = point + } + } else { + publicKeyPoint = publicKey + } + // publicKeyPoint = typeof publicKey === 'string' ? sm2Curve.ProjectivePoint.fromHex(publicKey) : + // publicKey const keypair = generateKeyPairHex() const k = utils.hexToNumber(keypair.privateKey) @@ -222,7 +247,22 @@ export function doVerifySignature(msg: string | Uint8Array, signHex: string, pub s = utils.hexToNumber(signHex.substring(64)) } - const PA = typeof publicKey === 'string' ? sm2Curve.ProjectivePoint.fromHex(publicKey) : publicKey + let PA: ProjPointType + + if (typeof publicKey === 'string') { + const cached = precomputedPublicKey.get(publicKey) + if (cached) { + PA = cached + } else { + const point = sm2Curve.ProjectivePoint.fromHex(publicKey) + sm2Curve.utils.precompute(undefined, point) + precomputedPublicKey.set(publicKey, point) + PA = point + } + } else { + PA = publicKey + } + const e = utils.hexToNumber(hashHex) // t = (r + s) mod n diff --git a/src/sm2/rng.ts b/src/sm2/rng.ts index 6ccc06b..68d18cd 100644 --- a/src/sm2/rng.ts +++ b/src/sm2/rng.ts @@ -5,6 +5,8 @@ // Web: globalThis.crypto // Node: async import("crypto").webcrypto // Mini Program: wx.getRandomValues +import globalThis from './globalThis'; + declare module wx { function getRandomValues(options: { length: number; diff --git a/src/sm2/sm3.ts b/src/sm2/sm3.ts index 3154a4e..c5ff4c9 100644 --- a/src/sm2/sm3.ts +++ b/src/sm2/sm3.ts @@ -1,16 +1,17 @@ // import assert from './_assert.js'; +import JSBI from 'jsbi'; import { Hash, createView, Input, toBytes, wrapConstructor } from '../sm3/utils.js'; const BoolA = (A: number, B: number, C: number) => ((A & B) | (A & C)) | (B & C) const BoolB = (A: number, B: number, C: number) => ((A ^ B) ^ C) const BoolC = (A: number, B: number, C: number) => (A & B) | ((~A) & C) // Polyfill for Safari 14 -function setBigUint64(view: DataView, byteOffset: number, value: bigint, isLE: boolean): void { - if (typeof view.setBigUint64 === 'function') return view.setBigUint64(byteOffset, value, isLE); - const _32n = BigInt(32); - const _u32_max = BigInt(0xffffffff); - const wh = Number((value >> _32n) & _u32_max); - const wl = Number(value & _u32_max); +function setBigUint64(view: DataView, byteOffset: number, value: JSBI, isLE: boolean): void { + // if (typeof view.setBigUint64 === 'function') return view.setBigUint64(byteOffset, value, isLE); + const _32n = JSBI.BigInt(32); + const _u32_max = JSBI.BigInt(0xffffffff); + const wh = Number((JSBI.bitwiseAnd(JSBI.signedRightShift(value, _32n), _u32_max))); + const wl = Number(JSBI.bitwiseAnd(value, _u32_max)); const h = isLE ? 4 : 0; const l = isLE ? 0 : 4; view.setUint32(byteOffset + h, wh, isLE); @@ -120,7 +121,7 @@ export abstract class SHA2> extends Hash { // Note: sha512 requires length to be 128bit integer, but length in JS will overflow before that // You need to write around 2 exabytes (u64_max / 8 / (1024**6)) for this to happen. // So we just write lowest 64 bits of that value. - setBigUint64(view, blockLen - 8, BigInt(this.length * 8), isLE); + setBigUint64(view, blockLen - 8, JSBI.BigInt(this.length * 8), isLE); this.process(view, 0); const oview = createView(out); const len = this.outputLen;