sm-crypto-wasm 0.3.0 → 0.7.0

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 (76) hide show
  1. package/README.md +76 -7
  2. package/dist/asn1/decode.d.ts +74 -0
  3. package/dist/asn1/decode.js +338 -0
  4. package/dist/asn1/encode.d.ts +68 -0
  5. package/dist/asn1/encode.js +275 -0
  6. package/dist/asn1/index.d.ts +11 -0
  7. package/dist/asn1/index.js +11 -0
  8. package/dist/asn1/oid.d.ts +22 -0
  9. package/dist/asn1/oid.js +107 -0
  10. package/dist/asn1/time.d.ts +39 -0
  11. package/dist/asn1/time.js +111 -0
  12. package/dist/asn1/types.d.ts +76 -0
  13. package/dist/asn1/types.js +71 -0
  14. package/dist/cms/errors.d.ts +13 -0
  15. package/dist/cms/errors.js +11 -0
  16. package/dist/cms/index.d.ts +13 -0
  17. package/dist/cms/index.js +10 -0
  18. package/dist/cms/oids.d.ts +17 -0
  19. package/dist/cms/oids.js +17 -0
  20. package/dist/cms/parse.d.ts +75 -0
  21. package/dist/cms/parse.js +326 -0
  22. package/dist/cms/sign.d.ts +43 -0
  23. package/dist/cms/sign.js +142 -0
  24. package/dist/cms/verify.d.ts +42 -0
  25. package/dist/cms/verify.js +173 -0
  26. package/dist/index.d.ts +3 -0
  27. package/dist/index.js +3 -0
  28. package/dist/pem/asn1.d.ts +10 -69
  29. package/dist/pem/asn1.js +10 -292
  30. package/dist/pem/pkcs8.js +9 -1
  31. package/dist/pem/sm2.d.ts +16 -0
  32. package/dist/pem/sm2.js +37 -5
  33. package/dist/runtime/base64.core.d.ts +22 -0
  34. package/dist/runtime/base64.core.js +68 -0
  35. package/dist/runtime/convert.d.ts +12 -3
  36. package/dist/runtime/convert.js +31 -12
  37. package/dist/sm2/index.d.ts +69 -0
  38. package/dist/sm2/index.js +188 -0
  39. package/dist/sm2/sm2.wasm +0 -0
  40. package/dist/webcrypto/algorithms.d.ts +42 -0
  41. package/dist/webcrypto/algorithms.js +120 -0
  42. package/dist/webcrypto/cryptoKey.d.ts +64 -0
  43. package/dist/webcrypto/cryptoKey.js +109 -0
  44. package/dist/webcrypto/errors.d.ts +19 -0
  45. package/dist/webcrypto/errors.js +45 -0
  46. package/dist/webcrypto/index.d.ts +14 -0
  47. package/dist/webcrypto/index.js +11 -0
  48. package/dist/webcrypto/subtle.d.ts +68 -0
  49. package/dist/webcrypto/subtle.js +358 -0
  50. package/dist/x509/encode.d.ts +59 -0
  51. package/dist/x509/encode.js +175 -0
  52. package/dist/x509/errors.d.ts +14 -0
  53. package/dist/x509/errors.js +14 -0
  54. package/dist/x509/extensions.d.ts +27 -0
  55. package/dist/x509/extensions.js +215 -0
  56. package/dist/x509/index.d.ts +14 -0
  57. package/dist/x509/index.js +11 -0
  58. package/dist/x509/ip.d.ts +12 -0
  59. package/dist/x509/ip.js +101 -0
  60. package/dist/x509/issue.d.ts +44 -0
  61. package/dist/x509/issue.js +414 -0
  62. package/dist/x509/oids.d.ts +40 -0
  63. package/dist/x509/oids.js +52 -0
  64. package/dist/x509/parse.d.ts +24 -0
  65. package/dist/x509/parse.js +436 -0
  66. package/dist/x509/sha1.d.ts +9 -0
  67. package/dist/x509/sha1.js +72 -0
  68. package/dist/x509/sha256.d.ts +9 -0
  69. package/dist/x509/sha256.js +84 -0
  70. package/dist/x509/sig.d.ts +20 -0
  71. package/dist/x509/sig.js +77 -0
  72. package/dist/x509/types.d.ts +235 -0
  73. package/dist/x509/types.js +7 -0
  74. package/dist/x509/verify.d.ts +44 -0
  75. package/dist/x509/verify.js +66 -0
  76. package/package.json +16 -3
@@ -0,0 +1,142 @@
1
+ /**
2
+ * CMS SignedData 签名(detached / attached)。
3
+ *
4
+ * 签名的计算对象是 `signedAttrs` 的 DER 编码(SET tag 0x31)——与验签层
5
+ * 使用完全相同的规则;`contentType` / `messageDigest` / `signingTime` 三个
6
+ * 属性按 DER SET OF 排序后编码。
7
+ *
8
+ * 只做 SignedData,不做 EnvelopedData / RFC 3161 时间戳。
9
+ */
10
+ import { explicit, implicit, integer, octetString, oid, parseDer, seq, setOf, time, } from '../asn1/index.js';
11
+ import { bytesToBase64, bytesToPem, hexToBytes } from '../runtime/convert.js';
12
+ import * as sm2 from '../sm2/index.js';
13
+ import * as sm3 from '../sm3/index.js';
14
+ import { parseCertificate } from '../x509/parse.js';
15
+ import { rawSignatureToDer } from '../x509/sig.js';
16
+ import { CmsError } from './errors.js';
17
+ import { OID_CMS_CONTENT_TYPE, OID_CMS_DATA, OID_CMS_MESSAGE_DIGEST, OID_CMS_SIGNED_DATA, OID_CMS_SIGNING_TIME, OID_SM3, OID_SM3_WITH_SM2, } from './oids.js';
18
+ /**
19
+ * 生成 CMS SignedData(单签名者)。
20
+ *
21
+ * 调用前需要预热 wasm:`await sm2.ensureLoaded(); await sm3.ensureLoaded();`
22
+ *
23
+ * @throws CmsError 缺少签名者证书或输入不合法。
24
+ */
25
+ export function signData(opts) {
26
+ const content = toDataBytes(opts.data);
27
+ const signerCertificate = resolveSignerCertificate(opts);
28
+ const chain = (opts.certificates ?? []).map((item) => resolveCertificate(item));
29
+ const certificates = dedupeCertificates([signerCertificate, ...chain]);
30
+ const digest = hexToBytes(sm3.hash(content), 'messageDigest');
31
+ const signingTime = opts.signingTime === false ? undefined : (opts.signingTime ?? new Date());
32
+ const attributes = [
33
+ attribute(OID_CMS_CONTENT_TYPE, setOf([oid(OID_CMS_DATA)])),
34
+ attribute(OID_CMS_MESSAGE_DIGEST, setOf([octetString(digest)])),
35
+ ];
36
+ if (signingTime !== undefined) {
37
+ attributes.push(attribute(OID_CMS_SIGNING_TIME, setOf([time(signingTime)])));
38
+ }
39
+ const signedAttrs = setOf(attributes);
40
+ const signature = hexToBytes(sm2.signWithID(opts.privateKey, signedAttrs.raw, opts.userID ?? new Uint8Array(0)), 'signature');
41
+ const signatureDer = rawSignatureToDer(signature);
42
+ const signerInfo = seq([
43
+ integer(1),
44
+ issuerAndSerialNumber(signerCertificate),
45
+ algorithmIdentifier(OID_SM3),
46
+ implicit(0, signedAttrs),
47
+ algorithmIdentifier(OID_SM3_WITH_SM2),
48
+ octetString(signatureDer),
49
+ ]);
50
+ const encapContentInfo = opts.detached === false
51
+ ? seq([oid(OID_CMS_DATA), explicit(0, octetString(content))])
52
+ : seq([oid(OID_CMS_DATA)]);
53
+ const signedDataChildren = [
54
+ integer(1),
55
+ setOf([algorithmIdentifier(OID_SM3)]),
56
+ encapContentInfo,
57
+ ];
58
+ if (certificates.length > 0) {
59
+ signedDataChildren.push(implicit(0, setOf(certificates.map((cert) => parseDer(cert.raw)))));
60
+ }
61
+ signedDataChildren.push(setOf([signerInfo]));
62
+ const signedData = seq(signedDataChildren);
63
+ const contentInfo = seq([oid(OID_CMS_SIGNED_DATA), explicit(0, signedData)]);
64
+ const der = new Uint8Array(contentInfo.raw);
65
+ return {
66
+ der,
67
+ pem: bytesToPem(der, 'CMS'),
68
+ base64: bytesToBase64(der),
69
+ };
70
+ }
71
+ // ---------- 编码辅助 ----------
72
+ function attribute(attrOid, values) {
73
+ return seq([oid(attrOid), values]);
74
+ }
75
+ function algorithmIdentifier(oidValue) {
76
+ return seq([oid(oidValue)]);
77
+ }
78
+ function issuerAndSerialNumber(cert) {
79
+ let issuer;
80
+ try {
81
+ issuer = parseDer(cert.issuer.raw);
82
+ }
83
+ catch (err) {
84
+ throw new CmsError('signData: failed to re-encode issuer name', { cause: err });
85
+ }
86
+ return seq([issuer, integer(cert.serialNumber)]);
87
+ }
88
+ // ---------- 输入辅助 ----------
89
+ /** 字符串按 UTF-8 处理;CMS 的 data 语义是报文内容,不是 hex。 */
90
+ function toDataBytes(data) {
91
+ if (data instanceof Uint8Array)
92
+ return new Uint8Array(data);
93
+ if (typeof data === 'string')
94
+ return new TextEncoder().encode(data);
95
+ throw new TypeError('signData: data must be a string or Uint8Array');
96
+ }
97
+ function resolveSignerCertificate(opts) {
98
+ const input = opts.certificate ?? opts.certificates?.[0];
99
+ if (input === undefined) {
100
+ throw new CmsError('signData: certificate is required to build the SignerInfo');
101
+ }
102
+ return resolveCertificate(input);
103
+ }
104
+ function resolveCertificate(input) {
105
+ if (isCertificateLike(input))
106
+ return input;
107
+ try {
108
+ return parseCertificate(input);
109
+ }
110
+ catch (err) {
111
+ throw new CmsError('signData: invalid certificate', { cause: err });
112
+ }
113
+ }
114
+ function isCertificateLike(value) {
115
+ return (typeof value === 'object' &&
116
+ value !== null &&
117
+ !(value instanceof Uint8Array) &&
118
+ 'tbsCertificate' in value &&
119
+ 'subjectPublicKey' in value);
120
+ }
121
+ function dedupeCertificates(certificates) {
122
+ const seen = new Set();
123
+ const out = [];
124
+ for (const cert of certificates) {
125
+ const key = base64Key(cert.raw);
126
+ if (seen.has(key))
127
+ continue;
128
+ seen.add(key);
129
+ out.push(cert);
130
+ }
131
+ return out;
132
+ }
133
+ function base64Key(bytes) {
134
+ let key = '';
135
+ for (let i = 0; i < bytes.length; i++)
136
+ key += String.fromCharCode(bytes[i]);
137
+ return key;
138
+ }
139
+ /** 证书集合中的证书原始 DER(供外部检查)。 */
140
+ export function certificateChainDer(certificates) {
141
+ return certificates.map((cert) => new Uint8Array(cert.raw));
142
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * CMS SignedData 验签。
3
+ *
4
+ * 只验证签名与 messageDigest,**不建立信任链**、不检查证书有效期/用途;
5
+ * 需要信任判断时由调用方对 `signers[i].certificate` 另行处理。
6
+ */
7
+ import type { Certificate } from '../x509/types.js';
8
+ import { type CmsVerifyErrorCode } from './errors.js';
9
+ import { type CmsSignerInfo } from './parse.js';
10
+ /** `verifySignedData` 的选项。 */
11
+ export interface VerifySignedDataOptions {
12
+ /** detached 模式下的原始内容(字符串按 UTF-8 编码)。 */
13
+ readonly detachedData?: string | Uint8Array;
14
+ /** 额外候选证书(CMS 未内嵌签名者证书时使用)。 */
15
+ readonly certificates?: readonly (Certificate | string | Uint8Array)[];
16
+ /** 显式指定 SM2 用户标识;省略时依次尝试空标识与 GM/T 默认标识。 */
17
+ readonly userID?: Uint8Array;
18
+ }
19
+ /** 单个签名者的验签结果。 */
20
+ export interface VerifySignerResult {
21
+ readonly valid: boolean;
22
+ readonly certificate?: Certificate;
23
+ readonly signingTime?: Date;
24
+ /** 人类可读的补充说明。 */
25
+ readonly error?: string;
26
+ /** 结构化失败原因(`valid === true` 时不存在)。 */
27
+ readonly errorCode?: CmsVerifyErrorCode;
28
+ }
29
+ /** `verifySignedData` 的结果。 */
30
+ export interface VerifySignedDataResult {
31
+ readonly valid: boolean;
32
+ readonly signers: readonly VerifySignerResult[];
33
+ }
34
+ /**
35
+ * 验证 CMS SignedData。
36
+ *
37
+ * 结构完全无法解析时返回 `{ valid: false, signers: [{ errorCode: 'MALFORMED' }] }`,
38
+ * 而不是抛错;wasm 未预热等环境错误仍会抛出。
39
+ */
40
+ export declare function verifySignedData(cms: string | Uint8Array, opts?: VerifySignedDataOptions): VerifySignedDataResult;
41
+ /** 供调试:签名者标识的十六进制表示。 */
42
+ export declare function signerIdentifierHex(signer: CmsSignerInfo): string;
@@ -0,0 +1,173 @@
1
+ /**
2
+ * CMS SignedData 验签。
3
+ *
4
+ * 只验证签名与 messageDigest,**不建立信任链**、不检查证书有效期/用途;
5
+ * 需要信任判断时由调用方对 `signers[i].certificate` 另行处理。
6
+ */
7
+ import { bytesToHex, hexToBytes } from '../runtime/convert.js';
8
+ import * as sm2 from '../sm2/index.js';
9
+ import * as sm3 from '../sm3/index.js';
10
+ import { parseCertificate } from '../x509/parse.js';
11
+ import { derSignatureToRaw } from '../x509/sig.js';
12
+ import { DEFAULT_SM2_USER_ID } from '../x509/verify.js';
13
+ import { CmsError } from './errors.js';
14
+ import { OID_CMS_CONTENT_TYPE, OID_CMS_MESSAGE_DIGEST, OID_CMS_SIGNING_TIME, OID_SM3, OID_SM3_WITH_SM2, } from './oids.js';
15
+ import { attributeOctets, attributeOid, attributeTime, parseSignedData, } from './parse.js';
16
+ /** SM2 空用户标识(OpenSSL CMS 默认)。 */
17
+ const EMPTY_USER_ID = new Uint8Array(0);
18
+ /**
19
+ * 验证 CMS SignedData。
20
+ *
21
+ * 结构完全无法解析时返回 `{ valid: false, signers: [{ errorCode: 'MALFORMED' }] }`,
22
+ * 而不是抛错;wasm 未预热等环境错误仍会抛出。
23
+ */
24
+ export function verifySignedData(cms, opts = {}) {
25
+ let parsed;
26
+ try {
27
+ parsed = parseSignedData(cms);
28
+ }
29
+ catch (err) {
30
+ return {
31
+ valid: false,
32
+ signers: [
33
+ {
34
+ valid: false,
35
+ errorCode: 'MALFORMED',
36
+ error: err instanceof Error ? err.message : String(err),
37
+ },
38
+ ],
39
+ };
40
+ }
41
+ const certificates = [
42
+ ...parsed.certificates,
43
+ ...(opts.certificates ?? []).map((cert) => resolveCertificate(cert)),
44
+ ];
45
+ const detachedData = opts.detachedData === undefined ? undefined : toDataBytes(opts.detachedData);
46
+ const signers = parsed.signerInfos.map((signer) => verifySigner(parsed, signer, certificates, detachedData, opts.userID));
47
+ return {
48
+ valid: signers.length > 0 && signers.every((signer) => signer.valid),
49
+ signers,
50
+ };
51
+ }
52
+ function verifySigner(parsed, signer, certificates, detachedData, userID) {
53
+ if (signer.digestAlgorithm !== OID_SM3) {
54
+ return fail('UNSUPPORTED_ALGORITHM', `unsupported digest algorithm ${signer.digestAlgorithm}`);
55
+ }
56
+ if (signer.signatureAlgorithm !== OID_SM3_WITH_SM2) {
57
+ return fail('UNSUPPORTED_ALGORITHM', `unsupported signature algorithm ${signer.signatureAlgorithm}`);
58
+ }
59
+ const certificate = findCertificate(signer, certificates);
60
+ if (certificate === undefined) {
61
+ return fail('NO_CERTIFICATE', 'signer certificate is not embedded and was not provided');
62
+ }
63
+ if (!certificate.supported) {
64
+ return fail('UNSUPPORTED_ALGORITHM', 'signer certificate public key is not SM2');
65
+ }
66
+ const content = parsed.eContent ?? detachedData;
67
+ let signingInput;
68
+ let signingTime;
69
+ if (signer.signedAttrs !== undefined) {
70
+ if (content === undefined) {
71
+ return fail('MALFORMED', 'detached data is required but was not supplied');
72
+ }
73
+ const attributes = signer.signedAttrs.attributes;
74
+ const contentType = attributeOid(attributeByOid(attributes, OID_CMS_CONTENT_TYPE));
75
+ if (contentType !== parsed.eContentType) {
76
+ return fail('DIGEST_MISMATCH', `contentType attribute ${String(contentType)} does not match ${parsed.eContentType}`);
77
+ }
78
+ const expectedDigest = attributeOctets(attributeByOid(attributes, OID_CMS_MESSAGE_DIGEST));
79
+ if (expectedDigest === undefined) {
80
+ return fail('MALFORMED', 'missing messageDigest attribute');
81
+ }
82
+ const actualDigest = hexToBytes(sm3.hash(content), 'messageDigest');
83
+ if (!equalBytes(expectedDigest, actualDigest)) {
84
+ return fail('DIGEST_MISMATCH', 'messageDigest does not match the content');
85
+ }
86
+ signingInput = signer.signedAttrs.raw;
87
+ signingTime = attributeTime(attributeByOid(attributes, OID_CMS_SIGNING_TIME));
88
+ }
89
+ else {
90
+ if (content === undefined) {
91
+ return fail('MALFORMED', 'detached data is required but was not supplied');
92
+ }
93
+ signingInput = content;
94
+ }
95
+ let signature;
96
+ try {
97
+ signature = derSignatureToRaw(signer.signature);
98
+ }
99
+ catch {
100
+ return fail('SIGNATURE_INVALID', 'malformed DER signature');
101
+ }
102
+ const valid = verifyWithUserId(certificate.subjectPublicKey, signingInput, signature, userID);
103
+ if (!valid)
104
+ return fail('SIGNATURE_INVALID', 'SM2 signature verification failed');
105
+ return {
106
+ valid: true,
107
+ certificate,
108
+ ...(signingTime === undefined ? {} : { signingTime }),
109
+ };
110
+ }
111
+ function verifyWithUserId(publicKey, message, signature, userID) {
112
+ if (userID !== undefined) {
113
+ return sm2.verifyWithID(publicKey, message, signature, userID);
114
+ }
115
+ // 先试 OpenSSL CMS 的空标识,再试 GM/T 默认标识。
116
+ if (sm2.verifyWithID(publicKey, message, signature, EMPTY_USER_ID))
117
+ return true;
118
+ return sm2.verifyWithID(publicKey, message, signature, DEFAULT_SM2_USER_ID);
119
+ }
120
+ function findCertificate(signer, certificates) {
121
+ const sid = signer.sid;
122
+ if (sid.type === 'issuerAndSerialNumber') {
123
+ return certificates.find((cert) => cert.serialNumber === sid.serialNumber && equalBytes(cert.issuer.raw, sid.issuerRaw));
124
+ }
125
+ return certificates.find((cert) => cert.subjectKeyIdentifier !== undefined &&
126
+ equalBytes(cert.subjectKeyIdentifier, sid.keyIdentifier));
127
+ }
128
+ function attributeByOid(attributes, target) {
129
+ return attributes.find((attribute) => attribute.oid === target);
130
+ }
131
+ function resolveCertificate(input) {
132
+ if (isCertificateLike(input))
133
+ return input;
134
+ try {
135
+ return parseCertificate(input);
136
+ }
137
+ catch (err) {
138
+ throw new CmsError('verifySignedData: invalid certificate', { cause: err });
139
+ }
140
+ }
141
+ function isCertificateLike(value) {
142
+ return (typeof value === 'object' &&
143
+ value !== null &&
144
+ !(value instanceof Uint8Array) &&
145
+ 'tbsCertificate' in value &&
146
+ 'subjectPublicKey' in value);
147
+ }
148
+ function toDataBytes(data) {
149
+ if (data instanceof Uint8Array)
150
+ return new Uint8Array(data);
151
+ if (typeof data === 'string')
152
+ return new TextEncoder().encode(data);
153
+ throw new TypeError('verifySignedData: detachedData must be a string or Uint8Array');
154
+ }
155
+ function equalBytes(a, b) {
156
+ if (a.length !== b.length)
157
+ return false;
158
+ for (let i = 0; i < a.length; i++) {
159
+ if (a[i] !== b[i])
160
+ return false;
161
+ }
162
+ return true;
163
+ }
164
+ function fail(code, message) {
165
+ return { valid: false, errorCode: code, error: message };
166
+ }
167
+ /** 供调试:签名者标识的十六进制表示。 */
168
+ export function signerIdentifierHex(signer) {
169
+ if (signer.sid.type === 'subjectKeyIdentifier') {
170
+ return bytesToHex(signer.sid.keyIdentifier);
171
+ }
172
+ return `${bytesToHex(signer.sid.issuerRaw)}#${signer.sid.serialNumber.toString(16)}`;
173
+ }
package/dist/index.d.ts CHANGED
@@ -16,6 +16,7 @@
16
16
  *
17
17
  * spec/plan 对应:plan Task 12;spec §5 产物结构、§6 API。
18
18
  */
19
+ export * as cms from './cms/index.js';
19
20
  export { bytesToHex, fromBytes, hexToBytes, toBytes } from './runtime/convert.js';
20
21
  export { CiphertextError, DecryptionError, InvalidCiphertextError, InvalidKeyError, InvalidPublicKeyError, isSmCryptoError, SmCryptoError, } from './runtime/errors.js';
21
22
  export { loadWasm, loadWasmSync } from './runtime/loadWasm.js';
@@ -23,4 +24,6 @@ export * as sm2 from './sm2/index.js';
23
24
  export * as sm3 from './sm3/index.js';
24
25
  export * as sm4 from './sm4/index.js';
25
26
  export * as sm9 from './sm9/index.js';
27
+ export * as webcrypto from './webcrypto/index.js';
28
+ export * as x509 from './x509/index.js';
26
29
  export * as zuc from './zuc/index.js';
package/dist/index.js CHANGED
@@ -16,6 +16,7 @@
16
16
  *
17
17
  * spec/plan 对应:plan Task 12;spec §5 产物结构、§6 API。
18
18
  */
19
+ export * as cms from './cms/index.js';
19
20
  export { bytesToHex, fromBytes, hexToBytes, toBytes } from './runtime/convert.js';
20
21
  // 运行时工具(错误类型、编解码)也导出,便于调用方捕获本库错误。
21
22
  export { CiphertextError, DecryptionError, InvalidCiphertextError, InvalidKeyError, InvalidPublicKeyError, isSmCryptoError, SmCryptoError, } from './runtime/errors.js';
@@ -24,4 +25,6 @@ export * as sm2 from './sm2/index.js';
24
25
  export * as sm3 from './sm3/index.js';
25
26
  export * as sm4 from './sm4/index.js';
26
27
  export * as sm9 from './sm9/index.js';
28
+ export * as webcrypto from './webcrypto/index.js';
29
+ export * as x509 from './x509/index.js';
27
30
  export * as zuc from './zuc/index.js';
@@ -1,74 +1,15 @@
1
1
  /**
2
- * 最小化 ASN.1 DER 解析器与编码器(BER/DER 子集)。
2
+ * 旧 ASN.1 入口(兼容层)。
3
3
  *
4
- * 本仓库只用到以下类型,刻意不做全 BER:
5
- * - INTEGER (0x02)、BIT STRING (0x03)、OCTET STRING (0x04)、NULL (0x05)
6
- * - OBJECT IDENTIFIER (0x06)、SEQUENCE (0x30, constructed)
4
+ * 实现已迁移到 `src/asn1/`(DER/BER 通用层)。本文件只保留历史导出名,
5
+ * 使 `src/pem/*` 与既有测试无需改动:
7
6
  *
8
- * 不支持 indefinite-length encoding(length byte = 0x80),国密 PKCS#8
9
- * 也用不到。长度只支持 definite-length(短/长两种格式)。
7
+ * - `parseDer` → `src/asn1/decode.ts`
8
+ * - `encodeDer` → `src/asn1/encode.ts` 的 `encode`
9
+ * - `asn1Int` / `asn1Octets` / `asn1Oid` / `asn1Null` / `asn1Seq` → 对应构造器
10
+ * - `encodeOid` / `decodeOid` / `asn1IntValue` → OID 与 INTEGER 解码
10
11
  *
11
- * spec/plan 对应:plan Task 4;spec §12.1 Q3(SM9 PKCS#8 + 自定义 OID)。
12
+ * spec/plan 对应:X.509/CMS plan 阶段 0「迁移」。
12
13
  */
13
- /** ASN.1 节点。children 仅对 constructed 类型存在。 */
14
- export interface Asn1Node {
15
- /** UNIVERSAL=0、APPLICATION=1、CONTEXT=2、PRIVATE=3。 */
16
- readonly cls: number;
17
- /** UNIVERSAL 标签值(如 INTEGER=2、SEQUENCE=16);其它类自行解释。 */
18
- readonly tag: number;
19
- /** 长度(DER 存储的是真实字节长度)。 */
20
- readonly length: number;
21
- /** 节点原始字节(含 tag+length+value),用于 round-trip 与错误诊断。 */
22
- readonly raw: Uint8Array;
23
- /** primitive 节点的 value;OCTET STRING/BIT STRING/INTEGER 等直接看这里。 */
24
- readonly value: Uint8Array;
25
- /** constructed 节点的子节点。 */
26
- readonly children?: Asn1Node[];
27
- }
28
- /** 抛错信息统一封装。 */
29
- export declare class Asn1Error extends Error {
30
- readonly name = "Asn1Error";
31
- }
32
- /**
33
- * 解析一段 DER 字节。
34
- *
35
- * @param buf DER 字节。
36
- * @returns 解析后的根节点(通常为 SEQUENCE)。
37
- * @throws Asn1Error 输入截断、tag/length 不合法、不支持的构造。
38
- */
39
- export declare function parseDer(buf: Uint8Array): Asn1Node;
40
- /**
41
- * 将 Asn1Node 编码回 DER 字节。**重新计算 length 与 raw**。
42
- *
43
- * @param node 待编码节点。
44
- * @returns DER 字节。
45
- */
46
- export declare function encodeDer(node: Asn1Node): Uint8Array;
47
- /**
48
- * 便捷构造函数:构造一个 INTEGER 节点。
49
- *
50
- * @param value 大端整数(可为负:负数用二进制补码表示)。
51
- */
52
- export declare function asn1Int(value: bigint | number): Asn1Node;
53
- /** 便捷构造函数:构造一个 OCTET STRING 节点。 */
54
- export declare function asn1Octets(value: Uint8Array): Asn1Node;
55
- /** 便捷构造函数:构造一个 OID 节点(输入如 `"1.2.156.10197.1.100"`)。 */
56
- export declare function asn1Oid(oid: string): Asn1Node;
57
- /** 便捷构造函数:构造一个 NULL 节点。 */
58
- export declare function asn1Null(): Asn1Node;
59
- /** 便捷构造函数:构造一个 SEQUENCE 节点。 */
60
- export declare function asn1Seq(children: Asn1Node[]): Asn1Node;
61
- /**
62
- * OID 编码:第一个子标识符 = a*40 + b,后续每个子标识符按 7-bit 编码。
63
- *
64
- * 例:`1.2.156.10197.1.100` → [0x2a, 0x86, 0x48, 0xce, 0x3d, 0x01, 0x01, 0x64]
65
- * 1*40+2 = 42 = 0x2a;156、10197、1、100 都用 base-128 编码。
66
- *
67
- * @param oid 点分 OID 字符串。
68
- * @returns OID 内容字节(不含 tag 和 length)。
69
- */
70
- export declare function encodeOid(oid: string): Uint8Array;
71
- /** 反向解码。 */
72
- export declare function decodeOid(bytes: Uint8Array): string;
73
- /** 读一个 INTEGER 返回 bigint。 */
74
- export declare function asn1IntValue(node: Asn1Node): bigint;
14
+ 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
+ export { Asn1Error, type Asn1Node } from '../asn1/types.js';