sm-crypto-wasm 0.3.1 → 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.
Files changed (127) hide show
  1. package/README.md +62 -834
  2. package/dist/asn1/decode.d.ts +165 -0
  3. package/dist/asn1/decode.js +457 -0
  4. package/dist/asn1/encode.d.ts +169 -0
  5. package/dist/asn1/encode.js +414 -0
  6. package/dist/asn1/index.d.ts +17 -0
  7. package/dist/asn1/index.js +17 -0
  8. package/dist/asn1/oid.d.ts +40 -0
  9. package/dist/asn1/oid.js +136 -0
  10. package/dist/asn1/time.d.ts +71 -0
  11. package/dist/asn1/time.js +165 -0
  12. package/dist/asn1/types.d.ts +160 -0
  13. package/dist/asn1/types.js +112 -0
  14. package/dist/cms/errors.d.ts +23 -0
  15. package/dist/cms/errors.js +17 -0
  16. package/dist/cms/index.d.ts +18 -0
  17. package/dist/cms/index.js +15 -0
  18. package/dist/cms/oids.d.ts +47 -0
  19. package/dist/cms/oids.js +47 -0
  20. package/dist/cms/parse.d.ts +217 -0
  21. package/dist/cms/parse.js +420 -0
  22. package/dist/cms/sign.d.ts +113 -0
  23. package/dist/cms/sign.js +194 -0
  24. package/dist/cms/verify.d.ts +117 -0
  25. package/dist/cms/verify.js +222 -0
  26. package/dist/common/hybrid.d.ts +25 -0
  27. package/dist/common/hybrid.js +25 -0
  28. package/dist/common/sealed.d.ts +119 -15
  29. package/dist/common/sealed.js +67 -9
  30. package/dist/esbuild-plugin.d.ts +74 -5
  31. package/dist/esbuild-plugin.js +69 -4
  32. package/dist/index.d.ts +22 -0
  33. package/dist/index.js +25 -0
  34. package/dist/pem/asn1.d.ts +19 -65
  35. package/dist/pem/asn1.js +19 -288
  36. package/dist/pem/index.d.ts +2 -0
  37. package/dist/pem/index.js +2 -0
  38. package/dist/pem/pkcs8.d.ts +64 -8
  39. package/dist/pem/pkcs8.js +75 -7
  40. package/dist/pem/sm2.d.ts +90 -2
  41. package/dist/pem/sm2.js +204 -15
  42. package/dist/pem/sm9.d.ts +42 -2
  43. package/dist/pem/sm9.js +42 -2
  44. package/dist/runtime/base64.core.d.ts +25 -1
  45. package/dist/runtime/base64.core.js +31 -1
  46. package/dist/runtime/convert.d.ts +70 -7
  47. package/dist/runtime/convert.js +129 -9
  48. package/dist/runtime/errors.d.ts +64 -8
  49. package/dist/runtime/errors.js +64 -8
  50. package/dist/runtime/index.d.ts +5 -0
  51. package/dist/runtime/index.js +5 -0
  52. package/dist/runtime/loadWasm.browser.d.ts +33 -0
  53. package/dist/runtime/loadWasm.browser.js +46 -0
  54. package/dist/runtime/loadWasm.core.d.ts +59 -3
  55. package/dist/runtime/loadWasm.core.js +53 -3
  56. package/dist/runtime/loadWasm.d.ts +44 -0
  57. package/dist/runtime/loadWasm.js +79 -4
  58. package/dist/runtime/random.browser.d.ts +24 -0
  59. package/dist/runtime/random.browser.js +31 -1
  60. package/dist/runtime/random.core.d.ts +21 -0
  61. package/dist/runtime/random.core.js +27 -0
  62. package/dist/runtime/random.d.ts +28 -0
  63. package/dist/runtime/random.js +40 -1
  64. package/dist/sm2/exchange.d.ts +132 -16
  65. package/dist/sm2/exchange.js +120 -9
  66. package/dist/sm2/hybrid.d.ts +65 -3
  67. package/dist/sm2/hybrid.js +53 -1
  68. package/dist/sm2/index.d.ts +324 -16
  69. package/dist/sm2/index.js +530 -19
  70. package/dist/sm2/kem.d.ts +62 -5
  71. package/dist/sm2/kem.js +37 -2
  72. package/dist/sm2/keyInput.d.ts +36 -3
  73. package/dist/sm2/keyInput.js +36 -3
  74. package/dist/sm2/sm2.wasm +0 -0
  75. package/dist/sm3/index.d.ts +30 -0
  76. package/dist/sm3/index.js +55 -1
  77. package/dist/sm4/index.d.ts +125 -17
  78. package/dist/sm4/index.js +201 -14
  79. package/dist/sm9/core.d.ts +58 -8
  80. package/dist/sm9/core.js +36 -5
  81. package/dist/sm9/hybrid.d.ts +66 -3
  82. package/dist/sm9/hybrid.js +46 -1
  83. package/dist/sm9/index.d.ts +265 -19
  84. package/dist/sm9/index.js +175 -5
  85. package/dist/sm9/input.d.ts +37 -2
  86. package/dist/sm9/input.js +50 -1
  87. package/dist/sm9/kem.d.ts +57 -4
  88. package/dist/sm9/kem.js +47 -1
  89. package/dist/webcrypto/algorithms.d.ts +88 -0
  90. package/dist/webcrypto/algorithms.js +168 -0
  91. package/dist/webcrypto/cryptoKey.d.ts +130 -0
  92. package/dist/webcrypto/cryptoKey.js +167 -0
  93. package/dist/webcrypto/errors.d.ts +43 -0
  94. package/dist/webcrypto/errors.js +69 -0
  95. package/dist/webcrypto/index.d.ts +24 -0
  96. package/dist/webcrypto/index.js +21 -0
  97. package/dist/webcrypto/subtle.d.ts +118 -0
  98. package/dist/webcrypto/subtle.js +426 -0
  99. package/dist/x509/encode.d.ts +129 -0
  100. package/dist/x509/encode.js +248 -0
  101. package/dist/x509/errors.d.ts +29 -0
  102. package/dist/x509/errors.js +29 -0
  103. package/dist/x509/extensions.d.ts +66 -0
  104. package/dist/x509/extensions.js +273 -0
  105. package/dist/x509/index.d.ts +21 -0
  106. package/dist/x509/index.js +30 -0
  107. package/dist/x509/ip.d.ts +21 -0
  108. package/dist/x509/ip.js +122 -0
  109. package/dist/x509/issue.d.ts +87 -0
  110. package/dist/x509/issue.js +521 -0
  111. package/dist/x509/oids.d.ts +74 -0
  112. package/dist/x509/oids.js +103 -0
  113. package/dist/x509/parse.d.ts +45 -0
  114. package/dist/x509/parse.js +550 -0
  115. package/dist/x509/sha1.d.ts +20 -0
  116. package/dist/x509/sha1.js +89 -0
  117. package/dist/x509/sha256.d.ts +20 -0
  118. package/dist/x509/sha256.js +114 -0
  119. package/dist/x509/sig.d.ts +35 -0
  120. package/dist/x509/sig.js +104 -0
  121. package/dist/x509/types.d.ts +490 -0
  122. package/dist/x509/types.js +13 -0
  123. package/dist/x509/verify.d.ts +91 -0
  124. package/dist/x509/verify.js +105 -0
  125. package/dist/zuc/index.d.ts +128 -10
  126. package/dist/zuc/index.js +136 -7
  127. package/package.json +57 -28
@@ -0,0 +1,165 @@
1
+ /**
2
+ * ASN.1 DER/BER 解析器。
3
+ *
4
+ * 覆盖 X.509 与 CMS 需要的 BER/DER 子集:
5
+ * - definite-length(短/长格式);
6
+ * - BER indefinite-length(`0x80` + EOC `00 00`),**仅 `parseBer` 允许**;
7
+ * - 高 tag number(`0x1f` + base128);
8
+ * - constructed / primitive 递归。
9
+ *
10
+ * 严格性:
11
+ * - `parseDer` 拒绝 indefinite、非最小长度编码、EOC、尾部多余字节;
12
+ * - `parseBer` 放宽长度编码并允许 indefinite,但仍拒绝截断与尾部多余字节;
13
+ * - 单对象解析一律拒绝尾部多余字节(红线 #3)。
14
+ *
15
+ * 每个节点保留 `raw`(输入缓冲上的视图),供 CMS 验签与证书 TBS 直接使用。
16
+ *
17
+ * ASN.1 DER/BER parser.
18
+ *
19
+ * Covers the BER/DER subset needed by X.509 and CMS:
20
+ * - definite-length (short and long form);
21
+ * - BER indefinite-length (`0x80` + EOC `00 00`), allowed **only in `parseBer`**;
22
+ * - high tag numbers (`0x1f` + base128);
23
+ * - recursive constructed / primitive handling.
24
+ *
25
+ * Strictness:
26
+ * - `parseDer` rejects indefinite length, non-minimal length encodings, EOC and
27
+ * trailing bytes;
28
+ * - `parseBer` relaxes length encoding and allows indefinite length, but still
29
+ * rejects truncation and trailing bytes;
30
+ * - single-object parsing always rejects trailing bytes (red line #3).
31
+ *
32
+ * Every node keeps `raw` (a view onto the input buffer) so that CMS signature
33
+ * verification and certificate TBS hashing can use it directly.
34
+ */
35
+ import { type Asn1Node } from './types.js';
36
+ /**
37
+ * 解析选项。
38
+ *
39
+ * Parse options.
40
+ */
41
+ export interface ParseOptions {
42
+ /**
43
+ * 允许 BER indefinite-length。仅 CMS 路径应开启。
44
+ *
45
+ * Allow BER indefinite-length; only the CMS path should enable this.
46
+ */
47
+ allowIndefinite?: boolean;
48
+ /**
49
+ * 是否严格校验长度编码的最小形式(DER 默认开启)。
50
+ *
51
+ * Whether to enforce minimal length encoding (on by default for DER).
52
+ */
53
+ strictLength?: boolean;
54
+ }
55
+ /**
56
+ * 解析单个 DER 对象。
57
+ *
58
+ * @throws Asn1Error 截断、超出深度、非 DER 编码、或根节点后有多余字节。
59
+ *
60
+ * Parses a single DER object.
61
+ *
62
+ * @throws Asn1Error On truncation, excessive depth, non-DER encoding, or trailing
63
+ * bytes after the root node.
64
+ */
65
+ export declare function parseDer(buf: Uint8Array): Asn1Node;
66
+ /**
67
+ * 解析单个 BER 对象(允许 indefinite-length)。
68
+ *
69
+ * @throws Asn1Error 截断、超出深度、或根节点后有多余字节。
70
+ *
71
+ * Parses a single BER object (indefinite-length allowed).
72
+ *
73
+ * @throws Asn1Error On truncation, excessive depth, or trailing bytes after the
74
+ * root node.
75
+ */
76
+ export declare function parseBer(buf: Uint8Array): Asn1Node;
77
+ /**
78
+ * 解析连续的多个 DER 对象(证书 bundle / CMS 文件)。
79
+ *
80
+ * 返回按出现顺序排列的节点;允许空输入并返回空数组。
81
+ *
82
+ * Parses a sequence of consecutive DER objects (a certificate bundle or a CMS file).
83
+ *
84
+ * Returns the nodes in order of appearance; empty input is allowed and yields an
85
+ * empty array.
86
+ */
87
+ export declare function parseDerAll(buf: Uint8Array): Asn1Node[];
88
+ /**
89
+ * 解析连续的多个 BER 对象(允许 indefinite-length)。
90
+ *
91
+ * Parses a sequence of consecutive BER objects (indefinite-length allowed).
92
+ */
93
+ export declare function parseBerAll(buf: Uint8Array): Asn1Node[];
94
+ /**
95
+ * 读取 INTEGER(二进制补码,支持负数)。
96
+ *
97
+ * @throws Asn1Error 节点不是 INTEGER。
98
+ *
99
+ * Reads an INTEGER (two's complement, negative values supported).
100
+ *
101
+ * @throws Asn1Error When the node is not an INTEGER.
102
+ */
103
+ export declare function readInteger(node: Asn1Node, what?: string): bigint;
104
+ /**
105
+ * 读取 BOOLEAN(DER 中 TRUE 必须为 0xFF)。
106
+ *
107
+ * Reads a BOOLEAN (DER requires TRUE to be 0xFF).
108
+ */
109
+ export declare function readBoolean(node: Asn1Node, what?: string): boolean;
110
+ /**
111
+ * BIT STRING 内容。
112
+ *
113
+ * Contents of a BIT STRING.
114
+ */
115
+ export interface BitString {
116
+ /**
117
+ * 去掉 unused bits 后的有效字节(尾部未用位保留,由调用方屏蔽)。
118
+ *
119
+ * The payload bytes, with the unused-bits header removed (the trailing unused
120
+ * bits stay in place and must be masked by the caller).
121
+ */
122
+ readonly bytes: Uint8Array;
123
+ /**
124
+ * 末尾未使用位数(0..7)。
125
+ *
126
+ * Number of unused trailing bits (0..7).
127
+ */
128
+ readonly unusedBits: number;
129
+ }
130
+ /**
131
+ * 读取 BIT STRING。
132
+ *
133
+ * Reads a BIT STRING.
134
+ */
135
+ export declare function readBitString(node: Asn1Node, what?: string): BitString;
136
+ /**
137
+ * 读取 OCTET STRING。
138
+ *
139
+ * Reads an OCTET STRING.
140
+ */
141
+ export declare function readOctetString(node: Asn1Node, what?: string): Uint8Array;
142
+ /**
143
+ * 读取 OBJECT IDENTIFIER。
144
+ *
145
+ * Reads an OBJECT IDENTIFIER.
146
+ */
147
+ export declare function readOid(node: Asn1Node, what?: string): string;
148
+ /**
149
+ * 读取 UTCTime / GeneralizedTime。
150
+ *
151
+ * Reads a UTCTime / GeneralizedTime.
152
+ */
153
+ export declare function readTime(node: Asn1Node, what?: string): Date;
154
+ /**
155
+ * 读取 ASN.1 字符串。
156
+ *
157
+ * 支持 UTF8String / PrintableString / IA5String / NumericString /
158
+ * TeletexString / BMPString / UniversalString;其他类型抛 `Asn1Error`。
159
+ *
160
+ * Reads an ASN.1 string.
161
+ *
162
+ * Supports UTF8String / PrintableString / IA5String / NumericString /
163
+ * TeletexString / BMPString / UniversalString; other types throw `Asn1Error`.
164
+ */
165
+ export declare function readString(node: Asn1Node, what?: string): string;
@@ -0,0 +1,457 @@
1
+ /**
2
+ * ASN.1 DER/BER 解析器。
3
+ *
4
+ * 覆盖 X.509 与 CMS 需要的 BER/DER 子集:
5
+ * - definite-length(短/长格式);
6
+ * - BER indefinite-length(`0x80` + EOC `00 00`),**仅 `parseBer` 允许**;
7
+ * - 高 tag number(`0x1f` + base128);
8
+ * - constructed / primitive 递归。
9
+ *
10
+ * 严格性:
11
+ * - `parseDer` 拒绝 indefinite、非最小长度编码、EOC、尾部多余字节;
12
+ * - `parseBer` 放宽长度编码并允许 indefinite,但仍拒绝截断与尾部多余字节;
13
+ * - 单对象解析一律拒绝尾部多余字节(红线 #3)。
14
+ *
15
+ * 每个节点保留 `raw`(输入缓冲上的视图),供 CMS 验签与证书 TBS 直接使用。
16
+ *
17
+ * ASN.1 DER/BER parser.
18
+ *
19
+ * Covers the BER/DER subset needed by X.509 and CMS:
20
+ * - definite-length (short and long form);
21
+ * - BER indefinite-length (`0x80` + EOC `00 00`), allowed **only in `parseBer`**;
22
+ * - high tag numbers (`0x1f` + base128);
23
+ * - recursive constructed / primitive handling.
24
+ *
25
+ * Strictness:
26
+ * - `parseDer` rejects indefinite length, non-minimal length encodings, EOC and
27
+ * trailing bytes;
28
+ * - `parseBer` relaxes length encoding and allows indefinite length, but still
29
+ * rejects truncation and trailing bytes;
30
+ * - single-object parsing always rejects trailing bytes (red line #3).
31
+ *
32
+ * Every node keeps `raw` (a view onto the input buffer) so that CMS signature
33
+ * verification and certificate TBS hashing can use it directly.
34
+ */
35
+ import { decodeOid } from './oid.js';
36
+ import { parseTime } from './time.js';
37
+ import { Asn1Class, Asn1Error, UniversalTag } from './types.js';
38
+ /**
39
+ * constructed 嵌套深度上限,防栈溢出与恶意深嵌套。
40
+ *
41
+ * Upper bound on constructed nesting depth, to avoid stack overflow and hostile
42
+ * deeply nested input.
43
+ */
44
+ const MAX_DEPTH = 64;
45
+ /**
46
+ * 解析单个 DER 对象。
47
+ *
48
+ * @throws Asn1Error 截断、超出深度、非 DER 编码、或根节点后有多余字节。
49
+ *
50
+ * Parses a single DER object.
51
+ *
52
+ * @throws Asn1Error On truncation, excessive depth, non-DER encoding, or trailing
53
+ * bytes after the root node.
54
+ */
55
+ export function parseDer(buf) {
56
+ return parseSingle(buf, { allowIndefinite: false, strictLength: true }, 'parseDer');
57
+ }
58
+ /**
59
+ * 解析单个 BER 对象(允许 indefinite-length)。
60
+ *
61
+ * @throws Asn1Error 截断、超出深度、或根节点后有多余字节。
62
+ *
63
+ * Parses a single BER object (indefinite-length allowed).
64
+ *
65
+ * @throws Asn1Error On truncation, excessive depth, or trailing bytes after the
66
+ * root node.
67
+ */
68
+ export function parseBer(buf) {
69
+ return parseSingle(buf, { allowIndefinite: true, strictLength: false }, 'parseBer');
70
+ }
71
+ /**
72
+ * 解析连续的多个 DER 对象(证书 bundle / CMS 文件)。
73
+ *
74
+ * 返回按出现顺序排列的节点;允许空输入并返回空数组。
75
+ *
76
+ * Parses a sequence of consecutive DER objects (a certificate bundle or a CMS file).
77
+ *
78
+ * Returns the nodes in order of appearance; empty input is allowed and yields an
79
+ * empty array.
80
+ */
81
+ export function parseDerAll(buf) {
82
+ return parseAll(buf, { allowIndefinite: false, strictLength: true }, 'parseDerAll');
83
+ }
84
+ /**
85
+ * 解析连续的多个 BER 对象(允许 indefinite-length)。
86
+ *
87
+ * Parses a sequence of consecutive BER objects (indefinite-length allowed).
88
+ */
89
+ export function parseBerAll(buf) {
90
+ return parseAll(buf, { allowIndefinite: true, strictLength: false }, 'parseBerAll');
91
+ }
92
+ /**
93
+ * 解析根节点,并要求输入完全被消费。
94
+ *
95
+ * Parses the root node and requires the whole input to be consumed.
96
+ */
97
+ function parseSingle(buf, opts, where) {
98
+ const reader = new Reader(buf, opts, where);
99
+ const node = reader.readNode(0);
100
+ if (reader.remaining() !== 0) {
101
+ throw new Asn1Error(`${where}: ${reader.remaining()} trailing bytes after root node`);
102
+ }
103
+ return node;
104
+ }
105
+ /**
106
+ * 解析所有顶层对象,直到输入耗尽。
107
+ *
108
+ * Parses every top-level object until the input is exhausted.
109
+ */
110
+ function parseAll(buf, opts, where) {
111
+ const reader = new Reader(buf, opts, where);
112
+ const nodes = [];
113
+ while (reader.remaining() > 0) {
114
+ nodes.push(reader.readNode(0));
115
+ }
116
+ return nodes;
117
+ }
118
+ /**
119
+ * 游标式 TLV 读取器(持有单一输入缓冲与位置)。
120
+ *
121
+ * Cursor-style TLV reader over a single input buffer.
122
+ */
123
+ class Reader {
124
+ buf;
125
+ opts;
126
+ where;
127
+ pos = 0;
128
+ constructor(buf, opts, where) {
129
+ this.buf = buf;
130
+ this.opts = opts;
131
+ this.where = where;
132
+ }
133
+ /**
134
+ * 距输入末尾还剩多少字节。
135
+ *
136
+ * Number of bytes left before the end of the input.
137
+ */
138
+ remaining() {
139
+ return this.buf.length - this.pos;
140
+ }
141
+ /**
142
+ * 读取一个字节,越界则报错。
143
+ *
144
+ * Reads one byte, throwing on out-of-bounds.
145
+ */
146
+ byte() {
147
+ if (this.pos >= this.buf.length) {
148
+ throw new Asn1Error(`${this.where}: truncated at offset ${this.pos}`);
149
+ }
150
+ return this.buf[this.pos++];
151
+ }
152
+ /**
153
+ * 读取一个完整 TLV。
154
+ *
155
+ * Reads one complete TLV.
156
+ */
157
+ readNode(depth) {
158
+ if (depth > MAX_DEPTH) {
159
+ throw new Asn1Error(`${this.where}: nesting deeper than ${MAX_DEPTH}`);
160
+ }
161
+ const start = this.pos;
162
+ const first = this.byte();
163
+ const cls = ((first >> 6) & 0x03);
164
+ const constructed = (first & 0x20) !== 0;
165
+ const tag = (first & 0x1f) === 0x1f ? this.readHighTag() : first & 0x1f;
166
+ // EndOfContent 只能作为 indefinite-length 的结束标记出现。
167
+ //
168
+ // EndOfContent may only appear as the terminator of an indefinite-length value.
169
+ if (cls === Asn1Class.Universal && tag === UniversalTag.EndOfContent && !constructed) {
170
+ const lb = this.byte();
171
+ if (lb !== 0)
172
+ throw new Asn1Error(`${this.where}: malformed EndOfContent`);
173
+ throw new Asn1Error(`${this.where}: unexpected EndOfContent at offset ${start}`);
174
+ }
175
+ const lb = this.byte();
176
+ let length = 0;
177
+ let indefinite = false;
178
+ if (lb === 0x80) {
179
+ if (!this.opts.allowIndefinite) {
180
+ throw new Asn1Error(`${this.where}: indefinite-length encoding is not allowed`);
181
+ }
182
+ if (!constructed) {
183
+ throw new Asn1Error(`${this.where}: indefinite length on primitive tag ${tag}`);
184
+ }
185
+ indefinite = true;
186
+ }
187
+ else if (lb < 0x80) {
188
+ length = lb;
189
+ }
190
+ else {
191
+ length = this.readLongLength(lb & 0x7f);
192
+ }
193
+ const contentStart = this.pos;
194
+ if (indefinite) {
195
+ // indefinite:一直读到 EOC(00 00)。
196
+ //
197
+ // Indefinite: keep reading until the EOC (00 00).
198
+ const children = [];
199
+ for (;;) {
200
+ if (this.remaining() < 2) {
201
+ throw new Asn1Error(`${this.where}: truncated indefinite-length content`);
202
+ }
203
+ if (this.buf[this.pos] === 0 && this.buf[this.pos + 1] === 0) {
204
+ this.pos += 2;
205
+ break;
206
+ }
207
+ children.push(this.readNode(depth + 1));
208
+ }
209
+ const contentEnd = this.pos - 2;
210
+ const value = this.buf.subarray(contentStart, contentEnd);
211
+ return {
212
+ cls,
213
+ tag,
214
+ constructed,
215
+ indefinite,
216
+ length: value.length,
217
+ value,
218
+ children,
219
+ raw: this.buf.subarray(start, this.pos),
220
+ };
221
+ }
222
+ if (this.pos + length > this.buf.length) {
223
+ throw new Asn1Error(`${this.where}: truncated value, need ${length} bytes at offset ${this.pos}`);
224
+ }
225
+ this.pos += length;
226
+ const value = this.buf.subarray(contentStart, contentStart + length);
227
+ if (constructed) {
228
+ const inner = new Reader(value, this.opts, this.where);
229
+ const children = [];
230
+ while (inner.remaining() > 0) {
231
+ children.push(inner.readNode(depth + 1));
232
+ }
233
+ return {
234
+ cls,
235
+ tag,
236
+ constructed,
237
+ indefinite,
238
+ length,
239
+ value,
240
+ children,
241
+ raw: this.buf.subarray(start, this.pos),
242
+ };
243
+ }
244
+ return {
245
+ cls,
246
+ tag,
247
+ constructed,
248
+ indefinite,
249
+ length,
250
+ value,
251
+ raw: this.buf.subarray(start, this.pos),
252
+ };
253
+ }
254
+ /**
255
+ * 高 tag number:后续字节 base128。
256
+ *
257
+ * High tag number: subsequent bytes are base128.
258
+ */
259
+ readHighTag() {
260
+ let value = 0;
261
+ let count = 0;
262
+ for (;;) {
263
+ const b = this.byte();
264
+ if (count === 0 && b === 0x80) {
265
+ throw new Asn1Error(`${this.where}: non-minimal high tag number`);
266
+ }
267
+ if (count >= 4) {
268
+ throw new Asn1Error(`${this.where}: tag number too large`);
269
+ }
270
+ value = value * 128 + (b & 0x7f);
271
+ count++;
272
+ if ((b & 0x80) === 0)
273
+ return value;
274
+ }
275
+ }
276
+ /**
277
+ * 长格式长度(`n` 为 1..4)。
278
+ *
279
+ * Long-form length (`n` is 1..4).
280
+ */
281
+ readLongLength(n) {
282
+ if (n === 0)
283
+ throw new Asn1Error(`${this.where}: reserved length encoding 0x80`);
284
+ if (n > 4)
285
+ throw new Asn1Error(`${this.where}: length of ${n} bytes is not supported`);
286
+ const lengthStart = this.pos;
287
+ let length = 0;
288
+ for (let i = 0; i < n; i++) {
289
+ length = length * 256 + this.byte();
290
+ }
291
+ if (length < 0 || !Number.isSafeInteger(length)) {
292
+ throw new Asn1Error(`${this.where}: length out of range`);
293
+ }
294
+ if (this.opts.strictLength) {
295
+ if (this.buf[lengthStart] === 0) {
296
+ throw new Asn1Error(`${this.where}: non-minimal long-form length (leading zero)`);
297
+ }
298
+ if (length < 0x80) {
299
+ throw new Asn1Error(`${this.where}: non-minimal long-form length ${length}`);
300
+ }
301
+ }
302
+ return length;
303
+ }
304
+ }
305
+ // ----------------- 常用值读取 -----------------
306
+ //
307
+ // ----------------- Common value readers -----------------
308
+ /**
309
+ * 读取 INTEGER(二进制补码,支持负数)。
310
+ *
311
+ * @throws Asn1Error 节点不是 INTEGER。
312
+ *
313
+ * Reads an INTEGER (two's complement, negative values supported).
314
+ *
315
+ * @throws Asn1Error When the node is not an INTEGER.
316
+ */
317
+ export function readInteger(node, what = 'INTEGER') {
318
+ if (node.cls !== Asn1Class.Universal || node.tag !== UniversalTag.Integer) {
319
+ throw new Asn1Error(`${what}: expected INTEGER`);
320
+ }
321
+ if (node.value.length === 0)
322
+ throw new Asn1Error(`${what}: empty INTEGER`);
323
+ let value = 0n;
324
+ for (let i = 0; i < node.value.length; i++) {
325
+ value = (value << 8n) | BigInt(node.value[i]);
326
+ }
327
+ if ((node.value[0] & 0x80) !== 0) {
328
+ value -= 1n << BigInt(node.value.length * 8);
329
+ }
330
+ return value;
331
+ }
332
+ /**
333
+ * 读取 BOOLEAN(DER 中 TRUE 必须为 0xFF)。
334
+ *
335
+ * Reads a BOOLEAN (DER requires TRUE to be 0xFF).
336
+ */
337
+ export function readBoolean(node, what = 'BOOLEAN') {
338
+ if (node.cls !== Asn1Class.Universal || node.tag !== UniversalTag.Boolean) {
339
+ throw new Asn1Error(`${what}: expected BOOLEAN`);
340
+ }
341
+ if (node.value.length !== 1)
342
+ throw new Asn1Error(`${what}: BOOLEAN must be 1 byte`);
343
+ return node.value[0] !== 0;
344
+ }
345
+ /**
346
+ * 读取 BIT STRING。
347
+ *
348
+ * Reads a BIT STRING.
349
+ */
350
+ export function readBitString(node, what = 'BIT STRING') {
351
+ if (node.cls !== Asn1Class.Universal || node.tag !== UniversalTag.BitString) {
352
+ throw new Asn1Error(`${what}: expected BIT STRING`);
353
+ }
354
+ if (node.value.length === 0)
355
+ throw new Asn1Error(`${what}: empty BIT STRING`);
356
+ const unusedBits = node.value[0];
357
+ if (unusedBits > 7)
358
+ throw new Asn1Error(`${what}: invalid unused-bit count ${unusedBits}`);
359
+ return { bytes: node.value.subarray(1), unusedBits };
360
+ }
361
+ /**
362
+ * 读取 OCTET STRING。
363
+ *
364
+ * Reads an OCTET STRING.
365
+ */
366
+ export function readOctetString(node, what = 'OCTET STRING') {
367
+ if (node.cls !== Asn1Class.Universal || node.tag !== UniversalTag.OctetString) {
368
+ throw new Asn1Error(`${what}: expected OCTET STRING`);
369
+ }
370
+ return node.value;
371
+ }
372
+ /**
373
+ * 读取 OBJECT IDENTIFIER。
374
+ *
375
+ * Reads an OBJECT IDENTIFIER.
376
+ */
377
+ export function readOid(node, what = 'OBJECT IDENTIFIER') {
378
+ if (node.cls !== Asn1Class.Universal || node.tag !== UniversalTag.ObjectIdentifier) {
379
+ throw new Asn1Error(`${what}: expected OBJECT IDENTIFIER`);
380
+ }
381
+ return decodeOid(node.value);
382
+ }
383
+ /**
384
+ * 读取 UTCTime / GeneralizedTime。
385
+ *
386
+ * Reads a UTCTime / GeneralizedTime.
387
+ */
388
+ export function readTime(node, what = 'Time') {
389
+ if (node.cls !== Asn1Class.Universal) {
390
+ throw new Asn1Error(`${what}: expected UTCTime or GeneralizedTime`);
391
+ }
392
+ if (node.tag !== UniversalTag.UtcTime && node.tag !== UniversalTag.GeneralizedTime) {
393
+ throw new Asn1Error(`${what}: expected UTCTime or GeneralizedTime`);
394
+ }
395
+ const text = latin1(node.value);
396
+ return parseTime(text);
397
+ }
398
+ /**
399
+ * 读取 ASN.1 字符串。
400
+ *
401
+ * 支持 UTF8String / PrintableString / IA5String / NumericString /
402
+ * TeletexString / BMPString / UniversalString;其他类型抛 `Asn1Error`。
403
+ *
404
+ * Reads an ASN.1 string.
405
+ *
406
+ * Supports UTF8String / PrintableString / IA5String / NumericString /
407
+ * TeletexString / BMPString / UniversalString; other types throw `Asn1Error`.
408
+ */
409
+ export function readString(node, what = 'string') {
410
+ if (node.cls !== Asn1Class.Universal) {
411
+ throw new Asn1Error(`${what}: expected a UNIVERSAL string type`);
412
+ }
413
+ switch (node.tag) {
414
+ case UniversalTag.Utf8String:
415
+ return new TextDecoder('utf-8', { fatal: true }).decode(node.value);
416
+ case UniversalTag.PrintableString:
417
+ case UniversalTag.Ia5String:
418
+ case UniversalTag.NumericString:
419
+ case UniversalTag.TeletexString:
420
+ return latin1(node.value);
421
+ case UniversalTag.BmpString: {
422
+ if (node.value.length % 2 !== 0)
423
+ throw new Asn1Error(`${what}: odd-length BMPString`);
424
+ let out = '';
425
+ for (let i = 0; i < node.value.length; i += 2) {
426
+ out += String.fromCharCode((node.value[i] << 8) | node.value[i + 1]);
427
+ }
428
+ return out;
429
+ }
430
+ case UniversalTag.UniversalString: {
431
+ if (node.value.length % 4 !== 0)
432
+ throw new Asn1Error(`${what}: bad UniversalString`);
433
+ let out = '';
434
+ for (let i = 0; i < node.value.length; i += 4) {
435
+ const cp = node.value[i] * 0x1000000 +
436
+ (node.value[i + 1] << 16) +
437
+ (node.value[i + 2] << 8) +
438
+ node.value[i + 3];
439
+ out += String.fromCodePoint(cp);
440
+ }
441
+ return out;
442
+ }
443
+ default:
444
+ throw new Asn1Error(`${what}: unsupported string tag ${node.tag}`);
445
+ }
446
+ }
447
+ /**
448
+ * 字节 → 单字节字符(低 8 位),用于 ASCII 类字符串。
449
+ *
450
+ * Bytes to one-code-unit string, used for ASCII-like string types.
451
+ */
452
+ function latin1(bytes) {
453
+ let out = '';
454
+ for (let i = 0; i < bytes.length; i++)
455
+ out += String.fromCharCode(bytes[i]);
456
+ return out;
457
+ }