sm-crypto-wasm 0.1.1 → 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/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/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": [
|