@h-ai/crypto 1.0.0-beta.14 → 1.0.0-beta.16
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 +2 -2
- package/README.ja.md +2 -2
- package/README.md +2 -2
- package/dist/index.d.ts +24 -6
- package/dist/index.js +59 -22
- package/dist/index.js.map +1 -1
- package/package.json +4 -4
package/README.en.md
CHANGED
|
@@ -250,7 +250,7 @@ const server = crypto.transport.createServer({ serverKeyRing, keyStore })
|
|
|
250
250
|
|
|
251
251
|
Key exchange returns `{ serverKeyId, serverPublicKey, clientId }`. Encrypted requests carry `serverKeyId`; the server selects that private key and rejects unknown/revoked IDs without trying other keys. Responses use the client's public key. Rotate by distributing both old and new versions to every node, then switching `activeKeyId` and rebuilding managers during rollout. Retain the old version through the transition, then remove it from every node. Old clients must call `destroy()` and negotiate again; do not automatically replay non-idempotent operations. Distribute the same ring through a Secret Manager or server-only private environment. Never put it in ordinary YAML, PUBLIC_/VITE_ variables, logs or public responses. CLI project creation generates separate key rings in Git-ignored `.env.dev.local/.env.test.local/.env.prod.local`. After cloning, distribute each environment’s ring through protected files or a Secret Manager. Runtime does not generate keys.
|
|
252
252
|
|
|
253
|
-
Only authenticated protocol v2 is accepted. Independent random SM4-CBC and HMAC-SM3 keys are wrapped with SM2; verify the sender signature and MAC before CBC decryption. Clients sign requests and the selected server key version signs responses. Authentication covers version, direction, serverKeyId, clientId, a random 128-bit requestId acting as the nonce, issuedAt, HTTP method, the SM3 pathHash of pathname plus the original search, and response status.
|
|
253
|
+
Only authenticated protocol v2 is accepted. Independent random SM4-CBC and HMAC-SM3 keys are wrapped with SM2; verify the sender signature and MAC before CBC decryption. Clients sign requests and the selected server key version signs responses. Authentication covers version, direction, serverKeyId, clientId, a random 128-bit requestId acting as the nonce, issuedAt, HTTP method, the SM3 pathHash of pathname plus the original search, and response status. By default, requests expire after five minutes and client timestamps may be up to 30 seconds ahead. Set `maxRequestAgeSeconds` (1–600) and `maxFutureSkewSeconds` (0–300) on the server to adjust these limits in whole seconds. The shared registry atomically consumes (clientId, requestId) and retains the nonce until issuedAt plus 11 minutes, covering the longest configurable window and server clock differences. Storage failures reject requests. Synchronize node clocks and use a Redis noeviction/durable policy that preserves live nonces. Transport replay prevention does not replace business idempotency keys.
|
|
254
254
|
|
|
255
255
|
Empty POSTs still send an authenticated envelope for empty plaintext. GET/HEAD carry it in X-Hai-Transport; bodyless responses such as 204/304/HEAD use the same header. Other requests/responses carry a JSON envelope and X-Encrypted: true. The client must match the original request context and HTTP status; missing markers, old formats and authentication failures reject. Serv/Kit hosts explicitly allow plaintext through `encryption: disabled` path rules or host `excludePaths`. Callers must select ordinary requests; API Client/Kit wrappers use their own `excludePaths`. CORS must allow and expose X-Hai-Transport. TLS authenticates handshake keys; this protocol does not replace IAM authorization.
|
|
256
256
|
|
|
@@ -258,7 +258,7 @@ Set `transport.maxPlaintextBytes` for both request and response JSON plaintext,
|
|
|
258
258
|
|
|
259
259
|
Ordinary routes retain `transport.maxPlaintextBytes` (auto/omitted: 1 MiB). Declare Feedback report intake, evidence, attachments and ZIP download capacity separately using `routePlaintextBytes` and `feedback.budget.maxTransportBytes`; `*` matches one complete path segment and explicit host route values take precedence. Handshakes, adapter-node and proxies use the maximum route capacity. HTTP adapters admit at most 2 explicitly declared material requests above 1 MiB and a separate 16 ordinary business requests, releasing admission after the entire response or failure. Busy admission returns 503 / `hai:crypto:072`; business writes are not automatically replayed.
|
|
260
260
|
|
|
261
|
-
|
|
261
|
+
Serv/Kit return an encrypted HTTP 400 for an out-of-window request after validating request binding, signature and MAC. The client throws `TransportTimeError` (`hai:crypto:071`), displays the server’s UTC time at validation, and asks the user to enable automatic synchronization or set the device time manually before refreshing. `serverTime` is Unix milliseconds. Clients still show synchronization guidance when an older server omits it. `api-client` maps the error to `VALIDATION_FAILED` and preserves `cause.transportCode` and `cause.serverTime`.
|
|
262
262
|
|
|
263
263
|
Custom HTTP adapters check `error instanceof TransportTimeError && error.context`, then encrypt the rejection with `createTransportErrorResponse(error, { manager, context: error.context })`. Never classify an unverified message by its timestamp.
|
|
264
264
|
|
package/README.ja.md
CHANGED
|
@@ -250,7 +250,7 @@ const server = crypto.transport.createServer({ serverKeyRing, keyStore })
|
|
|
250
250
|
|
|
251
251
|
鍵交換は `{ serverKeyId, serverPublicKey, clientId }` を返し、暗号化リクエストは `serverKeyId` を送ります。サーバーは指定した秘密鍵だけを選び、不明・失効 ID は他の鍵を試さず拒否します。応答はクライアント公開鍵で暗号化します。更新時は全ノードに新旧の鍵を先に配布し、続いて `activeKeyId` を切り替えて manager を順次再構築します。移行期間後に古い ID を全ノードから削除します。旧クライアントは `destroy()` 後に再交渉し、非冪等処理を自動再送しません。同じ鍵環を Secret Manager またはサーバー専用の非公開環境から配布し、通常の YAML、PUBLIC_/VITE_、ログ、公開応答には含めないでください。CLI は Git 対象外の `.env.dev.local/.env.test.local/.env.prod.local` に独立した鍵環を作成します。クローン後は保護ファイルまたは Secret Manager で環境ごとに配布します。実行時には生成しません。
|
|
252
252
|
|
|
253
|
-
認証済み v2 のみを受け付けます。独立したランダム SM4-CBC 鍵と HMAC-SM3 鍵を SM2 で包み、送信者署名、MAC の順に検証してから CBC を復号します。要求はクライアント秘密鍵、応答は選択されたサーバー鍵バージョンで署名します。version、direction、serverKeyId、clientId、nonce を兼ねるランダム 128 ビット requestId、issuedAt、HTTP method、pathname と元の search の SM3 pathHash、応答 status
|
|
253
|
+
認証済み v2 のみを受け付けます。独立したランダム SM4-CBC 鍵と HMAC-SM3 鍵を SM2 で包み、送信者署名、MAC の順に検証してから CBC を復号します。要求はクライアント秘密鍵、応答は選択されたサーバー鍵バージョンで署名します。version、direction、serverKeyId、clientId、nonce を兼ねるランダム 128 ビット requestId、issuedAt、HTTP method、pathname と元の search の SM3 pathHash、応答 status を認証します。既定では要求は 5 分で期限切れになり、クライアント時刻はサーバーより最大 30 秒先まで許容されます。サーバー側の `maxRequestAgeSeconds`(1~600)と `maxFutureSkewSeconds`(0~300)で整数秒単位に調整できます。共有レジストリは (clientId, requestId) をアトミックに消費し、設定可能な最長の有効期間とノード間の時刻差をカバーするため、nonce を issuedAt の 11 分後まで保持します。ストレージ障害時は要求を拒否します。時計同期と Redis の noeviction/永続化により有効な nonce を保持してください。業務の再試行には別の冪等キーが必要です。
|
|
254
254
|
|
|
255
255
|
空の POST も空平文の認証封装を送ります。GET/HEAD は X-Hai-Transport ヘッダー、204/304/HEAD など本文なし応答も同じヘッダーを使います。他の要求・応答は JSON 本文と X-Encrypted: true を使います。クライアントは元の要求コンテキストと HTTP status を照合し、マーカー欠落、旧形式、認証失敗では reject します。平文 endpoint は Serv/Kit の `encryption: disabled` ルールまたはホストの `excludePaths` で明示的に許可し、呼出側も通常の要求を選びます。API Client/Kit のラッパーは自身の `excludePaths` を使用します。CORS では X-Hai-Transport を許可・公開してください。鍵交換の公開鍵認証は TLS に依存し、IAM の業務認可は別に必要です。
|
|
256
256
|
|
|
@@ -258,7 +258,7 @@ const server = crypto.transport.createServer({ serverKeyRing, keyStore })
|
|
|
258
258
|
|
|
259
259
|
通常業務は `transport.maxPlaintextBytes`(auto/省略は 1 MiB)を維持します。Feedback の受理・証拠・添付・ZIP ダウンロードは `routePlaintextBytes` と `feedback.budget.maxTransportBytes` で別途宣言します。`*` は完全な1パス区間のみ一致し、ホストの明示値が優先です。ハンドシェイク、adapter-node、プロキシは経路の最大値を使用します。HTTP アダプターは個別宣言された 1 MiB 超の資料経路を最大2件、通常業務を別枠で16件まで許可し、応答完了・失敗時に解放します。混雑は503 / `hai:crypto:072` を返し、業務書込は自動再送しません。
|
|
260
260
|
|
|
261
|
-
|
|
261
|
+
Serv/Kit は要求の結合、署名、MAC を検証した後、時刻の許容範囲を超えた要求に暗号化した HTTP 400 を返します。クライアントは `TransportTimeError`(`hai:crypto:071`)をスローし、検証時のサーバー UTC 時刻を表示して、自動時刻同期を有効にするか手動で時刻を設定してから再読み込みするよう案内します。`serverTime` は Unix ミリ秒です。旧サーバーが値を返さない場合も時刻調整の案内を表示します。`api-client` は `VALIDATION_FAILED` に変換し、`cause.transportCode` と `cause.serverTime` を保持します。
|
|
262
262
|
|
|
263
263
|
独自 HTTP アダプターでは `error instanceof TransportTimeError && error.context` を確認し、`createTransportErrorResponse(error, { manager, context: error.context })` で拒否応答を暗号化します。未認証の報文のタイムスタンプだけで時刻異常を診断しないでください。
|
|
264
264
|
|
package/README.md
CHANGED
|
@@ -250,7 +250,7 @@ const server = crypto.transport.createServer({ serverKeyRing, keyStore })
|
|
|
250
250
|
|
|
251
251
|
握手返回 `{ serverKeyId, serverPublicKey, clientId }`;请求密文的 `context` 携带 `serverKeyId`,服务端按 ID 选择私钥,未知或撤销 ID 直接拒绝,不尝试其它密钥。响应仍用客户端公钥加密。轮换先向所有节点分发包含新旧版本的 ring,再切换 `activeKeyId` 并滚动重建 manager;过渡期保留旧版本,结束后删除旧 ID 并更新所有节点。旧客户端此后须 `destroy()` 后重新协商;不要自动重试非幂等业务。部署通过 Secret Manager 或服务端私有环境分发同一 ring,禁止写入普通 YAML、PUBLIC_/VITE_、日志或公开响应。CLI 创建服务端项目时为 `.env.dev.local/.env.test.local/.env.prod.local` 分别生成独立密钥环,文件不提交 Git;克隆后由受保护配置或 Secret Manager 分发对应环境秘密,也可通过私有环境或 Secret Manager 覆盖。运行时不生成密钥。
|
|
252
252
|
|
|
253
|
-
认证协议仅支持 v2:独立随机 SM4-CBC 密钥和 HMAC-SM3 密钥由 SM2 封装,先验签、再验证 MAC,最后解密 CBC。请求由客户端私钥签名,响应由所选服务端版本签名。认证字段包括 version、direction、serverKeyId、clientId、128 位随机 requestId(兼 nonce)、issuedAt、HTTP method、pathname + 原始 search 的 SM3 pathHash,以及响应 status
|
|
253
|
+
认证协议仅支持 v2:独立随机 SM4-CBC 密钥和 HMAC-SM3 密钥由 SM2 封装,先验签、再验证 MAC,最后解密 CBC。请求由客户端私钥签名,响应由所选服务端版本签名。认证字段包括 version、direction、serverKeyId、clientId、128 位随机 requestId(兼 nonce)、issuedAt、HTTP method、pathname + 原始 search 的 SM3 pathHash,以及响应 status。请求默认有效期为 5 分钟,客户端时间最多可领先服务器 30 秒。服务端可用 `maxRequestAgeSeconds`(1~600)和 `maxFutureSkewSeconds`(0~300)调整,单位为整数秒。共享 registry 原子消费 (clientId, requestId),nonce 保留到 issuedAt + 11 分钟,覆盖最长可配窗口及节点时钟差,存储故障拒绝请求。Redis 必须保留有效 nonce,使用 noeviction/可靠持久化策略;各节点时钟须同步。此能力防止同一传输消息重放,业务重试仍需自己的幂等键。
|
|
254
254
|
|
|
255
255
|
空 POST 也发送空明文的认证封装;GET/HEAD 将封装放在 X-Hai-Transport 头,204/304/HEAD 等无正文响应也使用同名头承载认证封装。其它请求/响应在 JSON 正文承载封装并带 X-Encrypted: true。客户端必须关联原请求上下文和 HTTP status,缺标记、旧格式或认证失败一律 reject;明文接口由 Serv/Kit 路径规则 `encryption: disabled` 或宿主 `excludePaths` 显式允许;调用方同步选择普通请求,API Client/Kit 包装器使用自己的 `excludePaths`。CORS 须允许并暴露 X-Hai-Transport。握手公钥的真实性依赖 TLS,不替代 IAM 业务授权。
|
|
256
256
|
|
|
@@ -258,7 +258,7 @@ const server = crypto.transport.createServer({ serverKeyRing, keyStore })
|
|
|
258
258
|
|
|
259
259
|
普通业务仍使用 `transport.maxPlaintextBytes`(auto/省略为 1 MiB);Feedback 的报告受理、证据、附件及 ZIP 下载路由通过 `routePlaintextBytes` 单独声明 `feedback.budget.maxTransportBytes`。路径中的 `*` 仅匹配一个完整路径段;宿主显式路由值优先。握手、adapter-node 与代理使用各路由最大容量,普通业务不会随材料预算扩大。HTTP 适配器为单独声明且大于 1 MiB 的材料路由保留最多 2 个槽,普通业务另有 16 个槽,完整响应/失败后释放;忙碌返回 503 / `hai:crypto:072`,不自动重放业务写入。
|
|
260
260
|
|
|
261
|
-
|
|
261
|
+
Serv/Kit 对通过请求绑定、签名和 MAC 校验但超出时间窗口的请求返回加密的 HTTP 400 错误。客户端抛出 `TransportTimeError`(`hai:crypto:071`),显示校验时的服务器 UTC 时间,提示开启设备的自动校时或手工修改时间后刷新。`serverTime` 为 Unix 毫秒,旧服务端未提供该值时仍显示校时提示。`api-client` 映射为 `VALIDATION_FAILED`,在 `cause.transportCode` 和 `cause.serverTime` 保留诊断信息。
|
|
262
262
|
|
|
263
263
|
自定义 HTTP 适配器检查失败原因 `error instanceof TransportTimeError && error.context` 后,使用 `createTransportErrorResponse(error, { manager, context: error.context })` 加密拒绝响应;不能根据未认证报文的时间戳给出可信时间诊断。
|
|
264
264
|
|
package/dist/index.d.ts
CHANGED
|
@@ -165,9 +165,9 @@ declare const TRANSPORT_PROTOCOL: {
|
|
|
165
165
|
readonly ENVELOPE_HEADER: "X-Hai-Transport";
|
|
166
166
|
/** 唯一支持的认证协议版本;旧未认证格式拒绝。 */
|
|
167
167
|
readonly VERSION: 2;
|
|
168
|
-
/**
|
|
168
|
+
/** 默认请求有效期(毫秒)。 */
|
|
169
169
|
readonly REQUEST_WINDOW_MS: 300000;
|
|
170
|
-
/**
|
|
170
|
+
/** 默认允许的客户端未来偏差(毫秒),也是部署约定的节点对时误差上限。 */
|
|
171
171
|
readonly CLOCK_SKEW_MS: 30000;
|
|
172
172
|
/** 标识响应体已加密的响应头名。 */
|
|
173
173
|
readonly ENCRYPTED_HEADER: "X-Encrypted";
|
|
@@ -176,6 +176,19 @@ declare const TRANSPORT_PROTOCOL: {
|
|
|
176
176
|
/** 密钥协商端点的默认子路径(相对于挂载前缀)。 */
|
|
177
177
|
readonly DEFAULT_KEY_EXCHANGE_PATH: "/_hai/key-exchange";
|
|
178
178
|
};
|
|
179
|
+
/**
|
|
180
|
+
* 服务端传输时间配置的允许范围,供 Crypto 工厂与 Serv/Kit 配置共用。
|
|
181
|
+
* @example
|
|
182
|
+
* ```ts
|
|
183
|
+
* import { TRANSPORT_TIME_LIMITS } from '@h-ai/crypto'
|
|
184
|
+
* const latest = TRANSPORT_TIME_LIMITS.maxRequestAgeSeconds
|
|
185
|
+
* ```
|
|
186
|
+
*/
|
|
187
|
+
declare const TRANSPORT_TIME_LIMITS: Readonly<{
|
|
188
|
+
maxFutureSkewSeconds: 300;
|
|
189
|
+
minRequestAgeSeconds: 1;
|
|
190
|
+
maxRequestAgeSeconds: 600;
|
|
191
|
+
}>;
|
|
179
192
|
/**
|
|
180
193
|
* 密钥协商请求;只发送客户端公钥,私钥始终留在客户端,注册仍验证曲线点。
|
|
181
194
|
*
|
|
@@ -296,7 +309,7 @@ interface TransportMessageContext {
|
|
|
296
309
|
readonly clientId: string;
|
|
297
310
|
/** 128 位随机十六进制 nonce,同时用于响应关联。 */
|
|
298
311
|
readonly requestId: string;
|
|
299
|
-
/** 请求创建时间,Unix
|
|
312
|
+
/** 请求创建时间,Unix 毫秒;时间窗口由服务端配置。 */
|
|
300
313
|
readonly issuedAt: number;
|
|
301
314
|
/** 大写 HTTP 方法,必须与实际 Request 完全一致。 */
|
|
302
315
|
readonly method: string;
|
|
@@ -573,6 +586,10 @@ interface TransportKeyStore {
|
|
|
573
586
|
* ```
|
|
574
587
|
*/
|
|
575
588
|
interface TransportCreateServerOptions {
|
|
589
|
+
/** 请求时间允许领先服务器的秒数,默认 30,整数范围 0~300。 */
|
|
590
|
+
readonly maxFutureSkewSeconds?: number;
|
|
591
|
+
/** 请求从签发起的有效秒数,默认 300,整数范围 1~600。 */
|
|
592
|
+
readonly maxRequestAgeSeconds?: number;
|
|
576
593
|
/** 双向业务明文 UTF-8 字节上限;默认 1 MiB,整数范围 1 KiB~64 MiB。 */
|
|
577
594
|
readonly maxPlaintextBytes?: number;
|
|
578
595
|
/** 必填受管服务端密钥环;没有隐式随机生成或进程密钥回退。 */
|
|
@@ -1944,7 +1961,7 @@ declare function isValidSM2PublicKey(publicKey: unknown): publicKey is string;
|
|
|
1944
1961
|
/** @h-ai/crypto — serv/kit 共用的安全传输错误响应。 */
|
|
1945
1962
|
|
|
1946
1963
|
/**
|
|
1947
|
-
*
|
|
1964
|
+
* 已认证请求的时间窗口错误;附带校验失败时的服务器时间(Unix 毫秒)。
|
|
1948
1965
|
* @example
|
|
1949
1966
|
* ```ts
|
|
1950
1967
|
* import { TransportTimeError } from '@h-ai/crypto'
|
|
@@ -1955,7 +1972,8 @@ declare function isValidSM2PublicKey(publicKey: unknown): publicKey is string;
|
|
|
1955
1972
|
declare class TransportTimeError extends Error {
|
|
1956
1973
|
readonly context?: TransportMessageContext | undefined;
|
|
1957
1974
|
readonly code: string | number;
|
|
1958
|
-
|
|
1975
|
+
readonly serverTime?: number;
|
|
1976
|
+
constructor(context?: TransportMessageContext | undefined, serverTime?: number);
|
|
1959
1977
|
}
|
|
1960
1978
|
/**
|
|
1961
1979
|
* 创建脱敏传输错误;已验证绑定、签名和 MAC 的时间/响应容量错误继续加密,请求读取超限返回 HTTP 413。
|
|
@@ -2211,4 +2229,4 @@ interface ReldbTransportKeyStore extends TransportKeyStore {
|
|
|
2211
2229
|
*/
|
|
2212
2230
|
declare function createReldbTransportKeyStore(options: CreateReldbTransportKeyStoreOptions): ReldbTransportKeyStore;
|
|
2213
2231
|
|
|
2214
|
-
export { type AsymmetricEncryptOptions, type AsymmetricOperations, type CipherMode, type CreateRedisTransportKeyStoreOptions, type CreateReldbTransportKeyStoreOptions, type CryptoFunctions, type EncryptWithIVResult, type EncryptedPayload, type EncryptedRequestPayload, HaiCryptoError, type HashOperations, type HashOptions, type KeyExchangeRequest, type KeyExchangeResponse, type KeyPair, type PasswordConfig, type PasswordOperations, type ReadTransportBodyOptions, type ReldbTransportKeyStore, type SignOptions, type SymmetricCbcEncryptedPayload, type SymmetricCiphertextEncoding, type SymmetricDecryptInput, type SymmetricEncryptOptions, type SymmetricEncryptedPayload, type SymmetricMode, type SymmetricOperations, TRANSPORT_BODY_LIMITS, TRANSPORT_PLAINTEXT_RANGE, TRANSPORT_PROTOCOL, type TransportBodyLimits, TransportBodyTooLargeError, type TransportClient, type TransportCreateServerOptions, type TransportCryptoServiceLike, type TransportEncryptionManager, type TransportKeyPair, type TransportKeyStore, type TransportMessageContext, type TransportOperations, type TransportRequestContext, type TransportServerKeyRing, TransportTimeError, authenticateTransportRequest, createInMemoryKeyStore, createRedisTransportKeyStore, createReldbTransportKeyStore, createTransportBodyPolicy, createTransportErrorResponse, crypto, isValidSM2PublicKey, parseTransportServerKeyRing, readTransportBody, resolveTransportBodyLimits };
|
|
2232
|
+
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, TRANSPORT_TIME_LIMITS, type TransportBodyLimits, TransportBodyTooLargeError, type TransportClient, type TransportCreateServerOptions, type TransportCryptoServiceLike, type TransportEncryptionManager, type TransportKeyPair, type TransportKeyStore, type TransportMessageContext, type TransportOperations, type TransportRequestContext, type TransportServerKeyRing, TransportTimeError, authenticateTransportRequest, createInMemoryKeyStore, createRedisTransportKeyStore, createReldbTransportKeyStore, createTransportBodyPolicy, createTransportErrorResponse, crypto, isValidSM2PublicKey, parseTransportServerKeyRing, readTransportBody, resolveTransportBodyLimits };
|
package/dist/index.js
CHANGED
|
@@ -58,7 +58,9 @@ var en_US_default = {
|
|
|
58
58
|
crypto_transportSessionKeyDecryptFailed: "Failed to decrypt transport session key",
|
|
59
59
|
crypto_transportBodyLimitInvalid: "Transport plaintext limit must be an integer between 1024 and 67108864 bytes",
|
|
60
60
|
crypto_transportBodyTooLarge: "Encrypted transport payload exceeds the configured byte limit",
|
|
61
|
-
crypto_transportTimeInvalid: "Your device and server clocks may be out of sync, or the request has expired. Enable automatic time synchronization
|
|
61
|
+
crypto_transportTimeInvalid: "Your device and server clocks may be out of sync, or the request has expired. Enable automatic time synchronization or set your device time manually, then refresh to retry. If this continues, ask an administrator to check the server clock.",
|
|
62
|
+
crypto_transportTimeInvalidWithServerTime: "Your device and server clocks may be out of sync, or the request has expired. Server time is {serverTime}. Enable automatic time synchronization or set your device time manually, then refresh to retry. If this continues, ask an administrator to check the server clock.",
|
|
63
|
+
crypto_transportTimeConfigInvalid: "Invalid transport time settings. Future clock skew must be an integer from 0 to 300 seconds and request lifetime from 1 to 600 seconds.",
|
|
62
64
|
crypto_transportBodyLimitConflict: "Explicit transport limit {limit} bytes is below the enabled module requirement of {required} bytes; adjust the module budget or explicit limit",
|
|
63
65
|
crypto_transportBusy: "Material processing is busy. Please retry shortly."
|
|
64
66
|
};
|
|
@@ -118,7 +120,9 @@ var ja_JP_default = {
|
|
|
118
120
|
crypto_transportSessionKeyDecryptFailed: "\u901A\u4FE1\u30BB\u30C3\u30B7\u30E7\u30F3\u9375\u306E\u5FA9\u53F7\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
|
|
119
121
|
crypto_transportBodyLimitInvalid: "\u901A\u4FE1\u5E73\u6587\u306E\u4E0A\u9650\u306F 1024 \u304B\u3089 67108864 \u30D0\u30A4\u30C8\u306E\u6574\u6570\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
120
122
|
crypto_transportBodyTooLarge: "\u6697\u53F7\u5316\u901A\u4FE1\u306E\u30C7\u30FC\u30BF\u304C\u8A2D\u5B9A\u3055\u308C\u305F\u30D0\u30A4\u30C8\u4E0A\u9650\u3092\u8D85\u3048\u3066\u3044\u307E\u3059",
|
|
121
|
-
crypto_transportTimeInvalid: "\u7AEF\u672B\u3068\u30B5\u30FC\u30D0\u30FC\u306E\u6642\u523B\u304C\u540C\u671F\u3057\u3066\u3044\u306A\u3044\u304B\u3001\u8981\u6C42\u306E\u6709\u52B9\u671F\u9650\u304C\u5207\u308C\u3066\u3044\u307E\u3059\u3002\u81EA\u52D5\u6642\u523B\u540C\u671F\u3092\u6709\u52B9\u306B\u3057\u3066\u30DA\u30FC\u30B8\u3092\u518D\u8AAD\u307F\u8FBC\u307F\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u89E3\u6D88\u3057\u306A\u3044\u5834\u5408\u306F\u7BA1\u7406\u8005\u306B\u30B5\u30FC\u30D0\u30FC\u306E\u6642\u523B\u78BA\u8A8D\u3092\u4F9D\u983C\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
123
|
+
crypto_transportTimeInvalid: "\u7AEF\u672B\u3068\u30B5\u30FC\u30D0\u30FC\u306E\u6642\u523B\u304C\u540C\u671F\u3057\u3066\u3044\u306A\u3044\u304B\u3001\u8981\u6C42\u306E\u6709\u52B9\u671F\u9650\u304C\u5207\u308C\u3066\u3044\u307E\u3059\u3002\u7AEF\u672B\u306E\u81EA\u52D5\u6642\u523B\u540C\u671F\u3092\u6709\u52B9\u306B\u3059\u308B\u304B\u3001\u624B\u52D5\u3067\u6642\u523B\u3092\u8A2D\u5B9A\u3057\u3066\u304B\u3089\u3001\u30DA\u30FC\u30B8\u3092\u518D\u8AAD\u307F\u8FBC\u307F\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u89E3\u6D88\u3057\u306A\u3044\u5834\u5408\u306F\u7BA1\u7406\u8005\u306B\u30B5\u30FC\u30D0\u30FC\u306E\u6642\u523B\u78BA\u8A8D\u3092\u4F9D\u983C\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
124
|
+
crypto_transportTimeInvalidWithServerTime: "\u7AEF\u672B\u3068\u30B5\u30FC\u30D0\u30FC\u306E\u6642\u523B\u304C\u540C\u671F\u3057\u3066\u3044\u306A\u3044\u304B\u3001\u8981\u6C42\u306E\u6709\u52B9\u671F\u9650\u304C\u5207\u308C\u3066\u3044\u307E\u3059\u3002\u30B5\u30FC\u30D0\u30FC\u6642\u523B\u306F {serverTime} \u3067\u3059\u3002\u7AEF\u672B\u306E\u81EA\u52D5\u6642\u523B\u540C\u671F\u3092\u6709\u52B9\u306B\u3059\u308B\u304B\u3001\u624B\u52D5\u3067\u6642\u523B\u3092\u8A2D\u5B9A\u3057\u3066\u304B\u3089\u3001\u30DA\u30FC\u30B8\u3092\u518D\u8AAD\u307F\u8FBC\u307F\u3057\u3066\u304F\u3060\u3055\u3044\u3002\u89E3\u6D88\u3057\u306A\u3044\u5834\u5408\u306F\u7BA1\u7406\u8005\u306B\u30B5\u30FC\u30D0\u30FC\u306E\u6642\u523B\u78BA\u8A8D\u3092\u4F9D\u983C\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
125
|
+
crypto_transportTimeConfigInvalid: "\u30C8\u30E9\u30F3\u30B9\u30DD\u30FC\u30C8\u306E\u6642\u523B\u8A2D\u5B9A\u304C\u7121\u52B9\u3067\u3059\u3002\u672A\u6765\u65B9\u5411\u306E\u6642\u523B\u5DEE\u306F 0\uFF5E300 \u79D2\u3001\u8981\u6C42\u306E\u6709\u52B9\u671F\u9593\u306F 1\uFF5E600 \u79D2\u306E\u6574\u6570\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
|
|
122
126
|
crypto_transportBodyLimitConflict: "\u660E\u793A\u3055\u308C\u305F\u8EE2\u9001\u4E0A\u9650 {limit} \u30D0\u30A4\u30C8\u306F\u6709\u52B9\u306A\u30E2\u30B8\u30E5\u30FC\u30EB\u306E\u5FC5\u8981\u91CF {required} \u30D0\u30A4\u30C8\u672A\u6E80\u3067\u3059\u3002\u4E88\u7B97\u307E\u305F\u306F\u4E0A\u9650\u3092\u5909\u66F4\u3057\u3066\u304F\u3060\u3055\u3044",
|
|
123
127
|
crypto_transportBusy: "\u8CC7\u6599\u306E\u51E6\u7406\u304C\u6DF7\u307F\u5408\u3063\u3066\u3044\u307E\u3059\u3002\u3057\u3070\u3089\u304F\u3057\u3066\u304B\u3089\u518D\u8A66\u884C\u3057\u3066\u304F\u3060\u3055\u3044\u3002"
|
|
124
128
|
};
|
|
@@ -178,7 +182,9 @@ var zh_CN_default = {
|
|
|
178
182
|
crypto_transportSessionKeyDecryptFailed: "\u4F20\u8F93\u52A0\u5BC6\u4F1A\u8BDD\u5BC6\u94A5\u89E3\u5BC6\u5931\u8D25",
|
|
179
183
|
crypto_transportBodyLimitInvalid: "\u4F20\u8F93\u660E\u6587\u5BB9\u91CF\u5FC5\u987B\u662F 1024 \u5230 67108864 \u5B57\u8282\u4E4B\u95F4\u7684\u6574\u6570",
|
|
180
184
|
crypto_transportBodyTooLarge: "\u52A0\u5BC6\u4F20\u8F93\u62A5\u6587\u8D85\u8FC7\u914D\u7F6E\u7684\u5B57\u8282\u4E0A\u9650",
|
|
181
|
-
crypto_transportTimeInvalid: "\u8BBE\u5907\u4E0E\u670D\u52A1\u5668\u65F6\u95F4\u53EF\u80FD\u4E0D\u540C\u6B65\uFF0C\u6216\u8BF7\u6C42\u5DF2\u8FC7\u671F\u3002\u8BF7\u5F00\u542F\u81EA\u52A8\u6821\u65F6\
|
|
185
|
+
crypto_transportTimeInvalid: "\u8BBE\u5907\u4E0E\u670D\u52A1\u5668\u65F6\u95F4\u53EF\u80FD\u4E0D\u540C\u6B65\uFF0C\u6216\u8BF7\u6C42\u5DF2\u8FC7\u671F\u3002\u8BF7\u5F00\u542F\u8BBE\u5907\u7684\u81EA\u52A8\u6821\u65F6\u6216\u624B\u5DE5\u4FEE\u6539\u65F6\u95F4\uFF0C\u7136\u540E\u5237\u65B0\u91CD\u8BD5\u3002\u82E5\u4ECD\u5931\u8D25\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u68C0\u67E5\u670D\u52A1\u5668\u65F6\u95F4\u3002",
|
|
186
|
+
crypto_transportTimeInvalidWithServerTime: "\u8BBE\u5907\u4E0E\u670D\u52A1\u5668\u65F6\u95F4\u53EF\u80FD\u4E0D\u540C\u6B65\uFF0C\u6216\u8BF7\u6C42\u5DF2\u8FC7\u671F\u3002\u670D\u52A1\u5668\u65F6\u95F4\u4E3A {serverTime}\u3002\u8BF7\u5F00\u542F\u8BBE\u5907\u7684\u81EA\u52A8\u6821\u65F6\u6216\u624B\u5DE5\u4FEE\u6539\u65F6\u95F4\uFF0C\u7136\u540E\u5237\u65B0\u91CD\u8BD5\u3002\u82E5\u4ECD\u5931\u8D25\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u68C0\u67E5\u670D\u52A1\u5668\u65F6\u95F4\u3002",
|
|
187
|
+
crypto_transportTimeConfigInvalid: "\u4F20\u8F93\u65F6\u95F4\u914D\u7F6E\u65E0\u6548\u3002\u5141\u8BB8\u7684\u672A\u6765\u504F\u5DEE\u987B\u4E3A 0 \u81F3 300 \u79D2\u7684\u6574\u6570\uFF0C\u8BF7\u6C42\u6709\u6548\u671F\u987B\u4E3A 1 \u81F3 600 \u79D2\u7684\u6574\u6570\u3002",
|
|
182
188
|
crypto_transportBodyLimitConflict: "\u663E\u5F0F\u4F20\u8F93\u4E0A\u9650 {limit} \u5B57\u8282\u5C0F\u4E8E\u5DF2\u542F\u7528\u6A21\u5757\u6240\u9700\u7684 {required} \u5B57\u8282\uFF1B\u8BF7\u8C03\u6574\u6A21\u5757\u9884\u7B97\u6216\u663E\u5F0F\u4E0A\u9650",
|
|
183
189
|
crypto_transportBusy: "\u6750\u6599\u5904\u7406\u7E41\u5FD9\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5"
|
|
184
190
|
};
|
|
@@ -979,9 +985,9 @@ var TRANSPORT_PROTOCOL = {
|
|
|
979
985
|
ENVELOPE_HEADER: "X-Hai-Transport",
|
|
980
986
|
/** 唯一支持的认证协议版本;旧未认证格式拒绝。 */
|
|
981
987
|
VERSION: 2,
|
|
982
|
-
/**
|
|
988
|
+
/** 默认请求有效期(毫秒)。 */
|
|
983
989
|
REQUEST_WINDOW_MS: 3e5,
|
|
984
|
-
/**
|
|
990
|
+
/** 默认允许的客户端未来偏差(毫秒),也是部署约定的节点对时误差上限。 */
|
|
985
991
|
CLOCK_SKEW_MS: 3e4,
|
|
986
992
|
/** 标识响应体已加密的响应头名。 */
|
|
987
993
|
ENCRYPTED_HEADER: "X-Encrypted",
|
|
@@ -990,6 +996,11 @@ var TRANSPORT_PROTOCOL = {
|
|
|
990
996
|
/** 密钥协商端点的默认子路径(相对于挂载前缀)。 */
|
|
991
997
|
DEFAULT_KEY_EXCHANGE_PATH: "/_hai/key-exchange"
|
|
992
998
|
};
|
|
999
|
+
var TRANSPORT_TIME_LIMITS = Object.freeze({
|
|
1000
|
+
maxFutureSkewSeconds: 300,
|
|
1001
|
+
minRequestAgeSeconds: 1,
|
|
1002
|
+
maxRequestAgeSeconds: 600
|
|
1003
|
+
});
|
|
993
1004
|
|
|
994
1005
|
// src/transport/crypto-transport-codec.ts
|
|
995
1006
|
function authenticatedText(payload) {
|
|
@@ -1090,29 +1101,41 @@ function sameTransportContext(a, b) {
|
|
|
1090
1101
|
|
|
1091
1102
|
// src/transport/crypto-transport-error.ts
|
|
1092
1103
|
var TransportTimeError = class extends Error {
|
|
1093
|
-
constructor(context) {
|
|
1094
|
-
|
|
1104
|
+
constructor(context, serverTime) {
|
|
1105
|
+
const validTime = typeof serverTime === "number" && Number.isSafeInteger(serverTime) && serverTime >= 0 && serverTime <= 864e13 ? serverTime : void 0;
|
|
1106
|
+
const message = validTime === void 0 ? cryptoM("crypto_transportTimeInvalid") : cryptoM("crypto_transportTimeInvalidWithServerTime", { params: { serverTime: new Date(validTime).toISOString().replace("T", " ").replace(/\.\d{3}Z$/, " UTC") } });
|
|
1107
|
+
super(message);
|
|
1095
1108
|
this.context = context;
|
|
1096
1109
|
this.name = "TransportTimeError";
|
|
1110
|
+
this.serverTime = validTime;
|
|
1097
1111
|
}
|
|
1098
1112
|
context;
|
|
1099
1113
|
code = HaiCryptoError.TRANSPORT_TIME_INVALID.code;
|
|
1114
|
+
serverTime;
|
|
1100
1115
|
};
|
|
1101
1116
|
async function createTransportErrorResponse(error, authenticated) {
|
|
1102
|
-
const
|
|
1103
|
-
const
|
|
1117
|
+
const isTimeError = error instanceof TransportTimeError;
|
|
1118
|
+
const status = isTimeError ? 400 : error.direction === "request" ? 413 : 500;
|
|
1119
|
+
const publicError = { code: error.code, message: isTimeError ? cryptoM("crypto_transportTimeInvalid") : error.message, httpStatus: status, details: isTimeError ? void 0 : {
|
|
1104
1120
|
direction: error.direction,
|
|
1105
1121
|
stage: error.stage,
|
|
1106
1122
|
limitBytes: error.limitBytes,
|
|
1107
1123
|
actualBytes: error.actualBytes
|
|
1108
|
-
} }
|
|
1124
|
+
} };
|
|
1109
1125
|
if (authenticated) {
|
|
1126
|
+
const encryptedError = isTimeError && error.serverTime !== void 0 ? { ...publicError, message: error.message, details: { serverTime: error.serverTime } } : publicError;
|
|
1127
|
+
const body = JSON.stringify({ success: false, error: encryptedError });
|
|
1110
1128
|
const encrypted = await authenticated.manager.encryptResponse(authenticated.context, body, status);
|
|
1111
1129
|
if (encrypted.success) {
|
|
1112
|
-
|
|
1130
|
+
const headers = new Headers({ [TRANSPORT_PROTOCOL.ENCRYPTED_HEADER]: TRANSPORT_PROTOCOL.ENCRYPTED_HEADER_VALUE });
|
|
1131
|
+
if (authenticated.context.method === "HEAD") {
|
|
1132
|
+
headers.set(TRANSPORT_PROTOCOL.ENVELOPE_HEADER, JSON.stringify(encrypted.data));
|
|
1133
|
+
return new Response(null, { status, headers });
|
|
1134
|
+
}
|
|
1135
|
+
return Response.json(encrypted.data, { status, headers });
|
|
1113
1136
|
}
|
|
1114
1137
|
}
|
|
1115
|
-
return new Response(
|
|
1138
|
+
return new Response(JSON.stringify({ success: false, error: publicError }), { status, headers: { "Content-Type": "application/json; charset=utf-8" } });
|
|
1116
1139
|
}
|
|
1117
1140
|
|
|
1118
1141
|
// src/transport/crypto-transport-client.ts
|
|
@@ -1338,8 +1361,10 @@ function createTransportClient(options) {
|
|
|
1338
1361
|
failure = JSON.parse(decResult.data);
|
|
1339
1362
|
} catch {
|
|
1340
1363
|
}
|
|
1341
|
-
if (failure?.success === false && failure.error?.code === HaiCryptoError.TRANSPORT_TIME_INVALID.code)
|
|
1342
|
-
|
|
1364
|
+
if (failure?.success === false && failure.error?.code === HaiCryptoError.TRANSPORT_TIME_INVALID.code) {
|
|
1365
|
+
const serverTime = failure.error.details?.serverTime;
|
|
1366
|
+
throw new TransportTimeError(void 0, typeof serverTime === "number" ? serverTime : void 0);
|
|
1367
|
+
}
|
|
1343
1368
|
}
|
|
1344
1369
|
if (response.status === 500) {
|
|
1345
1370
|
let failure;
|
|
@@ -1420,6 +1445,16 @@ function isTransportServerKeyRing(value) {
|
|
|
1420
1445
|
|
|
1421
1446
|
// src/transport/crypto-transport-server.ts
|
|
1422
1447
|
function createTransportEncryption(cryptoService, options) {
|
|
1448
|
+
const {
|
|
1449
|
+
maxFutureSkewSeconds = TRANSPORT_PROTOCOL.CLOCK_SKEW_MS / 1e3,
|
|
1450
|
+
maxRequestAgeSeconds = TRANSPORT_PROTOCOL.REQUEST_WINDOW_MS / 1e3
|
|
1451
|
+
} = options ?? {};
|
|
1452
|
+
if (!Number.isInteger(maxFutureSkewSeconds) || maxFutureSkewSeconds < 0 || maxFutureSkewSeconds > TRANSPORT_TIME_LIMITS.maxFutureSkewSeconds || !Number.isInteger(maxRequestAgeSeconds) || maxRequestAgeSeconds < TRANSPORT_TIME_LIMITS.minRequestAgeSeconds || maxRequestAgeSeconds > TRANSPORT_TIME_LIMITS.maxRequestAgeSeconds) {
|
|
1453
|
+
return err(HaiCryptoError.INVALID_INPUT, cryptoM("crypto_transportTimeConfigInvalid"));
|
|
1454
|
+
}
|
|
1455
|
+
const futureSkewMs = maxFutureSkewSeconds * 1e3;
|
|
1456
|
+
const requestAgeMs = maxRequestAgeSeconds * 1e3;
|
|
1457
|
+
const nonceRetentionMs = TRANSPORT_TIME_LIMITS.maxRequestAgeSeconds * 1e3 + 2 * TRANSPORT_PROTOCOL.CLOCK_SKEW_MS;
|
|
1423
1458
|
let bodyLimits;
|
|
1424
1459
|
try {
|
|
1425
1460
|
bodyLimits = resolveTransportBodyLimits(options?.maxPlaintextBytes);
|
|
@@ -1494,18 +1529,20 @@ function createTransportEncryption(cryptoService, options) {
|
|
|
1494
1529
|
const plaintext = openTransportPayload(cryptoService, authenticated, key.privateKey, clientPublicKey, bodyLimits);
|
|
1495
1530
|
if (!plaintext.success)
|
|
1496
1531
|
return plaintext;
|
|
1497
|
-
const timeFailure = () => {
|
|
1498
|
-
const error = new TransportTimeError(c);
|
|
1532
|
+
const timeFailure = (serverTime) => {
|
|
1533
|
+
const error = new TransportTimeError(c, serverTime);
|
|
1499
1534
|
return err(HaiCryptoError.TRANSPORT_TIME_INVALID, error.message, error);
|
|
1500
1535
|
};
|
|
1501
1536
|
const now = Date.now();
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1537
|
+
const expiresAt = c.issuedAt + requestAgeMs;
|
|
1538
|
+
if (c.issuedAt > now + futureSkewMs || now >= expiresAt)
|
|
1539
|
+
return timeFailure(now);
|
|
1540
|
+
if (!await keyStore.claimRequest(c.clientId, c.requestId, c.issuedAt + nonceRetentionMs)) {
|
|
1505
1541
|
return transportAuthenticationFailure();
|
|
1506
1542
|
}
|
|
1507
|
-
|
|
1508
|
-
|
|
1543
|
+
const claimedAt = Date.now();
|
|
1544
|
+
if (claimedAt >= expiresAt)
|
|
1545
|
+
return timeFailure(claimedAt);
|
|
1509
1546
|
return plaintext;
|
|
1510
1547
|
} catch {
|
|
1511
1548
|
return transportAuthenticationFailure();
|
|
@@ -1984,6 +2021,6 @@ function createReldbTransportKeyStore(options) {
|
|
|
1984
2021
|
};
|
|
1985
2022
|
}
|
|
1986
2023
|
|
|
1987
|
-
export { HaiCryptoError, TRANSPORT_BODY_LIMITS, TRANSPORT_PLAINTEXT_RANGE, TRANSPORT_PROTOCOL, TransportBodyTooLargeError, TransportTimeError, authenticateTransportRequest, createInMemoryKeyStore, createRedisTransportKeyStore, createReldbTransportKeyStore, createTransportBodyPolicy, createTransportErrorResponse, crypto2 as crypto, isValidSM2PublicKey, parseTransportServerKeyRing, readTransportBody, resolveTransportBodyLimits };
|
|
2024
|
+
export { HaiCryptoError, TRANSPORT_BODY_LIMITS, TRANSPORT_PLAINTEXT_RANGE, TRANSPORT_PROTOCOL, TRANSPORT_TIME_LIMITS, TransportBodyTooLargeError, TransportTimeError, authenticateTransportRequest, createInMemoryKeyStore, createRedisTransportKeyStore, createReldbTransportKeyStore, createTransportBodyPolicy, createTransportErrorResponse, crypto2 as crypto, isValidSM2PublicKey, parseTransportServerKeyRing, readTransportBody, resolveTransportBodyLimits };
|
|
1988
2025
|
//# sourceMappingURL=index.js.map
|
|
1989
2026
|
//# sourceMappingURL=index.js.map
|