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