sm-crypto-wasm 0.7.0 → 0.7.1
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 +41 -876
- package/dist/asn1/decode.d.ts +103 -12
- package/dist/asn1/decode.js +129 -10
- package/dist/asn1/encode.d.ts +120 -19
- package/dist/asn1/encode.js +158 -19
- package/dist/asn1/index.d.ts +6 -0
- package/dist/asn1/index.js +6 -0
- package/dist/asn1/oid.d.ts +18 -0
- package/dist/asn1/oid.js +30 -1
- package/dist/asn1/time.d.ts +32 -0
- package/dist/asn1/time.js +55 -1
- package/dist/asn1/types.d.ts +100 -16
- package/dist/asn1/types.js +47 -6
- package/dist/cms/errors.d.ts +12 -2
- package/dist/cms/errors.js +7 -1
- package/dist/cms/index.d.ts +5 -0
- package/dist/cms/index.js +5 -0
- package/dist/cms/oids.d.ts +37 -7
- package/dist/cms/oids.js +37 -7
- package/dist/cms/parse.d.ts +155 -13
- package/dist/cms/parse.js +98 -4
- package/dist/cms/sign.d.ts +80 -10
- package/dist/cms/sign.js +54 -2
- package/dist/cms/verify.d.ts +84 -9
- package/dist/cms/verify.js +54 -5
- package/dist/common/hybrid.d.ts +25 -0
- package/dist/common/hybrid.js +25 -0
- package/dist/common/sealed.d.ts +119 -15
- package/dist/common/sealed.js +67 -9
- package/dist/esbuild-plugin.d.ts +74 -5
- package/dist/esbuild-plugin.js +69 -4
- package/dist/index.d.ts +19 -0
- package/dist/index.js +22 -0
- package/dist/pem/asn1.d.ts +13 -0
- package/dist/pem/asn1.js +13 -0
- package/dist/pem/index.d.ts +2 -0
- package/dist/pem/index.js +2 -0
- package/dist/pem/pkcs8.d.ts +64 -8
- package/dist/pem/pkcs8.js +66 -6
- package/dist/pem/sm2.d.ts +76 -4
- package/dist/pem/sm2.js +169 -12
- package/dist/pem/sm9.d.ts +42 -2
- package/dist/pem/sm9.js +42 -2
- package/dist/runtime/base64.core.d.ts +25 -1
- package/dist/runtime/base64.core.js +31 -1
- package/dist/runtime/convert.d.ts +70 -7
- package/dist/runtime/convert.js +129 -9
- package/dist/runtime/errors.d.ts +64 -8
- package/dist/runtime/errors.js +64 -8
- package/dist/runtime/index.d.ts +5 -0
- package/dist/runtime/index.js +5 -0
- package/dist/runtime/loadWasm.browser.d.ts +33 -0
- package/dist/runtime/loadWasm.browser.js +46 -0
- package/dist/runtime/loadWasm.core.d.ts +59 -3
- package/dist/runtime/loadWasm.core.js +53 -3
- package/dist/runtime/loadWasm.d.ts +44 -0
- package/dist/runtime/loadWasm.js +79 -4
- package/dist/runtime/random.browser.d.ts +24 -0
- package/dist/runtime/random.browser.js +31 -1
- package/dist/runtime/random.core.d.ts +21 -0
- package/dist/runtime/random.core.js +27 -0
- package/dist/runtime/random.d.ts +28 -0
- package/dist/runtime/random.js +40 -1
- package/dist/sm2/exchange.d.ts +132 -16
- package/dist/sm2/exchange.js +120 -9
- package/dist/sm2/hybrid.d.ts +65 -3
- package/dist/sm2/hybrid.js +53 -1
- package/dist/sm2/index.d.ts +260 -21
- package/dist/sm2/index.js +346 -23
- package/dist/sm2/kem.d.ts +62 -5
- package/dist/sm2/kem.js +37 -2
- package/dist/sm2/keyInput.d.ts +36 -3
- package/dist/sm2/keyInput.js +36 -3
- package/dist/sm3/index.d.ts +30 -0
- package/dist/sm3/index.js +55 -1
- package/dist/sm4/index.d.ts +125 -17
- package/dist/sm4/index.js +201 -14
- package/dist/sm9/core.d.ts +58 -8
- package/dist/sm9/core.js +36 -5
- package/dist/sm9/hybrid.d.ts +66 -3
- package/dist/sm9/hybrid.js +46 -1
- package/dist/sm9/index.d.ts +265 -19
- package/dist/sm9/index.js +175 -5
- package/dist/sm9/input.d.ts +37 -2
- package/dist/sm9/input.js +50 -1
- package/dist/sm9/kem.d.ts +57 -4
- package/dist/sm9/kem.js +47 -1
- package/dist/webcrypto/algorithms.d.ts +58 -12
- package/dist/webcrypto/algorithms.js +58 -10
- package/dist/webcrypto/cryptoKey.d.ts +81 -15
- package/dist/webcrypto/cryptoKey.js +69 -11
- package/dist/webcrypto/errors.d.ts +29 -5
- package/dist/webcrypto/errors.js +29 -5
- package/dist/webcrypto/index.d.ts +10 -0
- package/dist/webcrypto/index.js +10 -0
- package/dist/webcrypto/subtle.d.ts +53 -3
- package/dist/webcrypto/subtle.js +69 -1
- package/dist/x509/encode.d.ts +86 -16
- package/dist/x509/encode.js +89 -16
- package/dist/x509/errors.d.ts +16 -1
- package/dist/x509/errors.js +16 -1
- package/dist/x509/extensions.d.ts +46 -7
- package/dist/x509/extensions.js +67 -9
- package/dist/x509/index.d.ts +7 -0
- package/dist/x509/index.js +19 -0
- package/dist/x509/ip.d.ts +9 -0
- package/dist/x509/ip.js +21 -0
- package/dist/x509/issue.d.ts +45 -2
- package/dist/x509/issue.js +107 -0
- package/dist/x509/oids.d.ts +41 -7
- package/dist/x509/oids.js +58 -7
- package/dist/x509/parse.d.ts +21 -0
- package/dist/x509/parse.js +115 -1
- package/dist/x509/sha1.d.ts +12 -1
- package/dist/x509/sha1.js +18 -1
- package/dist/x509/sha256.d.ts +12 -1
- package/dist/x509/sha256.js +31 -1
- package/dist/x509/sig.d.ts +15 -0
- package/dist/x509/sig.js +28 -1
- package/dist/x509/types.d.ts +312 -57
- package/dist/x509/types.js +6 -0
- package/dist/x509/verify.d.ts +51 -4
- package/dist/x509/verify.js +42 -3
- package/dist/zuc/index.d.ts +128 -10
- package/dist/zuc/index.js +136 -7
- package/package.json +53 -36
package/dist/esbuild-plugin.js
CHANGED
|
@@ -23,7 +23,9 @@
|
|
|
23
23
|
* platform: 'browser',
|
|
24
24
|
* format: 'esm',
|
|
25
25
|
* outdir: 'dist/assets', // 产物 JS 落这里
|
|
26
|
+
* // output JS goes here
|
|
26
27
|
* plugins: [smCryptoWasm()], // → wasm 复制到 dist/<algo>/<algo>.wasm
|
|
28
|
+
* // copies wasm to dist/<algo>/<algo>.wasm
|
|
27
29
|
* });
|
|
28
30
|
* ```
|
|
29
31
|
*
|
|
@@ -31,19 +33,69 @@
|
|
|
31
33
|
* 产物目录的上级。
|
|
32
34
|
*
|
|
33
35
|
* 只需用到部分算法时传 `algorithms` 少复制几个。
|
|
36
|
+
*
|
|
37
|
+
* esbuild plugin: place the `.wasm` assets **where the runtime can resolve them** in a
|
|
38
|
+
* browser bundle.
|
|
39
|
+
*
|
|
40
|
+
* ## Why it is needed
|
|
41
|
+
*
|
|
42
|
+
* The browser loader fetches wasm with `fetch(new URL('../<algo>/<algo>.wasm',
|
|
43
|
+
* import.meta.url))` (a form both Vite and Webpack 5 understand: they archive the asset
|
|
44
|
+
* and rewrite the URL). **esbuild does not** — neither `--outfile` nor `--outdir` moves
|
|
45
|
+
* the file that `new URL` points at (measured on 0.28.2). Result: the build succeeds and
|
|
46
|
+
* `fetch` 404s at runtime.
|
|
47
|
+
*
|
|
48
|
+
* After the build finishes this plugin copies `dist/<algo>/<algo>.wasm` to that relative
|
|
49
|
+
* location: the runtime URL is `../<algo>/<algo>.wasm` relative to **the emitted JS
|
|
50
|
+
* file**, so the target base directory is **the parent of the directory holding the
|
|
51
|
+
* emitted JS** (derived from esbuild's `outdir` / `outfile`).
|
|
52
|
+
*
|
|
53
|
+
* ## Usage
|
|
54
|
+
*
|
|
55
|
+
* ```ts
|
|
56
|
+
* import { smCryptoWasm } from 'sm-crypto-wasm/esbuild-plugin';
|
|
57
|
+
*
|
|
58
|
+
* await esbuild.build({
|
|
59
|
+
* entryPoints: ['src/main.ts'],
|
|
60
|
+
* bundle: true,
|
|
61
|
+
* platform: 'browser',
|
|
62
|
+
* format: 'esm',
|
|
63
|
+
* outdir: 'dist/assets', // the emitted JS lands here
|
|
64
|
+
* plugins: [smCryptoWasm()], // → wasm is copied to dist/<algo>/<algo>.wasm
|
|
65
|
+
* });
|
|
66
|
+
* ```
|
|
67
|
+
*
|
|
68
|
+
* Pointing `outdir` at a subdirectory (e.g. `dist/assets`) is recommended so the wasm
|
|
69
|
+
* lands in `dist/<algo>/` instead of the parent of the output directory.
|
|
70
|
+
*
|
|
71
|
+
* Pass `algorithms` when only some algorithms are used, to copy fewer files.
|
|
34
72
|
*/
|
|
35
73
|
import { copyFileSync, existsSync, mkdirSync } from 'node:fs';
|
|
36
74
|
import { dirname, join, resolve } from 'node:path';
|
|
37
75
|
import { fileURLToPath } from 'node:url';
|
|
38
|
-
/** 支持的算法名(与加载器白名单一致)。
|
|
76
|
+
/** 支持的算法名(与加载器白名单一致)。
|
|
77
|
+
*
|
|
78
|
+
* Supported algorithm names (in sync with the loader allow-list).
|
|
79
|
+
*/
|
|
39
80
|
const WASM_ALGOS = ['sm2', 'sm3', 'sm4', 'sm9', 'zuc'];
|
|
40
|
-
/** 本包 `dist/` 的位置(编译后插件自身就在 `dist/` 下)。
|
|
81
|
+
/** 本包 `dist/` 的位置(编译后插件自身就在 `dist/` 下)。
|
|
82
|
+
*
|
|
83
|
+
* Location of this package's `dist/` (once compiled, the plugin itself lives under
|
|
84
|
+
* `dist/`).
|
|
85
|
+
*/
|
|
41
86
|
const THIS_DIST_DIR = dirname(fileURLToPath(import.meta.url));
|
|
42
87
|
/**
|
|
43
88
|
* 推导 wasm 的目标基目录。
|
|
44
89
|
*
|
|
45
90
|
* 运行时相对**产物 JS 文件**解析 `../<algo>/<algo>.wasm`,所以目标是产物 JS 目录的上级。
|
|
46
91
|
* esbuild 通常把入口产物放在 `outdir` 根(单入口时 `outbase` 就是入口所在目录)。
|
|
92
|
+
*
|
|
93
|
+
* Derive the base directory for the wasm output.
|
|
94
|
+
*
|
|
95
|
+
* At runtime `../<algo>/<algo>.wasm` is resolved relative to **the emitted JS file**, so
|
|
96
|
+
* the target is the parent of the emitted JS directory. esbuild usually puts the entry
|
|
97
|
+
* output at the root of `outdir` (with a single entry, `outbase` is the entry's own
|
|
98
|
+
* directory).
|
|
47
99
|
*/
|
|
48
100
|
function resolveTargetBase(opts) {
|
|
49
101
|
if (opts.outdir)
|
|
@@ -57,6 +109,11 @@ function resolveTargetBase(opts) {
|
|
|
57
109
|
*
|
|
58
110
|
* @param options 见 {@link SmCryptoWasmPluginOptions}。
|
|
59
111
|
* @returns 可直接放进 `plugins` 数组的插件对象。
|
|
112
|
+
*
|
|
113
|
+
* Create the esbuild plugin: copy the wasm assets after a successful build.
|
|
114
|
+
*
|
|
115
|
+
* @param options See {@link SmCryptoWasmPluginOptions}.
|
|
116
|
+
* @returns A plugin object that can be placed directly in the `plugins` array.
|
|
60
117
|
*/
|
|
61
118
|
export function smCryptoWasm(options = {}) {
|
|
62
119
|
const algos = options.algorithms ?? WASM_ALGOS;
|
|
@@ -66,15 +123,23 @@ export function smCryptoWasm(options = {}) {
|
|
|
66
123
|
setup(build) {
|
|
67
124
|
build.onEnd((result) => {
|
|
68
125
|
// 构建失败时不产出资源,避免留下半套产物。
|
|
126
|
+
//
|
|
127
|
+
// Emit no assets when the build failed, so no half-written output is left behind.
|
|
69
128
|
if (result.errors.length > 0)
|
|
70
129
|
return;
|
|
71
130
|
const targetBase = options.outDir ?? resolveTargetBase(build.initialOptions);
|
|
131
|
+
// `write: false` 等场景:没有落盘位置
|
|
132
|
+
//
|
|
133
|
+
// Cases such as `write: false`: there is no location to write to.
|
|
72
134
|
if (targetBase === undefined)
|
|
73
|
-
return;
|
|
135
|
+
return;
|
|
74
136
|
for (const algo of algos) {
|
|
75
137
|
const from = join(sourceDir, algo, `${algo}.wasm`);
|
|
138
|
+
// 只装了部分算法时静默跳过。
|
|
139
|
+
//
|
|
140
|
+
// Silently skip algorithms that were not installed.
|
|
76
141
|
if (!existsSync(from))
|
|
77
|
-
continue;
|
|
142
|
+
continue;
|
|
78
143
|
const to = join(targetBase, algo, `${algo}.wasm`);
|
|
79
144
|
mkdirSync(dirname(to), { recursive: true });
|
|
80
145
|
copyFileSync(from, to);
|
package/dist/index.d.ts
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* import { sm3, sm4, zuc, sm2 } from 'sm-crypto-wasm';
|
|
7
7
|
*
|
|
8
8
|
* await sm3.ensureLoaded(); // 首次用某算法前预热其 wasm
|
|
9
|
+
* // preload that algorithm's wasm before first use
|
|
9
10
|
* sm3.hash('hello');
|
|
10
11
|
* ```
|
|
11
12
|
*
|
|
@@ -15,6 +16,24 @@
|
|
|
15
16
|
* `import { encryptECB } from 'sm-crypto-wasm/sm4'`。
|
|
16
17
|
*
|
|
17
18
|
* spec/plan 对应:plan Task 12;spec §5 产物结构、§6 API。
|
|
19
|
+
*
|
|
20
|
+
* sm-crypto-wasm aggregate entry point (facade).
|
|
21
|
+
*
|
|
22
|
+
* Usage:
|
|
23
|
+
* ```ts
|
|
24
|
+
* import { sm3, sm4, zuc, sm2 } from 'sm-crypto-wasm';
|
|
25
|
+
*
|
|
26
|
+
* await sm3.ensureLoaded(); // warm an algorithm's wasm before first use
|
|
27
|
+
* sm3.hash('hello');
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* Design: every algorithm is its own namespace exposing `ensureLoaded()` plus its
|
|
31
|
+
* business functions. The wasm is split per algorithm and only the algorithm that
|
|
32
|
+
* is actually called gets loaded (see the memoization in
|
|
33
|
+
* `src/runtime/loadWasm.ts`). Sub-path imports are supported for further
|
|
34
|
+
* tree-shaking: `import { encryptECB } from 'sm-crypto-wasm/sm4'`.
|
|
35
|
+
*
|
|
36
|
+
* spec/plan: plan Task 12; spec §5 artifact layout, §6 API.
|
|
18
37
|
*/
|
|
19
38
|
export * as cms from './cms/index.js';
|
|
20
39
|
export { bytesToHex, fromBytes, hexToBytes, toBytes } from './runtime/convert.js';
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
* import { sm3, sm4, zuc, sm2 } from 'sm-crypto-wasm';
|
|
7
7
|
*
|
|
8
8
|
* await sm3.ensureLoaded(); // 首次用某算法前预热其 wasm
|
|
9
|
+
* // preload that algorithm's wasm before first use
|
|
9
10
|
* sm3.hash('hello');
|
|
10
11
|
* ```
|
|
11
12
|
*
|
|
@@ -15,10 +16,31 @@
|
|
|
15
16
|
* `import { encryptECB } from 'sm-crypto-wasm/sm4'`。
|
|
16
17
|
*
|
|
17
18
|
* spec/plan 对应:plan Task 12;spec §5 产物结构、§6 API。
|
|
19
|
+
*
|
|
20
|
+
* sm-crypto-wasm aggregate entry point (facade).
|
|
21
|
+
*
|
|
22
|
+
* Usage:
|
|
23
|
+
* ```ts
|
|
24
|
+
* import { sm3, sm4, zuc, sm2 } from 'sm-crypto-wasm';
|
|
25
|
+
*
|
|
26
|
+
* await sm3.ensureLoaded(); // warm an algorithm's wasm before first use
|
|
27
|
+
* sm3.hash('hello');
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* Design: every algorithm is its own namespace exposing `ensureLoaded()` plus its
|
|
31
|
+
* business functions. The wasm is split per algorithm and only the algorithm that
|
|
32
|
+
* is actually called gets loaded (see the memoization in
|
|
33
|
+
* `src/runtime/loadWasm.ts`). Sub-path imports are supported for further
|
|
34
|
+
* tree-shaking: `import { encryptECB } from 'sm-crypto-wasm/sm4'`.
|
|
35
|
+
*
|
|
36
|
+
* spec/plan: plan Task 12; spec §5 artifact layout, §6 API.
|
|
18
37
|
*/
|
|
19
38
|
export * as cms from './cms/index.js';
|
|
20
39
|
export { bytesToHex, fromBytes, hexToBytes, toBytes } from './runtime/convert.js';
|
|
21
40
|
// 运行时工具(错误类型、编解码)也导出,便于调用方捕获本库错误。
|
|
41
|
+
//
|
|
42
|
+
// Runtime helpers (error types, codecs) are re-exported so callers can catch
|
|
43
|
+
// this library's errors.
|
|
22
44
|
export { CiphertextError, DecryptionError, InvalidCiphertextError, InvalidKeyError, InvalidPublicKeyError, isSmCryptoError, SmCryptoError, } from './runtime/errors.js';
|
|
23
45
|
export { loadWasm, loadWasmSync } from './runtime/loadWasm.js';
|
|
24
46
|
export * as sm2 from './sm2/index.js';
|
package/dist/pem/asn1.d.ts
CHANGED
|
@@ -10,6 +10,19 @@
|
|
|
10
10
|
* - `encodeOid` / `decodeOid` / `asn1IntValue` → OID 与 INTEGER 解码
|
|
11
11
|
*
|
|
12
12
|
* spec/plan 对应:X.509/CMS plan 阶段 0「迁移」。
|
|
13
|
+
*
|
|
14
|
+
* Legacy ASN.1 entry point (compatibility layer).
|
|
15
|
+
*
|
|
16
|
+
* The implementation moved to `src/asn1/` (the shared DER/BER layer). This file only
|
|
17
|
+
* keeps the historical export names so that `src/pem/*` and the existing tests did not
|
|
18
|
+
* have to change:
|
|
19
|
+
*
|
|
20
|
+
* - `parseDer` → `src/asn1/decode.ts`
|
|
21
|
+
* - `encodeDer` → `encode` in `src/asn1/encode.ts`
|
|
22
|
+
* - `asn1Int` / `asn1Octets` / `asn1Oid` / `asn1Null` / `asn1Seq` → the matching builders
|
|
23
|
+
* - `encodeOid` / `decodeOid` / `asn1IntValue` → OID and INTEGER decoding
|
|
24
|
+
*
|
|
25
|
+
* spec/plan: X.509/CMS plan phase 0, "migration".
|
|
13
26
|
*/
|
|
14
27
|
export { decodeOid, encode as encodeDer, encodeOid, integer as asn1Int, nullValue as asn1Null, octetString as asn1Octets, oid as asn1Oid, parseDer, readInteger as asn1IntValue, seq as asn1Seq, } from '../asn1/index.js';
|
|
15
28
|
export { Asn1Error, type Asn1Node } from '../asn1/types.js';
|
package/dist/pem/asn1.js
CHANGED
|
@@ -10,6 +10,19 @@
|
|
|
10
10
|
* - `encodeOid` / `decodeOid` / `asn1IntValue` → OID 与 INTEGER 解码
|
|
11
11
|
*
|
|
12
12
|
* spec/plan 对应:X.509/CMS plan 阶段 0「迁移」。
|
|
13
|
+
*
|
|
14
|
+
* Legacy ASN.1 entry point (compatibility layer).
|
|
15
|
+
*
|
|
16
|
+
* The implementation moved to `src/asn1/` (the shared DER/BER layer). This file only
|
|
17
|
+
* keeps the historical export names so that `src/pem/*` and the existing tests did not
|
|
18
|
+
* have to change:
|
|
19
|
+
*
|
|
20
|
+
* - `parseDer` → `src/asn1/decode.ts`
|
|
21
|
+
* - `encodeDer` → `encode` in `src/asn1/encode.ts`
|
|
22
|
+
* - `asn1Int` / `asn1Octets` / `asn1Oid` / `asn1Null` / `asn1Seq` → the matching builders
|
|
23
|
+
* - `encodeOid` / `decodeOid` / `asn1IntValue` → OID and INTEGER decoding
|
|
24
|
+
*
|
|
25
|
+
* spec/plan: X.509/CMS plan phase 0, "migration".
|
|
13
26
|
*/
|
|
14
27
|
export { decodeOid, encode as encodeDer, encodeOid, integer as asn1Int, nullValue as asn1Null, octetString as asn1Octets, oid as asn1Oid, parseDer, readInteger as asn1IntValue, seq as asn1Seq, } from '../asn1/index.js';
|
|
15
28
|
export { Asn1Error } from '../asn1/types.js';
|
package/dist/pem/index.d.ts
CHANGED
package/dist/pem/index.js
CHANGED
package/dist/pem/pkcs8.d.ts
CHANGED
|
@@ -9,27 +9,83 @@
|
|
|
9
9
|
* - AlgorithmIdentifier ::= SEQUENCE { algorithm OID, parameters ANY DEFINED BY algorithm OPTIONAL }
|
|
10
10
|
*
|
|
11
11
|
* 本仓库只支持"parameters = NULL"这一类算法(SM2 / SM9 都符合)。
|
|
12
|
+
*
|
|
13
|
+
* PKCS#8 PrivateKeyInfo and SPKI SubjectPublicKeyInfo parsing/encoding.
|
|
14
|
+
*
|
|
15
|
+
* spec/plan: plan Task 4; spec §12.1 Q3.
|
|
16
|
+
*
|
|
17
|
+
* Types involved:
|
|
18
|
+
* - PrivateKeyInfo ::= SEQUENCE { version INTEGER, algorithm AlgorithmIdentifier,
|
|
19
|
+
* privateKey OCTET STRING [, attributes ...] }
|
|
20
|
+
* - SubjectPublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier,
|
|
21
|
+
* subjectPublicKey BIT STRING }
|
|
22
|
+
* - AlgorithmIdentifier ::= SEQUENCE { algorithm OID, parameters ANY DEFINED BY
|
|
23
|
+
* algorithm OPTIONAL }
|
|
24
|
+
*
|
|
25
|
+
* This repository only supports algorithms of the "parameters = NULL" kind (both SM2
|
|
26
|
+
* and SM9 qualify).
|
|
12
27
|
*/
|
|
13
28
|
/**
|
|
14
29
|
* PrivateKeyInfo 的最小化解析结果。
|
|
30
|
+
*
|
|
31
|
+
* The minimally parsed result of a PrivateKeyInfo.
|
|
15
32
|
*/
|
|
16
33
|
export interface ParsedPrivateKey {
|
|
17
|
-
/**
|
|
34
|
+
/**
|
|
35
|
+
* 算法 OID,如 `"1.2.156.10197.1.100"`(SM9)。
|
|
36
|
+
*
|
|
37
|
+
* Algorithm OID, e.g. `"1.2.156.10197.1.100"` (SM9).
|
|
38
|
+
*/
|
|
18
39
|
readonly algorithmOid: string;
|
|
19
|
-
/**
|
|
40
|
+
/**
|
|
41
|
+
* 原始私钥字节(EC privateKey OCTET STRING 内容,含可选 public-key 拼接)。
|
|
42
|
+
*
|
|
43
|
+
* Raw private key bytes (the contents of the EC privateKey OCTET STRING, including an
|
|
44
|
+
* optional concatenated public key).
|
|
45
|
+
*/
|
|
20
46
|
readonly privateKey: Uint8Array;
|
|
21
47
|
}
|
|
22
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* 编码 PrivateKeyInfo PEM。
|
|
50
|
+
*
|
|
51
|
+
* Encodes a PrivateKeyInfo PEM.
|
|
52
|
+
*/
|
|
23
53
|
export declare function encodePkcs8PrivateKey(algorithmOid: string, privateKey: Uint8Array): string;
|
|
24
|
-
/**
|
|
54
|
+
/**
|
|
55
|
+
* 解析 PRIVATE KEY PEM。
|
|
56
|
+
*
|
|
57
|
+
* Parses a PRIVATE KEY PEM.
|
|
58
|
+
*/
|
|
25
59
|
export declare function parsePkcs8PrivateKey(pem: string): ParsedPrivateKey;
|
|
26
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* SPKI 解析结果。
|
|
62
|
+
*
|
|
63
|
+
* The parsed result of an SPKI.
|
|
64
|
+
*/
|
|
27
65
|
export interface ParsedPublicKey {
|
|
66
|
+
/**
|
|
67
|
+
* 算法 OID,如 `"1.2.156.10197.1.100"`(SM9)。
|
|
68
|
+
*
|
|
69
|
+
* Algorithm OID, e.g. `"1.2.156.10197.1.100"` (SM9).
|
|
70
|
+
*/
|
|
28
71
|
readonly algorithmOid: string;
|
|
29
|
-
/**
|
|
72
|
+
/**
|
|
73
|
+
* 公钥 BIT STRING 内容。对 EC 类算法:未使用字节 0x00 + X || Y(66 字节)。
|
|
74
|
+
*
|
|
75
|
+
* Contents of the public key BIT STRING. For EC-like algorithms: the unused-bits byte
|
|
76
|
+
* 0x00 followed by X || Y (66 bytes).
|
|
77
|
+
*/
|
|
30
78
|
readonly publicKey: Uint8Array;
|
|
31
79
|
}
|
|
32
|
-
/**
|
|
80
|
+
/**
|
|
81
|
+
* 编码 SPKI PEM(算法 OID + 公钥 BIT STRING)。
|
|
82
|
+
*
|
|
83
|
+
* Encodes an SPKI PEM (algorithm OID + public key BIT STRING).
|
|
84
|
+
*/
|
|
33
85
|
export declare function encodeSpkiPublicKey(algorithmOid: string, publicKey: Uint8Array): string;
|
|
34
|
-
/**
|
|
86
|
+
/**
|
|
87
|
+
* 解析 PUBLIC KEY PEM。
|
|
88
|
+
*
|
|
89
|
+
* Parses a PUBLIC KEY PEM.
|
|
90
|
+
*/
|
|
35
91
|
export declare function parseSpkiPublicKey(pem: string): ParsedPublicKey;
|
package/dist/pem/pkcs8.js
CHANGED
|
@@ -9,11 +9,30 @@
|
|
|
9
9
|
* - AlgorithmIdentifier ::= SEQUENCE { algorithm OID, parameters ANY DEFINED BY algorithm OPTIONAL }
|
|
10
10
|
*
|
|
11
11
|
* 本仓库只支持"parameters = NULL"这一类算法(SM2 / SM9 都符合)。
|
|
12
|
+
*
|
|
13
|
+
* PKCS#8 PrivateKeyInfo and SPKI SubjectPublicKeyInfo parsing/encoding.
|
|
14
|
+
*
|
|
15
|
+
* spec/plan: plan Task 4; spec §12.1 Q3.
|
|
16
|
+
*
|
|
17
|
+
* Types involved:
|
|
18
|
+
* - PrivateKeyInfo ::= SEQUENCE { version INTEGER, algorithm AlgorithmIdentifier,
|
|
19
|
+
* privateKey OCTET STRING [, attributes ...] }
|
|
20
|
+
* - SubjectPublicKeyInfo ::= SEQUENCE { algorithm AlgorithmIdentifier,
|
|
21
|
+
* subjectPublicKey BIT STRING }
|
|
22
|
+
* - AlgorithmIdentifier ::= SEQUENCE { algorithm OID, parameters ANY DEFINED BY
|
|
23
|
+
* algorithm OPTIONAL }
|
|
24
|
+
*
|
|
25
|
+
* This repository only supports algorithms of the "parameters = NULL" kind (both SM2
|
|
26
|
+
* and SM9 qualify).
|
|
12
27
|
*/
|
|
13
28
|
import { base64ToBytes, bytesToBase64 } from '../runtime/convert.js';
|
|
14
29
|
import { InvalidKeyError } from '../runtime/errors.js';
|
|
15
30
|
import { asn1Int, asn1Null, asn1Octets, asn1Oid, asn1Seq, parseDer } from './asn1.js';
|
|
16
|
-
/**
|
|
31
|
+
/**
|
|
32
|
+
* 编码 PrivateKeyInfo PEM。
|
|
33
|
+
*
|
|
34
|
+
* Encodes a PrivateKeyInfo PEM.
|
|
35
|
+
*/
|
|
17
36
|
export function encodePkcs8PrivateKey(algorithmOid, privateKey) {
|
|
18
37
|
const seq = asn1Seq([
|
|
19
38
|
asn1Int(0),
|
|
@@ -22,12 +41,19 @@ export function encodePkcs8PrivateKey(algorithmOid, privateKey) {
|
|
|
22
41
|
]);
|
|
23
42
|
return derToPem(seq.raw, 'PRIVATE KEY');
|
|
24
43
|
}
|
|
25
|
-
/**
|
|
44
|
+
/**
|
|
45
|
+
* 解析 PRIVATE KEY PEM。
|
|
46
|
+
*
|
|
47
|
+
* Parses a PRIVATE KEY PEM.
|
|
48
|
+
*/
|
|
26
49
|
export function parsePkcs8PrivateKey(pem) {
|
|
27
50
|
const seq = parseDer(pemToDer(pem));
|
|
28
51
|
if (seq.tag !== 0x10) {
|
|
29
|
-
//
|
|
30
|
-
//
|
|
52
|
+
// `src/asn1` 存的是 UNIVERSAL tag 编号(SEQUENCE = 16 = 0x10),不是首字节
|
|
53
|
+
// 0x30,因此这里按编号比较。
|
|
54
|
+
//
|
|
55
|
+
// `src/asn1` stores the UNIVERSAL tag number (SEQUENCE = 16 = 0x10) rather than
|
|
56
|
+
// the leading byte 0x30, so the comparison uses the number.
|
|
31
57
|
throw new InvalidKeyError('parsePkcs8PrivateKey: top-level is not SEQUENCE');
|
|
32
58
|
}
|
|
33
59
|
if (!seq.children || seq.children.length < 3) {
|
|
@@ -55,9 +81,15 @@ export function parsePkcs8PrivateKey(pem) {
|
|
|
55
81
|
privateKey: privateKeyOctets.value,
|
|
56
82
|
};
|
|
57
83
|
}
|
|
58
|
-
/**
|
|
84
|
+
/**
|
|
85
|
+
* 编码 SPKI PEM(算法 OID + 公钥 BIT STRING)。
|
|
86
|
+
*
|
|
87
|
+
* Encodes an SPKI PEM (algorithm OID + public key BIT STRING).
|
|
88
|
+
*/
|
|
59
89
|
export function encodeSpkiPublicKey(algorithmOid, publicKey) {
|
|
60
90
|
// BIT STRING:未使用位 = 0,前缀一字节 0x00。
|
|
91
|
+
//
|
|
92
|
+
// BIT STRING: zero unused bits, prefixed with a single 0x00 byte.
|
|
61
93
|
const bitStringValue = new Uint8Array(publicKey.length + 1);
|
|
62
94
|
bitStringValue.set(publicKey, 1);
|
|
63
95
|
const seq = asn1Seq([
|
|
@@ -66,7 +98,11 @@ export function encodeSpkiPublicKey(algorithmOid, publicKey) {
|
|
|
66
98
|
]);
|
|
67
99
|
return derToPem(seq.raw, 'PUBLIC KEY');
|
|
68
100
|
}
|
|
69
|
-
/**
|
|
101
|
+
/**
|
|
102
|
+
* 解析 PUBLIC KEY PEM。
|
|
103
|
+
*
|
|
104
|
+
* Parses a PUBLIC KEY PEM.
|
|
105
|
+
*/
|
|
70
106
|
export function parseSpkiPublicKey(pem) {
|
|
71
107
|
const seq = parseDer(pemToDer(pem));
|
|
72
108
|
if (seq.tag !== 0x10) {
|
|
@@ -88,6 +124,8 @@ export function parseSpkiPublicKey(pem) {
|
|
|
88
124
|
throw new InvalidKeyError('parseSpkiPublicKey: missing BIT STRING');
|
|
89
125
|
}
|
|
90
126
|
// 去掉 BIT STRING 第一个字节的"未使用位"计数(我们固定为 0)。
|
|
127
|
+
//
|
|
128
|
+
// Strip the leading unused-bit count of the BIT STRING (always 0 in our encoding).
|
|
91
129
|
const inner = bitString.value.subarray(1);
|
|
92
130
|
return {
|
|
93
131
|
algorithmOid: decodeOidFromNode(oidNode),
|
|
@@ -95,6 +133,11 @@ export function parseSpkiPublicKey(pem) {
|
|
|
95
133
|
};
|
|
96
134
|
}
|
|
97
135
|
// ----------------- 内部 -----------------
|
|
136
|
+
//
|
|
137
|
+
// ----------------- Internals -----------------
|
|
138
|
+
// 从 ASN.1 节点解码 OID。
|
|
139
|
+
//
|
|
140
|
+
// Decodes an OID from an ASN.1 node.
|
|
98
141
|
function decodeOidFromNode(node) {
|
|
99
142
|
if (node.tag !== 0x06)
|
|
100
143
|
throw new InvalidKeyError('decodeOidFromNode: not OID');
|
|
@@ -115,6 +158,9 @@ function decodeOidFromNode(node) {
|
|
|
115
158
|
throw new InvalidKeyError('decodeOid: truncated');
|
|
116
159
|
return parts.join('.');
|
|
117
160
|
}
|
|
161
|
+
// PEM → DER。
|
|
162
|
+
//
|
|
163
|
+
// PEM to DER.
|
|
118
164
|
function pemToDer(pem) {
|
|
119
165
|
const lines = pem.split(/\r?\n/);
|
|
120
166
|
const body = [];
|
|
@@ -138,6 +184,9 @@ function pemToDer(pem) {
|
|
|
138
184
|
const b64 = body.join('');
|
|
139
185
|
return base64ToBytes(b64);
|
|
140
186
|
}
|
|
187
|
+
// DER → PEM。
|
|
188
|
+
//
|
|
189
|
+
// DER to PEM.
|
|
141
190
|
function derToPem(der, kind) {
|
|
142
191
|
const b64 = bytesToBase64(der);
|
|
143
192
|
const lines = [];
|
|
@@ -146,6 +195,9 @@ function derToPem(der, kind) {
|
|
|
146
195
|
}
|
|
147
196
|
return `-----BEGIN ${kind}-----\n${lines.join('\n')}\n-----END ${kind}-----\n`;
|
|
148
197
|
}
|
|
198
|
+
// 构造 BIT STRING 节点。
|
|
199
|
+
//
|
|
200
|
+
// Builds a BIT STRING node.
|
|
149
201
|
function bitStringNode(value) {
|
|
150
202
|
const lengthBytes = encodeLen(value.length);
|
|
151
203
|
const raw = new Uint8Array(1 + lengthBytes.length + value.length);
|
|
@@ -165,6 +217,9 @@ function bitStringNode(value) {
|
|
|
165
217
|
raw,
|
|
166
218
|
};
|
|
167
219
|
}
|
|
220
|
+
// 编码 DER 长度。
|
|
221
|
+
//
|
|
222
|
+
// Encodes a DER length.
|
|
168
223
|
function encodeLen(n) {
|
|
169
224
|
if (n < 0x80)
|
|
170
225
|
return new Uint8Array([n]);
|
|
@@ -179,3 +234,8 @@ function encodeLen(n) {
|
|
|
179
234
|
// base64 编解码统一走 `../runtime/convert.js` 的跨运行时实现(Node 走 Buffer
|
|
180
235
|
// 快路径、浏览器回落 btoa/atob);本文件原先自带一份,其 Node 分支直接引用了
|
|
181
236
|
// `Buffer`,在浏览器里会 ReferenceError。
|
|
237
|
+
//
|
|
238
|
+
// base64 encoding/decoding goes through the cross-runtime implementation in
|
|
239
|
+
// `../runtime/convert.js` (a Buffer fast path on Node, a btoa/atob fallback in the
|
|
240
|
+
// browser). This file used to carry its own copy whose Node branch referenced `Buffer`
|
|
241
|
+
// directly, which raised a ReferenceError in the browser.
|
package/dist/pem/sm2.d.ts
CHANGED
|
@@ -24,10 +24,44 @@
|
|
|
24
24
|
*
|
|
25
25
|
* 编码函数接受/产出与解析一致的格式(私钥 PKCS#8、公钥 SPKI),供
|
|
26
26
|
* 密钥生成与测试使用。
|
|
27
|
+
*
|
|
28
|
+
* SM2 key PEM encoding/decoding (PKCS#8 / SEC1 / SPKI).
|
|
29
|
+
*
|
|
30
|
+
* Unlike the generic parsing in `./pkcs8`, this layer knows the SM2 encodings:
|
|
31
|
+
* - Private keys:
|
|
32
|
+
* - PKCS#8 `PrivateKeyInfo` (`-----BEGIN PRIVATE KEY-----`); its `privateKey` OCTET
|
|
33
|
+
* STRING holds an RFC 5915 `ECPrivateKey`:
|
|
34
|
+
* ```
|
|
35
|
+
* ECPrivateKey ::= SEQUENCE {
|
|
36
|
+
* version INTEGER (1),
|
|
37
|
+
* privateKey OCTET STRING, -- 32-byte d
|
|
38
|
+
* parameters [0] EXPLICIT OBJECT IDENTIFIER OPTIONAL, -- sm2p256v1
|
|
39
|
+
* publicKey [1] EXPLICIT BIT STRING OPTIONAL
|
|
40
|
+
* }
|
|
41
|
+
* ```
|
|
42
|
+
* - SEC1 `ECPrivateKey` (`-----BEGIN EC PRIVATE KEY-----`) is exactly that structure.
|
|
43
|
+
* - Public keys: SPKI `SubjectPublicKeyInfo` (`-----BEGIN PUBLIC KEY-----`) with
|
|
44
|
+
* `algorithm = { id-ecPublicKey, sm2p256v1 }` and a 65-byte uncompressed point
|
|
45
|
+
* `04 || X || Y` as `subjectPublicKey`.
|
|
46
|
+
*
|
|
47
|
+
* Algorithm identifiers / curve OIDs:
|
|
48
|
+
* - id-ecPublicKey = 1.2.840.10045.2.1
|
|
49
|
+
* - sm2p256v1 = 1.2.156.10197.1.301
|
|
50
|
+
*
|
|
51
|
+
* The encoders produce the same formats the parsers accept (PKCS#8 for private keys,
|
|
52
|
+
* SPKI for public keys) for key generation and tests.
|
|
53
|
+
*/
|
|
54
|
+
/**
|
|
55
|
+
* `id-ecPublicKey` OID。
|
|
56
|
+
*
|
|
57
|
+
* The `id-ecPublicKey` OID.
|
|
27
58
|
*/
|
|
28
|
-
/** `id-ecPublicKey` OID。 */
|
|
29
59
|
export declare const OID_ID_EC_PUBLIC_KEY = "1.2.840.10045.2.1";
|
|
30
|
-
/**
|
|
60
|
+
/**
|
|
61
|
+
* `sm2p256v1`(推荐曲线)OID。
|
|
62
|
+
*
|
|
63
|
+
* The `sm2p256v1` (recommended curve) OID.
|
|
64
|
+
*/
|
|
31
65
|
export declare const OID_SM2_CURVE = "1.2.156.10197.1.301";
|
|
32
66
|
/**
|
|
33
67
|
* 解析 SM2 私钥 PEM,返回 32 字节私钥 d。
|
|
@@ -35,24 +69,44 @@ export declare const OID_SM2_CURVE = "1.2.156.10197.1.301";
|
|
|
35
69
|
* 支持 PKCS#8(`BEGIN PRIVATE KEY`)与 SEC1(`BEGIN EC PRIVATE KEY`)。
|
|
36
70
|
*
|
|
37
71
|
* @throws InvalidKeyError PEM/DER 结构非法、或曲线不是 SM2。
|
|
72
|
+
*
|
|
73
|
+
* Parses an SM2 private key PEM and returns the 32-byte private key d.
|
|
74
|
+
*
|
|
75
|
+
* Supports PKCS#8 (`BEGIN PRIVATE KEY`) and SEC1 (`BEGIN EC PRIVATE KEY`).
|
|
76
|
+
*
|
|
77
|
+
* @throws InvalidKeyError When the PEM/DER structure is invalid or the curve is not SM2.
|
|
38
78
|
*/
|
|
39
79
|
export declare function parseSm2PrivateKeyPem(pem: string): Uint8Array;
|
|
40
80
|
/**
|
|
41
81
|
* 解析 SM2 私钥 DER(PKCS#8 或 SEC1),返回 32 字节私钥 d。
|
|
42
82
|
*
|
|
43
83
|
* WebCrypto 的 `importKey('pkcs8', ...)` 使用这个入口。
|
|
84
|
+
*
|
|
85
|
+
* Parses an SM2 private key DER (PKCS#8 or SEC1) and returns the 32-byte private key d.
|
|
86
|
+
*
|
|
87
|
+
* WebCrypto's `importKey('pkcs8', ...)` uses this entry point.
|
|
44
88
|
*/
|
|
45
89
|
export declare function parseSm2PrivateKeyDer(der: Uint8Array): Uint8Array;
|
|
46
90
|
/**
|
|
47
91
|
* 解析 SM2 公钥 PEM,返回 65 字节未压缩点 `04 || X || Y`。
|
|
48
92
|
*
|
|
49
93
|
* @throws InvalidKeyError PEM/DER 结构非法、曲线不是 SM2、或点不是未压缩形式。
|
|
94
|
+
*
|
|
95
|
+
* Parses an SM2 public key PEM and returns the 65-byte uncompressed point
|
|
96
|
+
* `04 || X || Y`.
|
|
97
|
+
*
|
|
98
|
+
* @throws InvalidKeyError When the PEM/DER structure is invalid, the curve is not SM2,
|
|
99
|
+
* or the point is not in uncompressed form.
|
|
50
100
|
*/
|
|
51
101
|
export declare function parseSm2PublicKeyPem(pem: string): Uint8Array;
|
|
52
102
|
/**
|
|
53
103
|
* 解析 SM2 公钥 DER(SPKI),返回 65 字节未压缩点。
|
|
54
104
|
*
|
|
55
105
|
* WebCrypto 的 `importKey('spki', ...)` 使用这个入口。
|
|
106
|
+
*
|
|
107
|
+
* Parses an SM2 public key DER (SPKI) and returns the 65-byte uncompressed point.
|
|
108
|
+
*
|
|
109
|
+
* WebCrypto's `importKey('spki', ...)` uses this entry point.
|
|
56
110
|
*/
|
|
57
111
|
export declare function parseSm2PublicKeyDer(der: Uint8Array): Uint8Array;
|
|
58
112
|
/**
|
|
@@ -60,15 +114,33 @@ export declare function parseSm2PublicKeyDer(der: Uint8Array): Uint8Array;
|
|
|
60
114
|
*
|
|
61
115
|
* @param d 32 字节私钥(hex / Uint8Array)。
|
|
62
116
|
* @param pub 可选公钥(64 或 65 字节);给出则内嵌到 ECPrivateKey 的 `[1]`。
|
|
117
|
+
*
|
|
118
|
+
* Encodes an SM2 private key as PKCS#8 PEM (`BEGIN PRIVATE KEY`).
|
|
119
|
+
*
|
|
120
|
+
* @param d 32-byte private key (hex / Uint8Array).
|
|
121
|
+
* @param pub Optional public key (64 or 65 bytes); when given it is embedded into the
|
|
122
|
+
* `[1]` field of the ECPrivateKey.
|
|
63
123
|
*/
|
|
64
124
|
export declare function encodeSm2PrivateKeyPem(d: string | Uint8Array, pub?: string | Uint8Array): string;
|
|
65
|
-
/**
|
|
125
|
+
/**
|
|
126
|
+
* 编码 SM2 私钥为 PKCS#8 DER。
|
|
127
|
+
*
|
|
128
|
+
* Encodes an SM2 private key as PKCS#8 DER.
|
|
129
|
+
*/
|
|
66
130
|
export declare function encodeSm2PrivateKeyDer(d: string | Uint8Array, pub?: string | Uint8Array): Uint8Array;
|
|
67
131
|
/**
|
|
68
132
|
* 编码 SM2 公钥为 SPKI PEM(`BEGIN PUBLIC KEY`)。
|
|
69
133
|
*
|
|
70
134
|
* @param pub 64(X || Y)或 65(04 || X || Y)字节公钥。
|
|
135
|
+
*
|
|
136
|
+
* Encodes an SM2 public key as SPKI PEM (`BEGIN PUBLIC KEY`).
|
|
137
|
+
*
|
|
138
|
+
* @param pub 64-byte (X || Y) or 65-byte (04 || X || Y) public key.
|
|
71
139
|
*/
|
|
72
140
|
export declare function encodeSm2PublicKeyPem(pub: string | Uint8Array): string;
|
|
73
|
-
/**
|
|
141
|
+
/**
|
|
142
|
+
* 编码 SM2 公钥为 SPKI DER。
|
|
143
|
+
*
|
|
144
|
+
* Encodes an SM2 public key as SPKI DER.
|
|
145
|
+
*/
|
|
74
146
|
export declare function encodeSm2PublicKeyDer(pub: string | Uint8Array): Uint8Array;
|