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