@h-ai/crypto 0.1.0-alpha.67 → 0.1.0-alpha.68

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/dist/index.d.ts CHANGED
@@ -13,13 +13,27 @@ import { ReldbFunctions } from '@h-ai/reldb';
13
13
  */
14
14
 
15
15
  /**
16
- * 传输加密协议常量。
16
+ * 认证传输v2的头名、窗口与默认协商子路径;各HTTP适配器共用,不手写漂移常量。
17
17
  *
18
- * 所有使用端必须复用这组常量,避免「header 名拼写漂移导致两端协议错配」。
18
+ * @example
19
+ * ```ts
20
+ * import { TRANSPORT_PROTOCOL } from '@h-ai/crypto'
21
+ * const headers = new Headers()
22
+ * headers.set(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER, TRANSPORT_PROTOCOL.ENCRYPTED_HEADER_VALUE)
23
+ * const keyExchangePath = TRANSPORT_PROTOCOL.DEFAULT_KEY_EXCHANGE_PATH
24
+ * ```
19
25
  */
20
26
  declare const TRANSPORT_PROTOCOL: {
21
27
  /** 客户端 ID 请求头名。 */
22
28
  readonly CLIENT_ID_HEADER: "X-Client-Id";
29
+ /** GET/HEAD 等无正文请求的认证封装;禁止与正文封装同时出现。 */
30
+ readonly ENVELOPE_HEADER: "X-Hai-Transport";
31
+ /** 唯一支持的认证协议版本;旧未认证格式拒绝。 */
32
+ readonly VERSION: 2;
33
+ /** 请求允许的过去时间窗口(毫秒)。 */
34
+ readonly REQUEST_WINDOW_MS: 300000;
35
+ /** 节点时钟允许的最大未来偏差(毫秒)。 */
36
+ readonly CLOCK_SKEW_MS: 30000;
23
37
  /** 标识响应体已加密的响应头名。 */
24
38
  readonly ENCRYPTED_HEADER: "X-Encrypted";
25
39
  /** `X-Encrypted` 响应头的开启值。 */
@@ -28,45 +42,218 @@ declare const TRANSPORT_PROTOCOL: {
28
42
  readonly DEFAULT_KEY_EXCHANGE_PATH: "/_hai/key-exchange";
29
43
  };
30
44
  /**
31
- * 密钥协商请求体(客户端 → 服务端)。
45
+ * 密钥协商请求;只发送客户端公钥,私钥始终留在客户端,注册仍验证曲线点。
46
+ *
47
+ * @example
48
+ * ```ts
49
+ * import { crypto, type KeyExchangeRequest } from '@h-ai/crypto'
50
+ * const initialized = await crypto.init()
51
+ * if (!initialized.success) throw new Error(initialized.error.message)
52
+ * try {
53
+ * const generated = crypto.asymmetric.generateKeyPair()
54
+ * if (!generated.success) throw new Error(generated.error.message)
55
+ * const pair = generated.data
56
+ * const body: KeyExchangeRequest = { clientPublicKey: pair.publicKey }
57
+ * const serialized = JSON.stringify(body)
58
+ * }
59
+ * finally { await crypto.close() }
60
+ * ```
32
61
  */
33
62
  interface KeyExchangeRequest {
34
63
  readonly clientPublicKey: string;
35
64
  }
36
65
  /**
37
- * 密钥协商响应体(服务端 → 客户端)。
66
+ * TLS协商响应,公开服务端版本、公钥和registry生成的clientId;不含服务端私钥。
67
+ *
68
+ * @example
69
+ * ```ts
70
+ * import type { KeyExchangeRequest, KeyExchangeResponse, TransportEncryptionManager } from '@h-ai/crypto'
71
+ * async function exchange(manager: TransportEncryptionManager, body: KeyExchangeRequest): Promise<KeyExchangeResponse> {
72
+ * const clientId = await manager.registerClientKey(body.clientPublicKey)
73
+ * return { clientId, serverKeyId: manager.getServerKeyId(), serverPublicKey: manager.getServerPublicKey() }
74
+ * }
75
+ * ```
38
76
  */
39
77
  interface KeyExchangeResponse {
78
+ /** 服务端当前加密密钥版本;业务请求必须带回该值。 */
79
+ readonly serverKeyId: string;
40
80
  readonly serverPublicKey: string;
41
81
  readonly clientId: string;
42
82
  }
43
- /** 非对称密钥对。 */
83
+ /**
84
+ * 传输使用的SM2公私钥对;服务端版本从私有Secret注入,客户端可本地生成独立会话密钥。
85
+ *
86
+ * @example
87
+ * ```ts
88
+ * import { crypto, type TransportKeyPair } from '@h-ai/crypto'
89
+ * const initialized = await crypto.init()
90
+ * if (!initialized.success) throw new Error(initialized.error.message)
91
+ * try {
92
+ * const generated = crypto.asymmetric.generateKeyPair()
93
+ * if (!generated.success) throw new Error(generated.error.message)
94
+ * const pair = generated.data
95
+ * const clientPair: TransportKeyPair = pair
96
+ * const publicKey = clientPair.publicKey
97
+ * }
98
+ * finally { await crypto.close() }
99
+ * ```
100
+ */
44
101
  interface TransportKeyPair {
45
102
  readonly publicKey: string;
46
103
  readonly privateKey: string;
47
104
  }
48
105
  /**
49
- * 端到端传输的加密载荷格式。
106
+ * 完整v2认证封装;encryptedKey含独立SM4/HMAC密钥,signature覆盖认证字段和MAC。
107
+ * HTTP适配器只用成功认证得到的上下文生成响应,不自行拼接示例密文。
50
108
  *
51
- * `encryptedKey` 用对端非对称公钥加密的对称会话密钥;`ciphertext` + `iv`
52
- * 为该会话密钥加密的明文。两端必须使用同一份字段命名。
109
+ * @example
110
+ * ```ts
111
+ * import { authenticateTransportRequest, type EncryptedPayload, type TransportEncryptionManager } from '@h-ai/crypto'
112
+ * async function sealResponse(manager: TransportEncryptionManager, request: Request): Promise<EncryptedPayload> {
113
+ * const verified = await authenticateTransportRequest(manager, request)
114
+ * if (!verified.success) throw new Error(verified.error.message)
115
+ * const encrypted = await manager.encryptResponse(verified.data.context, JSON.stringify({ accepted: true }), 200)
116
+ * if (!encrypted.success) throw new Error(encrypted.error.message)
117
+ * return encrypted.data
118
+ * }
119
+ * ```
53
120
  */
54
121
  interface EncryptedPayload {
122
+ /** 认证的请求上下文;响应必须原样关联同一个请求。 */
123
+ readonly context: TransportMessageContext;
124
+ /** 认证方向,防止把合法响应反射到请求入口。 */
125
+ readonly direction: 'request' | 'response';
126
+ /** 请求固定为 0;响应认证实际 HTTP 状态码。 */
127
+ readonly status: number;
128
+ /** SM2 封装的 JSON [SM4 密钥 hex, 独立 HMAC 密钥 hex]。 */
55
129
  readonly encryptedKey: string;
130
+ /** SM4-CBC 密文 hex;认证输入包含原始编码值。 */
56
131
  readonly ciphertext: string;
132
+ /** 随机 16 字节 CBC IV 的 hex 编码。 */
57
133
  readonly iv: string;
134
+ /** 独立随机密钥计算的 HMAC-SM3;先验证后解密 CBC。 */
135
+ readonly tag: string;
136
+ /** 对完整封装和 tag 的发送方 SM2 签名,防止公钥持有者伪造发送方。 */
137
+ readonly signature: string;
138
+ }
139
+ /**
140
+ * 认证的单次HTTP上下文;128位nonce、版本、客户端、时间、方法和实际路径SM3摘要共同绑定。
141
+ * 服务端共享registry只允许消费一次nonce;响应必须关联成功认证后的同一上下文。
142
+ *
143
+ * @example
144
+ * ```ts
145
+ * import { authenticateTransportRequest, type TransportEncryptionManager, type TransportMessageContext } from '@h-ai/crypto'
146
+ * async function contextOf(manager: TransportEncryptionManager, request: Request): Promise<TransportMessageContext> {
147
+ * const verified = await authenticateTransportRequest(manager, request)
148
+ * if (!verified.success) throw new Error(verified.error.message)
149
+ * return verified.data.context
150
+ * }
151
+ * ```
152
+ */
153
+ interface TransportMessageContext {
154
+ /** 唯一受支持的协议版本。 */
155
+ readonly version: 2;
156
+ /** 协商选择的受管服务端密钥版本。 */
157
+ readonly serverKeyId: string;
158
+ /** registry 注册的客户端 ID;发送方必须证明持有对应私钥。 */
159
+ readonly clientId: string;
160
+ /** 128 位随机十六进制 nonce,同时用于响应关联。 */
161
+ readonly requestId: string;
162
+ /** 请求创建时间,Unix 毫秒;允许过去 5 分钟、未来 30 秒。 */
163
+ readonly issuedAt: number;
164
+ /** 大写 HTTP 方法,必须与实际 Request 完全一致。 */
165
+ readonly method: string;
166
+ /** 实际 pathname + 原始 search 的 SM3 摘要,64 位小写 hex,避免在认证头中重复长 URL。 */
167
+ readonly pathHash: string;
168
+ }
169
+ /**
170
+ * 从实际HTTP Request提取的可信绑定输入;不能直接复制密文的自报方法、路径或客户端。
171
+ *
172
+ * @example
173
+ * ```ts
174
+ * import type { TransportRequestContext } from '@h-ai/crypto'
175
+ * function expectedContext(request: Request): TransportRequestContext {
176
+ * const clientId = request.headers.get('X-Client-Id')
177
+ * if (!clientId) throw new Error('Missing client ID')
178
+ * const url = new URL(request.url)
179
+ * return { clientId, method: request.method, path: url.pathname + url.search }
180
+ * }
181
+ * ```
182
+ */
183
+ interface TransportRequestContext {
184
+ /** 实际 X-Client-Id 头,用于 registry 查询及发送方验签。 */
185
+ readonly clientId: string;
186
+ /** 实际 Request.method。 */
187
+ readonly method: string;
188
+ /** 实际 URL.pathname + URL.search,保留查询参数顺序及编码。 */
189
+ readonly path: string;
190
+ }
191
+ /**
192
+ * 客户端发出的完整请求封装,direction=request且status=0;结构类型不构成认证证明。
193
+ *
194
+ * @example
195
+ * ```ts
196
+ * import type { EncryptedRequestPayload, TransportEncryptionManager } from '@h-ai/crypto'
197
+ * async function decrypt(manager: TransportEncryptionManager, request: Request, payload: EncryptedRequestPayload): Promise<string> {
198
+ * const clientId = request.headers.get('X-Client-Id')
199
+ * if (!clientId) throw new Error('Missing client ID')
200
+ * const url = new URL(request.url)
201
+ * const result = await manager.decryptRequest(payload, { clientId, method: request.method, path: url.pathname + url.search })
202
+ * if (!result.success) throw new Error(result.error.message)
203
+ * return result.data
204
+ * }
205
+ * ```
206
+ */
207
+ interface EncryptedRequestPayload extends EncryptedPayload {
208
+ readonly direction: 'request';
209
+ readonly status: 0;
58
210
  }
59
211
  /**
60
- * 传输加密所需的非对称 + 对称能力子集。
212
+ * 服务端受管版本密钥环;所有节点同版本对应同密钥,新协商选activeKeyId,旧版本保留到撤销。
213
+ * 先分发新旧版本再切活动ID;删除旧版本即撤销,私钥不进入PUBLIC_/VITE_/日志。
61
214
  *
62
- * 不直接依赖 `@h-ai/crypto` 的具体实现,便于测试时注入 mock;
63
- * 实际使用时 `crypto` 模块的实例本身即可结构兼容。
215
+ * @example
216
+ * ```ts
217
+ * import { parseTransportServerKeyRing, type TransportServerKeyRing } from '@h-ai/crypto'
218
+ * const parsed = parseTransportServerKeyRing(process.env.HAI_CRYPTO_TRANSPORT_SERVERKEYRING)
219
+ * if (!parsed.success) throw new Error(parsed.error.message)
220
+ * const ring: TransportServerKeyRing = parsed.data
221
+ * ```
222
+ */
223
+ interface TransportServerKeyRing {
224
+ /** 新协商使用的版本;1–64 个字母、数字、下划线或连字符。 */
225
+ readonly activeKeyId: string;
226
+ /** ID 与密钥对;仅供服务端保管,所有节点必须分发相同版本的相同密钥。 */
227
+ readonly keys: Readonly<Record<string, TransportKeyPair>>;
228
+ }
229
+ /**
230
+ * 传输协议实际需要的SM2/SM3/HMAC/SM4能力;可结构化注入真实crypto,不仿造另一套算法。
231
+ *
232
+ * @example
233
+ * ```ts
234
+ * import { crypto, type TransportCryptoServiceLike } from '@h-ai/crypto'
235
+ * const initialized = await crypto.init()
236
+ * if (!initialized.success) throw new Error(initialized.error.message)
237
+ * try {
238
+ * const service: TransportCryptoServiceLike = crypto
239
+ * const digest = service.hash.hash('/api/v1/orders')
240
+ * if (!digest.success) throw new Error(digest.error.message)
241
+ * }
242
+ * finally { await crypto.close() }
243
+ * ```
64
244
  */
65
245
  interface TransportCryptoServiceLike {
66
246
  asymmetric: {
67
247
  generateKeyPair: () => HaiResult<TransportKeyPair>;
68
248
  encrypt: (data: string, publicKey: string) => HaiResult<string>;
69
249
  decrypt: (ciphertext: string, privateKey: string) => HaiResult<string>;
250
+ sign: (data: string, privateKey: string) => HaiResult<string>;
251
+ verify: (data: string, signature: string, publicKey: string) => HaiResult<boolean>;
252
+ };
253
+ hash: {
254
+ /** SM3 摘要,用于实际请求路径绑定,不使用其它摘要算法替换。 */
255
+ hash: (data: string) => HaiResult<string>;
256
+ hmac: (data: string, key: string) => HaiResult<string>;
70
257
  };
71
258
  symmetric: {
72
259
  generateKey: () => string;
@@ -78,93 +265,467 @@ interface TransportCryptoServiceLike {
78
265
  };
79
266
  }
80
267
  /**
81
- * 客户端公钥存储抽象。
268
+ * 客户端公钥与nonce存储契约;生产显式共享Redis/数据库,真实共享与原子性由提供者保证。
269
+ * get的故障不能当未注册,claimRequest故障不能当成功;有效nonce不得在expiresAt前驱逐。
82
270
  *
83
- * 默认实现为进程内 Map({@link createInMemoryKeyStore}),多节点部署时建议
84
- * 接入 Redis / 数据库实现以保证跨节点一致性。
85
- *
86
- * 设计原则:方法均为异步签名,避免后续替换为分布式实现时大改接口;
87
- * 内存实现以 `Promise.resolve` 包裹即可。
271
+ * @example
272
+ * ```ts
273
+ * import { crypto, createRedisTransportKeyStore, parseTransportServerKeyRing } from '@h-ai/crypto'
274
+ * import { cache } from '@h-ai/cache'
275
+ * const initialized = await crypto.init()
276
+ * if (!initialized.success) throw new Error(initialized.error.message)
277
+ * try {
278
+ * const connected = await cache.init({ type: 'redis', host: 'cache', port: 6379, password: process.env.HAI_CACHE_PASSWORD })
279
+ * if (!connected.success) throw new Error(connected.error.message)
280
+ * try {
281
+ * const registry = createRedisTransportKeyStore({ cache, ttlSeconds: 3600 })
282
+ * const store: import('@h-ai/crypto').TransportKeyStore = registry
283
+ * const scope = store.stateScope
284
+ * // Redis部署须noeviction/可靠持久化,并同步各节点时钟。
285
+ * }
286
+ * finally { await cache.close() }
287
+ * }
288
+ * finally { await crypto.close() }
289
+ * ```
88
290
  */
89
291
  interface TransportKeyStore {
90
- /** 注册新客户端,返回服务端生成的 `clientId`。 */
292
+ /** 公钥与nonce状态的真实共享能力;生产只接受shared。 */
293
+ readonly stateScope: 'local' | 'shared';
294
+ /**
295
+ * 注册有效 SM2 公钥,返回服务端生成的 clientId;格式/曲线点无效或存储故障必须 reject。
296
+ * 容量策略见具体工厂契约:数据库满容量拒绝,开发/测试内存实现允许 FIFO 淘汰公钥。
297
+ * @param publicKey - 实际调用输入,按该方法约束验证。
298
+ * @returns 异步操作结果;存储或网络故障按方法契约拒绝。
299
+ *
300
+ * @example
301
+ * ```ts
302
+ * import { crypto } from '@h-ai/crypto'
303
+ * const initialized = await crypto.init()
304
+ * if (!initialized.success) throw new Error(initialized.error.message)
305
+ * try {
306
+ * const generated = crypto.asymmetric.generateKeyPair()
307
+ * if (!generated.success) throw new Error(generated.error.message)
308
+ * const pair = generated.data
309
+ * const { createInMemoryKeyStore } = await import('@h-ai/crypto')
310
+ * const registry = createInMemoryKeyStore(100)
311
+ * try { const clientId = await registry.register(pair.publicKey) }
312
+ * finally { await registry.close?.() }
313
+ * // 本例仅开发/测试;生产替换为共享Redis/数据库registry。
314
+ * }
315
+ * finally { await crypto.close() }
316
+ * ```
317
+ */
91
318
  register: (publicKey: string) => Promise<string>;
92
- /** 查询已注册客户端公钥。 */
319
+ /**
320
+ * 读取已注册有效公钥;缺失返回undefined,存储失败拒绝,不能伪装未注册。
321
+ * @param clientId - registry生成的客户端标识。
322
+ * @returns 异步操作结果;存储或网络故障按方法契约拒绝。
323
+ *
324
+ * @example
325
+ * ```ts
326
+ * import { crypto } from '@h-ai/crypto'
327
+ * const initialized = await crypto.init()
328
+ * if (!initialized.success) throw new Error(initialized.error.message)
329
+ * try {
330
+ * const generated = crypto.asymmetric.generateKeyPair()
331
+ * if (!generated.success) throw new Error(generated.error.message)
332
+ * const pair = generated.data
333
+ * const { createInMemoryKeyStore } = await import('@h-ai/crypto')
334
+ * const registry = createInMemoryKeyStore(100)
335
+ * try { const clientId = await registry.register(pair.publicKey); const publicKey = await registry.get(clientId) }
336
+ * finally { await registry.close?.() }
337
+ * }
338
+ * finally { await crypto.close() }
339
+ * ```
340
+ */
93
341
  get: (clientId: string) => Promise<string | undefined>;
94
- /** 主动删除(可选,便于客户端登出时清理)。 */
342
+ /**
343
+ * 原子消费客户端 requestId nonce;成功返回 true,重放返回 false。
344
+ * 必须跨节点共享且在 expiresAt(Unix 毫秒)前不驱逐;存储故障必须 reject,禁止当作成功。
345
+ * @param clientId - registry生成的客户端标识。
346
+ * @param requestId - 本次128位随机nonce。
347
+ * @param expiresAt - nonce至少保留到的Unix毫秒期限。
348
+ * @returns 异步操作结果;存储或网络故障按方法契约拒绝。
349
+ *
350
+ * @example
351
+ * ```ts
352
+ * import { crypto } from '@h-ai/crypto'
353
+ * const initialized = await crypto.init()
354
+ * if (!initialized.success) throw new Error(initialized.error.message)
355
+ * try {
356
+ * const generated = crypto.asymmetric.generateKeyPair()
357
+ * if (!generated.success) throw new Error(generated.error.message)
358
+ * const pair = generated.data
359
+ * const { createInMemoryKeyStore } = await import('@h-ai/crypto')
360
+ * const registry = createInMemoryKeyStore(100)
361
+ * try {
362
+ * const clientId = await registry.register(pair.publicKey)
363
+ * const requestId = globalThis.crypto.randomUUID().replaceAll('-', '')
364
+ * const expiresAt = Date.now() + 360000
365
+ * const first = await registry.claimRequest(clientId, requestId, expiresAt)
366
+ * const replay = await registry.claimRequest(clientId, requestId, expiresAt)
367
+ * // 单进程测试预期true/false;生产需要跨节点共享原子消费且有效期间不驱逐。
368
+ * }
369
+ * finally { await registry.close?.() }
370
+ * }
371
+ * finally { await crypto.close() }
372
+ * ```
373
+ */
374
+ claimRequest: (clientId: string, requestId: string, expiresAt: number) => Promise<boolean>;
375
+ /**
376
+ * 主动删除(可选,便于客户端登出时清理)。
377
+ * @param clientId - registry生成的客户端标识。
378
+ * @returns 异步操作结果;存储或网络故障按方法契约拒绝。
379
+ *
380
+ * @example
381
+ * ```ts
382
+ * import { crypto } from '@h-ai/crypto'
383
+ * const initialized = await crypto.init()
384
+ * if (!initialized.success) throw new Error(initialized.error.message)
385
+ * try {
386
+ * const generated = crypto.asymmetric.generateKeyPair()
387
+ * if (!generated.success) throw new Error(generated.error.message)
388
+ * const pair = generated.data
389
+ * const { createInMemoryKeyStore } = await import('@h-ai/crypto')
390
+ * const registry = createInMemoryKeyStore(100)
391
+ * try { const clientId = await registry.register(pair.publicKey); await registry.delete?.(clientId) }
392
+ * finally { await registry.close?.() }
393
+ * }
394
+ * finally { await crypto.close() }
395
+ * ```
396
+ */
95
397
  delete?: (clientId: string) => Promise<void>;
96
- /** 释放底层资源(关闭连接、清理定时器等)。 */
398
+ /**
399
+ * 可选释放registry自有资源;借用的cache/reldb不由registry关闭。
400
+ * @returns 异步操作结果;存储或网络故障按方法契约拒绝。
401
+ *
402
+ * @example
403
+ * ```ts
404
+ * import { createInMemoryKeyStore } from '@h-ai/crypto'
405
+ * const registry = createInMemoryKeyStore(100)
406
+ * await registry.close?.()
407
+ * // 共享registry借用的cache/reldb仍由应用反序关闭。
408
+ * ```
409
+ */
97
410
  close?: () => Promise<void>;
98
411
  }
99
412
  /**
100
- * 创建服务端传输加密管理器的配置。
413
+ * 服务端创建配置;受管密钥环和共享registry均必填,没有进程随机密钥回退。
101
414
  *
102
- * `keyStore` 未传时,默认使用 {@link createInMemoryKeyStore};此时可通过 `maxClients`
103
- * 控制进程内 FIFO 容量。若传入共享 `keyStore`,则 `maxClients` 不再生效。
415
+ * @example
416
+ * ```ts
417
+ * import { crypto, createRedisTransportKeyStore, parseTransportServerKeyRing } from '@h-ai/crypto'
418
+ * import { cache } from '@h-ai/cache'
419
+ * const initialized = await crypto.init()
420
+ * if (!initialized.success) throw new Error(initialized.error.message)
421
+ * try {
422
+ * const connected = await cache.init({ type: 'redis', host: 'cache', port: 6379, password: process.env.HAI_CACHE_PASSWORD })
423
+ * if (!connected.success) throw new Error(connected.error.message)
424
+ * try {
425
+ * const registry = createRedisTransportKeyStore({ cache, ttlSeconds: 3600 })
426
+ * const parsed = parseTransportServerKeyRing(process.env.HAI_CRYPTO_TRANSPORT_SERVERKEYRING)
427
+ * if (!parsed.success) throw new Error(parsed.error.message)
428
+ * const options: import('@h-ai/crypto').TransportCreateServerOptions = { serverKeyRing: parsed.data, keyStore: registry }
429
+ * const created = crypto.transport.createServer(options)
430
+ * if (!created.success) throw new Error(created.error.message)
431
+ * await created.data.close()
432
+ * }
433
+ * finally { await cache.close() }
434
+ * }
435
+ * finally { await crypto.close() }
436
+ * ```
104
437
  */
105
438
  interface TransportCreateServerOptions {
106
- /** 客户端公钥存储;多节点部署时建议注入共享实现。 */
107
- readonly keyStore?: TransportKeyStore;
108
- /** 默认内存 keyStore 的最大容量(仅当 `keyStore` 未提供时生效)。 */
109
- readonly maxClients?: number;
439
+ /** 必填受管服务端密钥环;没有隐式随机生成或进程密钥回退。 */
440
+ readonly serverKeyRing: TransportServerKeyRing;
441
+ /** 客户端公钥存储;生产必须注入跨节点共享实现。 */
442
+ readonly keyStore: TransportKeyStore;
110
443
  }
111
444
  /**
112
- * 服务端传输加密管理器。
113
- *
114
- * 由 {@link createTransportEncryption} 创建;负责服务端密钥对持有、客户端密钥
115
- * 注册与请求/响应的加解密。
116
- *
117
- * @remarks
118
- * 典型调用顺序:
445
+ * 服务端认证传输管理器;注册公钥,按真实HTTP上下文验签/MAC/时效并消费nonce,随后才返回明文。
446
+ * 优先使用authenticateTransportRequest;响应只用已认证上下文。close释放registry自有资源,不关闭借用的cache/reldb。
119
447
  *
120
- * 1. `registerClientKey(clientPublicKey)`:在密钥协商端点中保存客户端公钥,并生成 `clientId`。
121
- * 2. `getServerPublicKey()`:把服务端公钥返回给客户端,供后续请求加密会话密钥。
122
- * 3. `decryptRequest(payload)`:普通业务请求进入业务逻辑前,先把密文请求体解成明文。
123
- * 4. `encryptResponse(clientId, data)`:业务处理完成后,按当前客户端公钥重新加密响应体。
124
- * 5. `close()`:服务关闭时释放底层 keyStore 资源。
448
+ * @example
449
+ * ```ts
450
+ * import { crypto, createRedisTransportKeyStore, parseTransportServerKeyRing } from '@h-ai/crypto'
451
+ * import { cache } from '@h-ai/cache'
452
+ * const initialized = await crypto.init()
453
+ * if (!initialized.success) throw new Error(initialized.error.message)
454
+ * try {
455
+ * const connected = await cache.init({ type: 'redis', host: 'cache', port: 6379, password: process.env.HAI_CACHE_PASSWORD })
456
+ * if (!connected.success) throw new Error(connected.error.message)
457
+ * try {
458
+ * const registry = createRedisTransportKeyStore({ cache, ttlSeconds: 3600 })
459
+ * const ring = parseTransportServerKeyRing(process.env.HAI_CRYPTO_TRANSPORT_SERVERKEYRING)
460
+ * if (!ring.success) throw new Error(ring.error.message)
461
+ * const created = crypto.transport.createServer({ serverKeyRing: ring.data, keyStore: registry })
462
+ * if (!created.success) throw new Error(created.error.message)
463
+ * const manager = created.data
464
+ * try {
465
+ * const currentVersion = manager.getServerKeyId()
466
+ * const publicKey = manager.getServerPublicKey()
467
+ * }
468
+ * finally { await manager.close() }
469
+ * }
470
+ * finally { await cache.close() }
471
+ * }
472
+ * finally { await crypto.close() }
473
+ * ```
125
474
  */
126
475
  interface TransportEncryptionManager {
476
+ /**
477
+ * 当前新协商使用的服务端密钥 ID。
478
+ * @returns 直接返回值,使用前先初始化模块。
479
+ *
480
+ * @example
481
+ * ```ts
482
+ * import type { TransportEncryptionManager } from '@h-ai/crypto'
483
+ * function activeVersion(manager: TransportEncryptionManager): string { return manager.getServerKeyId() }
484
+ * ```
485
+ */
486
+ getServerKeyId: () => string;
487
+ /**
488
+ * 返回当前活动版本的公开公钥,只用于可信TLS协商,不含私钥。
489
+ * @returns 直接返回值,使用前先初始化模块。
490
+ *
491
+ * @example
492
+ * ```ts
493
+ * import type { TransportEncryptionManager } from '@h-ai/crypto'
494
+ * function activePublicKey(manager: TransportEncryptionManager): string { return manager.getServerPublicKey() }
495
+ * ```
496
+ */
127
497
  getServerPublicKey: () => string;
498
+ /**
499
+ * 验证 SM2 公钥编码和曲线点后向显式 registry 注册。
500
+ * @throws 公钥无效或 registry 写入失败;HTTP 协商适配器负责转换成失败响应。
501
+ * @param clientPublicKey - 实际调用输入,按该方法约束验证。
502
+ * @returns 异步操作结果;存储或网络故障按方法契约拒绝。
503
+ *
504
+ * @example
505
+ * ```ts
506
+ * import type { TransportEncryptionManager } from '@h-ai/crypto'
507
+ * async function register(manager: TransportEncryptionManager, clientPublicKey: string): Promise<string> {
508
+ * // 方法校验编码/曲线点后写入共享registry;异常由HTTP协商适配器转成失败响应。
509
+ * return manager.registerClientKey(clientPublicKey)
510
+ * }
511
+ * ```
512
+ */
128
513
  registerClientKey: (clientPublicKey: string) => Promise<string>;
514
+ /**
515
+ * 查询客户端公钥;registry 读取故障会 reject,不作为未注册处理。
516
+ * @param clientId - registry生成的客户端标识。
517
+ * @returns 异步操作结果;存储或网络故障按方法契约拒绝。
518
+ *
519
+ * @example
520
+ * ```ts
521
+ * import type { TransportEncryptionManager } from '@h-ai/crypto'
522
+ * async function lookup(manager: TransportEncryptionManager, clientId: string): Promise<string | undefined> {
523
+ * return manager.getClientPublicKey(clientId)
524
+ * }
525
+ * ```
526
+ */
129
527
  getClientPublicKey: (clientId: string) => Promise<string | undefined>;
130
- encryptResponse: (clientId: string, data: string) => Promise<HaiResult<EncryptedPayload>>;
131
- decryptRequest: (payload: EncryptedPayload) => HaiResult<string>;
132
- /** 关闭:释放 keyStore 资源。 */
528
+ /**
529
+ * 按已认证请求上下文加密、MAC 并签名响应,绑定实际 HTTP 状态。
530
+ * registry 及加密故障均返回失败 HaiResult;上下文只能来自成功的请求认证。
531
+ * @param request - 实际调用输入,按该方法约束验证。
532
+ * @param data - 实际调用输入,按该方法约束验证。
533
+ * @param status - 实际调用输入,按该方法约束验证。
534
+ * @returns HaiResult,先检查success再读取data。
535
+ *
536
+ * @example
537
+ * ```ts
538
+ * import { authenticateTransportRequest, type EncryptedPayload, type TransportEncryptionManager } from '@h-ai/crypto'
539
+ * async function sealResponse(manager: TransportEncryptionManager, request: Request): Promise<EncryptedPayload> {
540
+ * const verified = await authenticateTransportRequest(manager, request)
541
+ * if (!verified.success) throw new Error(verified.error.message)
542
+ * const encrypted = await manager.encryptResponse(verified.data.context, JSON.stringify({ accepted: true }), 200)
543
+ * if (!encrypted.success) throw new Error(encrypted.error.message)
544
+ * return encrypted.data
545
+ * }
546
+ * ```
547
+ */
548
+ encryptResponse: (request: TransportMessageContext, data: string, status: number) => Promise<HaiResult<EncryptedPayload>>;
549
+ /**
550
+ * 验证发送方、MAC、HTTP 绑定和时效,原子消费 nonce 后才返回明文;错误不含密文或 Secret。
551
+ * expected 须从实际 Request 提取,不从 payload 复制。HTTP 优先用 authenticateTransportRequest。
552
+ * @param payload - 实际调用输入,按该方法约束验证。
553
+ * @param expected - 实际调用输入,按该方法约束验证。
554
+ * @returns HaiResult,先检查success再读取data。
555
+ *
556
+ * @example
557
+ * ```ts
558
+ * import type { EncryptedRequestPayload, TransportEncryptionManager } from '@h-ai/crypto'
559
+ * async function decrypt(manager: TransportEncryptionManager, request: Request, payload: EncryptedRequestPayload): Promise<string> {
560
+ * const clientId = request.headers.get('X-Client-Id')
561
+ * if (!clientId) throw new Error('Missing client ID')
562
+ * const url = new URL(request.url)
563
+ * const result = await manager.decryptRequest(payload, { clientId, method: request.method, path: url.pathname + url.search })
564
+ * if (!result.success) throw new Error(result.error.message)
565
+ * return result.data
566
+ * }
567
+ * ```
568
+ */
569
+ decryptRequest: (payload: EncryptedRequestPayload, expected: TransportRequestContext) => Promise<HaiResult<string>>;
570
+ /**
571
+ * 释放registry自有资源;应用仍负责借用cache/reldb的关闭。
572
+ * @returns 异步操作结果;存储或网络故障按方法契约拒绝。
573
+ *
574
+ * @example
575
+ * ```ts
576
+ * import { crypto, createRedisTransportKeyStore, parseTransportServerKeyRing } from '@h-ai/crypto'
577
+ * import { cache } from '@h-ai/cache'
578
+ * const initialized = await crypto.init()
579
+ * if (!initialized.success) throw new Error(initialized.error.message)
580
+ * try {
581
+ * const connected = await cache.init({ type: 'redis', host: 'cache', port: 6379, password: process.env.HAI_CACHE_PASSWORD })
582
+ * if (!connected.success) throw new Error(connected.error.message)
583
+ * try {
584
+ * const registry = createRedisTransportKeyStore({ cache, ttlSeconds: 3600 })
585
+ * const ring = parseTransportServerKeyRing(process.env.HAI_CRYPTO_TRANSPORT_SERVERKEYRING)
586
+ * if (!ring.success) throw new Error(ring.error.message)
587
+ * const created = crypto.transport.createServer({ serverKeyRing: ring.data, keyStore: registry })
588
+ * if (!created.success) throw new Error(created.error.message)
589
+ * const manager = created.data
590
+ * try {
591
+ * const currentVersion = manager.getServerKeyId()
592
+ * const publicKey = manager.getServerPublicKey()
593
+ * }
594
+ * finally { await manager.close() }
595
+ * }
596
+ * finally { await cache.close() }
597
+ * }
598
+ * finally { await crypto.close() }
599
+ * ```
600
+ */
133
601
  close: () => Promise<void>;
134
602
  }
135
603
  /**
136
- * 客户端传输加密会话。
604
+ * 客户端单会话;首次调用自动协商,后续请求生成随机nonce并认证响应关联/签名/MAC。
605
+ * init返回HaiResult;encryptedFetch按fetch契约reject;destroy清本地会话,不代表撤销服务端registry。
137
606
  *
138
- * 由 {@link createTransportClient} 创建;包装宿主 `fetch`,
139
- * 自动完成首次密钥协商并对后续请求自动加解密。
140
- *
141
- * @remarks
142
- * 典型调用顺序:
143
- *
144
- * 1. `init()`(可选):预先完成一次密钥协商。
145
- * 2. `encryptedFetch()`:业务请求统一走这里;若当前会话尚未 ready,会先自动执行 `init()`。
146
- * 3. `ready()`:查看当前会话是否已经拿到 `clientId` 与服务端公钥。
147
- * 4. `destroy()`:登出、切租户或切环境时清空会话;下一次请求会重新协商。
607
+ * @example
608
+ * ```ts
609
+ * import { crypto } from '@h-ai/crypto'
610
+ * const initialized = await crypto.init()
611
+ * if (!initialized.success) throw new Error(initialized.error.message)
612
+ * try {
613
+ * const client = crypto.transport.createClient({ keyExchangeUrl: 'https://api.example.com/api/v1/_hai/key-exchange' })
614
+ * try {
615
+ * const ready = await client.init()
616
+ * if (!ready.success) throw new Error(ready.error.message)
617
+ * const response = await client.encryptedFetch('https://api.example.com/api/v1/orders')
618
+ * if (!response.ok) throw new Error('HTTP request failed')
619
+ * }
620
+ * finally { client.destroy() }
621
+ * }
622
+ * finally { await crypto.close() }
623
+ * ```
148
624
  */
149
625
  interface TransportClient {
150
626
  /**
151
627
  * 完成密钥协商;多次调用幂等(同一会话只协商一次)。
152
628
  *
153
629
  * 通常无需手动调用:`encryptedFetch` 在首次请求前会自动 `init()`。
630
+ * 协商在 30 秒内完成,握手 JSON 与单个公钥分别受 TRANSPORT_BODY_LIMITS 限制。
631
+ * @returns HaiResult,先检查success再读取data。
632
+ *
633
+ * @example
634
+ * ```ts
635
+ * import { crypto } from '@h-ai/crypto'
636
+ * const initialized = await crypto.init()
637
+ * if (!initialized.success) throw new Error(initialized.error.message)
638
+ * try {
639
+ * const client = crypto.transport.createClient({ keyExchangeUrl: 'https://api.example.com/api/v1/_hai/key-exchange' })
640
+ * try {
641
+ * const ready = await client.init()
642
+ * if (!ready.success) throw new Error(ready.error.message)
643
+ * const response = await client.encryptedFetch('https://api.example.com/api/v1/orders')
644
+ * if (!response.ok) throw new Error('HTTP request failed')
645
+ * }
646
+ * finally { client.destroy() }
647
+ * }
648
+ * finally { await crypto.close() }
649
+ * ```
154
650
  */
155
651
  init: () => Promise<HaiResult<void>>;
156
652
  /**
157
653
  * 包装后的 fetch;签名与全局 `fetch` 一致,请求/响应自动加解密。
654
+ * 明文/密文按 TRANSPORT_BODY_LIMITS 有界读取原流;超限或取消释放 reader,保留调用方 signal。
655
+ * 仅接受关联同一请求上下文与 HTTP status 的认证响应;缺标记也拒绝,不回退明文或密文。
158
656
  *
159
657
  * @throws 与原生 fetch 一样,网络错误、密钥协商失败、加密失败或解密失败会 reject。
658
+ *
659
+ * @example
660
+ * ```ts
661
+ * import { crypto } from '@h-ai/crypto'
662
+ * const initialized = await crypto.init()
663
+ * if (!initialized.success) throw new Error(initialized.error.message)
664
+ * try {
665
+ * const client = crypto.transport.createClient({ keyExchangeUrl: 'https://api.example.com/api/v1/_hai/key-exchange' })
666
+ * try {
667
+ * const ready = await client.init()
668
+ * if (!ready.success) throw new Error(ready.error.message)
669
+ * const response = await client.encryptedFetch('https://api.example.com/api/v1/orders')
670
+ * if (!response.ok) throw new Error('HTTP request failed')
671
+ * }
672
+ * finally { client.destroy() }
673
+ * }
674
+ * finally { await crypto.close() }
675
+ * ```
160
676
  */
161
677
  encryptedFetch: typeof fetch;
162
- /** 是否已完成密钥协商。 */
678
+ /**
679
+ * 是否已完成密钥协商。
680
+ * @returns 直接boolean,false表示不匹配/未就绪。
681
+ *
682
+ * @example
683
+ * ```ts
684
+ * import { crypto } from '@h-ai/crypto'
685
+ * const initialized = await crypto.init()
686
+ * if (!initialized.success) throw new Error(initialized.error.message)
687
+ * try {
688
+ * const client = crypto.transport.createClient({ keyExchangeUrl: 'https://api.example.com/api/v1/_hai/key-exchange' })
689
+ * try { const ready = client.ready() }
690
+ * finally { client.destroy() }
691
+ * }
692
+ * finally { await crypto.close() }
693
+ * ```
694
+ */
163
695
  ready: () => boolean;
164
- /** 销毁会话(清空客户端密钥)。 */
696
+ /**
697
+ * 清空本地客户端密钥与协商状态;下次请求重新协商,不撤销服务端身份或业务效果。
698
+ * @returns 直接返回值,使用前先初始化模块。
699
+ *
700
+ * @example
701
+ * ```ts
702
+ * import { crypto } from '@h-ai/crypto'
703
+ * const initialized = await crypto.init()
704
+ * if (!initialized.success) throw new Error(initialized.error.message)
705
+ * try {
706
+ * const client = crypto.transport.createClient({ keyExchangeUrl: 'https://api.example.com/api/v1/_hai/key-exchange' })
707
+ * client.destroy()
708
+ * // 只清本地会话;不撤销服务端registry或业务会话。
709
+ * }
710
+ * finally { await crypto.close() }
711
+ * ```
712
+ */
165
713
  destroy: () => void;
166
714
  }
167
715
 
716
+ /**
717
+ * 加密领域标准错误集合;先判断 HaiResult.success,错误上下文不得附带明文或私钥。
718
+ *
719
+ * @example
720
+ * ```ts
721
+ * import { crypto, HaiCryptoError } from '@h-ai/crypto'
722
+ * const result = crypto.hash.hash('hello')
723
+ * if (!result.success && result.error.code === HaiCryptoError.NOT_INITIALIZED.code) {
724
+ * // 尚未初始化时按生命周期处理,不忽略错误继续加密。
725
+ * const message = result.error.message
726
+ * }
727
+ * ```
728
+ */
168
729
  declare const HaiCryptoError: {
169
730
  readonly INVALID_INPUT: _h_ai_core.HaiErrorDef;
170
731
  readonly INVALID_KEY: _h_ai_core.HaiErrorDef;
@@ -179,39 +740,182 @@ declare const HaiCryptoError: {
179
740
  readonly HMAC_FAILED: _h_ai_core.HaiErrorDef;
180
741
  readonly INVALID_IV: _h_ai_core.HaiErrorDef;
181
742
  };
182
- /** 密文模式:0=C1C2C3(旧版),1=C1C3C2(国标) */
743
+ /**
744
+ * SM2 密文排列:1为C1C3C2默认值,0为显式C1C2C3;解密必须匹配加密排列。
745
+ *
746
+ * @example
747
+ * ```ts
748
+ * import { crypto, type CipherMode } from '@h-ai/crypto'
749
+ * const initialized = await crypto.init()
750
+ * if (!initialized.success) throw new Error(initialized.error.message)
751
+ * try {
752
+ * const generated = crypto.asymmetric.generateKeyPair()
753
+ * if (!generated.success) throw new Error(generated.error.message)
754
+ * const pair = generated.data
755
+ * const mode: CipherMode = 1
756
+ * const encrypted = crypto.asymmetric.encrypt('hello', pair.publicKey, { cipherMode: mode })
757
+ * if (!encrypted.success) throw new Error(encrypted.error.message)
758
+ * const decrypted = crypto.asymmetric.decrypt(encrypted.data, pair.privateKey, { cipherMode: mode })
759
+ * if (!decrypted.success) throw new Error(decrypted.error.message)
760
+ * }
761
+ * finally { await crypto.close() }
762
+ * ```
763
+ */
183
764
  type CipherMode = 0 | 1;
184
- /** 非对称密钥对 */
765
+ /**
766
+ * SM2 公私钥对;私钥必须保密,不写日志、公开配置或响应。
767
+ *
768
+ * @example
769
+ * ```ts
770
+ * import { crypto, type KeyPair } from '@h-ai/crypto'
771
+ * const initialized = await crypto.init()
772
+ * if (!initialized.success) throw new Error(initialized.error.message)
773
+ * try {
774
+ * const generated = crypto.asymmetric.generateKeyPair()
775
+ * if (!generated.success) throw new Error(generated.error.message)
776
+ * const pair = generated.data
777
+ * const keyPair: KeyPair = pair
778
+ * const publicKey = keyPair.publicKey
779
+ * }
780
+ * finally { await crypto.close() }
781
+ * ```
782
+ */
185
783
  interface KeyPair {
186
784
  /** 公钥(十六进制字符串,包含 04 前缀为非压缩格式) */
187
785
  publicKey: string;
188
786
  /** 私钥(十六进制字符串,64 字符) */
189
787
  privateKey: string;
190
788
  }
191
- /** 非对称加密选项 */
789
+ /**
790
+ * SM2 密文排列和输出编码;加解密 cipherMode 必须一致,解密可识别受支持的 base64 输出。
791
+ *
792
+ * @example
793
+ * ```ts
794
+ * import { crypto, type AsymmetricEncryptOptions } from '@h-ai/crypto'
795
+ * const initialized = await crypto.init()
796
+ * if (!initialized.success) throw new Error(initialized.error.message)
797
+ * try {
798
+ * const generated = crypto.asymmetric.generateKeyPair()
799
+ * if (!generated.success) throw new Error(generated.error.message)
800
+ * const pair = generated.data
801
+ * const options: AsymmetricEncryptOptions = { cipherMode: 1, outputFormat: 'base64' }
802
+ * const encrypted = crypto.asymmetric.encrypt('hello', pair.publicKey, options)
803
+ * if (!encrypted.success) throw new Error(encrypted.error.message)
804
+ * const decrypted = crypto.asymmetric.decrypt(encrypted.data, pair.privateKey, options)
805
+ * if (!decrypted.success) throw new Error(decrypted.error.message)
806
+ * }
807
+ * finally { await crypto.close() }
808
+ * ```
809
+ */
192
810
  interface AsymmetricEncryptOptions {
193
811
  /** 密文模式:0=C1C2C3(旧版),1=C1C3C2(国标,默认) */
194
812
  cipherMode?: CipherMode;
195
813
  /** 输出格式 */
196
814
  outputFormat?: 'hex' | 'base64';
197
815
  }
198
- /** 签名选项 */
816
+ /**
817
+ * SM2 签名的哈希与用户标识参数;验签必须使用相同设置,默认启用哈希。
818
+ *
819
+ * @example
820
+ * ```ts
821
+ * import { crypto, type SignOptions } from '@h-ai/crypto'
822
+ * const initialized = await crypto.init()
823
+ * if (!initialized.success) throw new Error(initialized.error.message)
824
+ * try {
825
+ * const generated = crypto.asymmetric.generateKeyPair()
826
+ * if (!generated.success) throw new Error(generated.error.message)
827
+ * const pair = generated.data
828
+ * const options: SignOptions = { hash: true, userId: 'orders-service' }
829
+ * const signature = crypto.asymmetric.sign('hello', pair.privateKey, options)
830
+ * if (!signature.success) throw new Error(signature.error.message)
831
+ * const verified = crypto.asymmetric.verify('hello', signature.data, pair.publicKey, options)
832
+ * if (!verified.success) throw new Error(verified.error.message)
833
+ * const accepted = verified.data
834
+ * }
835
+ * finally { await crypto.close() }
836
+ * ```
837
+ */
199
838
  interface SignOptions {
200
839
  /** 是否对数据进行哈希(默认 true) */
201
840
  hash?: boolean;
202
841
  /** 用户 ID(默认 "1234567812345678") */
203
842
  userId?: string;
204
843
  }
205
- /** 哈希选项 */
844
+ /**
845
+ * 字符串输入编码;hex 表示解码后的字节,Uint8Array 直接作为字节输入。
846
+ *
847
+ * @example
848
+ * ```ts
849
+ * import { crypto, type HashOptions } from '@h-ai/crypto'
850
+ * const initialized = await crypto.init()
851
+ * if (!initialized.success) throw new Error(initialized.error.message)
852
+ * try {
853
+ * const options: HashOptions = { inputEncoding: 'hex' }
854
+ * const digest = crypto.hash.hash('6869', options)
855
+ * if (!digest.success) throw new Error(digest.error.message)
856
+ * }
857
+ * finally { await crypto.close() }
858
+ * ```
859
+ */
206
860
  interface HashOptions {
207
861
  /** 输入编码 */
208
862
  inputEncoding?: 'utf8' | 'hex';
209
863
  }
210
- /** 对称加密模式 */
864
+ /**
865
+ * SM4 的 CBC/ECB 模式;新数据使用默认 CBC,ECB 会暴露相同明文块结构,两者都不提供 MAC。
866
+ *
867
+ * @example
868
+ * ```ts
869
+ * import { crypto, type SymmetricMode } from '@h-ai/crypto'
870
+ * const initialized = await crypto.init()
871
+ * if (!initialized.success) throw new Error(initialized.error.message)
872
+ * try {
873
+ * const mode: SymmetricMode = 'cbc'
874
+ * const key = crypto.symmetric.generateKey()
875
+ * const encrypted = crypto.symmetric.encrypt('hello', key, { mode })
876
+ * if (!encrypted.success) throw new Error(encrypted.error.message)
877
+ * }
878
+ * finally { await crypto.close() }
879
+ * ```
880
+ */
211
881
  type SymmetricMode = 'ecb' | 'cbc';
212
- /** 对称密文编码 */
882
+ /**
883
+ * 结构化 SM4 ciphertext 的 hex/base64 编码标识,解密必须保留。
884
+ *
885
+ * @example
886
+ * ```ts
887
+ * import { crypto, type SymmetricCiphertextEncoding } from '@h-ai/crypto'
888
+ * const initialized = await crypto.init()
889
+ * if (!initialized.success) throw new Error(initialized.error.message)
890
+ * try {
891
+ * const outputFormat: SymmetricCiphertextEncoding = 'base64'
892
+ * const key = crypto.symmetric.generateKey()
893
+ * const encrypted = crypto.symmetric.encrypt('hello', key, { outputFormat })
894
+ * if (!encrypted.success) throw new Error(encrypted.error.message)
895
+ * const decrypted = crypto.symmetric.decrypt(encrypted.data, key)
896
+ * if (!decrypted.success) throw new Error(decrypted.error.message)
897
+ * }
898
+ * finally { await crypto.close() }
899
+ * ```
900
+ */
213
901
  type SymmetricCiphertextEncoding = 'hex' | 'base64';
214
- /** 对称加密选项。默认 CBC,并自动生成随机 IV。 */
902
+ /**
903
+ * SM4 模式、IV 与编码;CBC 未提供 IV 时随机生成,手动 IV 必须每次独立随机。
904
+ *
905
+ * @example
906
+ * ```ts
907
+ * import { crypto, type SymmetricEncryptOptions } from '@h-ai/crypto'
908
+ * const initialized = await crypto.init()
909
+ * if (!initialized.success) throw new Error(initialized.error.message)
910
+ * try {
911
+ * const options: SymmetricEncryptOptions = { mode: 'cbc', outputFormat: 'base64' }
912
+ * const key = crypto.symmetric.generateKey()
913
+ * const encrypted = crypto.symmetric.encrypt('hello', key, options)
914
+ * if (!encrypted.success) throw new Error(encrypted.error.message)
915
+ * }
916
+ * finally { await crypto.close() }
917
+ * ```
918
+ */
215
919
  interface SymmetricEncryptOptions {
216
920
  /** 加密模式;默认 cbc */
217
921
  mode?: SymmetricMode;
@@ -220,7 +924,25 @@ interface SymmetricEncryptOptions {
220
924
  /** 密文输出编码;默认 hex */
221
925
  outputFormat?: SymmetricCiphertextEncoding;
222
926
  }
223
- /** 对称加密结构化结果。 */
927
+ /**
928
+ * SM4 密文及 mode/encoding/可选 IV;保留全部字段才能正确解密,不含消息认证能力。
929
+ *
930
+ * @example
931
+ * ```ts
932
+ * import { crypto, type SymmetricEncryptedPayload } from '@h-ai/crypto'
933
+ * const initialized = await crypto.init()
934
+ * if (!initialized.success) throw new Error(initialized.error.message)
935
+ * try {
936
+ * const key = crypto.symmetric.generateKey()
937
+ * const encrypted = crypto.symmetric.encrypt('hello', key)
938
+ * if (!encrypted.success) throw new Error(encrypted.error.message)
939
+ * const payload: SymmetricEncryptedPayload = encrypted.data
940
+ * const decrypted = crypto.symmetric.decrypt(payload, key)
941
+ * if (!decrypted.success) throw new Error(decrypted.error.message)
942
+ * }
943
+ * finally { await crypto.close() }
944
+ * ```
945
+ */
224
946
  interface SymmetricEncryptedPayload {
225
947
  /** 加密模式 */
226
948
  readonly mode: SymmetricMode;
@@ -231,16 +953,87 @@ interface SymmetricEncryptedPayload {
231
953
  /** ciphertext 字段的编码 */
232
954
  readonly encoding: SymmetricCiphertextEncoding;
233
955
  }
234
- /** CBC 模式结构化结果。 */
956
+ /**
957
+ * CBC 结构化密文,mode固定cbc且IV必填;仍需上层消息认证。
958
+ *
959
+ * @example
960
+ * ```ts
961
+ * import { crypto, type SymmetricCbcEncryptedPayload } from '@h-ai/crypto'
962
+ * const initialized = await crypto.init()
963
+ * if (!initialized.success) throw new Error(initialized.error.message)
964
+ * try {
965
+ * const key = crypto.symmetric.generateKey()
966
+ * const encrypted = crypto.symmetric.encryptWithIV('hello', key)
967
+ * if (!encrypted.success) throw new Error(encrypted.error.message)
968
+ * const payload: SymmetricCbcEncryptedPayload = encrypted.data
969
+ * const decrypted = crypto.symmetric.decrypt(payload, key)
970
+ * if (!decrypted.success) throw new Error(decrypted.error.message)
971
+ * }
972
+ * finally { await crypto.close() }
973
+ * ```
974
+ */
235
975
  interface SymmetricCbcEncryptedPayload extends SymmetricEncryptedPayload {
236
976
  readonly mode: 'cbc';
237
977
  readonly iv: string;
238
978
  }
239
- /** 对称解密输入。 */
979
+ /**
980
+ * SM4 解密输入别名;传入加密返回的完整结构,不能用裸密文猜测IV或编码。
981
+ *
982
+ * @example
983
+ * ```ts
984
+ * import { crypto, type SymmetricDecryptInput } from '@h-ai/crypto'
985
+ * const initialized = await crypto.init()
986
+ * if (!initialized.success) throw new Error(initialized.error.message)
987
+ * try {
988
+ * const key = crypto.symmetric.generateKey()
989
+ * const encrypted = crypto.symmetric.encrypt('hello', key)
990
+ * if (!encrypted.success) throw new Error(encrypted.error.message)
991
+ * const payload: SymmetricDecryptInput = encrypted.data
992
+ * const decrypted = crypto.symmetric.decrypt(payload, key)
993
+ * if (!decrypted.success) throw new Error(decrypted.error.message)
994
+ * }
995
+ * finally { await crypto.close() }
996
+ * ```
997
+ */
240
998
  type SymmetricDecryptInput = SymmetricEncryptedPayload;
241
- /** 带 IV 加密结果。 */
999
+ /**
1000
+ * encryptWithIV 的CBC结构化结果别名;含 mode、ciphertext、iv 与 encoding。
1001
+ *
1002
+ * @example
1003
+ * ```ts
1004
+ * import { crypto, type EncryptWithIVResult } from '@h-ai/crypto'
1005
+ * const initialized = await crypto.init()
1006
+ * if (!initialized.success) throw new Error(initialized.error.message)
1007
+ * try {
1008
+ * const key = crypto.symmetric.generateKey()
1009
+ * const encrypted = crypto.symmetric.encryptWithIV('hello', key)
1010
+ * if (!encrypted.success) throw new Error(encrypted.error.message)
1011
+ * const payload: EncryptWithIVResult = encrypted.data
1012
+ * const decrypted = crypto.symmetric.decrypt(payload, key)
1013
+ * if (!decrypted.success) throw new Error(decrypted.error.message)
1014
+ * }
1015
+ * finally { await crypto.close() }
1016
+ * ```
1017
+ */
242
1018
  type EncryptWithIVResult = SymmetricCbcEncryptedPayload;
243
- /** 密码哈希配置 */
1019
+ /**
1020
+ * 迭代SM3密码参数;默认盐长16、迭代10000,合法范围以实现校验为准,已有哈希自行携带参数。
1021
+ *
1022
+ * @example
1023
+ * ```ts
1024
+ * import { crypto, type PasswordConfig } from '@h-ai/crypto'
1025
+ * const initialized = await crypto.init()
1026
+ * if (!initialized.success) throw new Error(initialized.error.message)
1027
+ * try {
1028
+ * const config: PasswordConfig = { saltLength: 16, iterations: 10000 }
1029
+ * const hashed = crypto.password.hash('test-password', config)
1030
+ * if (!hashed.success) throw new Error(hashed.error.message)
1031
+ * const verified = crypto.password.verify('test-password', hashed.data)
1032
+ * if (!verified.success) throw new Error(verified.error.message)
1033
+ * }
1034
+ * finally { await crypto.close() }
1035
+ * ```
1036
+ */
244
1037
  interface PasswordConfig {
245
1038
  /** 盐值长度(默认 16) */
246
1039
  saltLength?: number;
@@ -248,15 +1041,47 @@ interface PasswordConfig {
248
1041
  iterations?: number;
249
1042
  }
250
1043
  /**
251
- * 非对称加密操作接口
1044
+ * SM2 操作域;先初始化,所有 HaiResult 先检查 success,boolean 校验器按直接返回值使用。
1045
+ * 公钥曲线点有效不等于对端身份可信;可信公钥分发仍由TLS/受管信任链保证。
252
1046
  *
253
- * 通过 `crypto.asymmetric` 访问,需先调用 `crypto.init()`。
1047
+ * @example
1048
+ * ```ts
1049
+ * import { crypto, type AsymmetricOperations } from '@h-ai/crypto'
1050
+ * const initialized = await crypto.init()
1051
+ * if (!initialized.success) throw new Error(initialized.error.message)
1052
+ * try {
1053
+ * const generated = crypto.asymmetric.generateKeyPair()
1054
+ * if (!generated.success) throw new Error(generated.error.message)
1055
+ * const pair = generated.data
1056
+ * const operations: AsymmetricOperations = crypto.asymmetric
1057
+ * const signature = operations.sign('hello', pair.privateKey)
1058
+ * if (!signature.success) throw new Error(signature.error.message)
1059
+ * const verified = operations.verify('hello', signature.data, pair.publicKey)
1060
+ * if (!verified.success) throw new Error(verified.error.message)
1061
+ * const matches = verified.data
1062
+ * }
1063
+ * finally { await crypto.close() }
1064
+ * ```
254
1065
  */
255
1066
  interface AsymmetricOperations {
256
1067
  /**
257
1068
  * 生成密钥对
258
1069
  *
259
1070
  * @returns 成功时包含公私钥对;失败时返回 KEY_GENERATION_FAILED
1071
+ *
1072
+ * @example
1073
+ * ```ts
1074
+ * import { crypto } from '@h-ai/crypto'
1075
+ * const initialized = await crypto.init()
1076
+ * if (!initialized.success) throw new Error(initialized.error.message)
1077
+ * try {
1078
+ * const generated = crypto.asymmetric.generateKeyPair()
1079
+ * if (!generated.success) throw new Error(generated.error.message)
1080
+ * const pair = generated.data
1081
+ * const publicKey = pair.publicKey
1082
+ * }
1083
+ * finally { await crypto.close() }
1084
+ * ```
260
1085
  */
261
1086
  generateKeyPair: () => HaiResult<KeyPair>;
262
1087
  /**
@@ -266,6 +1091,21 @@ interface AsymmetricOperations {
266
1091
  * @param publicKey - 公钥(十六进制,支持带/不带 04 前缀)
267
1092
  * @param options - 加密选项(密文模式、输出格式)
268
1093
  * @returns 成功时返回密文字符串;失败时返回 INVALID_KEY 或 ENCRYPTION_FAILED
1094
+ *
1095
+ * @example
1096
+ * ```ts
1097
+ * import { crypto } from '@h-ai/crypto'
1098
+ * const initialized = await crypto.init()
1099
+ * if (!initialized.success) throw new Error(initialized.error.message)
1100
+ * try {
1101
+ * const generated = crypto.asymmetric.generateKeyPair()
1102
+ * if (!generated.success) throw new Error(generated.error.message)
1103
+ * const pair = generated.data
1104
+ * const encrypted = crypto.asymmetric.encrypt('hello', pair.publicKey, { cipherMode: 1, outputFormat: 'hex' })
1105
+ * if (!encrypted.success) throw new Error(encrypted.error.message)
1106
+ * }
1107
+ * finally { await crypto.close() }
1108
+ * ```
269
1109
  */
270
1110
  encrypt: (data: string, publicKey: string, options?: AsymmetricEncryptOptions) => HaiResult<string>;
271
1111
  /**
@@ -277,6 +1117,23 @@ interface AsymmetricOperations {
277
1117
  * @param privateKey - 私钥(64 字符十六进制)
278
1118
  * @param options - 解密选项(密文模式需与加密时一致)
279
1119
  * @returns 成功时返回明文;失败时返回 INVALID_KEY 或 DECRYPTION_FAILED
1120
+ *
1121
+ * @example
1122
+ * ```ts
1123
+ * import { crypto } from '@h-ai/crypto'
1124
+ * const initialized = await crypto.init()
1125
+ * if (!initialized.success) throw new Error(initialized.error.message)
1126
+ * try {
1127
+ * const generated = crypto.asymmetric.generateKeyPair()
1128
+ * if (!generated.success) throw new Error(generated.error.message)
1129
+ * const pair = generated.data
1130
+ * const encrypted = crypto.asymmetric.encrypt('hello', pair.publicKey)
1131
+ * if (!encrypted.success) throw new Error(encrypted.error.message)
1132
+ * const decrypted = crypto.asymmetric.decrypt(encrypted.data, pair.privateKey)
1133
+ * if (!decrypted.success) throw new Error(decrypted.error.message)
1134
+ * }
1135
+ * finally { await crypto.close() }
1136
+ * ```
280
1137
  */
281
1138
  decrypt: (ciphertext: string, privateKey: string, options?: AsymmetricEncryptOptions) => HaiResult<string>;
282
1139
  /**
@@ -286,6 +1143,21 @@ interface AsymmetricOperations {
286
1143
  * @param privateKey - 私钥(64 字符十六进制)
287
1144
  * @param options - 签名选项(hash 开关、userId)
288
1145
  * @returns 成功时返回签名字符串;失败时返回 INVALID_KEY 或 SIGN_FAILED
1146
+ *
1147
+ * @example
1148
+ * ```ts
1149
+ * import { crypto } from '@h-ai/crypto'
1150
+ * const initialized = await crypto.init()
1151
+ * if (!initialized.success) throw new Error(initialized.error.message)
1152
+ * try {
1153
+ * const generated = crypto.asymmetric.generateKeyPair()
1154
+ * if (!generated.success) throw new Error(generated.error.message)
1155
+ * const pair = generated.data
1156
+ * const signature = crypto.asymmetric.sign('hello', pair.privateKey)
1157
+ * if (!signature.success) throw new Error(signature.error.message)
1158
+ * }
1159
+ * finally { await crypto.close() }
1160
+ * ```
289
1161
  */
290
1162
  sign: (data: string, privateKey: string, options?: SignOptions) => HaiResult<string>;
291
1163
  /**
@@ -296,25 +1168,84 @@ interface AsymmetricOperations {
296
1168
  * @param publicKey - 公钥(支持带/不带 04 前缀)
297
1169
  * @param options - 验签选项
298
1170
  * @returns 成功时返回 boolean;失败时返回 INVALID_KEY 或 VERIFY_FAILED
1171
+ *
1172
+ * @example
1173
+ * ```ts
1174
+ * import { crypto } from '@h-ai/crypto'
1175
+ * const initialized = await crypto.init()
1176
+ * if (!initialized.success) throw new Error(initialized.error.message)
1177
+ * try {
1178
+ * const generated = crypto.asymmetric.generateKeyPair()
1179
+ * if (!generated.success) throw new Error(generated.error.message)
1180
+ * const pair = generated.data
1181
+ * const signature = crypto.asymmetric.sign('hello', pair.privateKey)
1182
+ * if (!signature.success) throw new Error(signature.error.message)
1183
+ * const verified = crypto.asymmetric.verify('hello', signature.data, pair.publicKey)
1184
+ * if (!verified.success) throw new Error(verified.error.message)
1185
+ * const accepted = verified.data
1186
+ * }
1187
+ * finally { await crypto.close() }
1188
+ * ```
299
1189
  */
300
1190
  verify: (data: string, signature: string, publicKey: string, options?: SignOptions) => HaiResult<boolean>;
301
1191
  /**
302
- * 校验公钥格式是否合法
1192
+ * 校验128/130字符SM2非压缩公钥编码及曲线点;有效不代表对端持有私钥。
1193
+ * @param key - 本次操作的密钥,不记录到日志。
1194
+ * @returns 直接boolean,false表示不匹配/未就绪。
303
1195
  *
304
- * 合法格式:128 字符十六进制(无前缀)或 130 字符(含 04 前缀)。
1196
+ * @example
1197
+ * ```ts
1198
+ * import { crypto } from '@h-ai/crypto'
1199
+ * const initialized = await crypto.init()
1200
+ * if (!initialized.success) throw new Error(initialized.error.message)
1201
+ * try {
1202
+ * const generated = crypto.asymmetric.generateKeyPair()
1203
+ * if (!generated.success) throw new Error(generated.error.message)
1204
+ * const pair = generated.data
1205
+ * const valid = crypto.asymmetric.isValidPublicKey(pair.publicKey)
1206
+ * }
1207
+ * finally { await crypto.close() }
1208
+ * ```
305
1209
  */
306
1210
  isValidPublicKey: (key: string) => boolean;
307
1211
  /**
308
- * 校验私钥格式是否合法
1212
+ * 校验64字符十六进制私钥格式;这是格式检查,不是密钥配对证明。
1213
+ * @param key - 本次操作的密钥,不记录到日志。
1214
+ * @returns 直接boolean,false表示不匹配/未就绪。
309
1215
  *
310
- * 合法格式:64 字符十六进制。
1216
+ * @example
1217
+ * ```ts
1218
+ * import { crypto } from '@h-ai/crypto'
1219
+ * const initialized = await crypto.init()
1220
+ * if (!initialized.success) throw new Error(initialized.error.message)
1221
+ * try {
1222
+ * const generated = crypto.asymmetric.generateKeyPair()
1223
+ * if (!generated.success) throw new Error(generated.error.message)
1224
+ * const pair = generated.data
1225
+ * const valid = crypto.asymmetric.isValidPrivateKey(pair.privateKey)
1226
+ * }
1227
+ * finally { await crypto.close() }
1228
+ * ```
311
1229
  */
312
1230
  isValidPrivateKey: (key: string) => boolean;
313
1231
  }
314
1232
  /**
315
- * 哈希操作接口
1233
+ * SM3 摘要、HMAC-SM3 与常量时间摘要匹配操作;摘要不是加密,HMAC密钥不能公开。
316
1234
  *
317
- * 通过 `crypto.hash` 访问,需先调用 `crypto.init()`。
1235
+ * @example
1236
+ * ```ts
1237
+ * import { crypto, type HashOperations } from '@h-ai/crypto'
1238
+ * const initialized = await crypto.init()
1239
+ * if (!initialized.success) throw new Error(initialized.error.message)
1240
+ * try {
1241
+ * const operations: HashOperations = crypto.hash
1242
+ * const digest = operations.hash('hello')
1243
+ * if (!digest.success) throw new Error(digest.error.message)
1244
+ * const verified = operations.verify('hello', digest.data)
1245
+ * if (!verified.success) throw new Error(verified.error.message)
1246
+ * }
1247
+ * finally { await crypto.close() }
1248
+ * ```
318
1249
  */
319
1250
  interface HashOperations {
320
1251
  /**
@@ -323,6 +1254,18 @@ interface HashOperations {
323
1254
  * @param data - 待哈希数据(字符串或 Uint8Array)
324
1255
  * @param options - 输入编码选项
325
1256
  * @returns 成功时返回 64 字符十六进制哈希值;失败时返回 HASH_FAILED
1257
+ *
1258
+ * @example
1259
+ * ```ts
1260
+ * import { crypto } from '@h-ai/crypto'
1261
+ * const initialized = await crypto.init()
1262
+ * if (!initialized.success) throw new Error(initialized.error.message)
1263
+ * try {
1264
+ * const digest = crypto.hash.hash('6869', { inputEncoding: 'hex' })
1265
+ * if (!digest.success) throw new Error(digest.error.message)
1266
+ * }
1267
+ * finally { await crypto.close() }
1268
+ * ```
326
1269
  */
327
1270
  hash: (data: string | Uint8Array, options?: HashOptions) => HaiResult<string>;
328
1271
  /**
@@ -334,6 +1277,19 @@ interface HashOperations {
334
1277
  * @param data - 待计算数据
335
1278
  * @param key - HMAC 密钥
336
1279
  * @returns 成功时返回 64 字符十六进制 HMAC 值;失败时返回 HMAC_FAILED
1280
+ *
1281
+ * @example
1282
+ * ```ts
1283
+ * import { crypto } from '@h-ai/crypto'
1284
+ * const initialized = await crypto.init()
1285
+ * if (!initialized.success) throw new Error(initialized.error.message)
1286
+ * try {
1287
+ * const key = crypto.symmetric.generateKey()
1288
+ * const tag = crypto.hash.hmac('hello', key)
1289
+ * if (!tag.success) throw new Error(tag.error.message)
1290
+ * }
1291
+ * finally { await crypto.close() }
1292
+ * ```
337
1293
  */
338
1294
  hmac: (data: string, key: string) => HaiResult<string>;
339
1295
  /**
@@ -344,19 +1300,77 @@ interface HashOperations {
344
1300
  * @param data - 原始数据
345
1301
  * @param expectedHash - 期望的哈希值
346
1302
  * @returns 成功时返回 boolean;失败时返回 HASH_FAILED
1303
+ *
1304
+ * @example
1305
+ * ```ts
1306
+ * import { crypto } from '@h-ai/crypto'
1307
+ * const initialized = await crypto.init()
1308
+ * if (!initialized.success) throw new Error(initialized.error.message)
1309
+ * try {
1310
+ * const digest = crypto.hash.hash('hello')
1311
+ * if (!digest.success) throw new Error(digest.error.message)
1312
+ * const verified = crypto.hash.verify('hello', digest.data)
1313
+ * if (!verified.success) throw new Error(verified.error.message)
1314
+ * const matches = verified.data
1315
+ * }
1316
+ * finally { await crypto.close() }
1317
+ * ```
347
1318
  */
348
1319
  verify: (data: string, expectedHash: string) => HaiResult<boolean>;
349
1320
  }
350
1321
  /**
351
- * 对称加密操作接口
1322
+ * SM4 同步操作域;默认CBC与随机IV,返回完整结构化密文;CBC原语不提供完整性认证。
352
1323
  *
353
- * 通过 `crypto.symmetric` 访问,需先调用 `crypto.init()`。
354
- * 默认使用 CBC 并自动生成 IV;密文以结构化字段返回。
1324
+ * @example
1325
+ * ```ts
1326
+ * import { crypto, type SymmetricOperations } from '@h-ai/crypto'
1327
+ * const initialized = await crypto.init()
1328
+ * if (!initialized.success) throw new Error(initialized.error.message)
1329
+ * try {
1330
+ * const operations: SymmetricOperations = crypto.symmetric
1331
+ * const key = operations.generateKey()
1332
+ * const encrypted = operations.encrypt('hello', key)
1333
+ * if (!encrypted.success) throw new Error(encrypted.error.message)
1334
+ * const decrypted = operations.decrypt(encrypted.data, key)
1335
+ * if (!decrypted.success) throw new Error(decrypted.error.message)
1336
+ * }
1337
+ * finally { await crypto.close() }
1338
+ * ```
355
1339
  */
356
1340
  interface SymmetricOperations {
357
- /** 生成随机密钥(16 字节 = 32 个十六进制字符) */
1341
+ /**
1342
+ * 生成随机密钥(16 字节 = 32 个十六进制字符)
1343
+ * @returns 直接返回值,使用前先初始化模块。
1344
+ *
1345
+ * @example
1346
+ * ```ts
1347
+ * import { crypto } from '@h-ai/crypto'
1348
+ * const initialized = await crypto.init()
1349
+ * if (!initialized.success) throw new Error(initialized.error.message)
1350
+ * try {
1351
+ * const key = crypto.symmetric.generateKey()
1352
+ * const valid = crypto.symmetric.isValidKey(key)
1353
+ * }
1354
+ * finally { await crypto.close() }
1355
+ * ```
1356
+ */
358
1357
  generateKey: () => string;
359
- /** 生成随机 IV(16 字节 = 32 个十六进制字符) */
1358
+ /**
1359
+ * 生成随机 IV(16 字节 = 32 个十六进制字符)
1360
+ * @returns 直接返回值,使用前先初始化模块。
1361
+ *
1362
+ * @example
1363
+ * ```ts
1364
+ * import { crypto } from '@h-ai/crypto'
1365
+ * const initialized = await crypto.init()
1366
+ * if (!initialized.success) throw new Error(initialized.error.message)
1367
+ * try {
1368
+ * const iv = crypto.symmetric.generateIV()
1369
+ * const valid = crypto.symmetric.isValidIV(iv)
1370
+ * }
1371
+ * finally { await crypto.close() }
1372
+ * ```
1373
+ */
360
1374
  generateIV: () => string;
361
1375
  /**
362
1376
  * 对称加密
@@ -368,6 +1382,19 @@ interface SymmetricOperations {
368
1382
  * @param key - 密钥(32 字符十六进制)
369
1383
  * @param options - 加密模式/IV/输出格式
370
1384
  * @returns 成功时返回结构化密文;失败时返回 INVALID_KEY/INVALID_IV/ENCRYPTION_FAILED
1385
+ *
1386
+ * @example
1387
+ * ```ts
1388
+ * import { crypto } from '@h-ai/crypto'
1389
+ * const initialized = await crypto.init()
1390
+ * if (!initialized.success) throw new Error(initialized.error.message)
1391
+ * try {
1392
+ * const key = crypto.symmetric.generateKey()
1393
+ * const encrypted = crypto.symmetric.encrypt('hello', key)
1394
+ * if (!encrypted.success) throw new Error(encrypted.error.message)
1395
+ * }
1396
+ * finally { await crypto.close() }
1397
+ * ```
371
1398
  */
372
1399
  encrypt: (data: string, key: string, options?: SymmetricEncryptOptions) => HaiResult<SymmetricEncryptedPayload>;
373
1400
  /**
@@ -378,6 +1405,21 @@ interface SymmetricOperations {
378
1405
  * @param payload - 结构化密文(包含 mode/ciphertext/iv/encoding)
379
1406
  * @param key - 密钥(32 字符十六进制)
380
1407
  * @returns 成功时返回明文;失败时返回 INVALID_KEY/INVALID_IV/DECRYPTION_FAILED
1408
+ *
1409
+ * @example
1410
+ * ```ts
1411
+ * import { crypto } from '@h-ai/crypto'
1412
+ * const initialized = await crypto.init()
1413
+ * if (!initialized.success) throw new Error(initialized.error.message)
1414
+ * try {
1415
+ * const key = crypto.symmetric.generateKey()
1416
+ * const encrypted = crypto.symmetric.encrypt('hello', key)
1417
+ * if (!encrypted.success) throw new Error(encrypted.error.message)
1418
+ * const decrypted = crypto.symmetric.decrypt(encrypted.data, key)
1419
+ * if (!decrypted.success) throw new Error(decrypted.error.message)
1420
+ * }
1421
+ * finally { await crypto.close() }
1422
+ * ```
381
1423
  */
382
1424
  decrypt: (payload: SymmetricDecryptInput, key: string) => HaiResult<string>;
383
1425
  /**
@@ -385,43 +1427,103 @@ interface SymmetricOperations {
385
1427
  *
386
1428
  * @param data - 待加密明文
387
1429
  * @param key - 密钥(32 字符十六进制)
388
- * @returns 成功时返回 { ciphertext, iv };失败时同 encrypt
1430
+ * @returns 成功时返回完整 { mode, ciphertext, iv, encoding };失败时同 encrypt
1431
+ *
1432
+ * @example
1433
+ * ```ts
1434
+ * import { crypto } from '@h-ai/crypto'
1435
+ * const initialized = await crypto.init()
1436
+ * if (!initialized.success) throw new Error(initialized.error.message)
1437
+ * try {
1438
+ * const key = crypto.symmetric.generateKey()
1439
+ * const encrypted = crypto.symmetric.encryptWithIV('hello', key)
1440
+ * if (!encrypted.success) throw new Error(encrypted.error.message)
1441
+ * }
1442
+ * finally { await crypto.close() }
1443
+ * ```
389
1444
  */
390
1445
  encryptWithIV: (data: string, key: string) => HaiResult<EncryptWithIVResult>;
391
1446
  /**
392
1447
  * 带 IV 解密(CBC 模式)
393
1448
  *
1449
+ * 不提供完整性认证。传输场景必须用 transport 先验签和验证 MAC,禁止直接对 HTTP 密文调用此 primitive。
1450
+ *
394
1451
  * @param ciphertext - 密文
395
1452
  * @param key - 密钥
396
1453
  * @param iv - 加密时使用的 IV
397
1454
  * @returns 成功时返回明文;失败时同 decrypt
1455
+ *
1456
+ * @example
1457
+ * ```ts
1458
+ * import { crypto } from '@h-ai/crypto'
1459
+ * const initialized = await crypto.init()
1460
+ * if (!initialized.success) throw new Error(initialized.error.message)
1461
+ * try {
1462
+ * const key = crypto.symmetric.generateKey()
1463
+ * const encrypted = crypto.symmetric.encryptWithIV('hello', key)
1464
+ * if (!encrypted.success) throw new Error(encrypted.error.message)
1465
+ * const decrypted = crypto.symmetric.decryptWithIV(encrypted.data.ciphertext, key, encrypted.data.iv)
1466
+ * if (!decrypted.success) throw new Error(decrypted.error.message)
1467
+ * }
1468
+ * finally { await crypto.close() }
1469
+ * ```
398
1470
  */
399
1471
  decryptWithIV: (ciphertext: string, key: string, iv: string) => HaiResult<string>;
400
1472
  /**
401
- * 从密码和盐值派生密钥
402
- *
403
- * 内部仅执行单次哈希(password + salt) 取前 32 字符作为密钥。
404
- *
405
- * ⚠️ 安全警告:**此实现不是标准 KDF**,不具备密码爆破抗性。
406
- * - 禁止用于密码存储(请用 `crypto.password.hash`)。
407
- * - 禁止用于高价值密钥派生(请在应用层采用 PBKDF2 / scrypt / Argon2)。
1473
+ * 校验密钥格式是否合法(32 字符十六进制)
1474
+ * @param key - 本次操作的密钥,不记录到日志。
1475
+ * @returns 直接boolean,false表示不匹配/未就绪。
408
1476
  *
409
- * @deprecated 未来版本可能移除。详见模块 README 安全声明。
410
- * @param password - 密码
411
- * @param salt - 盐值
412
- * @returns 32 字符十六进制密钥
1477
+ * @example
1478
+ * ```ts
1479
+ * import { crypto } from '@h-ai/crypto'
1480
+ * const initialized = await crypto.init()
1481
+ * if (!initialized.success) throw new Error(initialized.error.message)
1482
+ * try {
1483
+ * const key = crypto.symmetric.generateKey()
1484
+ * const valid = crypto.symmetric.isValidKey(key)
1485
+ * }
1486
+ * finally { await crypto.close() }
1487
+ * ```
413
1488
  */
414
- deriveKey: (password: string, salt: string) => string;
415
- /** 校验密钥格式是否合法(32 字符十六进制) */
416
1489
  isValidKey: (key: string) => boolean;
417
- /** 校验 IV 格式是否合法(32 字符十六进制) */
1490
+ /**
1491
+ * 校验 IV 格式是否合法(32 字符十六进制)
1492
+ * @param iv - 实际调用输入,按该方法约束验证。
1493
+ * @returns 直接boolean,false表示不匹配/未就绪。
1494
+ *
1495
+ * @example
1496
+ * ```ts
1497
+ * import { crypto } from '@h-ai/crypto'
1498
+ * const initialized = await crypto.init()
1499
+ * if (!initialized.success) throw new Error(initialized.error.message)
1500
+ * try {
1501
+ * const iv = crypto.symmetric.generateIV()
1502
+ * const valid = crypto.symmetric.isValidIV(iv)
1503
+ * }
1504
+ * finally { await crypto.close() }
1505
+ * ```
1506
+ */
418
1507
  isValidIV: (iv: string) => boolean;
419
1508
  }
420
1509
  /**
421
- * 密码哈希操作接口
1510
+ * 迭代加盐SM3密码存储操作;验证从 $hai$ 格式读取参数,返回成功false表示密码不匹配。
422
1511
  *
423
- * 通过 `crypto.password` 访问,需先调用 `crypto.init()`。
424
- * 使用迭代加盐的方式生成密码哈希。
1512
+ * @example
1513
+ * ```ts
1514
+ * import { crypto, type PasswordOperations } from '@h-ai/crypto'
1515
+ * const initialized = await crypto.init()
1516
+ * if (!initialized.success) throw new Error(initialized.error.message)
1517
+ * try {
1518
+ * const operations: PasswordOperations = crypto.password
1519
+ * const hashed = operations.hash('test-password')
1520
+ * if (!hashed.success) throw new Error(hashed.error.message)
1521
+ * const verified = operations.verify('test-password', hashed.data)
1522
+ * if (!verified.success) throw new Error(verified.error.message)
1523
+ * const matches = verified.data
1524
+ * }
1525
+ * finally { await crypto.close() }
1526
+ * ```
425
1527
  */
426
1528
  interface PasswordOperations {
427
1529
  /**
@@ -432,6 +1534,18 @@ interface PasswordOperations {
432
1534
  * @param password - 明文密码(不能为空)
433
1535
  * @param config - 可选配置(盐值长度、迭代次数)
434
1536
  * @returns 成功时返回格式化的哈希字符串;失败时返回 INVALID_INPUT 或 HASH_FAILED
1537
+ *
1538
+ * @example
1539
+ * ```ts
1540
+ * import { crypto } from '@h-ai/crypto'
1541
+ * const initialized = await crypto.init()
1542
+ * if (!initialized.success) throw new Error(initialized.error.message)
1543
+ * try {
1544
+ * const hashed = crypto.password.hash('test-password', { iterations: 10000, saltLength: 16 })
1545
+ * if (!hashed.success) throw new Error(hashed.error.message)
1546
+ * }
1547
+ * finally { await crypto.close() }
1548
+ * ```
435
1549
  */
436
1550
  hash: (password: string, config?: PasswordConfig) => HaiResult<string>;
437
1551
  /**
@@ -442,61 +1556,86 @@ interface PasswordOperations {
442
1556
  * @param password - 待验证的明文密码
443
1557
  * @param hash - 存储的哈希值(格式: `$hai$<iterations>$<salt>$<hash>`)
444
1558
  * @returns 成功时返回 boolean;失败时返回 INVALID_INPUT 或 VERIFY_FAILED
1559
+ *
1560
+ * @example
1561
+ * ```ts
1562
+ * import { crypto } from '@h-ai/crypto'
1563
+ * const initialized = await crypto.init()
1564
+ * if (!initialized.success) throw new Error(initialized.error.message)
1565
+ * try {
1566
+ * const hashed = crypto.password.hash('test-password')
1567
+ * if (!hashed.success) throw new Error(hashed.error.message)
1568
+ * const verified = crypto.password.verify('test-password', hashed.data)
1569
+ * if (!verified.success) throw new Error(verified.error.message)
1570
+ * const matches = verified.data
1571
+ * }
1572
+ * finally { await crypto.close() }
1573
+ * ```
445
1574
  */
446
1575
  verify: (password: string, hash: string) => HaiResult<boolean>;
447
1576
  }
448
1577
  /**
449
- * 传输加密操作接口(端到端混合加密)。
450
- *
451
- * 通过 `crypto.transport` 访问,需先调用 `crypto.init()`。
452
- *
453
- * @remarks
454
- * 使用流程(直接使用底层 transport 工厂时):
455
- *
456
- * 1. 先 `await crypto.init()`,确保 `crypto.asymmetric` / `crypto.symmetric` 已就绪。
457
- * 2. 服务端调用 `crypto.transport.createServer()` 创建 `TransportEncryptionManager`;该管理器负责持有服务端密钥对,并管理客户端公钥。多节点部署时可通过 `options.keyStore` 注入共享存储。
458
- * 3. 服务端暴露一个 POST 密钥协商端点:接收 `{ clientPublicKey }`,调用 `manager.registerClientKey()` 注册客户端公钥,再返回 `{ serverPublicKey: manager.getServerPublicKey(), clientId }`。
459
- * 4. 客户端调用 `crypto.transport.createClient({ keyExchangeUrl })` 创建会话;首次 `client.init()` 或 `client.encryptedFetch()` 会自动完成密钥协商。
460
- * 5. 协商完成后,客户端每次请求都会附带 `X-Client-Id`;若请求有 body,则 body 会被包装成 `{ encryptedKey, ciphertext, iv }`,并设置 `X-Encrypted: true`。
461
- * 6. 服务端在业务逻辑前调用 `manager.decryptRequest()` 解密请求体,在返回前调用 `manager.encryptResponse(clientId, data)` 为当前客户端重新加密响应。
462
- * 7. 客户端收到 `X-Encrypted: true` 的响应后会自动解密;调用 `client.destroy()` 可清空当前会话,下次请求会重新协商。
1578
+ * 认证传输工厂;需先初始化crypto,服务端显式受管密钥环+共享registry,客户端通过TLS协商公钥。
1579
+ * 协议v2绑定方向、HTTP方法/路径/状态、密钥版本和nonce,验签/MAC成功后才解密;防重放不替代业务幂等。
463
1580
  *
464
- * 常规应用优先使用上层封装,而不是手写 HTTP 协商细节:
465
- * - `serv.createApp({ transport: { crypto } })`
466
- * - `kit.createHandle({ crypto: { crypto, transport: true } })`
467
- * - `apiClient.init({ transport: { crypto } })`
468
- *
469
- * @example 服务端
470
- * ```ts
471
- * const result = crypto.transport.createServer({
472
- * keyStore: createRedisTransportKeyStore({ cache, ttlSeconds: 3600 }),
473
- * })
474
- * if (!result.success) throw result.error
475
- * const manager = result.data
476
- * ```
477
- *
478
- * @example 客户端
1581
+ * @example
479
1582
  * ```ts
480
- * const client = crypto.transport.createClient({
481
- * keyExchangeUrl: 'https://api.example.com/api/v1/_hai/key-exchange',
482
- * })
483
- * const resp = await client.encryptedFetch('https://api.example.com/api/v1/echo', {
484
- * method: 'POST', body: JSON.stringify({ hello: 'world' }),
485
- * })
1583
+ * import { crypto } from '@h-ai/crypto'
1584
+ * const initialized = await crypto.init()
1585
+ * if (!initialized.success) throw new Error(initialized.error.message)
1586
+ * try {
1587
+ * const client = crypto.transport.createClient({ keyExchangeUrl: 'https://api.example.com/api/v1/_hai/key-exchange' })
1588
+ * try {
1589
+ * const ready = await client.init()
1590
+ * if (!ready.success) throw new Error(ready.error.message)
1591
+ * const response = await client.encryptedFetch('https://api.example.com/api/v1/orders')
1592
+ * if (!response.ok) throw new Error('HTTP request failed')
1593
+ * }
1594
+ * finally { client.destroy() }
1595
+ * }
1596
+ * finally { await crypto.close() }
486
1597
  * ```
487
1598
  */
488
1599
  interface TransportOperations {
489
1600
  /**
490
1601
  * 创建服务端传输加密管理器。
491
1602
  *
492
- * options.keyStore:注入共享客户端公钥存储。
493
- * options.maxClients:默认内存 keyStore 的最大客户端数(默认 10000,仅在未传 keyStore 时生效)。
494
- * 成功返回 manager;密钥生成失败返回 `HaiCommonError.INTERNAL_ERROR`。
1603
+ * serverKeyRing/keyStore 均必填;生产使用相同受管密钥环与共享 registry。
1604
+ * 成功返回 manager;缺失、格式错误或公私钥错配返回脱敏 HaiResult 错误。
495
1605
  *
496
1606
  * @remarks
497
1607
  * 通常在服务启动阶段创建一次,并交给 HTTP 中间件 / 路由处理器复用。
1608
+ * @param options - 实际调用输入,按该方法约束验证。
1609
+ * @returns HaiResult,先检查success再读取data。
1610
+ *
1611
+ * @example
1612
+ * ```ts
1613
+ * import { crypto, createRedisTransportKeyStore, parseTransportServerKeyRing } from '@h-ai/crypto'
1614
+ * import { cache } from '@h-ai/cache'
1615
+ * const initialized = await crypto.init()
1616
+ * if (!initialized.success) throw new Error(initialized.error.message)
1617
+ * try {
1618
+ * const connected = await cache.init({ type: 'redis', host: 'cache', port: 6379, password: process.env.HAI_CACHE_PASSWORD })
1619
+ * if (!connected.success) throw new Error(connected.error.message)
1620
+ * try {
1621
+ * const registry = createRedisTransportKeyStore({ cache, ttlSeconds: 3600 })
1622
+ * const ring = parseTransportServerKeyRing(process.env.HAI_CRYPTO_TRANSPORT_SERVERKEYRING)
1623
+ * if (!ring.success) throw new Error(ring.error.message)
1624
+ * const created = crypto.transport.createServer({ serverKeyRing: ring.data, keyStore: registry })
1625
+ * if (!created.success) throw new Error(created.error.message)
1626
+ * const manager = created.data
1627
+ * try {
1628
+ * const currentVersion = manager.getServerKeyId()
1629
+ * const publicKey = manager.getServerPublicKey()
1630
+ * }
1631
+ * finally { await manager.close() }
1632
+ * }
1633
+ * finally { await cache.close() }
1634
+ * }
1635
+ * finally { await crypto.close() }
1636
+ * ```
498
1637
  */
499
- createServer: (options?: TransportCreateServerOptions) => HaiResult<TransportEncryptionManager>;
1638
+ createServer: (options: TransportCreateServerOptions) => HaiResult<TransportEncryptionManager>;
500
1639
  /**
501
1640
  * 创建客户端传输加密会话。
502
1641
  *
@@ -506,6 +1645,28 @@ interface TransportOperations {
506
1645
  * @remarks
507
1646
  * 返回的是“单会话”客户端:同一个实例会复用已协商得到的 `clientId` 与服务端公钥;
508
1647
  * 调用 `destroy()` 后会回到未协商状态。
1648
+ * @param options - 实际调用输入,按该方法约束验证。
1649
+ * @param options.keyExchangeUrl - 密钥协商端点完整 URL。
1650
+ * @param options.fetch - 实际发送 HTTP 请求的 fetch,默认 `globalThis.fetch`。
1651
+ * @returns 直接返回值,使用前先初始化模块。
1652
+ *
1653
+ * @example
1654
+ * ```ts
1655
+ * import { crypto } from '@h-ai/crypto'
1656
+ * const initialized = await crypto.init()
1657
+ * if (!initialized.success) throw new Error(initialized.error.message)
1658
+ * try {
1659
+ * const client = crypto.transport.createClient({ keyExchangeUrl: 'https://api.example.com/api/v1/_hai/key-exchange' })
1660
+ * try {
1661
+ * const ready = await client.init()
1662
+ * if (!ready.success) throw new Error(ready.error.message)
1663
+ * const response = await client.encryptedFetch('https://api.example.com/api/v1/orders')
1664
+ * if (!response.ok) throw new Error('HTTP request failed')
1665
+ * }
1666
+ * finally { client.destroy() }
1667
+ * }
1668
+ * finally { await crypto.close() }
1669
+ * ```
509
1670
  */
510
1671
  createClient: (options: {
511
1672
  keyExchangeUrl: string;
@@ -515,17 +1676,19 @@ interface TransportOperations {
515
1676
  readonly protocol: typeof TRANSPORT_PROTOCOL;
516
1677
  }
517
1678
  /**
518
- * 加密模块函数接口
519
- *
520
- * `crypto` 服务对象的类型定义。使用前必须调用 `init()` 初始化。
1679
+ * 加密模块服务接口;init检查HaiResult,close返回void,调用不能把原语当作已认证HTTP协议。
521
1680
  *
522
1681
  * @example
523
1682
  * ```ts
524
1683
  * import { crypto } from '@h-ai/crypto'
525
- *
526
- * await crypto.init()
527
- * const hash = crypto.hash.hash('hello')
528
- * await crypto.close()
1684
+ * const initialized = await crypto.init()
1685
+ * if (!initialized.success) throw new Error(initialized.error.message)
1686
+ * try {
1687
+ * const digest = crypto.hash.hash('hello')
1688
+ * if (!digest.success) throw new Error(digest.error.message)
1689
+ * const value = digest.data
1690
+ * }
1691
+ * finally { await crypto.close() }
529
1692
  * ```
530
1693
  */
531
1694
  interface CryptoFunctions {
@@ -536,12 +1699,30 @@ interface CryptoFunctions {
536
1699
  * 重复调用会先关闭再重新初始化。
537
1700
  *
538
1701
  * @returns 成功时返回 ok(undefined);失败时返回 INIT_FAILED
1702
+ *
1703
+ * @example
1704
+ * ```ts
1705
+ * import { crypto } from '@h-ai/crypto'
1706
+ * const initialized = await crypto.init()
1707
+ * if (!initialized.success) throw new Error(initialized.error.message)
1708
+ * await crypto.close()
1709
+ * ```
539
1710
  */
540
1711
  init: () => Promise<HaiResult<void>>;
541
1712
  /**
542
1713
  * 关闭加密模块,释放内部状态
543
1714
  *
544
1715
  * 关闭后再访问 asymmetric/hash/symmetric/password 会返回 NOT_INITIALIZED 错误。
1716
+ * @returns 异步操作结果;存储或网络故障按方法契约拒绝。
1717
+ *
1718
+ * @example
1719
+ * ```ts
1720
+ * import { crypto } from '@h-ai/crypto'
1721
+ * const initialized = await crypto.init()
1722
+ * if (!initialized.success) throw new Error(initialized.error.message)
1723
+ * await crypto.close()
1724
+ * const ready = crypto.isInitialized
1725
+ * ```
545
1726
  */
546
1727
  close: () => Promise<void>;
547
1728
  /** 是否已初始化 */
@@ -566,38 +1747,196 @@ interface CryptoFunctions {
566
1747
  */
567
1748
 
568
1749
  /**
569
- * 加密模块服务对象(统一入口)
570
- *
571
- * 使用前必须调用 `crypto.init()` 进行初始化。
572
- * 未初始化时访问 asymmetric/hash/symmetric/password 的任何方法均返回 NOT_INITIALIZED 错误。
1750
+ * 加密模块公开服务对象;先检查 init 的 HaiResult,再使用各操作域,最后 close。
1751
+ * SM2/SM3/SM4 原语不等于认证传输;HTTP 使用 transport 的签名、MAC、上下文和共享 nonce 校验。
573
1752
  *
574
1753
  * @example
575
1754
  * ```ts
576
1755
  * import { crypto } from '@h-ai/crypto'
577
- *
578
- * await crypto.init()
579
- * const hash = crypto.hash.hash('hello')
580
- * const keyPair = crypto.asymmetric.generateKeyPair()
581
- * await crypto.close()
1756
+ * const initialized = await crypto.init()
1757
+ * if (!initialized.success) throw new Error(initialized.error.message)
1758
+ * try {
1759
+ * const generated = crypto.asymmetric.generateKeyPair()
1760
+ * if (!generated.success) throw new Error(generated.error.message)
1761
+ * const pair = generated.data
1762
+ * const digest = crypto.hash.hash('hello')
1763
+ * if (!digest.success) throw new Error(digest.error.message)
1764
+ * const publicKeyValid = crypto.asymmetric.isValidPublicKey(pair.publicKey)
1765
+ * }
1766
+ * finally { await crypto.close() }
582
1767
  * ```
583
1768
  */
584
1769
  declare const crypto: CryptoFunctions;
585
1770
 
1771
+ /**
1772
+ * @h-ai/crypto — SM2 公钥输入校验。
1773
+ * @module crypto-public-key
1774
+ */
1775
+ /**
1776
+ * 纯函数校验 SM2 公钥的编码及曲线点,不初始化模块或访问 registry。
1777
+ * 仅接受128字符无前缀或130字符04前缀的十六进制非压缩点;拒绝曲线外点及其它编码。
1778
+ * @param publicKey - 不可信的输入。
1779
+ * @returns 有效字符串时为 true,并收窄类型;不证明密钥所有权,注册后仍须验签。
1780
+ *
1781
+ * @example
1782
+ * ```ts
1783
+ * import { crypto, isValidSM2PublicKey } from '@h-ai/crypto'
1784
+ * const initialized = await crypto.init()
1785
+ * if (!initialized.success) throw new Error(initialized.error.message)
1786
+ * try {
1787
+ * const generated = crypto.asymmetric.generateKeyPair()
1788
+ * if (!generated.success) throw new Error(generated.error.message)
1789
+ * const pair = generated.data
1790
+ * const valid = isValidSM2PublicKey(pair.publicKey)
1791
+ * const invalid = isValidSM2PublicKey('not-a-public-key')
1792
+ * }
1793
+ * finally { await crypto.close() }
1794
+ * ```
1795
+ */
1796
+ declare function isValidSM2PublicKey(publicKey: unknown): publicKey is string;
1797
+
1798
+ /**
1799
+ * @h-ai/crypto — 传输协议的字节预算与有界正文读取。
1800
+ *
1801
+ * serv/kit 共享相同预算,不依赖可省略或伪造的 Content-Length。
1802
+ * @module crypto-transport-body
1803
+ */
1804
+ /**
1805
+ * 协议固定UTF-8字节预算;按实际流读取,不依赖Content-Length,非字符串字符数限制。
1806
+ *
1807
+ * @example
1808
+ * ```ts
1809
+ * import { readTransportBody, TRANSPORT_BODY_LIMITS } from '@h-ai/crypto'
1810
+ * const request = new Request('https://api.example.com/api/v1/orders', { method: 'POST', body: JSON.stringify({ message: 'hello' }) })
1811
+ * const text = await readTransportBody(request.body, { maxBytes: TRANSPORT_BODY_LIMITS.plaintextBytes, signal: request.signal })
1812
+ * ```
1813
+ */
1814
+ declare const TRANSPORT_BODY_LIMITS: {
1815
+ /** 密钥协商 JSON 的 UTF-8 总字节数。 */
1816
+ readonly keyExchangeBytes: 16384;
1817
+ /** GET/HEAD 请求及无正文响应的认证头封装 UTF-8 上限。 */
1818
+ readonly envelopeHeaderBytes: 16384;
1819
+ /** 单个协商公钥的 UTF-8 字节数。 */
1820
+ readonly publicKeyBytes: 4096;
1821
+ /** 解密后的请求/加密前的响应明文字节数。 */
1822
+ readonly plaintextBytes: 1048576;
1823
+ /** 密文 JSON 的 UTF-8 总字节数(2 MiB + 32 KiB,含 CBC 填充与全部认证字段)。 */
1824
+ readonly encryptedPayloadBytes: 2129920;
1825
+ };
1826
+ /**
1827
+ * 有界原流读取选项;maxBytes是非负安全整数,signal取消等待和底层流。
1828
+ *
1829
+ * @example
1830
+ * ```ts
1831
+ * import { readTransportBody, type ReadTransportBodyOptions } from '@h-ai/crypto'
1832
+ * const request = new Request('https://api.example.com/api/v1/orders', { method: 'POST', body: 'hello' })
1833
+ * const options: ReadTransportBodyOptions = { maxBytes: 1024, signal: request.signal }
1834
+ * const text = await readTransportBody(request.body, options)
1835
+ * ```
1836
+ */
1837
+ interface ReadTransportBodyOptions {
1838
+ /** 允许读取的字节总数,必须为非负安全整数。 */
1839
+ readonly maxBytes: number;
1840
+ /** 请求取消时中止等待中的读取并取消底层流。 */
1841
+ readonly signal?: AbortSignal;
1842
+ }
1843
+ /**
1844
+ * 逐块读取原始字节流并严格UTF-8解码;超限/取消后取消流并释放reader,清理失败不覆盖原异常。
1845
+ * 不要传入clone/tee分支,否则另一分支仍可能保留数据。
1846
+ * @param stream - 原始流;null返回空串但仍检查取消。
1847
+ * @param options - 字节预算与取消信号。
1848
+ * @returns 解码文本。
1849
+ * @throws RangeError预算无效或超限;非法UTF-8、流异常和signal.reason继续抛出。
1850
+ *
1851
+ * @example
1852
+ * ```ts
1853
+ * import { readTransportBody } from '@h-ai/crypto'
1854
+ * const request = new Request('https://api.example.com/api/v1/orders', { method: 'POST', body: 'hello' })
1855
+ * const text = await readTransportBody(request.body, { maxBytes: 1024, signal: request.signal })
1856
+ * ```
1857
+ */
1858
+ declare function readTransportBody(stream: ReadableStream<Uint8Array> | null, options: ReadTransportBodyOptions): Promise<string>;
1859
+
1860
+ /**
1861
+ * @h-ai/crypto — 服务端受管传输密钥环的安全解析。
1862
+ * @module crypto-transport-keyring
1863
+ */
1864
+
1865
+ /**
1866
+ * 解析服务端私有JSON或受管对象,不读取环境或初始化模块;实际密钥配对由createServer验证。
1867
+ * @param serialized - Secret Manager提供的JSON字符串或对象。
1868
+ * @returns 结构有效的密钥环或脱敏INVALID_KEY,不回显私钥/原始输入。
1869
+ *
1870
+ * @example
1871
+ * ```ts
1872
+ * import { parseTransportServerKeyRing, type TransportServerKeyRing } from '@h-ai/crypto'
1873
+ * const parsed = parseTransportServerKeyRing(process.env.HAI_CRYPTO_TRANSPORT_SERVERKEYRING)
1874
+ * if (!parsed.success) throw new Error(parsed.error.message)
1875
+ * const ring: TransportServerKeyRing = parsed.data
1876
+ * // 仅服务端使用;createServer还会校验每个版本的公私钥配对。
1877
+ * ```
1878
+ */
1879
+ declare function parseTransportServerKeyRing(serialized: unknown): HaiResult<TransportServerKeyRing>;
1880
+
1881
+ /**
1882
+ * @h-ai/crypto — serv/kit 共用的实际 HTTP 请求认证入口。
1883
+ * @module crypto-transport-request
1884
+ */
1885
+
1886
+ /**
1887
+ * 从实际HTTP请求提取可信方法/路径/查询/客户端ID并有界认证;成功后才执行业务。
1888
+ * 拒绝缺少认证及头/正文重复承载,空请求也认证;不会由payload自证HTTP上下文。
1889
+ * @param manager - 使用共享原子nonce registry的已创建管理器。
1890
+ * @param request - 未消费正文的真实Request。
1891
+ * @returns 明文和响应关联上下文;认证失败无明文。
1892
+ * @throws RangeError超限;流读取/取消沿用原异常,HTTP适配器负责转响应。
1893
+ *
1894
+ * @example
1895
+ * ```ts
1896
+ * import { authenticateTransportRequest, TRANSPORT_PROTOCOL, type TransportEncryptionManager } from '@h-ai/crypto'
1897
+ * async function handle(manager: TransportEncryptionManager, request: Request): Promise<Response> {
1898
+ * const verified = await authenticateTransportRequest(manager, request)
1899
+ * if (!verified.success) return new Response(null, { status: 400 })
1900
+ * const encrypted = await manager.encryptResponse(verified.data.context, JSON.stringify({ accepted: true }), 200)
1901
+ * if (!encrypted.success) return new Response(null, { status: 500 })
1902
+ * return Response.json(encrypted.data, { headers: { [TRANSPORT_PROTOCOL.ENCRYPTED_HEADER]: TRANSPORT_PROTOCOL.ENCRYPTED_HEADER_VALUE } })
1903
+ * }
1904
+ * ```
1905
+ */
1906
+ declare function authenticateTransportRequest(manager: TransportEncryptionManager, request: Request): Promise<HaiResult<{
1907
+ plaintext: string;
1908
+ context: TransportMessageContext;
1909
+ }>>;
1910
+
586
1911
  /**
587
1912
  * @h-ai/crypto — 传输加密内存 key store
588
1913
  *
589
- * 提供默认的进程内客户端公钥存储实现。
1914
+ * 提供显式开发/测试使用的进程内客户端公钥存储,生产使用共享 registry。
590
1915
  * @module crypto-transport-store-memory
591
1916
  */
592
1917
 
593
1918
  /**
594
- * 创建进程内 FIFO 客户端密钥存储。
1919
+ * 显式创建开发/测试用进程内FIFO公钥registry;不跨进程、不持久化,生产共享状态守卫拒绝它。
1920
+ * 有效nonce不会因公钥FIFO容量而提前逐出;过期nonce惰性清理。
1921
+ * @param maxClients - 公钥容量,默认10000,超限淘汰最早公钥。
1922
+ * @returns 本地registry;无效SM2公钥拒绝Promise。
595
1923
  *
596
- * ⚠️ 进程内实现:多节点部署时需让客户端首次请求后保持「会话粘性」(sticky session),
597
- * 否则后续请求路由到其他节点会因找不到客户端公钥而失败。需要跨节点请改用
598
- * Redis / 数据库实现 {@link TransportKeyStore}。
599
- *
600
- * @param maxClients - 最大客户端数(默认 10000),超过后按注册顺序淘汰最早条目。
1924
+ * @example
1925
+ * ```ts
1926
+ * import { crypto, createInMemoryKeyStore } from '@h-ai/crypto'
1927
+ * const initialized = await crypto.init()
1928
+ * if (!initialized.success) throw new Error(initialized.error.message)
1929
+ * try {
1930
+ * const generated = crypto.asymmetric.generateKeyPair()
1931
+ * if (!generated.success) throw new Error(generated.error.message)
1932
+ * const pair = generated.data
1933
+ * const registry = createInMemoryKeyStore(100)
1934
+ * const clientId = await registry.register(pair.publicKey)
1935
+ * const saved = await registry.get(clientId)
1936
+ * await registry.delete?.(clientId)
1937
+ * }
1938
+ * finally { await crypto.close() }
1939
+ * ```
601
1940
  */
602
1941
  declare function createInMemoryKeyStore(maxClients?: number): TransportKeyStore;
603
1942
 
@@ -608,18 +1947,59 @@ declare function createInMemoryKeyStore(maxClients?: number): TransportKeyStore;
608
1947
  * @module crypto-transport-store-redis
609
1948
  */
610
1949
 
611
- /** 创建 Redis 版传输 key store 的配置。 */
1950
+ /**
1951
+ * 借用已初始化cache的registry配置;只有Redis provider报告shared,内存cache不能满足生产。
1952
+ * TTL是客户端公钥寿命,nonce另按认证窗口持有;连接由应用关闭。
1953
+ *
1954
+ * @example
1955
+ * ```ts
1956
+ * import { crypto, createRedisTransportKeyStore, parseTransportServerKeyRing } from '@h-ai/crypto'
1957
+ * import { cache } from '@h-ai/cache'
1958
+ * const initialized = await crypto.init()
1959
+ * if (!initialized.success) throw new Error(initialized.error.message)
1960
+ * try {
1961
+ * const connected = await cache.init({ type: 'redis', host: 'cache', port: 6379, password: process.env.HAI_CACHE_PASSWORD })
1962
+ * if (!connected.success) throw new Error(connected.error.message)
1963
+ * try {
1964
+ * const registry = createRedisTransportKeyStore({ cache, ttlSeconds: 3600 })
1965
+ * const options: import('@h-ai/crypto').CreateRedisTransportKeyStoreOptions = { cache, ttlSeconds: 3600 }
1966
+ * const secondView = createRedisTransportKeyStore(options)
1967
+ * const scope = secondView.stateScope
1968
+ * }
1969
+ * finally { await cache.close() }
1970
+ * }
1971
+ * finally { await crypto.close() }
1972
+ * ```
1973
+ */
612
1974
  interface CreateRedisTransportKeyStoreOptions {
613
1975
  /** 已初始化的 `@h-ai/cache` 实例;生产环境通常应使用 Redis provider。 */
614
1976
  readonly cache: CacheFunctions;
615
- /** 客户端公钥的 TTL(秒);未传则保持不过期。 */
1977
+ /** 客户端公钥的 TTL(正整数秒,最大 2147483647);未传则保持不过期。 */
616
1978
  readonly ttlSeconds?: number;
617
1979
  }
618
1980
  /**
619
- * 创建基于 `@h-ai/cache` 的传输 key store。
1981
+ * 创建借用cache的公钥/nonce registry;生产必须Redis共享provider,nonce用单key原子CAS。
1982
+ * 公钥TTL省略则不过期,nonce有效期间禁止驱逐;容量由Redis部署预算管理,不提供DB maxClients。
1983
+ * @param options - 已初始化cache与可选正整数TTL秒,最大2147483647。
1984
+ * @returns registry;非法TTL同步TypeError,读写/公钥错误拒绝Promise,不关闭cache。
620
1985
  *
621
- * 说明:本实现依赖 cache 模块的 KV 语义;若要获得跨节点共享能力,应传入
622
- * 已连接 Redis 的 `cache` 实例,而不是内存 provider。
1986
+ * @example
1987
+ * ```ts
1988
+ * import { crypto, createRedisTransportKeyStore, parseTransportServerKeyRing } from '@h-ai/crypto'
1989
+ * import { cache } from '@h-ai/cache'
1990
+ * const initialized = await crypto.init()
1991
+ * if (!initialized.success) throw new Error(initialized.error.message)
1992
+ * try {
1993
+ * const connected = await cache.init({ type: 'redis', host: 'cache', port: 6379, password: process.env.HAI_CACHE_PASSWORD })
1994
+ * if (!connected.success) throw new Error(connected.error.message)
1995
+ * try {
1996
+ * const registry = createRedisTransportKeyStore({ cache, ttlSeconds: 3600 })
1997
+ * const scope = registry.stateScope
1998
+ * }
1999
+ * finally { await cache.close() }
2000
+ * }
2001
+ * finally { await crypto.close() }
2002
+ * ```
623
2003
  */
624
2004
  declare function createRedisTransportKeyStore(options: CreateRedisTransportKeyStoreOptions): TransportKeyStore;
625
2005
 
@@ -630,18 +2010,90 @@ declare function createRedisTransportKeyStore(options: CreateRedisTransportKeySt
630
2010
  * @module crypto-transport-store-reldb
631
2011
  */
632
2012
 
633
- /** 创建关系数据库版传输 key store 的配置。 */
2013
+ /**
2014
+ * 借用已初始化数据库的registry配置;TTL与共享maxClients各节点必须一致。
2015
+ * 生产使用PostgreSQL/MySQL,DB时钟维护期限;连接由应用关闭。
2016
+ *
2017
+ * @example
2018
+ * ```ts
2019
+ * import { createReldbTransportKeyStore } from '@h-ai/crypto'
2020
+ * import { reldb } from '@h-ai/reldb'
2021
+ * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })
2022
+ * if (!connected.success) throw new Error(connected.error.message)
2023
+ * try {
2024
+ * const registry = createReldbTransportKeyStore({ reldb, ttlSeconds: 3600, maxClients: 10000 })
2025
+ * const options: import('@h-ai/crypto').CreateReldbTransportKeyStoreOptions = { reldb, ttlSeconds: 3600, maxClients: 10000 }
2026
+ * const secondView = createReldbTransportKeyStore(options)
2027
+ * const removed = await secondView.pruneExpired()
2028
+ * }
2029
+ * finally { await reldb.close() }
2030
+ * ```
2031
+ */
634
2032
  interface CreateReldbTransportKeyStoreOptions {
635
2033
  /** 已初始化的 `@h-ai/reldb` 实例。 */
636
2034
  readonly reldb: ReldbFunctions;
637
- /** 客户端公钥的 TTL(秒);未传则保持不过期。 */
2035
+ /** 公钥 TTL,正整数秒且最大 2147483647;省略则不过期。 */
638
2036
  readonly ttlSeconds?: number;
2037
+ /** 所有节点共享的有效公钥容量,正安全整数,默认 10000;各节点必须一致。 */
2038
+ readonly maxClients?: number;
639
2039
  }
640
2040
  /**
641
- * 创建基于 `@h-ai/reldb` 的传输 key store。
2041
+ * 数据库registry及有界过期维护;注册自动维护,无流量时应用应定期调度pruneExpired。
2042
+ * 不淘汰有效公钥/nonce,不创建后台计时器;维护失败拒绝并可重试。
642
2043
  *
643
- * 首次读写时会自动创建 `hai_crypto_transport_client_keys` 表。
2044
+ * @example
2045
+ * ```ts
2046
+ * import { createReldbTransportKeyStore } from '@h-ai/crypto'
2047
+ * import { reldb } from '@h-ai/reldb'
2048
+ * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })
2049
+ * if (!connected.success) throw new Error(connected.error.message)
2050
+ * try {
2051
+ * const registry = createReldbTransportKeyStore({ reldb, ttlSeconds: 3600, maxClients: 10000 })
2052
+ * const removed = await registry.pruneExpired()
2053
+ * }
2054
+ * finally { await reldb.close() }
2055
+ * ```
2056
+ */
2057
+ interface ReldbTransportKeyStore extends TransportKeyStore {
2058
+ /**
2059
+ * 按数据库时钟最多删除 100 条过期公钥和 100 条过期 nonce,返回实际删除总数。
2060
+ * 不驱逐有效记录;建表或维护故障会 reject,之后允许重新调用。应用可以定期调度本方法。
2061
+ * @returns 异步操作结果;存储或网络故障按方法契约拒绝。
2062
+ *
2063
+ * @example
2064
+ * ```ts
2065
+ * import { createReldbTransportKeyStore } from '@h-ai/crypto'
2066
+ * import { reldb } from '@h-ai/reldb'
2067
+ * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })
2068
+ * if (!connected.success) throw new Error(connected.error.message)
2069
+ * try {
2070
+ * const registry = createReldbTransportKeyStore({ reldb, ttlSeconds: 3600, maxClients: 10000 })
2071
+ * const removed = await registry.pruneExpired()
2072
+ * }
2073
+ * finally { await reldb.close() }
2074
+ * ```
2075
+ */
2076
+ pruneExpired: () => Promise<number>;
2077
+ }
2078
+ /**
2079
+ * 创建共享数据库registry;首次操作惰性建表,失败可重试,事务锁住持久容量行后注册。
2080
+ * 每次注册有界维护;公钥TTL与过期维护使用数据库epoch毫秒,传输窗口仍要求节点时钟同步,不能提前删除有效nonce。
2081
+ * @param options - 已初始化数据库、公钥TTL和各节点一致的正安全整数maxClients,默认10000。
2082
+ * @returns registry;非法配置同步TypeError,操作故障拒绝Promise;不关闭借用数据库。
2083
+ *
2084
+ * @example
2085
+ * ```ts
2086
+ * import { createReldbTransportKeyStore } from '@h-ai/crypto'
2087
+ * import { reldb } from '@h-ai/reldb'
2088
+ * const connected = await reldb.init({ type: 'postgresql', host: 'db', port: 5432, database: 'hai', user: 'postgres', password: process.env.HAI_DB_PASSWORD })
2089
+ * if (!connected.success) throw new Error(connected.error.message)
2090
+ * try {
2091
+ * const registry = createReldbTransportKeyStore({ reldb, ttlSeconds: 3600, maxClients: 10000 })
2092
+ * const removed = await registry.pruneExpired()
2093
+ * }
2094
+ * finally { await reldb.close() }
2095
+ * ```
644
2096
  */
645
- declare function createReldbTransportKeyStore(options: CreateReldbTransportKeyStoreOptions): TransportKeyStore;
2097
+ declare function createReldbTransportKeyStore(options: CreateReldbTransportKeyStoreOptions): ReldbTransportKeyStore;
646
2098
 
647
- export { type AsymmetricEncryptOptions, type AsymmetricOperations, type CipherMode, type CreateRedisTransportKeyStoreOptions, type CreateReldbTransportKeyStoreOptions, type CryptoFunctions, type EncryptWithIVResult, type EncryptedPayload, HaiCryptoError, type HashOperations, type HashOptions, type KeyExchangeRequest, type KeyExchangeResponse, type KeyPair, type PasswordConfig, type PasswordOperations, type SignOptions, type SymmetricCbcEncryptedPayload, type SymmetricCiphertextEncoding, type SymmetricDecryptInput, type SymmetricEncryptOptions, type SymmetricEncryptedPayload, type SymmetricMode, type SymmetricOperations, TRANSPORT_PROTOCOL, type TransportClient, type TransportCreateServerOptions, type TransportCryptoServiceLike, type TransportEncryptionManager, type TransportKeyPair, type TransportKeyStore, type TransportOperations, createInMemoryKeyStore, createRedisTransportKeyStore, createReldbTransportKeyStore, crypto };
2099
+ export { type AsymmetricEncryptOptions, type AsymmetricOperations, type CipherMode, type CreateRedisTransportKeyStoreOptions, type CreateReldbTransportKeyStoreOptions, type CryptoFunctions, type EncryptWithIVResult, type EncryptedPayload, type EncryptedRequestPayload, HaiCryptoError, type HashOperations, type HashOptions, type KeyExchangeRequest, type KeyExchangeResponse, type KeyPair, type PasswordConfig, type PasswordOperations, type ReadTransportBodyOptions, type ReldbTransportKeyStore, type SignOptions, type SymmetricCbcEncryptedPayload, type SymmetricCiphertextEncoding, type SymmetricDecryptInput, type SymmetricEncryptOptions, type SymmetricEncryptedPayload, type SymmetricMode, type SymmetricOperations, TRANSPORT_BODY_LIMITS, TRANSPORT_PROTOCOL, type TransportClient, type TransportCreateServerOptions, type TransportCryptoServiceLike, type TransportEncryptionManager, type TransportKeyPair, type TransportKeyStore, type TransportMessageContext, type TransportOperations, type TransportRequestContext, type TransportServerKeyRing, authenticateTransportRequest, createInMemoryKeyStore, createRedisTransportKeyStore, createReldbTransportKeyStore, crypto, isValidSM2PublicKey, parseTransportServerKeyRing, readTransportBody };