sm-crypto-wasm 0.1.0 → 0.1.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +154 -418
- package/dist/esbuild-plugin.d.ts +70 -0
- package/dist/esbuild-plugin.js +85 -0
- package/dist/runtime/loadWasm.browser.d.ts +39 -0
- package/dist/runtime/loadWasm.browser.js +78 -0
- package/dist/runtime/loadWasm.core.d.ts +45 -0
- package/dist/runtime/loadWasm.core.js +70 -0
- package/dist/runtime/loadWasm.d.ts +16 -27
- package/dist/runtime/loadWasm.js +28 -95
- package/dist/runtime/random.browser.d.ts +25 -0
- package/dist/runtime/random.browser.js +50 -0
- package/dist/runtime/random.core.d.ts +20 -0
- package/dist/runtime/random.core.js +35 -0
- package/dist/runtime/random.d.ts +16 -4
- package/dist/runtime/random.js +33 -28
- package/dist/sm2/index.js +39 -25
- package/dist/sm4/index.js +2 -2
- package/dist/sm9/index.js +8 -4
- package/dist/sm9/sm9.wasm +0 -0
- package/package.json +9 -1
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 宿主随机数包装(**浏览器版**)。
|
|
3
|
+
*
|
|
4
|
+
* 打包时由 package.json 的 `browser` 字段替换 `random.js`。存在的唯一理由是:
|
|
5
|
+
* `node:crypto` 会让 Webpack 5 / esbuild / Rspack 在解析期构建失败。
|
|
6
|
+
* 与宿主无关的拒绝采样逻辑在 `random.core.ts`。本文件**零 `node:*` 引用**。
|
|
7
|
+
*
|
|
8
|
+
* 安全姿态与 Node 版一致:**只认 `globalThis.crypto`,取不到就抛错
|
|
9
|
+
* (fail-closed),绝不降级到非密码学随机源。**
|
|
10
|
+
*/
|
|
11
|
+
import { scalarBelowFrom } from './random.core.js';
|
|
12
|
+
/**
|
|
13
|
+
* 解析宿主 CSPRNG。浏览器里只有 `globalThis.crypto` 一个来源,
|
|
14
|
+
* 拿不到就抛错 —— 宁可不可用,也不用弱随机。
|
|
15
|
+
*/
|
|
16
|
+
function resolveHostCrypto() {
|
|
17
|
+
const g = globalThis.crypto;
|
|
18
|
+
if (g && typeof g.getRandomValues === 'function')
|
|
19
|
+
return g;
|
|
20
|
+
throw new Error('sm-crypto-wasm: 当前环境没有 WebCrypto(crypto.getRandomValues)。' +
|
|
21
|
+
'浏览器需要安全上下文(https 或 localhost);若已被移除或未提供,' +
|
|
22
|
+
'请勿继续 —— 本库不会用非密码学随机源代替。');
|
|
23
|
+
}
|
|
24
|
+
/** 宿主 CSPRNG 引用;模块加载时即解析。 */
|
|
25
|
+
const c = resolveHostCrypto();
|
|
26
|
+
/**
|
|
27
|
+
* 取 `n` 字节 CSPRNG 随机数。
|
|
28
|
+
*
|
|
29
|
+
* @param n 字节数,范围 `[0, 65536]`(WebCrypto `getRandomValues` 的规范上限)。
|
|
30
|
+
* @returns 新分配的字节数组(拷贝)。
|
|
31
|
+
* @throws RangeError n 不在范围内或不是整数。
|
|
32
|
+
*/
|
|
33
|
+
export function randomBytes(n) {
|
|
34
|
+
if (!Number.isInteger(n) || n < 0 || n > 65536) {
|
|
35
|
+
throw new RangeError(`randomBytes: length must be integer in [0, 65536], got ${n}`);
|
|
36
|
+
}
|
|
37
|
+
const out = new Uint8Array(n);
|
|
38
|
+
if (n > 0)
|
|
39
|
+
c.getRandomValues(out);
|
|
40
|
+
return out;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* 在 `[1, order)` 区间均匀取一个随机标量(用于 SM2 / SM9 椭圆曲线 k)。
|
|
44
|
+
*
|
|
45
|
+
* @param order 椭圆曲线子群阶(如 SM2 的 n、SM9 的 BN256 n)。
|
|
46
|
+
* @returns 满足 `1 ≤ k < order` 的大整数。
|
|
47
|
+
*/
|
|
48
|
+
export function randomScalarBelow(order) {
|
|
49
|
+
return scalarBelowFrom(order, randomBytes);
|
|
50
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 随机数共享核心(Node 版与浏览器版共用)。
|
|
3
|
+
*
|
|
4
|
+
* **零 `node:*` 引用** —— 浏览器版的模块图里不能出现 node 内置模块
|
|
5
|
+
* (Webpack / esbuild / Rspack 解析期即失败)。宿主 CSPRNG 的获取方式由
|
|
6
|
+
* `random.ts`(Node)与 `random.browser.ts`(浏览器)各自实现。
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* 在 `[1, order)` 区间均匀取一个随机标量(用于 SM2 / SM9 椭圆曲线 k)。
|
|
10
|
+
*
|
|
11
|
+
* 算法:256-bit 候选 + 拒绝采样。从宿主 CSPRNG 取 32 字节拼成大整数,
|
|
12
|
+
* 若为 0 或 ≥ `order` 则重抽。SM2 的 n 几乎等于 2²⁵⁶(一次命中),
|
|
13
|
+
* SM9 的 n ≈ 0.712 × 2²⁵⁶(平均约 1.4 次)。偏置上界 ≈ `order / 2²⁵⁶`,
|
|
14
|
+
* 量级在 2⁻¹²⁸ 以下。
|
|
15
|
+
*
|
|
16
|
+
* @param order 椭圆曲线子群阶(如 SM2 的 n、SM9 的 BN256 n)。
|
|
17
|
+
* @param randomBytes 宿主 CSPRNG 取字节函数(由调用方注入,保证两侧实现一致)。
|
|
18
|
+
* @returns 满足 `1 ≤ k < order` 的大整数。
|
|
19
|
+
*/
|
|
20
|
+
export declare function scalarBelowFrom(order: bigint, randomBytes: (n: number) => Uint8Array): bigint;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 随机数共享核心(Node 版与浏览器版共用)。
|
|
3
|
+
*
|
|
4
|
+
* **零 `node:*` 引用** —— 浏览器版的模块图里不能出现 node 内置模块
|
|
5
|
+
* (Webpack / esbuild / Rspack 解析期即失败)。宿主 CSPRNG 的获取方式由
|
|
6
|
+
* `random.ts`(Node)与 `random.browser.ts`(浏览器)各自实现。
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* 在 `[1, order)` 区间均匀取一个随机标量(用于 SM2 / SM9 椭圆曲线 k)。
|
|
10
|
+
*
|
|
11
|
+
* 算法:256-bit 候选 + 拒绝采样。从宿主 CSPRNG 取 32 字节拼成大整数,
|
|
12
|
+
* 若为 0 或 ≥ `order` 则重抽。SM2 的 n 几乎等于 2²⁵⁶(一次命中),
|
|
13
|
+
* SM9 的 n ≈ 0.712 × 2²⁵⁶(平均约 1.4 次)。偏置上界 ≈ `order / 2²⁵⁶`,
|
|
14
|
+
* 量级在 2⁻¹²⁸ 以下。
|
|
15
|
+
*
|
|
16
|
+
* @param order 椭圆曲线子群阶(如 SM2 的 n、SM9 的 BN256 n)。
|
|
17
|
+
* @param randomBytes 宿主 CSPRNG 取字节函数(由调用方注入,保证两侧实现一致)。
|
|
18
|
+
* @returns 满足 `1 ≤ k < order` 的大整数。
|
|
19
|
+
*/
|
|
20
|
+
export function scalarBelowFrom(order, randomBytes) {
|
|
21
|
+
if (order <= 1n)
|
|
22
|
+
throw new RangeError('randomScalarBelow: order must be > 1');
|
|
23
|
+
// 用 256-bit 候选:4 个 64-bit big-endian limb。
|
|
24
|
+
while (true) {
|
|
25
|
+
const buf = randomBytes(32); // 256 bits
|
|
26
|
+
let candidate = 0n;
|
|
27
|
+
for (let i = 0; i < 32; i++) {
|
|
28
|
+
candidate = (candidate << 8n) | BigInt(buf[i] ?? 0);
|
|
29
|
+
}
|
|
30
|
+
// 严格 < order(不允许 0)。
|
|
31
|
+
if (candidate > 0n && candidate < order)
|
|
32
|
+
return candidate;
|
|
33
|
+
// 否则重抽;理论上不到一次。
|
|
34
|
+
}
|
|
35
|
+
}
|
package/dist/runtime/random.d.ts
CHANGED
|
@@ -1,3 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 宿主随机数包装(**Node 版**)。
|
|
3
|
+
*
|
|
4
|
+
* 浏览器打包时本文件被 `random.browser.ts` 替换(package.json 的 `browser` 字段),
|
|
5
|
+
* 因为 `node:crypto` 会让 Webpack / esbuild / Rspack 在解析期构建失败。
|
|
6
|
+
* 与宿主无关的拒绝采样逻辑在 `random.core.ts`。
|
|
7
|
+
*
|
|
8
|
+
* 设计要点(spec §9 安全姿态):
|
|
9
|
+
* - **永远走宿主 CSPRNG**:Node 19+ 用 `globalThis.crypto`,Node 18 回退
|
|
10
|
+
* `node:crypto` 的 `webcrypto`(`globalThis.crypto` 直到 Node 19 才默认暴露)。
|
|
11
|
+
* - **取不到就抛错(fail-closed)**:绝不实例化本地 RNG,也绝不静默降级到
|
|
12
|
+
* 非密码学随机源。
|
|
13
|
+
* - **长度边界**:输入 `n ∈ [0, 65536]`,溢出抛 `RangeError`
|
|
14
|
+
* (65536 是 WebCrypto `getRandomValues` 的规范上限)。
|
|
15
|
+
*/
|
|
1
16
|
/**
|
|
2
17
|
* 取 `n` 字节 CSPRNG 随机数。
|
|
3
18
|
*
|
|
@@ -7,10 +22,7 @@
|
|
|
7
22
|
*/
|
|
8
23
|
export declare function randomBytes(n: number): Uint8Array;
|
|
9
24
|
/**
|
|
10
|
-
* 在 `[1, order)` 区间均匀取一个随机标量(用于 SM2/SM9 椭圆曲线 k)。
|
|
11
|
-
*
|
|
12
|
-
* 算法:64-bit limb rejection sampling。从 `getRandomValues` 取 4 个 64-bit
|
|
13
|
-
* limb 拼接成 256-bit 候选值;若 ≥ `order` 则重抽。偏置 ≤ 2^-128,足够低。
|
|
25
|
+
* 在 `[1, order)` 区间均匀取一个随机标量(用于 SM2 / SM9 椭圆曲线 k)。
|
|
14
26
|
*
|
|
15
27
|
* @param order 椭圆曲线子群阶(如 SM2 的 n、SM9 的 BN256 n)。
|
|
16
28
|
* @returns 满足 `1 ≤ k < order` 的大整数。
|
package/dist/runtime/random.js
CHANGED
|
@@ -1,18 +1,39 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* 宿主随机数包装(**Node 版**)。
|
|
3
|
+
*
|
|
4
|
+
* 浏览器打包时本文件被 `random.browser.ts` 替换(package.json 的 `browser` 字段),
|
|
5
|
+
* 因为 `node:crypto` 会让 Webpack / esbuild / Rspack 在解析期构建失败。
|
|
6
|
+
* 与宿主无关的拒绝采样逻辑在 `random.core.ts`。
|
|
3
7
|
*
|
|
4
8
|
* 设计要点(spec §9 安全姿态):
|
|
5
|
-
* - **永远走宿主 CSPRNG
|
|
6
|
-
*
|
|
7
|
-
* -
|
|
8
|
-
*
|
|
9
|
-
* - **长度边界**:输入 `n ∈ [0, 65536]`,溢出抛 `RangeError
|
|
9
|
+
* - **永远走宿主 CSPRNG**:Node 19+ 用 `globalThis.crypto`,Node 18 回退
|
|
10
|
+
* `node:crypto` 的 `webcrypto`(`globalThis.crypto` 直到 Node 19 才默认暴露)。
|
|
11
|
+
* - **取不到就抛错(fail-closed)**:绝不实例化本地 RNG,也绝不静默降级到
|
|
12
|
+
* 非密码学随机源。
|
|
13
|
+
* - **长度边界**:输入 `n ∈ [0, 65536]`,溢出抛 `RangeError`
|
|
14
|
+
* (65536 是 WebCrypto `getRandomValues` 的规范上限)。
|
|
15
|
+
*/
|
|
16
|
+
import * as nodeCrypto from 'node:crypto';
|
|
17
|
+
import { scalarBelowFrom } from './random.core.js';
|
|
18
|
+
/**
|
|
19
|
+
* 解析宿主 CSPRNG。**取不到就抛错,绝不静默降级到非密码学随机源。**
|
|
10
20
|
*
|
|
11
|
-
*
|
|
21
|
+
* - Node 19+:`globalThis.crypto`(WebCrypto);
|
|
22
|
+
* - Node 18:`node:crypto` 的 `webcrypto` —— `globalThis.crypto` 直到 Node 19 才
|
|
23
|
+
* 默认暴露,Node 18 需要 `--experimental-global-webcrypto`,所以回退是必要的。
|
|
12
24
|
*/
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
25
|
+
function resolveHostCrypto() {
|
|
26
|
+
const g = globalThis.crypto;
|
|
27
|
+
if (g && typeof g.getRandomValues === 'function')
|
|
28
|
+
return g;
|
|
29
|
+
const wc = nodeCrypto.webcrypto;
|
|
30
|
+
if (wc && typeof wc.getRandomValues === 'function')
|
|
31
|
+
return wc;
|
|
32
|
+
throw new Error('sm-crypto-wasm: 找不到宿主 CSPRNG(需要 WebCrypto 的 crypto.getRandomValues);' +
|
|
33
|
+
'Node 18 请加 --experimental-global-webcrypto,或先 polyfill globalThis.crypto。');
|
|
34
|
+
}
|
|
35
|
+
/** 宿主 CSPRNG 引用;模块加载时即解析。 */
|
|
36
|
+
const c = resolveHostCrypto();
|
|
16
37
|
/**
|
|
17
38
|
* 取 `n` 字节 CSPRNG 随机数。
|
|
18
39
|
*
|
|
@@ -30,27 +51,11 @@ export function randomBytes(n) {
|
|
|
30
51
|
return out;
|
|
31
52
|
}
|
|
32
53
|
/**
|
|
33
|
-
* 在 `[1, order)` 区间均匀取一个随机标量(用于 SM2/SM9 椭圆曲线 k)。
|
|
34
|
-
*
|
|
35
|
-
* 算法:64-bit limb rejection sampling。从 `getRandomValues` 取 4 个 64-bit
|
|
36
|
-
* limb 拼接成 256-bit 候选值;若 ≥ `order` 则重抽。偏置 ≤ 2^-128,足够低。
|
|
54
|
+
* 在 `[1, order)` 区间均匀取一个随机标量(用于 SM2 / SM9 椭圆曲线 k)。
|
|
37
55
|
*
|
|
38
56
|
* @param order 椭圆曲线子群阶(如 SM2 的 n、SM9 的 BN256 n)。
|
|
39
57
|
* @returns 满足 `1 ≤ k < order` 的大整数。
|
|
40
58
|
*/
|
|
41
59
|
export function randomScalarBelow(order) {
|
|
42
|
-
|
|
43
|
-
throw new RangeError('randomScalarBelow: order must be > 1');
|
|
44
|
-
// 用 256-bit 候选:4 个 64-bit big-endian limb。
|
|
45
|
-
while (true) {
|
|
46
|
-
const buf = randomBytes(32); // 256 bits
|
|
47
|
-
let candidate = 0n;
|
|
48
|
-
for (let i = 0; i < 32; i++) {
|
|
49
|
-
candidate = (candidate << 8n) | BigInt(buf[i] ?? 0);
|
|
50
|
-
}
|
|
51
|
-
// 严格 < order(不允许 0)。
|
|
52
|
-
if (candidate > 0n && candidate < order)
|
|
53
|
-
return candidate;
|
|
54
|
-
// 否则重抽;理论上不到一次。
|
|
55
|
-
}
|
|
60
|
+
return scalarBelowFrom(order, randomBytes);
|
|
56
61
|
}
|
package/dist/sm2/index.js
CHANGED
|
@@ -65,6 +65,24 @@ function getModule() {
|
|
|
65
65
|
throw new Error('sm2: 未初始化,请先 `await ensureLoaded()`。');
|
|
66
66
|
return mod;
|
|
67
67
|
}
|
|
68
|
+
/**
|
|
69
|
+
* 现取 WASM memory 视图。
|
|
70
|
+
*
|
|
71
|
+
* **每次都用**,绝不在多次 `alloc` / wasm 调用之间缓存 `new Uint8Array(memory.buffer)`:
|
|
72
|
+
* 任何一次分配都可能让 WASM 堆增长(`memory.grow`),旧的 ArrayBuffer 会被 detach,
|
|
73
|
+
* 复用旧视图会抛 `TypeError: Cannot perform Construct on a detached ArrayBuffer`。
|
|
74
|
+
*/
|
|
75
|
+
function memView() {
|
|
76
|
+
return new Uint8Array(getModule().memory.buffer);
|
|
77
|
+
}
|
|
78
|
+
/** 写入字节到 WASM 内存(现取视图)。 */
|
|
79
|
+
function putBytes(b, ptr) {
|
|
80
|
+
memView().set(b, ptr);
|
|
81
|
+
}
|
|
82
|
+
/** 读出 WASM 内存的拷贝(现取视图后 slice,不向上层交出会失效的视图)。 */
|
|
83
|
+
function readBytes(ptr, len) {
|
|
84
|
+
return new Uint8Array(getModule().memory.buffer, ptr, len).slice();
|
|
85
|
+
}
|
|
68
86
|
/**
|
|
69
87
|
* 从私钥派生公钥。
|
|
70
88
|
*
|
|
@@ -80,13 +98,13 @@ export function getPublicKey(privateKey) {
|
|
|
80
98
|
* `getPublicKey` 与缓存未命中时的 `derivePublicKeyBytes` 都走这里。
|
|
81
99
|
*/
|
|
82
100
|
function derivePublicKeyRaw(d) {
|
|
83
|
-
const { exports
|
|
101
|
+
const { exports } = getModule();
|
|
84
102
|
const dp = exports.alloc(32);
|
|
85
103
|
const op = exports.alloc(64);
|
|
86
104
|
try {
|
|
87
|
-
|
|
105
|
+
putBytes(d, dp);
|
|
88
106
|
exports.publicKeyOut(dp, op);
|
|
89
|
-
return
|
|
107
|
+
return readBytes(op, 64);
|
|
90
108
|
}
|
|
91
109
|
finally {
|
|
92
110
|
exports.freeAll();
|
|
@@ -209,7 +227,6 @@ export function sign(privateKey, message) {
|
|
|
209
227
|
const m = toBytes(message, 'message');
|
|
210
228
|
const pub = derivePublicKeyBytes(d);
|
|
211
229
|
const { exports } = getModule();
|
|
212
|
-
const mem = new Uint8Array(getModule().memory.buffer);
|
|
213
230
|
const dp = exports.alloc(32);
|
|
214
231
|
const pxp = exports.alloc(32);
|
|
215
232
|
const pyp = exports.alloc(32);
|
|
@@ -217,17 +234,17 @@ export function sign(privateKey, message) {
|
|
|
217
234
|
const kp = exports.alloc(32);
|
|
218
235
|
const op = exports.alloc(64);
|
|
219
236
|
try {
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
237
|
+
putBytes(d, dp);
|
|
238
|
+
putBytes(pub.subarray(0, 32), pxp);
|
|
239
|
+
putBytes(pub.subarray(32, 64), pyp);
|
|
240
|
+
putBytes(m, mp);
|
|
224
241
|
// 最多重试几次 k(极小概率 r/s 为 0 或 r+k==n)。
|
|
225
242
|
for (let attempt = 0; attempt < 16; attempt++) {
|
|
226
243
|
const k = randomK();
|
|
227
|
-
|
|
244
|
+
putBytes(k, kp);
|
|
228
245
|
const rc = exports.signOut(dp, pxp, pyp, mp, m.length, kp, op);
|
|
229
246
|
if (rc === 0) {
|
|
230
|
-
return fromBytes(
|
|
247
|
+
return fromBytes(readBytes(op, 64), 'hex');
|
|
231
248
|
}
|
|
232
249
|
}
|
|
233
250
|
throw new Error('sm2.sign: failed to produce a valid signature after retries');
|
|
@@ -251,16 +268,15 @@ export function verify(publicKey, message, signature) {
|
|
|
251
268
|
if (sig.length !== 64)
|
|
252
269
|
throw new InvalidCiphertextError('sm2: signature must be 64 bytes');
|
|
253
270
|
const { exports } = getModule();
|
|
254
|
-
const mem = new Uint8Array(getModule().memory.buffer);
|
|
255
271
|
const pxp = exports.alloc(32);
|
|
256
272
|
const pyp = exports.alloc(32);
|
|
257
273
|
const mp = exports.alloc(m.length);
|
|
258
274
|
const sigp = exports.alloc(64);
|
|
259
275
|
try {
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
276
|
+
putBytes(x, pxp);
|
|
277
|
+
putBytes(y, pyp);
|
|
278
|
+
putBytes(m, mp);
|
|
279
|
+
putBytes(sig, sigp);
|
|
264
280
|
return exports.verifyOut(pxp, pyp, mp, m.length, sigp) === 1;
|
|
265
281
|
}
|
|
266
282
|
finally {
|
|
@@ -286,21 +302,20 @@ export function encrypt(publicKey, message, opts = {}) {
|
|
|
286
302
|
}
|
|
287
303
|
const m = toBytes(message, 'message');
|
|
288
304
|
const { exports } = getModule();
|
|
289
|
-
const mem = new Uint8Array(getModule().memory.buffer);
|
|
290
305
|
const pxp = exports.alloc(32);
|
|
291
306
|
const pyp = exports.alloc(32);
|
|
292
307
|
const mp = exports.alloc(m.length);
|
|
293
308
|
const kp = exports.alloc(32);
|
|
294
309
|
const ctp = exports.alloc(97 + m.length);
|
|
295
310
|
try {
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
311
|
+
putBytes(x, pxp);
|
|
312
|
+
putBytes(y, pyp);
|
|
313
|
+
putBytes(m, mp);
|
|
299
314
|
for (let attempt = 0; attempt < 16; attempt++) {
|
|
300
|
-
|
|
315
|
+
putBytes(randomK(), kp);
|
|
301
316
|
const rc = exports.encryptOut(pxp, pyp, mp, m.length, kp, ctp);
|
|
302
317
|
if (rc === 1) {
|
|
303
|
-
const raw =
|
|
318
|
+
const raw = readBytes(ctp, 97 + m.length);
|
|
304
319
|
return fromBytes(format === 'C1C2C3' ? c1c3c2ToC1c2c3(raw) : raw, 'hex');
|
|
305
320
|
}
|
|
306
321
|
}
|
|
@@ -354,18 +369,17 @@ function decryptUnsafe(privateKey, ciphertext, opts) {
|
|
|
354
369
|
/** 按 C1C3C2 布局解密(wasm 侧约定的布局)。 */
|
|
355
370
|
function decryptRaw(d, ct) {
|
|
356
371
|
const { exports } = getModule();
|
|
357
|
-
const mem = new Uint8Array(getModule().memory.buffer);
|
|
358
372
|
const dp = exports.alloc(32);
|
|
359
373
|
const ctp = exports.alloc(ct.length);
|
|
360
374
|
const outLen = ct.length - 97;
|
|
361
375
|
const ptp = exports.alloc(outLen === 0 ? 1 : outLen);
|
|
362
376
|
try {
|
|
363
|
-
|
|
364
|
-
|
|
377
|
+
putBytes(d, dp);
|
|
378
|
+
putBytes(ct, ctp);
|
|
365
379
|
const rc = exports.decryptOut(dp, ctp, ct.length, ptp);
|
|
366
380
|
if (rc !== 1)
|
|
367
381
|
throw new DecryptionError('sm2.decrypt: C3 verification failed');
|
|
368
|
-
return fromBytes(
|
|
382
|
+
return fromBytes(readBytes(ptp, outLen), 'hex');
|
|
369
383
|
}
|
|
370
384
|
finally {
|
|
371
385
|
exports.freeAll();
|
package/dist/sm4/index.js
CHANGED
|
@@ -34,7 +34,7 @@ function withBuffers(key, data, outLen, fn) {
|
|
|
34
34
|
const mem = new Uint8Array(memory.buffer);
|
|
35
35
|
mem.set(key, keyPtr);
|
|
36
36
|
mem.set(data, inPtr);
|
|
37
|
-
return fn(keyPtr, inPtr, outPtr
|
|
37
|
+
return fn(keyPtr, inPtr, outPtr);
|
|
38
38
|
}
|
|
39
39
|
finally {
|
|
40
40
|
exports.freeAll();
|
|
@@ -244,7 +244,7 @@ function withGcmBuffers(k, v, a, data, fn) {
|
|
|
244
244
|
mem.set(a, aadPtr);
|
|
245
245
|
if (data.length !== 0)
|
|
246
246
|
mem.set(data, inPtr);
|
|
247
|
-
return fn({ keyPtr, ivPtr, aadPtr, inPtr, outPtr, tagPtr }
|
|
247
|
+
return fn({ keyPtr, ivPtr, aadPtr, inPtr, outPtr, tagPtr });
|
|
248
248
|
}
|
|
249
249
|
finally {
|
|
250
250
|
exports.freeAll();
|
package/dist/sm9/index.js
CHANGED
|
@@ -354,17 +354,21 @@ export function encrypt(publicKey, id, msg, r) {
|
|
|
354
354
|
const idP = exports.alloc(idB.length);
|
|
355
355
|
const msgP = exports.alloc(msgB.length);
|
|
356
356
|
const rP = exports.alloc(32);
|
|
357
|
-
|
|
357
|
+
// 密文布局 C1(65) || C3(32) || C2(|msg|),长度随明文增长 —— 按实际长度分配,
|
|
358
|
+
// 并把容量一并交给 wasm 侧校验。曾硬编码 alloc(4096):明文超过 3999 字节时
|
|
359
|
+
// sm9Encrypt 会写越界、破坏 GC 堆(几轮后 RuntimeError: memory access out of bounds)。
|
|
360
|
+
const ctCap = 65 + 32 + msgB.length;
|
|
361
|
+
const ctP = exports.alloc(ctCap);
|
|
358
362
|
try {
|
|
359
363
|
putBytes(key.bytes, keyP);
|
|
360
364
|
putBytes(idB, idP);
|
|
361
365
|
putBytes(msgB, msgP);
|
|
362
366
|
putBytes(rB, rP);
|
|
363
367
|
const ctLen = key.kind === 'public'
|
|
364
|
-
? exports.sm9EncryptPubOut(keyP, idP, idB.length, msgP, msgB.length, rP, ctP)
|
|
365
|
-
: exports.sm9EncryptOut(keyP, idP, idB.length, msgP, msgB.length, rP, ctP);
|
|
368
|
+
? exports.sm9EncryptPubOut(keyP, idP, idB.length, msgP, msgB.length, rP, ctP, ctCap)
|
|
369
|
+
: exports.sm9EncryptOut(keyP, idP, idB.length, msgP, msgB.length, rP, ctP, ctCap);
|
|
366
370
|
if (ctLen < 0)
|
|
367
|
-
throw new Error('sm9: encrypt
|
|
371
|
+
throw new Error('sm9: encrypt 失败(密文缓冲容量不足)');
|
|
368
372
|
return readHex(ctP, ctLen);
|
|
369
373
|
}
|
|
370
374
|
finally {
|
package/dist/sm9/sm9.wasm
CHANGED
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "sm-crypto-wasm",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "国密 SM2/SM3/SM4/SM9/ZUC 的 WASM 实现,Node 18+ 与现代浏览器均可使用,按算法分包懒加载。",
|
|
5
5
|
"description:en": "SM2/SM3/SM4/SM9/ZUC (China crypto) implemented in AssemblyScript -> WASM. Node 18+ & modern browsers, per-algorithm lazy loading.",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -30,6 +30,10 @@
|
|
|
30
30
|
},
|
|
31
31
|
"main": "./dist/index.js",
|
|
32
32
|
"types": "./dist/index.d.ts",
|
|
33
|
+
"browser": {
|
|
34
|
+
"./dist/runtime/loadWasm.js": "./dist/runtime/loadWasm.browser.js",
|
|
35
|
+
"./dist/runtime/random.js": "./dist/runtime/random.browser.js"
|
|
36
|
+
},
|
|
33
37
|
"exports": {
|
|
34
38
|
".": {
|
|
35
39
|
"types": "./dist/index.d.ts",
|
|
@@ -55,6 +59,10 @@
|
|
|
55
59
|
"types": "./dist/zuc/index.d.ts",
|
|
56
60
|
"import": "./dist/zuc/index.js"
|
|
57
61
|
},
|
|
62
|
+
"./esbuild-plugin": {
|
|
63
|
+
"types": "./dist/esbuild-plugin.d.ts",
|
|
64
|
+
"import": "./dist/esbuild-plugin.js"
|
|
65
|
+
},
|
|
58
66
|
"./package.json": "./package.json"
|
|
59
67
|
},
|
|
60
68
|
"files": [
|