@h-ai/crypto 0.1.0-alpha9 → 1.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { core, ok, err } from '@h-ai/core';
1
+ import { core, ok, err, HaiCommonError } from '@h-ai/core';
2
2
  import smCrypto from 'sm-crypto';
3
3
 
4
4
  // src/crypto-main.ts
@@ -33,7 +33,94 @@ var en_US_default = {
33
33
  crypto_passwordHashEmpty: "Password and hash cannot be empty",
34
34
  crypto_passwordHashFailed: "Password hashing failed",
35
35
  crypto_hashFormatInvalid: "Invalid hash format",
36
- crypto_passwordVerifyFailed: "Password verification failed"
36
+ crypto_passwordVerifyFailed: "Password verification failed",
37
+ crypto_transportClientKeyGenerateFailed: "Failed to generate transport client key pair",
38
+ crypto_transportServerKeyRingInvalid: "The server transport key ring is missing or invalid. Check that private HAI_CRYPTO_TRANSPORT_SERVERKEYRING contains complete valid JSON and distribute the same ring to every node.",
39
+ crypto_transportServerKeyUnknown: "Transport server key version is unknown or revoked",
40
+ crypto_transportKeyStoreRequired: "Transport encryption requires an explicit client public key store",
41
+ crypto_transportKeyStoreTtlInvalid: "Public-key store TTL must be an integer between 1 and 2147483647 seconds",
42
+ crypto_transportKeyStoreCapacityInvalid: "Public-key store capacity must be a positive safe integer",
43
+ crypto_transportKeyStoreCapacityMismatch: "Public-key store shared capacity settings do not match",
44
+ crypto_transportKeyStoreCapacityReached: "Public-key store capacity has been reached",
45
+ crypto_transportKeyStoreOperationFailed: "Public-key store operation failed",
46
+ crypto_transportAuthenticationFailed: "Transport authentication, request binding or replay validation failed",
47
+ crypto_transportKeyExchangeRequestFailed: "Transport key exchange request failed",
48
+ crypto_transportKeyExchangeHttpFailed: "Transport key exchange returned HTTP {status}",
49
+ crypto_transportKeyExchangeInvalidJson: "Transport key exchange response is not valid JSON",
50
+ crypto_transportKeyExchangeMissingFields: "Transport key exchange response is missing required fields",
51
+ crypto_transportClientNotInitialized: "Transport client has not completed key exchange",
52
+ crypto_transportClientNotRegistered: "Transport client is not registered or has expired",
53
+ crypto_transportRequestEncryptFailed: "Failed to encrypt transport request body",
54
+ crypto_transportRequestDecryptFailed: "Failed to decrypt transport request body",
55
+ crypto_transportResponseEncryptFailed: "Failed to encrypt transport response body",
56
+ crypto_transportResponseDecryptFailed: "Failed to decrypt transport response body",
57
+ crypto_transportSessionKeyEncryptFailed: "Failed to encrypt transport session key",
58
+ crypto_transportSessionKeyDecryptFailed: "Failed to decrypt transport session key",
59
+ crypto_transportBodyLimitInvalid: "Transport plaintext limit must be an integer between 1024 and 67108864 bytes",
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 and refresh to retry. If this continues, ask an administrator to check the server clock.",
62
+ crypto_transportBodyLimitConflict: "Explicit transport limit {limit} bytes is below the enabled module requirement of {required} bytes; adjust the module budget or explicit limit",
63
+ crypto_transportBusy: "Material processing is busy. Please retry shortly."
64
+ };
65
+
66
+ // messages/ja-JP.json
67
+ var ja_JP_default = {
68
+ $schema: "https://inlang.com/schema/inlang-message-format",
69
+ crypto_notInitialized: "\u6697\u53F7\u30E2\u30B8\u30E5\u30FC\u30EB\u304C\u521D\u671F\u5316\u3055\u308C\u3066\u3044\u307E\u305B\u3093\u3002\u5148\u306Bcrypto.init()\u3092\u547C\u3073\u51FA\u3057\u3066\u304F\u3060\u3055\u3044",
70
+ crypto_initFailed: "\u6697\u53F7\u30E2\u30B8\u30E5\u30FC\u30EB\u306E\u521D\u671F\u5316\u306B\u5931\u6557\u3057\u307E\u3057\u305F: {error}",
71
+ crypto_sm2PublicKeyInvalid: "SM2\u516C\u958B\u9375\u306E\u5F62\u5F0F\u304C\u7121\u52B9\u3067\u3059",
72
+ crypto_sm2PrivateKeyInvalid: "SM2\u79D8\u5BC6\u9375\u306E\u5F62\u5F0F\u304C\u7121\u52B9\u3067\u3059",
73
+ crypto_sm2EncryptEmpty: "SM2\u6697\u53F7\u5316\u306E\u7D50\u679C\u304C\u7A7A\u3067\u3059",
74
+ crypto_sm2DecryptFailed: "SM2\u5FA9\u53F7\u306B\u5931\u6557\u3057\u305F\u304B\u3001\u7D50\u679C\u304C\u7121\u52B9\u3067\u3059",
75
+ crypto_sm2SignEmpty: "SM2\u7F72\u540D\u306E\u7D50\u679C\u304C\u7A7A\u3067\u3059",
76
+ crypto_sm2KeyPairGenerateFailed: "SM2\u9375\u30DA\u30A2\u306E\u751F\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F: {error}",
77
+ crypto_sm2EncryptFailed: "SM2\u6697\u53F7\u5316\u306B\u5931\u6557\u3057\u307E\u3057\u305F: {error}",
78
+ crypto_sm2DecryptFailedWithError: "SM2\u5FA9\u53F7\u306B\u5931\u6557\u3057\u307E\u3057\u305F: {error}",
79
+ crypto_sm2SignFailed: "SM2\u7F72\u540D\u306B\u5931\u6557\u3057\u307E\u3057\u305F: {error}",
80
+ crypto_sm2VerifyFailed: "SM2\u7F72\u540D\u306E\u691C\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F: {error}",
81
+ crypto_sm3HashEmpty: "SM3\u30CF\u30C3\u30B7\u30E5\u306E\u7D50\u679C\u304C\u7A7A\u3067\u3059",
82
+ crypto_sm3HashFailed: "SM3\u30CF\u30C3\u30B7\u30E5\u306E\u8A08\u7B97\u306B\u5931\u6557\u3057\u307E\u3057\u305F: {error}",
83
+ crypto_sm3HmacFailed: "HMAC-SM3\u306E\u8A08\u7B97\u306B\u5931\u6557\u3057\u307E\u3057\u305F: {error}",
84
+ crypto_sm3VerifyFailed: "SM3\u30CF\u30C3\u30B7\u30E5\u306E\u691C\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F: {error}",
85
+ crypto_sm4KeyInvalid: "SM4\u9375\u306F16\u30D0\u30A4\u30C8\uFF0816\u9032\u657032\u6587\u5B57\uFF09\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059",
86
+ crypto_sm4CbcNeedIv: "CBC\u30E2\u30FC\u30C9\u306B\u306FIV\u304C\u5FC5\u8981\u3067\u3059",
87
+ crypto_sm4IvInvalid: "SM4\u306EIV\u306F16\u30D0\u30A4\u30C8\uFF0816\u9032\u657032\u6587\u5B57\uFF09\u3067\u3042\u308B\u5FC5\u8981\u304C\u3042\u308A\u307E\u3059",
88
+ crypto_sm4EncryptEmpty: "SM4\u6697\u53F7\u5316\u306E\u7D50\u679C\u304C\u7A7A\u3067\u3059",
89
+ crypto_sm4DecryptFailed: "SM4\u5FA9\u53F7\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
90
+ crypto_sm4EncryptFailed: "SM4\u6697\u53F7\u5316\u306B\u5931\u6557\u3057\u307E\u3057\u305F: {error}",
91
+ crypto_sm4DecryptFailedWithError: "SM4\u5FA9\u53F7\u306B\u5931\u6557\u3057\u307E\u3057\u305F: {error}",
92
+ crypto_passwordEmpty: "\u30D1\u30B9\u30EF\u30FC\u30C9\u3092\u7A7A\u306B\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093",
93
+ crypto_passwordHashEmpty: "\u30D1\u30B9\u30EF\u30FC\u30C9\u3068\u30CF\u30C3\u30B7\u30E5\u3092\u7A7A\u306B\u3059\u308B\u3053\u3068\u306F\u3067\u304D\u307E\u305B\u3093",
94
+ crypto_passwordHashFailed: "\u30D1\u30B9\u30EF\u30FC\u30C9\u306E\u30CF\u30C3\u30B7\u30E5\u5316\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
95
+ crypto_hashFormatInvalid: "\u30CF\u30C3\u30B7\u30E5\u306E\u5F62\u5F0F\u304C\u7121\u52B9\u3067\u3059",
96
+ crypto_passwordVerifyFailed: "\u30D1\u30B9\u30EF\u30FC\u30C9\u306E\u691C\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
97
+ crypto_transportClientKeyGenerateFailed: "\u901A\u4FE1\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8\u306E\u9375\u30DA\u30A2\u306E\u751F\u6210\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
98
+ crypto_transportServerKeyRingInvalid: "\u30B5\u30FC\u30D0\u30FC\u306E\u8EE2\u9001\u9375\u30EA\u30F3\u30B0\u304C\u4E0D\u8DB3\u307E\u305F\u306F\u4E0D\u6B63\u3067\u3059\u3002\u975E\u516C\u958B\u306E HAI_CRYPTO_TRANSPORT_SERVERKEYRING \u304C\u5B8C\u5168\u3067\u6709\u52B9\u306A JSON \u3067\u3042\u308B\u3053\u3068\u3092\u78BA\u8A8D\u3057\u3001\u5168\u30CE\u30FC\u30C9\u306B\u540C\u3058\u30EA\u30F3\u30B0\u3092\u914D\u5E03\u3057\u3066\u304F\u3060\u3055\u3044\u3002",
99
+ crypto_transportServerKeyUnknown: "\u30C8\u30E9\u30F3\u30B9\u30DD\u30FC\u30C8\u306E\u30B5\u30FC\u30D0\u30FC\u30AD\u30FC\u30D0\u30FC\u30B8\u30E7\u30F3\u304C\u4E0D\u660E\u3001\u307E\u305F\u306F\u5931\u52B9\u3057\u3066\u3044\u307E\u3059",
100
+ crypto_transportKeyStoreRequired: "\u30C8\u30E9\u30F3\u30B9\u30DD\u30FC\u30C8\u6697\u53F7\u5316\u306B\u306F\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8\u516C\u958B\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u660E\u793A\u7684\u306A\u6307\u5B9A\u304C\u5FC5\u8981\u3067\u3059",
101
+ crypto_transportKeyStoreTtlInvalid: "\u516C\u958B\u30AD\u30FC\u30B9\u30C8\u30A2\u306E TTL \u306F1\u301C2147483647\u79D2\u306E\u6574\u6570\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044",
102
+ crypto_transportKeyStoreCapacityInvalid: "\u516C\u958B\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u5BB9\u91CF\u306F\u6B63\u306E\u5B89\u5168\u306A\u6574\u6570\u3067\u6307\u5B9A\u3057\u3066\u304F\u3060\u3055\u3044",
103
+ crypto_transportKeyStoreCapacityMismatch: "\u516C\u958B\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u5171\u6709\u5BB9\u91CF\u8A2D\u5B9A\u304C\u4E00\u81F4\u3057\u307E\u305B\u3093",
104
+ crypto_transportKeyStoreCapacityReached: "\u516C\u958B\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u5BB9\u91CF\u4E0A\u9650\u306B\u9054\u3057\u307E\u3057\u305F",
105
+ crypto_transportKeyStoreOperationFailed: "\u516C\u958B\u30AD\u30FC\u30B9\u30C8\u30A2\u306E\u64CD\u4F5C\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
106
+ crypto_transportAuthenticationFailed: "\u30C8\u30E9\u30F3\u30B9\u30DD\u30FC\u30C8\u8A8D\u8A3C\u3001\u30EA\u30AF\u30A8\u30B9\u30C8\u3068\u306E\u7D10\u4ED8\u3051\u3001\u307E\u305F\u306F\u30EA\u30D7\u30EC\u30A4\u691C\u8A3C\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
107
+ crypto_transportKeyExchangeRequestFailed: "\u901A\u4FE1\u306E\u9375\u4EA4\u63DB\u30EA\u30AF\u30A8\u30B9\u30C8\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
108
+ crypto_transportKeyExchangeHttpFailed: "\u901A\u4FE1\u306E\u9375\u4EA4\u63DB\u3067HTTP {status}\u304C\u8FD4\u3055\u308C\u307E\u3057\u305F",
109
+ crypto_transportKeyExchangeInvalidJson: "\u901A\u4FE1\u306E\u9375\u4EA4\u63DB\u30EC\u30B9\u30DD\u30F3\u30B9\u304C\u6709\u52B9\u306AJSON\u3067\u306F\u3042\u308A\u307E\u305B\u3093",
110
+ crypto_transportKeyExchangeMissingFields: "\u901A\u4FE1\u306E\u9375\u4EA4\u63DB\u30EC\u30B9\u30DD\u30F3\u30B9\u306B\u5FC5\u9808\u30D5\u30A3\u30FC\u30EB\u30C9\u304C\u3042\u308A\u307E\u305B\u3093",
111
+ crypto_transportClientNotInitialized: "\u901A\u4FE1\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8\u306E\u9375\u4EA4\u63DB\u304C\u5B8C\u4E86\u3057\u3066\u3044\u307E\u305B\u3093",
112
+ crypto_transportClientNotRegistered: "\u901A\u4FE1\u30AF\u30E9\u30A4\u30A2\u30F3\u30C8\u304C\u767B\u9332\u3055\u308C\u3066\u3044\u306A\u3044\u304B\u3001\u6709\u52B9\u671F\u9650\u304C\u5207\u308C\u3066\u3044\u307E\u3059",
113
+ crypto_transportRequestEncryptFailed: "\u901A\u4FE1\u30EA\u30AF\u30A8\u30B9\u30C8\u672C\u6587\u306E\u6697\u53F7\u5316\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
114
+ crypto_transportRequestDecryptFailed: "\u901A\u4FE1\u30EA\u30AF\u30A8\u30B9\u30C8\u672C\u6587\u306E\u5FA9\u53F7\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
115
+ crypto_transportResponseEncryptFailed: "\u901A\u4FE1\u30EC\u30B9\u30DD\u30F3\u30B9\u672C\u6587\u306E\u6697\u53F7\u5316\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
116
+ crypto_transportResponseDecryptFailed: "\u901A\u4FE1\u30EC\u30B9\u30DD\u30F3\u30B9\u672C\u6587\u306E\u5FA9\u53F7\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
117
+ crypto_transportSessionKeyEncryptFailed: "\u901A\u4FE1\u30BB\u30C3\u30B7\u30E7\u30F3\u9375\u306E\u6697\u53F7\u5316\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
118
+ crypto_transportSessionKeyDecryptFailed: "\u901A\u4FE1\u30BB\u30C3\u30B7\u30E7\u30F3\u9375\u306E\u5FA9\u53F7\u306B\u5931\u6557\u3057\u307E\u3057\u305F",
119
+ 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
+ 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",
122
+ 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
+ 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"
37
124
  };
38
125
 
39
126
  // messages/zh-CN.json
@@ -66,13 +153,41 @@ var zh_CN_default = {
66
153
  crypto_passwordHashEmpty: "\u5BC6\u7801\u548C\u54C8\u5E0C\u503C\u4E0D\u80FD\u4E3A\u7A7A",
67
154
  crypto_passwordHashFailed: "\u5BC6\u7801\u54C8\u5E0C\u5931\u8D25",
68
155
  crypto_hashFormatInvalid: "\u65E0\u6548\u7684\u54C8\u5E0C\u683C\u5F0F",
69
- crypto_passwordVerifyFailed: "\u5BC6\u7801\u9A8C\u8BC1\u5931\u8D25"
156
+ crypto_passwordVerifyFailed: "\u5BC6\u7801\u9A8C\u8BC1\u5931\u8D25",
157
+ crypto_transportClientKeyGenerateFailed: "\u4F20\u8F93\u52A0\u5BC6\u5BA2\u6237\u7AEF\u5BC6\u94A5\u751F\u6210\u5931\u8D25",
158
+ crypto_transportServerKeyRingInvalid: "\u670D\u52A1\u7AEF\u4F20\u8F93\u5BC6\u94A5\u73AF\u7F3A\u5931\u6216\u65E0\u6548\u3002\u8BF7\u68C0\u67E5\u79C1\u6709\u73AF\u5883\u53D8\u91CF HAI_CRYPTO_TRANSPORT_SERVERKEYRING \u662F\u5426\u4E3A\u5B8C\u6574\u6709\u6548\u7684 JSON\uFF0C\u5E76\u5411\u6240\u6709\u8282\u70B9\u5206\u53D1\u540C\u4E00\u5BC6\u94A5\u73AF\u3002",
159
+ crypto_transportServerKeyUnknown: "\u670D\u52A1\u7AEF\u4F20\u8F93\u5BC6\u94A5\u7248\u672C\u672A\u77E5\u6216\u5DF2\u64A4\u9500",
160
+ crypto_transportKeyStoreRequired: "\u4F20\u8F93\u52A0\u5BC6\u5FC5\u987B\u663E\u5F0F\u63D0\u4F9B\u5BA2\u6237\u7AEF\u516C\u94A5\u5B58\u50A8",
161
+ crypto_transportKeyStoreTtlInvalid: "\u516C\u94A5\u5B58\u50A8 TTL \u5FC5\u987B\u4E3A 1 \u5230 2147483647 \u4E4B\u95F4\u7684\u6574\u6570\u79D2",
162
+ crypto_transportKeyStoreCapacityInvalid: "\u516C\u94A5\u5B58\u50A8\u5BB9\u91CF\u5FC5\u987B\u4E3A\u6B63\u5B89\u5168\u6574\u6570",
163
+ crypto_transportKeyStoreCapacityMismatch: "\u516C\u94A5\u5B58\u50A8\u7684\u5171\u4EAB\u5BB9\u91CF\u914D\u7F6E\u4E0D\u4E00\u81F4",
164
+ crypto_transportKeyStoreCapacityReached: "\u516C\u94A5\u5B58\u50A8\u5BB9\u91CF\u5DF2\u6EE1",
165
+ crypto_transportKeyStoreOperationFailed: "\u516C\u94A5\u5B58\u50A8\u64CD\u4F5C\u5931\u8D25",
166
+ crypto_transportAuthenticationFailed: "\u4F20\u8F93\u6D88\u606F\u8BA4\u8BC1\u3001\u8BF7\u6C42\u7ED1\u5B9A\u6216\u9632\u91CD\u653E\u6821\u9A8C\u5931\u8D25",
167
+ crypto_transportKeyExchangeRequestFailed: "\u4F20\u8F93\u52A0\u5BC6\u5BC6\u94A5\u534F\u5546\u8BF7\u6C42\u5931\u8D25",
168
+ crypto_transportKeyExchangeHttpFailed: "\u4F20\u8F93\u52A0\u5BC6\u5BC6\u94A5\u534F\u5546\u8FD4\u56DE HTTP {status}",
169
+ crypto_transportKeyExchangeInvalidJson: "\u4F20\u8F93\u52A0\u5BC6\u5BC6\u94A5\u534F\u5546\u54CD\u5E94\u4E0D\u662F\u6709\u6548 JSON",
170
+ crypto_transportKeyExchangeMissingFields: "\u4F20\u8F93\u52A0\u5BC6\u5BC6\u94A5\u534F\u5546\u54CD\u5E94\u7F3A\u5C11\u5FC5\u8981\u5B57\u6BB5",
171
+ crypto_transportClientNotInitialized: "\u4F20\u8F93\u52A0\u5BC6\u5BA2\u6237\u7AEF\u5C1A\u672A\u5B8C\u6210\u5BC6\u94A5\u534F\u5546",
172
+ crypto_transportClientNotRegistered: "\u4F20\u8F93\u52A0\u5BC6\u5BA2\u6237\u7AEF\u672A\u6CE8\u518C\u6216\u5DF2\u8FC7\u671F",
173
+ crypto_transportRequestEncryptFailed: "\u4F20\u8F93\u52A0\u5BC6\u8BF7\u6C42\u4F53\u52A0\u5BC6\u5931\u8D25",
174
+ crypto_transportRequestDecryptFailed: "\u4F20\u8F93\u52A0\u5BC6\u8BF7\u6C42\u4F53\u89E3\u5BC6\u5931\u8D25",
175
+ crypto_transportResponseEncryptFailed: "\u4F20\u8F93\u52A0\u5BC6\u54CD\u5E94\u4F53\u52A0\u5BC6\u5931\u8D25",
176
+ crypto_transportResponseDecryptFailed: "\u4F20\u8F93\u52A0\u5BC6\u54CD\u5E94\u4F53\u89E3\u5BC6\u5931\u8D25",
177
+ crypto_transportSessionKeyEncryptFailed: "\u4F20\u8F93\u52A0\u5BC6\u4F1A\u8BDD\u5BC6\u94A5\u52A0\u5BC6\u5931\u8D25",
178
+ crypto_transportSessionKeyDecryptFailed: "\u4F20\u8F93\u52A0\u5BC6\u4F1A\u8BDD\u5BC6\u94A5\u89E3\u5BC6\u5931\u8D25",
179
+ crypto_transportBodyLimitInvalid: "\u4F20\u8F93\u660E\u6587\u5BB9\u91CF\u5FC5\u987B\u662F 1024 \u5230 67108864 \u5B57\u8282\u4E4B\u95F4\u7684\u6574\u6570",
180
+ 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\u5E76\u5237\u65B0\u91CD\u8BD5\uFF1B\u82E5\u4ECD\u5931\u8D25\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458\u68C0\u67E5\u670D\u52A1\u5668\u65F6\u95F4\u3002",
182
+ 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
+ crypto_transportBusy: "\u6750\u6599\u5904\u7406\u7E41\u5FD9\uFF0C\u8BF7\u7A0D\u540E\u91CD\u8BD5"
70
184
  };
71
185
 
72
186
  // src/crypto-i18n.ts
73
187
  var cryptoM = core.i18n.createMessageGetter({
74
188
  "zh-CN": zh_CN_default,
75
- "en-US": en_US_default
189
+ "en-US": en_US_default,
190
+ "ja-JP": ja_JP_default
76
191
  });
77
192
  var CryptoErrorInfo = {
78
193
  INVALID_INPUT: "002:400",
@@ -86,11 +201,18 @@ var CryptoErrorInfo = {
86
201
  VERIFY_FAILED: "024:500",
87
202
  HASH_FAILED: "040:500",
88
203
  HMAC_FAILED: "041:500",
89
- INVALID_IV: "060:400"
204
+ INVALID_IV: "060:400",
205
+ TRANSPORT_PAYLOAD_TOO_LARGE: "070:413",
206
+ TRANSPORT_TIME_INVALID: "071:400",
207
+ TRANSPORT_BUSY: "072:503"
90
208
  };
91
209
  var HaiCryptoError = core.error.buildHaiErrorsDef("crypto", CryptoErrorInfo);
92
210
 
93
211
  // src/crypto-password.ts
212
+ var MIN_ITERATIONS = 1;
213
+ var MAX_ITERATIONS = 1e6;
214
+ var MIN_SALT_LENGTH = 8;
215
+ var MAX_SALT_LENGTH = 128;
94
216
  function generateSalt(length) {
95
217
  const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
96
218
  const randomBytes = new Uint8Array(length);
@@ -133,6 +255,18 @@ function createPasswordFunctions(deps) {
133
255
  cryptoM("crypto_passwordEmpty")
134
256
  );
135
257
  }
258
+ if (!Number.isInteger(iterations) || iterations < MIN_ITERATIONS || iterations > MAX_ITERATIONS) {
259
+ return err(
260
+ HaiCryptoError.INVALID_INPUT,
261
+ cryptoM("crypto_hashFormatInvalid")
262
+ );
263
+ }
264
+ if (!Number.isInteger(saltLength) || saltLength < MIN_SALT_LENGTH || saltLength > MAX_SALT_LENGTH) {
265
+ return err(
266
+ HaiCryptoError.INVALID_INPUT,
267
+ cryptoM("crypto_hashFormatInvalid")
268
+ );
269
+ }
136
270
  const salt = generateSalt(saltLength);
137
271
  const hashResult = iterateHash(hashOps, password, salt, iterations);
138
272
  if (!hashResult.success) {
@@ -176,7 +310,7 @@ function createPasswordFunctions(deps) {
176
310
  const storedIterations = Number.parseInt(parts[2], 10);
177
311
  const salt = parts[3];
178
312
  const storedHash = parts[4];
179
- if (Number.isNaN(storedIterations) || !salt || !storedHash) {
313
+ if (Number.isNaN(storedIterations) || storedIterations < MIN_ITERATIONS || storedIterations > MAX_ITERATIONS || !salt || !storedHash) {
180
314
  return err(
181
315
  HaiCryptoError.INVALID_INPUT,
182
316
  cryptoM("crypto_hashFormatInvalid")
@@ -197,6 +331,13 @@ function createPasswordFunctions(deps) {
197
331
  }
198
332
  };
199
333
  }
334
+ var SM2_PUBLIC_KEY_REGEX = /^(?:04)?[0-9a-f]{128}$/i;
335
+ function isValidSM2PublicKey(publicKey) {
336
+ if (typeof publicKey !== "string" || publicKey.length !== 128 && publicKey.length !== 130 || !SM2_PUBLIC_KEY_REGEX.test(publicKey))
337
+ return false;
338
+ const encoded = publicKey.length === 128 ? `04${publicKey}` : publicKey;
339
+ return smCrypto.sm2.verifyPublicKey(encoded) === true;
340
+ }
200
341
 
201
342
  // src/crypto-utils.ts
202
343
  function isBase64(str) {
@@ -220,6 +361,7 @@ function base64ToHex(base64) {
220
361
 
221
362
  // src/crypto-sm2.ts
222
363
  var { sm2 } = smCrypto;
364
+ var SM2_PRIVATE_KEY_REGEX = /^[0-9a-f]{64}$/i;
223
365
  function createSM2() {
224
366
  return {
225
367
  /**
@@ -261,7 +403,7 @@ function createSM2() {
261
403
  );
262
404
  }
263
405
  try {
264
- const key = publicKey.startsWith("04") ? publicKey : `04${publicKey}`;
406
+ const key = publicKey.length === 130 ? publicKey : `04${publicKey}`;
265
407
  const encrypted = sm2.doEncrypt(data, key, cipherMode);
266
408
  if (!encrypted) {
267
409
  return err(
@@ -375,7 +517,7 @@ function createSM2() {
375
517
  );
376
518
  }
377
519
  try {
378
- const key = publicKey.startsWith("04") ? publicKey : `04${publicKey}`;
520
+ const key = publicKey.length === 130 ? publicKey : `04${publicKey}`;
379
521
  const isValid = sm2.doVerifySignature(data, signature, key, { hash, userId });
380
522
  return ok(!!isValid);
381
523
  } catch (error) {
@@ -387,18 +529,16 @@ function createSM2() {
387
529
  }
388
530
  },
389
531
  /**
390
- * 校验公钥格式是否合法
532
+ * 校验公钥编码及曲线点是否合法
391
533
  *
392
534
  * 合法格式:128 字符十六进制(无前缀)或 130 字符(含 04 前缀)。
393
535
  *
394
536
  * @param key - 待校验公钥
395
- * @returns 格式合法返回 true
537
+ * @returns 编码有效且为 SM2 曲线上的非无穷远点时返回 true
538
+ * @example `crypto.asymmetric.isValidPublicKey(clientPublicKey)`
396
539
  */
397
540
  isValidPublicKey(key) {
398
- if (!key || typeof key !== "string")
399
- return false;
400
- const cleanKey = key.startsWith("04") ? key.slice(2) : key;
401
- return /^[0-9a-f]{128}$/i.test(cleanKey);
541
+ return isValidSM2PublicKey(key);
402
542
  },
403
543
  /**
404
544
  * 校验私钥格式是否合法
@@ -411,7 +551,7 @@ function createSM2() {
411
551
  isValidPrivateKey(key) {
412
552
  if (!key || typeof key !== "string")
413
553
  return false;
414
- return /^[0-9a-f]{64}$/i.test(key);
554
+ return SM2_PRIVATE_KEY_REGEX.test(key);
415
555
  }
416
556
  };
417
557
  }
@@ -527,7 +667,8 @@ function stringToBytes(str) {
527
667
  const encoder = new TextEncoder();
528
668
  return Array.from(encoder.encode(str));
529
669
  }
530
- var { sm3: sm32, sm4 } = smCrypto;
670
+ var { sm4 } = smCrypto;
671
+ var SM4_HEX_32_REGEX = /^[0-9a-f]{32}$/i;
531
672
  function createSM4() {
532
673
  return {
533
674
  /** 生成随机密钥(16 字节 = 32 个十六进制字符) */
@@ -541,45 +682,44 @@ function createSM4() {
541
682
  /**
542
683
  * SM4 对称加密
543
684
  *
544
- * 支持 ECB(默认)和 CBC 两种模式,使用 PKCS#7 填充。
545
- * CBC 模式需提供合法 IV。
685
+ * 默认使用 CBC 并自动生成随机 IV,返回结构化字段。
686
+ *
687
+ * ⚠️ 安全警告:ECB 模式会让相同明文块产生相同密文块,泄漏结构信息。
688
+ * 新数据使用默认 CBC,或每次显式传入新的随机 IV;CBC 仍需独立完整性认证,HTTP 使用 transport。
546
689
  *
547
690
  * @param data - 待加密明文
548
691
  * @param key - 密钥(32 字符十六进制)
549
692
  * @param options - 加密模式/IV/输出格式
550
- * @returns 成功时返回密文;失败时返回 INVALID_KEY/INVALID_IV/ENCRYPTION_FAILED
693
+ * @returns 成功时返回结构化密文;失败时返回 INVALID_KEY/INVALID_IV/ENCRYPTION_FAILED
551
694
  */
552
695
  encrypt(data, key, options = {}) {
553
696
  const {
554
- mode = "ecb",
697
+ mode = "cbc",
555
698
  iv,
556
699
  outputFormat = "hex"
557
700
  } = options;
701
+ const actualIv = mode === "cbc" ? iv ?? this.generateIV() : void 0;
558
702
  if (!this.isValidKey(key)) {
559
703
  return err(
560
704
  HaiCryptoError.INVALID_KEY,
561
705
  cryptoM("crypto_sm4KeyInvalid")
562
706
  );
563
707
  }
564
- if (mode === "cbc" && !iv) {
565
- return err(
566
- HaiCryptoError.INVALID_IV,
567
- cryptoM("crypto_sm4CbcNeedIv")
568
- );
569
- }
570
- if (mode === "cbc" && iv && !this.isValidIV(iv)) {
571
- return err(
572
- HaiCryptoError.INVALID_IV,
573
- cryptoM("crypto_sm4IvInvalid")
574
- );
708
+ if (mode === "cbc") {
709
+ if (!actualIv || !this.isValidIV(actualIv)) {
710
+ return err(
711
+ HaiCryptoError.INVALID_IV,
712
+ cryptoM("crypto_sm4IvInvalid")
713
+ );
714
+ }
575
715
  }
576
716
  try {
577
717
  const sm4Options = {
578
718
  mode,
579
719
  padding: "pkcs#7"
580
720
  };
581
- if (mode === "cbc" && iv) {
582
- sm4Options.iv = iv;
721
+ if (mode === "cbc" && actualIv) {
722
+ sm4Options.iv = actualIv;
583
723
  }
584
724
  const encrypted = sm4.encrypt(data, key, sm4Options);
585
725
  if (!encrypted) {
@@ -588,10 +728,12 @@ function createSM4() {
588
728
  cryptoM("crypto_sm4EncryptEmpty")
589
729
  );
590
730
  }
591
- if (outputFormat === "base64") {
592
- return ok(hexToBase64(encrypted));
593
- }
594
- return ok(encrypted);
731
+ return ok({
732
+ mode,
733
+ ciphertext: encodeCiphertext(encrypted, outputFormat),
734
+ ...actualIv ? { iv: actualIv } : {},
735
+ encoding: outputFormat
736
+ });
595
737
  } catch (error) {
596
738
  return err(
597
739
  HaiCryptoError.ENCRYPTION_FAILED,
@@ -603,33 +745,28 @@ function createSM4() {
603
745
  /**
604
746
  * SM4 对称解密
605
747
  *
606
- * 自动检测 base64 格式输入并转换为 hex。
607
- * 解密模式和 IV 需与加密时一致。
748
+ * 使用结构化字段解密;解密模式、IV 与密文编码均来自 payload。
608
749
  *
609
- * @param ciphertext - 密文(hex 或 base64)
750
+ * @param payload - 结构化密文
610
751
  * @param key - 密钥(32 字符十六进制)
611
- * @param options - 解密模式/IV
612
752
  * @returns 成功时返回明文;失败时返回 INVALID_KEY/INVALID_IV/DECRYPTION_FAILED
613
753
  */
614
- decrypt(ciphertext, key, options = {}) {
615
- const { mode = "ecb", iv } = options;
754
+ decrypt(payload, key) {
755
+ const { mode, iv } = payload;
616
756
  if (!this.isValidKey(key)) {
617
757
  return err(
618
758
  HaiCryptoError.INVALID_KEY,
619
759
  cryptoM("crypto_sm4KeyInvalid")
620
760
  );
621
761
  }
622
- if (mode === "cbc" && !iv) {
762
+ if (mode === "cbc" && (!iv || !this.isValidIV(iv))) {
623
763
  return err(
624
764
  HaiCryptoError.INVALID_IV,
625
- cryptoM("crypto_sm4CbcNeedIv")
765
+ cryptoM("crypto_sm4IvInvalid")
626
766
  );
627
767
  }
628
768
  try {
629
- let input = ciphertext;
630
- if (isBase64(ciphertext)) {
631
- input = base64ToHex(ciphertext);
632
- }
769
+ const input = decodeCiphertext(payload);
633
770
  const sm4Options = {
634
771
  mode,
635
772
  padding: "pkcs#7"
@@ -658,7 +795,7 @@ function createSM4() {
658
795
  *
659
796
  * @param data - 待加密明文
660
797
  * @param key - 密钥(32 字符十六进制)
661
- * @returns 成功时返回 { ciphertext, iv };失败时同 encrypt
798
+ * @returns 成功时返回完整 { mode, ciphertext, iv, encoding };失败时同 encrypt
662
799
  */
663
800
  encryptWithIV(data, key) {
664
801
  const iv = this.generateIV();
@@ -666,41 +803,33 @@ function createSM4() {
666
803
  if (!result.success) {
667
804
  return result;
668
805
  }
669
- return ok({ ciphertext: result.data, iv });
806
+ return ok({
807
+ mode: "cbc",
808
+ ciphertext: result.data.ciphertext,
809
+ iv,
810
+ encoding: result.data.encoding
811
+ });
670
812
  },
671
813
  /**
672
814
  * 带 IV 解密(CBC 模式)
673
815
  *
816
+ * 这是不含认证的底层 primitive;传输数据必须经 transport 验签及 MAC 后再调用,禁止直接用于 HTTP 解密。
817
+ *
674
818
  * @param ciphertext - 密文
675
819
  * @param key - 密钥
676
820
  * @param iv - 加密时使用的 IV
677
821
  * @returns 成功时返回明文;失败时同 decrypt
678
822
  */
679
823
  decryptWithIV(ciphertext, key, iv) {
680
- return this.decrypt(ciphertext, key, { mode: "cbc", iv });
681
- },
682
- /**
683
- * 从密码和盐值派生密钥
684
- *
685
- * 内部使用 SM3 哈希(password + salt) 取前 32 字符作为密钥。
686
- * 注意:此为简单派生,不适用于高安全场景。
687
- *
688
- * @param password - 密码
689
- * @param salt - 盐值
690
- * @returns 32 字符十六进制密钥
691
- */
692
- deriveKey(password, salt) {
693
- const combined = password + salt;
694
- const hash = sm32(combined);
695
- return hash.slice(0, 32);
824
+ return this.decrypt({ mode: "cbc", ciphertext, iv, encoding: "hex" }, key);
696
825
  },
697
826
  /** 校验密钥格式是否合法(32 字符十六进制) */
698
827
  isValidKey(key) {
699
- return /^[0-9a-f]{32}$/i.test(key);
828
+ return SM4_HEX_32_REGEX.test(key);
700
829
  },
701
830
  /** 校验 IV 格式是否合法(32 字符十六进制) */
702
831
  isValidIV(iv) {
703
- return /^[0-9a-f]{32}$/i.test(iv);
832
+ return SM4_HEX_32_REGEX.test(iv);
704
833
  }
705
834
  };
706
835
  }
@@ -709,6 +838,674 @@ function generateRandomHex(byteLength) {
709
838
  crypto.getRandomValues(bytes);
710
839
  return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
711
840
  }
841
+ function encodeCiphertext(ciphertext, encoding) {
842
+ return encoding === "base64" ? hexToBase64(ciphertext) : ciphertext;
843
+ }
844
+ function decodeCiphertext(payload) {
845
+ return payload.encoding === "base64" ? base64ToHex(payload.ciphertext) : payload.ciphertext;
846
+ }
847
+
848
+ // src/transport/crypto-transport-body.ts
849
+ var TRANSPORT_PLAINTEXT_RANGE = Object.freeze({ min: 1024, max: 64 * 1024 * 1024 });
850
+ var TRANSPORT_BODY_LIMITS = Object.freeze({
851
+ /** 密钥协商 JSON 的 UTF-8 总字节数。 */
852
+ keyExchangeBytes: 16384,
853
+ /** GET/HEAD 请求及无正文响应的认证头封装 UTF-8 上限。 */
854
+ envelopeHeaderBytes: 16384,
855
+ /** 单个协商公钥的 UTF-8 字节数。 */
856
+ publicKeyBytes: 4096,
857
+ /** 解密后的请求/加密前的响应明文字节数。 */
858
+ plaintextBytes: 1048576,
859
+ /** 密文 JSON 的 UTF-8 总字节数(2 MiB + 32 KiB,含 CBC 填充与全部认证字段)。 */
860
+ encryptedPayloadBytes: 2129920
861
+ });
862
+ function resolveTransportBodyLimits(maxPlaintextBytes, options = {}) {
863
+ const required = options.requiredPlaintextBytes;
864
+ if (required !== void 0 && (!Number.isSafeInteger(required) || required < TRANSPORT_PLAINTEXT_RANGE.min || required > TRANSPORT_PLAINTEXT_RANGE.max))
865
+ throw new RangeError(cryptoM("crypto_transportBodyLimitInvalid"));
866
+ if (maxPlaintextBytes === void 0)
867
+ maxPlaintextBytes = required ?? TRANSPORT_BODY_LIMITS.plaintextBytes;
868
+ if (!Number.isSafeInteger(maxPlaintextBytes) || maxPlaintextBytes < TRANSPORT_PLAINTEXT_RANGE.min || maxPlaintextBytes > TRANSPORT_PLAINTEXT_RANGE.max)
869
+ throw new RangeError(cryptoM("crypto_transportBodyLimitInvalid"));
870
+ if (required !== void 0 && maxPlaintextBytes < required)
871
+ throw new RangeError(cryptoM("crypto_transportBodyLimitConflict", { params: { limit: maxPlaintextBytes, required } }));
872
+ return Object.freeze({ ...TRANSPORT_BODY_LIMITS, plaintextBytes: maxPlaintextBytes, encryptedPayloadBytes: 2 * maxPlaintextBytes + 32768 });
873
+ }
874
+ var TransportBodyTooLargeError = class extends RangeError {
875
+ code = HaiCryptoError.TRANSPORT_PAYLOAD_TOO_LARGE.code;
876
+ direction;
877
+ stage;
878
+ limitBytes;
879
+ actualBytes;
880
+ constructor(limitBytes, actualBytes, options = {}) {
881
+ super(cryptoM("crypto_transportBodyTooLarge"));
882
+ this.name = "TransportBodyTooLargeError";
883
+ this.limitBytes = limitBytes;
884
+ this.actualBytes = actualBytes;
885
+ this.direction = options.direction ?? "request";
886
+ this.stage = options.stage ?? "plaintext";
887
+ }
888
+ };
889
+ async function readTransportBody(stream, options) {
890
+ if (!Number.isSafeInteger(options.maxBytes) || options.maxBytes < 0)
891
+ throw new RangeError("Invalid transport body byte limit");
892
+ if (!stream) {
893
+ options.signal?.throwIfAborted();
894
+ return "";
895
+ }
896
+ const reader = stream.getReader();
897
+ const signal = options.signal;
898
+ const decoder = new TextDecoder("utf-8", { fatal: true });
899
+ let completed = false;
900
+ let failure;
901
+ let onAbort;
902
+ try {
903
+ signal?.throwIfAborted();
904
+ const aborted = signal ? new Promise((_resolve, reject) => {
905
+ onAbort = () => reject(signal.reason);
906
+ signal.addEventListener("abort", onAbort, { once: true });
907
+ }) : void 0;
908
+ let bytes = 0;
909
+ let text = "";
910
+ while (true) {
911
+ const next = reader.read();
912
+ const chunk = await (aborted ? Promise.race([next, aborted]) : next);
913
+ if (chunk.done) {
914
+ completed = true;
915
+ return text + decoder.decode();
916
+ }
917
+ bytes += chunk.value.byteLength;
918
+ if (bytes > options.maxBytes)
919
+ throw new TransportBodyTooLargeError(options.maxBytes, bytes, options);
920
+ text += decoder.decode(chunk.value, { stream: true });
921
+ }
922
+ } catch (error) {
923
+ failure = error;
924
+ throw error;
925
+ } finally {
926
+ if (onAbort)
927
+ signal?.removeEventListener("abort", onAbort);
928
+ if (!completed) {
929
+ try {
930
+ void reader.cancel(failure).catch(() => {
931
+ });
932
+ } catch {
933
+ }
934
+ }
935
+ reader.releaseLock();
936
+ }
937
+ }
938
+ function createTransportBodyPolicy(maxPlaintextBytes, routes = {}) {
939
+ const ordinary = resolveTransportBodyLimits(maxPlaintextBytes);
940
+ const entries = Object.entries(routes);
941
+ if (entries.length > 100 || entries.some(([path]) => !path.startsWith("/") || path.includes("?") || path.includes("#") || path.split("/").some((part) => part.includes("*") && part !== "*")))
942
+ throw new RangeError(cryptoM("crypto_transportBodyLimitInvalid"));
943
+ const configured = entries.map(([path, bytes]) => ({ parts: path.split("/"), limits: resolveTransportBodyLimits(bytes) }));
944
+ const maximum = resolveTransportBodyLimits(Math.max(ordinary.plaintextBytes, ...configured.map((route) => route.limits.plaintextBytes)));
945
+ let small = 0;
946
+ let large = 0;
947
+ return {
948
+ maximum,
949
+ acquire(pathname) {
950
+ const parts = pathname.split("/");
951
+ const matching = configured.filter((route) => route.parts.length === parts.length && route.parts.every((part, index) => part === "*" || part === parts[index]));
952
+ const limits = matching.length ? matching.reduce((left, right) => left.limits.plaintextBytes < right.limits.plaintextBytes ? left : right).limits : ordinary;
953
+ const heavy = matching.length > 0 && limits.plaintextBytes > TRANSPORT_BODY_LIMITS.plaintextBytes;
954
+ if (heavy ? large >= 2 : small >= 16)
955
+ return null;
956
+ if (heavy)
957
+ large++;
958
+ else
959
+ small++;
960
+ let released = false;
961
+ return { limits, release() {
962
+ if (released)
963
+ return;
964
+ released = true;
965
+ if (heavy)
966
+ large--;
967
+ else
968
+ small--;
969
+ } };
970
+ }
971
+ };
972
+ }
973
+
974
+ // src/transport/crypto-transport-types.ts
975
+ var TRANSPORT_PROTOCOL = {
976
+ /** 客户端 ID 请求头名。 */
977
+ CLIENT_ID_HEADER: "X-Client-Id",
978
+ /** GET/HEAD 等无正文请求的认证封装;禁止与正文封装同时出现。 */
979
+ ENVELOPE_HEADER: "X-Hai-Transport",
980
+ /** 唯一支持的认证协议版本;旧未认证格式拒绝。 */
981
+ VERSION: 2,
982
+ /** 请求允许的过去时间窗口(毫秒)。 */
983
+ REQUEST_WINDOW_MS: 3e5,
984
+ /** 节点时钟允许的最大未来偏差(毫秒)。 */
985
+ CLOCK_SKEW_MS: 3e4,
986
+ /** 标识响应体已加密的响应头名。 */
987
+ ENCRYPTED_HEADER: "X-Encrypted",
988
+ /** `X-Encrypted` 响应头的开启值。 */
989
+ ENCRYPTED_HEADER_VALUE: "true",
990
+ /** 密钥协商端点的默认子路径(相对于挂载前缀)。 */
991
+ DEFAULT_KEY_EXCHANGE_PATH: "/_hai/key-exchange"
992
+ };
993
+
994
+ // src/transport/crypto-transport-codec.ts
995
+ function authenticatedText(payload) {
996
+ const c = payload.context;
997
+ return JSON.stringify([
998
+ "hai-transport",
999
+ c.version,
1000
+ payload.direction,
1001
+ c.serverKeyId,
1002
+ c.clientId,
1003
+ c.requestId,
1004
+ c.issuedAt,
1005
+ c.method,
1006
+ c.pathHash,
1007
+ payload.status,
1008
+ payload.encryptedKey,
1009
+ payload.ciphertext,
1010
+ payload.iv
1011
+ ]);
1012
+ }
1013
+ function transportAuthenticationFailure() {
1014
+ return err(HaiCryptoError.INVALID_INPUT, cryptoM("crypto_transportAuthenticationFailed"));
1015
+ }
1016
+ function hashTransportPath(service, path) {
1017
+ try {
1018
+ if (!path.startsWith("/") || new TextEncoder().encode(path).byteLength > 8192)
1019
+ return transportAuthenticationFailure();
1020
+ const digest = service.hash.hash(path);
1021
+ return digest.success && /^[a-f0-9]{64}$/.test(digest.data) ? digest : transportAuthenticationFailure();
1022
+ } catch {
1023
+ return transportAuthenticationFailure();
1024
+ }
1025
+ }
1026
+ function sealTransportPayload(service, plaintext, context, direction, status, recipientPublicKey, senderPrivateKey, limits = TRANSPORT_BODY_LIMITS) {
1027
+ try {
1028
+ const bytes = new TextEncoder().encode(plaintext).byteLength;
1029
+ if (bytes > limits.plaintextBytes)
1030
+ return err(HaiCryptoError.TRANSPORT_PAYLOAD_TOO_LARGE, cryptoM("crypto_transportBodyTooLarge"), new TransportBodyTooLargeError(limits.plaintextBytes, bytes, { direction }));
1031
+ const encryptionKey = service.symmetric.generateKey();
1032
+ const authenticationKey = service.symmetric.generateKey() + service.symmetric.generateKey();
1033
+ const encrypted = service.symmetric.encryptWithIV(plaintext, encryptionKey);
1034
+ const wrapped = service.asymmetric.encrypt(JSON.stringify([encryptionKey, authenticationKey]), recipientPublicKey);
1035
+ if (!encrypted.success || !wrapped.success)
1036
+ return transportAuthenticationFailure();
1037
+ const payload = { context: { ...context }, direction, status, encryptedKey: wrapped.data, ciphertext: encrypted.data.ciphertext, iv: encrypted.data.iv };
1038
+ const text = authenticatedText(payload);
1039
+ const tag = service.hash.hmac(text, authenticationKey);
1040
+ if (!tag.success)
1041
+ return transportAuthenticationFailure();
1042
+ const signature = service.asymmetric.sign(JSON.stringify([text, tag.data]), senderPrivateKey);
1043
+ if (!signature.success)
1044
+ return transportAuthenticationFailure();
1045
+ const authenticated = { ...payload, tag: tag.data, signature: signature.data };
1046
+ return isValidEncryptedPayload(authenticated, limits) ? ok(authenticated) : transportAuthenticationFailure();
1047
+ } catch {
1048
+ return transportAuthenticationFailure();
1049
+ }
1050
+ }
1051
+ function openTransportPayload(service, payload, recipientPrivateKey, senderPublicKey, limits = TRANSPORT_BODY_LIMITS) {
1052
+ try {
1053
+ if (!isValidEncryptedPayload(payload, limits))
1054
+ return transportAuthenticationFailure();
1055
+ const text = authenticatedText(payload);
1056
+ const verified = service.asymmetric.verify(JSON.stringify([text, payload.tag]), payload.signature, senderPublicKey);
1057
+ if (!verified.success || !verified.data)
1058
+ return transportAuthenticationFailure();
1059
+ const unwrapped = service.asymmetric.decrypt(payload.encryptedKey, recipientPrivateKey);
1060
+ if (!unwrapped.success)
1061
+ return transportAuthenticationFailure();
1062
+ const keys = JSON.parse(unwrapped.data);
1063
+ if (!Array.isArray(keys) || keys.length !== 2 || typeof keys[0] !== "string" || !/^[a-f0-9]{32}$/.test(keys[0]) || typeof keys[1] !== "string" || !/^[a-f0-9]{64}$/.test(keys[1])) {
1064
+ return transportAuthenticationFailure();
1065
+ }
1066
+ const tag = service.hash.hmac(text, keys[1]);
1067
+ if (!tag.success || !core.string.constantTimeEqual(tag.data, payload.tag))
1068
+ return transportAuthenticationFailure();
1069
+ const plaintext = service.symmetric.decryptWithIV(payload.ciphertext, keys[0], payload.iv);
1070
+ if (!plaintext.success)
1071
+ return transportAuthenticationFailure();
1072
+ const bytes = new TextEncoder().encode(plaintext.data).byteLength;
1073
+ if (bytes > limits.plaintextBytes)
1074
+ return err(HaiCryptoError.TRANSPORT_PAYLOAD_TOO_LARGE, cryptoM("crypto_transportBodyTooLarge"), new TransportBodyTooLargeError(limits.plaintextBytes, bytes, { direction: payload.direction }));
1075
+ return plaintext;
1076
+ } catch {
1077
+ return transportAuthenticationFailure();
1078
+ }
1079
+ }
1080
+ function isValidEncryptedPayload(value, limits = TRANSPORT_BODY_LIMITS) {
1081
+ if (!value || typeof value !== "object")
1082
+ return false;
1083
+ const p = value;
1084
+ const c = p.context;
1085
+ return Object.keys(p).length === 8 && !!c && typeof c === "object" && Object.keys(c).length === 7 && c.version === TRANSPORT_PROTOCOL.VERSION && typeof c.serverKeyId === "string" && /^[\w-]{1,64}$/.test(c.serverKeyId) && typeof c.clientId === "string" && /^[\w-]{1,128}$/.test(c.clientId) && typeof c.requestId === "string" && /^[a-f0-9]{32}$/.test(c.requestId) && Number.isSafeInteger(c.issuedAt) && c.issuedAt > 0 && typeof c.method === "string" && /^[A-Z]{1,16}$/.test(c.method) && typeof c.pathHash === "string" && /^[a-f0-9]{64}$/.test(c.pathHash) && (p.direction === "request" || p.direction === "response") && Number.isInteger(p.status) && (p.direction === "request" ? p.status === 0 : Number(p.status) >= 100 && Number(p.status) <= 599) && typeof p.encryptedKey === "string" && /^[a-f0-9]{1,8192}$/i.test(p.encryptedKey) && typeof p.ciphertext === "string" && p.ciphertext.length <= limits.encryptedPayloadBytes && p.ciphertext.length > 0 && p.ciphertext.length % 32 === 0 && /^[a-f0-9]+$/i.test(p.ciphertext) && typeof p.iv === "string" && /^[a-f0-9]{32}$/i.test(p.iv) && typeof p.tag === "string" && /^[a-f0-9]{64}$/.test(p.tag) && typeof p.signature === "string" && /^[a-f0-9]{128}$/i.test(p.signature);
1086
+ }
1087
+ function sameTransportContext(a, b) {
1088
+ return a.version === b.version && a.serverKeyId === b.serverKeyId && a.clientId === b.clientId && a.requestId === b.requestId && a.issuedAt === b.issuedAt && a.method === b.method && a.pathHash === b.pathHash;
1089
+ }
1090
+
1091
+ // src/transport/crypto-transport-error.ts
1092
+ var TransportTimeError = class extends Error {
1093
+ constructor(context) {
1094
+ super(cryptoM("crypto_transportTimeInvalid"));
1095
+ this.context = context;
1096
+ this.name = "TransportTimeError";
1097
+ }
1098
+ context;
1099
+ code = HaiCryptoError.TRANSPORT_TIME_INVALID.code;
1100
+ };
1101
+ async function createTransportErrorResponse(error, authenticated) {
1102
+ const status = error instanceof TransportTimeError ? 400 : error.direction === "request" ? 413 : 500;
1103
+ const body = JSON.stringify({ success: false, error: { code: error.code, message: error.message, httpStatus: status, details: error instanceof TransportTimeError ? void 0 : {
1104
+ direction: error.direction,
1105
+ stage: error.stage,
1106
+ limitBytes: error.limitBytes,
1107
+ actualBytes: error.actualBytes
1108
+ } } });
1109
+ if (authenticated) {
1110
+ const encrypted = await authenticated.manager.encryptResponse(authenticated.context, body, status);
1111
+ if (encrypted.success) {
1112
+ return Response.json(encrypted.data, { status, headers: { [TRANSPORT_PROTOCOL.ENCRYPTED_HEADER]: TRANSPORT_PROTOCOL.ENCRYPTED_HEADER_VALUE } });
1113
+ }
1114
+ }
1115
+ return new Response(body, { status, headers: { "Content-Type": "application/json; charset=utf-8" } });
1116
+ }
1117
+
1118
+ // src/transport/crypto-transport-client.ts
1119
+ function createTransportClient(options) {
1120
+ const baseFetch = options.fetch ?? fetch;
1121
+ const localLimits = options.maxPlaintextBytes === void 0 ? void 0 : resolveTransportBodyLimits(options.maxPlaintextBytes);
1122
+ let bodyLimits = localLimits ?? resolveTransportBodyLimits();
1123
+ let keyPair = null;
1124
+ let serverPublicKey = null;
1125
+ let serverKeyId = null;
1126
+ let clientId = null;
1127
+ let initPromise = null;
1128
+ async function doInit() {
1129
+ const kpResult = options.crypto.asymmetric.generateKeyPair();
1130
+ if (!kpResult.success)
1131
+ return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportClientKeyGenerateFailed"), kpResult.error);
1132
+ const localKeyPair = kpResult.data;
1133
+ if (new TextEncoder().encode(localKeyPair.publicKey).byteLength > TRANSPORT_BODY_LIMITS.publicKeyBytes)
1134
+ return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportClientKeyGenerateFailed"));
1135
+ const controller = new AbortController();
1136
+ const timeout = setTimeout(() => controller.abort(new DOMException("Key exchange timed out", "TimeoutError")), 3e4);
1137
+ try {
1138
+ let response;
1139
+ try {
1140
+ response = await baseFetch(options.keyExchangeUrl, {
1141
+ method: "POST",
1142
+ headers: { "Content-Type": "application/json" },
1143
+ body: JSON.stringify({ clientPublicKey: localKeyPair.publicKey }),
1144
+ signal: controller.signal
1145
+ });
1146
+ } catch (cause) {
1147
+ return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportKeyExchangeRequestFailed"), cause);
1148
+ }
1149
+ if (!response.ok) {
1150
+ void response.body?.cancel().catch(() => {
1151
+ });
1152
+ return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportKeyExchangeHttpFailed", { params: { status: response.status } }));
1153
+ }
1154
+ let body;
1155
+ try {
1156
+ body = JSON.parse(await readTransportBody(response.body, {
1157
+ maxBytes: TRANSPORT_BODY_LIMITS.keyExchangeBytes,
1158
+ signal: controller.signal
1159
+ }));
1160
+ } catch {
1161
+ return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportKeyExchangeInvalidJson"));
1162
+ }
1163
+ if (!isKeyExchangeResponse(body)) {
1164
+ return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportKeyExchangeMissingFields"));
1165
+ }
1166
+ try {
1167
+ const serverLimits = resolveTransportBodyLimits(body.maxPlaintextBytes);
1168
+ bodyLimits = resolveTransportBodyLimits(Math.min(localLimits?.plaintextBytes ?? serverLimits.plaintextBytes, serverLimits.plaintextBytes));
1169
+ } catch {
1170
+ return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportBodyLimitInvalid"));
1171
+ }
1172
+ keyPair = localKeyPair;
1173
+ serverPublicKey = body.serverPublicKey;
1174
+ serverKeyId = body.serverKeyId;
1175
+ clientId = body.clientId;
1176
+ return ok(void 0);
1177
+ } finally {
1178
+ clearTimeout(timeout);
1179
+ }
1180
+ }
1181
+ async function ensureReady() {
1182
+ if (clientId && serverPublicKey)
1183
+ return ok(void 0);
1184
+ if (!initPromise) {
1185
+ initPromise = doInit().then((result) => {
1186
+ if (!result.success)
1187
+ initPromise = null;
1188
+ return result;
1189
+ });
1190
+ }
1191
+ return initPromise;
1192
+ }
1193
+ function encryptBody(plaintext, context) {
1194
+ if (!serverPublicKey || !serverKeyId || !keyPair)
1195
+ return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportClientNotInitialized"));
1196
+ return sealTransportPayload(options.crypto, plaintext, context, "request", 0, serverPublicKey, keyPair.privateKey, bodyLimits);
1197
+ }
1198
+ function decryptPayload(payload) {
1199
+ if (!keyPair || !serverPublicKey)
1200
+ return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportClientNotInitialized"));
1201
+ return openTransportPayload(options.crypto, payload, keyPair.privateKey, serverPublicKey, bodyLimits);
1202
+ }
1203
+ const encryptedFetch = async (input, init) => {
1204
+ const requestInput = isRequestLike(input) ? input : void 0;
1205
+ const signal = init?.signal ?? requestInput?.signal ?? void 0;
1206
+ signal?.throwIfAborted();
1207
+ const targetUrl = typeof input === "string" ? input : input instanceof URL ? input.toString() : requestInput?.url ?? String(input);
1208
+ if (targetUrl === options.keyExchangeUrl)
1209
+ return baseFetch(input, init);
1210
+ let onAbort;
1211
+ let ready;
1212
+ try {
1213
+ ready = await (signal ? Promise.race([
1214
+ ensureReady(),
1215
+ new Promise((_resolve, reject) => {
1216
+ onAbort = () => reject(signal.reason);
1217
+ signal.addEventListener("abort", onAbort, { once: true });
1218
+ if (signal.aborted)
1219
+ onAbort();
1220
+ })
1221
+ ]) : ensureReady());
1222
+ } finally {
1223
+ if (onAbort)
1224
+ signal?.removeEventListener("abort", onAbort);
1225
+ }
1226
+ if (!ready.success)
1227
+ throw new Error(ready.error.message);
1228
+ const headers = new Headers(init?.headers ?? requestInput?.headers);
1229
+ headers.set(TRANSPORT_PROTOCOL.CLIENT_ID_HEADER, clientId);
1230
+ let rawBody = init?.body;
1231
+ if (rawBody == null && requestInput) {
1232
+ const method2 = (init?.method ?? requestInput.method).toUpperCase();
1233
+ if (method2 !== "GET" && method2 !== "HEAD") {
1234
+ const text = await readTransportBody(requestInput.body, { maxBytes: bodyLimits.plaintextBytes, signal });
1235
+ if (text)
1236
+ rawBody = text;
1237
+ }
1238
+ }
1239
+ const method = (init?.method ?? requestInput?.method ?? "GET").toUpperCase();
1240
+ const base = new URL(options.keyExchangeUrl, globalThis.location?.href ?? "http://localhost");
1241
+ const url = new URL(targetUrl, base);
1242
+ const pathHash = hashTransportPath(options.crypto, url.pathname + url.search);
1243
+ if (!pathHash.success)
1244
+ throw new Error(pathHash.error.message);
1245
+ const context = {
1246
+ version: TRANSPORT_PROTOCOL.VERSION,
1247
+ serverKeyId,
1248
+ clientId,
1249
+ requestId: options.crypto.symmetric.generateKey(),
1250
+ issuedAt: Date.now(),
1251
+ method,
1252
+ pathHash: pathHash.data
1253
+ };
1254
+ const plaintext = rawBody == null ? "" : await readTransportBody(new Response(rawBody).body, { maxBytes: bodyLimits.plaintextBytes, signal });
1255
+ const encResult = encryptBody(plaintext, context);
1256
+ if (!encResult.success)
1257
+ throw new Error(encResult.error.message);
1258
+ const serialized = JSON.stringify(encResult.data);
1259
+ if (new TextEncoder().encode(serialized).byteLength > bodyLimits.encryptedPayloadBytes)
1260
+ throw new TransportBodyTooLargeError(bodyLimits.encryptedPayloadBytes, new TextEncoder().encode(serialized).byteLength, { stage: "encrypted" });
1261
+ let nextBody;
1262
+ headers.delete(TRANSPORT_PROTOCOL.ENVELOPE_HEADER);
1263
+ headers.set(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER, TRANSPORT_PROTOCOL.ENCRYPTED_HEADER_VALUE);
1264
+ headers.delete("Content-Length");
1265
+ headers.delete("Content-Encoding");
1266
+ if (method === "GET" || method === "HEAD") {
1267
+ if (new TextEncoder().encode(serialized).byteLength > TRANSPORT_BODY_LIMITS.envelopeHeaderBytes)
1268
+ throw new TransportBodyTooLargeError(TRANSPORT_BODY_LIMITS.envelopeHeaderBytes, new TextEncoder().encode(serialized).byteLength, { stage: "header" });
1269
+ headers.set(TRANSPORT_PROTOCOL.ENVELOPE_HEADER, serialized);
1270
+ } else {
1271
+ nextBody = serialized;
1272
+ headers.set("Content-Type", "application/json");
1273
+ }
1274
+ const finalInput = requestInput ? requestInput.url : input;
1275
+ const finalInit = {
1276
+ ...init,
1277
+ method,
1278
+ headers,
1279
+ body: nextBody,
1280
+ cache: init?.cache ?? requestInput?.cache,
1281
+ credentials: init?.credentials ?? requestInput?.credentials,
1282
+ integrity: init?.integrity ?? requestInput?.integrity,
1283
+ keepalive: init?.keepalive ?? requestInput?.keepalive,
1284
+ mode: init?.mode ?? requestInput?.mode,
1285
+ redirect: init?.redirect ?? requestInput?.redirect,
1286
+ referrer: init?.referrer ?? requestInput?.referrer,
1287
+ referrerPolicy: init?.referrerPolicy ?? requestInput?.referrerPolicy,
1288
+ signal: init?.signal ?? requestInput?.signal
1289
+ };
1290
+ const response = await baseFetch(finalInput, finalInit);
1291
+ if (response.headers.get(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER) !== TRANSPORT_PROTOCOL.ENCRYPTED_HEADER_VALUE) {
1292
+ void response.body?.cancel().catch(() => {
1293
+ });
1294
+ if (response.status === 503)
1295
+ throw Object.assign(new Error(cryptoM("crypto_transportBusy")), { code: HaiCryptoError.TRANSPORT_BUSY.code, httpStatus: 503 });
1296
+ if (response.status === 413)
1297
+ throw new TransportBodyTooLargeError(bodyLimits.encryptedPayloadBytes, void 0, { stage: "encrypted" });
1298
+ throw new Error(cryptoM("crypto_transportResponseDecryptFailed"));
1299
+ }
1300
+ let payload;
1301
+ try {
1302
+ const envelope = response.headers.get(TRANSPORT_PROTOCOL.ENVELOPE_HEADER);
1303
+ if (envelope && response.body !== null) {
1304
+ void response.body.cancel().catch(() => {
1305
+ });
1306
+ throw new Error(cryptoM("crypto_transportResponseDecryptFailed"));
1307
+ }
1308
+ if (envelope && new TextEncoder().encode(envelope).byteLength > TRANSPORT_BODY_LIMITS.envelopeHeaderBytes)
1309
+ throw new TransportBodyTooLargeError(TRANSPORT_BODY_LIMITS.envelopeHeaderBytes, new TextEncoder().encode(envelope).byteLength, { direction: "response", stage: "header" });
1310
+ payload = JSON.parse(envelope ?? await readTransportBody(response.body, { maxBytes: bodyLimits.encryptedPayloadBytes, direction: "response", stage: "encrypted", signal }));
1311
+ } catch (error) {
1312
+ if (error instanceof TransportBodyTooLargeError)
1313
+ throw error;
1314
+ throw new Error(cryptoM("crypto_transportResponseDecryptFailed"), signal?.aborted ? { cause: signal.reason } : void 0);
1315
+ }
1316
+ if (!isValidEncryptedPayload(payload, bodyLimits) || payload.direction !== "response" || payload.status !== response.status || !sameTransportContext(payload.context, context)) {
1317
+ throw new Error(cryptoM("crypto_transportResponseDecryptFailed"));
1318
+ }
1319
+ const decResult = decryptPayload(payload);
1320
+ if (!decResult.success) {
1321
+ if (decResult.error.cause instanceof TransportBodyTooLargeError)
1322
+ throw decResult.error.cause;
1323
+ throw new Error(decResult.error.message);
1324
+ }
1325
+ if (new TextEncoder().encode(decResult.data).byteLength > bodyLimits.plaintextBytes)
1326
+ throw new TransportBodyTooLargeError(bodyLimits.plaintextBytes, new TextEncoder().encode(decResult.data).byteLength, { direction: "response" });
1327
+ if (response.status === 400) {
1328
+ let failure;
1329
+ try {
1330
+ failure = JSON.parse(decResult.data);
1331
+ } catch {
1332
+ }
1333
+ if (failure?.success === false && failure.error?.code === HaiCryptoError.TRANSPORT_TIME_INVALID.code)
1334
+ throw new TransportTimeError();
1335
+ }
1336
+ if (response.status === 500) {
1337
+ let failure;
1338
+ try {
1339
+ failure = JSON.parse(decResult.data);
1340
+ } catch {
1341
+ }
1342
+ const details = failure?.error?.details;
1343
+ if (failure?.success === false && failure.error?.code === "hai:crypto:070" && details?.direction === "response" && (details.stage === "plaintext" || details.stage === "encrypted" || details.stage === "header") && typeof details.limitBytes === "number" && Number.isSafeInteger(details.limitBytes) && details.limitBytes >= 0) {
1344
+ throw new TransportBodyTooLargeError(details.limitBytes, typeof details.actualBytes === "number" ? details.actualBytes : void 0, { direction: "response", stage: details.stage });
1345
+ }
1346
+ }
1347
+ const respHeaders = new Headers(response.headers);
1348
+ respHeaders.delete(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER);
1349
+ respHeaders.delete(TRANSPORT_PROTOCOL.ENVELOPE_HEADER);
1350
+ respHeaders.delete("Content-Length");
1351
+ respHeaders.delete("Content-Encoding");
1352
+ return new Response(method === "HEAD" || [204, 304].includes(response.status) ? null : decResult.data, {
1353
+ status: response.status,
1354
+ statusText: response.statusText,
1355
+ headers: respHeaders
1356
+ });
1357
+ };
1358
+ return {
1359
+ init: ensureReady,
1360
+ encryptedFetch,
1361
+ ready: () => clientId !== null && serverPublicKey !== null,
1362
+ destroy() {
1363
+ keyPair = null;
1364
+ serverPublicKey = null;
1365
+ serverKeyId = null;
1366
+ clientId = null;
1367
+ initPromise = null;
1368
+ }
1369
+ };
1370
+ }
1371
+ function isRequestLike(input) {
1372
+ if (typeof input !== "object" || input === null)
1373
+ return false;
1374
+ const candidate = input;
1375
+ const headers = candidate.headers;
1376
+ return typeof candidate.url === "string" && typeof headers?.get === "function" && typeof candidate.method === "string" && typeof candidate.clone === "function";
1377
+ }
1378
+ function isKeyExchangeResponse(body) {
1379
+ if (!body || typeof body !== "object")
1380
+ return false;
1381
+ const fields = body;
1382
+ return typeof fields.serverPublicKey === "string" && fields.serverPublicKey.length > 0 && new TextEncoder().encode(fields.serverPublicKey).byteLength <= TRANSPORT_BODY_LIMITS.publicKeyBytes && typeof fields.clientId === "string" && fields.clientId.length > 0 && typeof fields.serverKeyId === "string" && /^[\w-]{1,64}$/.test(fields.serverKeyId);
1383
+ }
1384
+ function parseTransportServerKeyRing(serialized) {
1385
+ try {
1386
+ const value = typeof serialized === "string" ? JSON.parse(serialized) : serialized;
1387
+ if (isTransportServerKeyRing(value))
1388
+ return ok(value);
1389
+ } catch {
1390
+ }
1391
+ return err(HaiCryptoError.INVALID_KEY, cryptoM("crypto_transportServerKeyRingInvalid"));
1392
+ }
1393
+ function isTransportServerKeyRing(value) {
1394
+ if (!value || typeof value !== "object")
1395
+ return false;
1396
+ const ring = value;
1397
+ if (typeof ring.activeKeyId !== "string" || !/^[\w-]{1,64}$/.test(ring.activeKeyId) || !ring.keys || typeof ring.keys !== "object" || Array.isArray(ring.keys)) {
1398
+ return false;
1399
+ }
1400
+ if (!Object.hasOwn(ring.keys, ring.activeKeyId))
1401
+ return false;
1402
+ return Object.entries(ring.keys).every(([id, pair]) => {
1403
+ if (!/^[\w-]{1,64}$/.test(id) || !pair || typeof pair !== "object")
1404
+ return false;
1405
+ const fields = pair;
1406
+ return typeof fields.publicKey === "string" && fields.publicKey.length > 0 && new TextEncoder().encode(fields.publicKey).byteLength <= TRANSPORT_BODY_LIMITS.publicKeyBytes && typeof fields.privateKey === "string" && fields.privateKey.length > 0 && fields.privateKey.length <= TRANSPORT_BODY_LIMITS.publicKeyBytes;
1407
+ });
1408
+ }
1409
+
1410
+ // src/transport/crypto-transport-server.ts
1411
+ function createTransportEncryption(cryptoService, options) {
1412
+ let bodyLimits;
1413
+ try {
1414
+ bodyLimits = resolveTransportBodyLimits(options?.maxPlaintextBytes);
1415
+ } catch {
1416
+ return err(HaiCryptoError.INVALID_INPUT, cryptoM("crypto_transportBodyLimitInvalid"));
1417
+ }
1418
+ const ring = parseTransportServerKeyRing(options?.serverKeyRing);
1419
+ if (!ring.success)
1420
+ return ring;
1421
+ if (!options.keyStore || typeof options.keyStore.register !== "function" || typeof options.keyStore.get !== "function" || typeof options.keyStore.claimRequest !== "function") {
1422
+ return err(HaiCryptoError.INVALID_INPUT, cryptoM("crypto_transportKeyStoreRequired"));
1423
+ }
1424
+ if (core.module.requiresSharedState(globalThis.process?.env ?? {}) && options.keyStore.stateScope !== "shared")
1425
+ return err(HaiCryptoError.INVALID_INPUT, cryptoM("crypto_transportKeyStoreRequired"));
1426
+ const serverKeys = /* @__PURE__ */ new Map();
1427
+ for (const [id, key] of Object.entries(ring.data.keys)) {
1428
+ const probe = cryptoService.asymmetric.encrypt("hai-transport-key-check", key.publicKey);
1429
+ if (!probe.success)
1430
+ return err(HaiCryptoError.INVALID_KEY, cryptoM("crypto_transportServerKeyRingInvalid"));
1431
+ const checked = cryptoService.asymmetric.decrypt(probe.data, key.privateKey);
1432
+ if (!checked.success || checked.data !== "hai-transport-key-check")
1433
+ return err(HaiCryptoError.INVALID_KEY, cryptoM("crypto_transportServerKeyRingInvalid"));
1434
+ serverKeys.set(id, { publicKey: key.publicKey, privateKey: key.privateKey });
1435
+ }
1436
+ const activeKeyId = ring.data.activeKeyId;
1437
+ const serverKeyPair = serverKeys.get(activeKeyId);
1438
+ const keyStore = options.keyStore;
1439
+ const manager = {
1440
+ bodyLimits,
1441
+ getServerKeyId() {
1442
+ return activeKeyId;
1443
+ },
1444
+ getServerPublicKey() {
1445
+ return serverKeyPair.publicKey;
1446
+ },
1447
+ async registerClientKey(clientPublicKey) {
1448
+ if (!isValidSM2PublicKey(clientPublicKey))
1449
+ throw new TypeError(cryptoM("crypto_sm2PublicKeyInvalid"));
1450
+ return keyStore.register(clientPublicKey);
1451
+ },
1452
+ async getClientPublicKey(clientId) {
1453
+ return keyStore.get(clientId);
1454
+ },
1455
+ async encryptResponse(request, data, status) {
1456
+ try {
1457
+ const context = { ...request };
1458
+ const clientPublicKey = await keyStore.get(context.clientId);
1459
+ if (!clientPublicKey)
1460
+ return err(HaiCommonError.NOT_FOUND, cryptoM("crypto_transportClientNotRegistered"));
1461
+ const key = serverKeys.get(context.serverKeyId);
1462
+ if (!key)
1463
+ return transportAuthenticationFailure();
1464
+ return sealTransportPayload(cryptoService, data, context, "response", status, clientPublicKey, key.privateKey, bodyLimits);
1465
+ } catch {
1466
+ return err(HaiCommonError.INTERNAL_ERROR, cryptoM("crypto_transportResponseEncryptFailed"));
1467
+ }
1468
+ },
1469
+ async decryptRequest(payload, expected) {
1470
+ try {
1471
+ if (!isValidEncryptedPayload(payload, bodyLimits) || payload.direction !== "request" || payload.status !== 0)
1472
+ return transportAuthenticationFailure();
1473
+ const authenticated = { ...payload, context: { ...payload.context } };
1474
+ const c = authenticated.context;
1475
+ const pathHash = hashTransportPath(cryptoService, expected.path);
1476
+ if (!pathHash.success || c.clientId !== expected.clientId || c.method !== expected.method || c.pathHash !== pathHash.data) {
1477
+ return transportAuthenticationFailure();
1478
+ }
1479
+ const key = serverKeys.get(c.serverKeyId);
1480
+ const clientPublicKey = await keyStore.get(c.clientId);
1481
+ if (!key || !clientPublicKey)
1482
+ return transportAuthenticationFailure();
1483
+ const plaintext = openTransportPayload(cryptoService, authenticated, key.privateKey, clientPublicKey, bodyLimits);
1484
+ if (!plaintext.success)
1485
+ return plaintext;
1486
+ const timeFailure = () => {
1487
+ const error = new TransportTimeError(c);
1488
+ return err(HaiCryptoError.TRANSPORT_TIME_INVALID, error.message, error);
1489
+ };
1490
+ const now = Date.now();
1491
+ if (c.issuedAt > now + TRANSPORT_PROTOCOL.CLOCK_SKEW_MS || now >= c.issuedAt + TRANSPORT_PROTOCOL.REQUEST_WINDOW_MS)
1492
+ return timeFailure();
1493
+ if (!await keyStore.claimRequest(c.clientId, c.requestId, c.issuedAt + TRANSPORT_PROTOCOL.REQUEST_WINDOW_MS + 2 * TRANSPORT_PROTOCOL.CLOCK_SKEW_MS)) {
1494
+ return transportAuthenticationFailure();
1495
+ }
1496
+ if (Date.now() >= c.issuedAt + TRANSPORT_PROTOCOL.REQUEST_WINDOW_MS)
1497
+ return timeFailure();
1498
+ return plaintext;
1499
+ } catch {
1500
+ return transportAuthenticationFailure();
1501
+ }
1502
+ },
1503
+ async close() {
1504
+ await keyStore.close?.();
1505
+ }
1506
+ };
1507
+ return ok(manager);
1508
+ }
712
1509
 
713
1510
  // src/crypto-main.ts
714
1511
  var logger = core.logger.child({ module: "crypto", scope: "main" });
@@ -726,6 +1523,14 @@ var notInitializedAsymmetric = notInitialized.proxy("sync");
726
1523
  var notInitializedHash = notInitialized.proxy("sync");
727
1524
  var notInitializedSymmetric = notInitialized.proxy("sync");
728
1525
  var notInitializedPassword = notInitialized.proxy("sync");
1526
+ function getCrypto() {
1527
+ return crypto2;
1528
+ }
1529
+ var transportOperations = {
1530
+ createServer: (options) => getCrypto().isInitialized ? createTransportEncryption(getCrypto(), options) : notInitialized.result(),
1531
+ createClient: (options) => createTransportClient({ crypto: getCrypto(), ...options }),
1532
+ protocol: TRANSPORT_PROTOCOL
1533
+ };
729
1534
  var crypto2 = {
730
1535
  /**
731
1536
  * 初始化加密模块
@@ -745,6 +1550,7 @@ var crypto2 = {
745
1550
  }
746
1551
  initInProgress = true;
747
1552
  try {
1553
+ await Promise.resolve();
748
1554
  if (initialized) {
749
1555
  logger.warn("Crypto module is already initialized, reinitializing");
750
1556
  await crypto2.close();
@@ -791,6 +1597,12 @@ var crypto2 = {
791
1597
  get password() {
792
1598
  return currentPassword ?? notInitializedPassword;
793
1599
  },
1600
+ /**
1601
+ * 传输加密操作(端到端混合加密)
1602
+ *
1603
+ * createServer 检查初始化后复用 asymmetric/symmetric;未初始化返回 NOT_INITIALIZED。
1604
+ */
1605
+ transport: transportOperations,
794
1606
  /** 是否已初始化 */
795
1607
  get isInitialized() {
796
1608
  return initialized;
@@ -814,7 +1626,353 @@ var crypto2 = {
814
1626
  logger.info("Crypto module closed");
815
1627
  }
816
1628
  };
1629
+ async function authenticateTransportRequest(manager, request, limits = manager.bodyLimits) {
1630
+ const reject = () => {
1631
+ void request.body?.cancel().catch(() => {
1632
+ });
1633
+ return transportAuthenticationFailure();
1634
+ };
1635
+ if (request.headers.get(TRANSPORT_PROTOCOL.ENCRYPTED_HEADER) !== TRANSPORT_PROTOCOL.ENCRYPTED_HEADER_VALUE)
1636
+ return reject();
1637
+ const clientId = request.headers.get(TRANSPORT_PROTOCOL.CLIENT_ID_HEADER);
1638
+ if (!clientId)
1639
+ return reject();
1640
+ const envelope = request.headers.get(TRANSPORT_PROTOCOL.ENVELOPE_HEADER);
1641
+ const bodyless = request.method === "GET" || request.method === "HEAD";
1642
+ if (bodyless && (!envelope || request.body !== null) || !bodyless && envelope !== null)
1643
+ return reject();
1644
+ if (envelope && new TextEncoder().encode(envelope).byteLength > TRANSPORT_BODY_LIMITS.envelopeHeaderBytes)
1645
+ throw new TransportBodyTooLargeError(TRANSPORT_BODY_LIMITS.envelopeHeaderBytes, new TextEncoder().encode(envelope).byteLength, { stage: "header" });
1646
+ const text = envelope ?? await readTransportBody(request.body, {
1647
+ maxBytes: limits.encryptedPayloadBytes,
1648
+ stage: "encrypted",
1649
+ signal: request.signal
1650
+ });
1651
+ let payload;
1652
+ try {
1653
+ payload = JSON.parse(text);
1654
+ } catch {
1655
+ return transportAuthenticationFailure();
1656
+ }
1657
+ if (!isValidEncryptedPayload(payload, limits) || payload.direction !== "request" || payload.status !== 0)
1658
+ return transportAuthenticationFailure();
1659
+ const url = new URL(request.url);
1660
+ const decrypted = await manager.decryptRequest(payload, {
1661
+ clientId,
1662
+ method: request.method,
1663
+ path: url.pathname + url.search
1664
+ });
1665
+ if (!decrypted.success) {
1666
+ if (decrypted.error.cause instanceof TransportBodyTooLargeError)
1667
+ throw decrypted.error.cause;
1668
+ return decrypted;
1669
+ }
1670
+ if (new TextEncoder().encode(decrypted.data).byteLength > limits.plaintextBytes)
1671
+ throw new TransportBodyTooLargeError(limits.plaintextBytes, new TextEncoder().encode(decrypted.data).byteLength);
1672
+ if (bodyless && decrypted.data !== "")
1673
+ return transportAuthenticationFailure();
1674
+ return ok({ plaintext: decrypted.data, context: payload.context });
1675
+ }
1676
+
1677
+ // src/transport/store-provider/crypto-transport-store-memory.ts
1678
+ var DEFAULT_MAX_CLIENTS = 1e4;
1679
+ function normalizeMaxClients(maxClients) {
1680
+ if (!Number.isFinite(maxClients))
1681
+ return DEFAULT_MAX_CLIENTS;
1682
+ return Math.max(1, Math.floor(maxClients));
1683
+ }
1684
+ function createClientId(counter) {
1685
+ const entropy = typeof crypto !== "undefined" && typeof crypto.randomUUID === "function" ? crypto.randomUUID().replace(/-/g, "") : Math.random().toString(36).slice(2, 12);
1686
+ return `c_${counter}_${Date.now()}_${entropy}`;
1687
+ }
1688
+ function createInMemoryKeyStore(maxClients = DEFAULT_MAX_CLIENTS) {
1689
+ const clientKeys = /* @__PURE__ */ new Map();
1690
+ const requests = /* @__PURE__ */ new Map();
1691
+ const capacity = normalizeMaxClients(maxClients);
1692
+ let counter = 0;
1693
+ return {
1694
+ get stateScope() {
1695
+ return "local";
1696
+ },
1697
+ async register(publicKey) {
1698
+ if (!isValidSM2PublicKey(publicKey))
1699
+ throw new TypeError(cryptoM("crypto_sm2PublicKeyInvalid"));
1700
+ counter++;
1701
+ const clientId = createClientId(counter);
1702
+ if (clientKeys.size >= capacity) {
1703
+ const oldest = clientKeys.keys().next().value;
1704
+ if (oldest !== void 0)
1705
+ clientKeys.delete(oldest);
1706
+ }
1707
+ clientKeys.set(clientId, publicKey);
1708
+ return clientId;
1709
+ },
1710
+ async get(clientId) {
1711
+ return clientKeys.get(clientId);
1712
+ },
1713
+ async claimRequest(clientId, requestId, expiresAt) {
1714
+ const now = Date.now();
1715
+ for (const [id2, expiry] of requests) {
1716
+ if (expiry <= now)
1717
+ requests.delete(id2);
1718
+ }
1719
+ const id = JSON.stringify([clientId, requestId]);
1720
+ if (requests.has(id) || expiresAt <= now)
1721
+ return false;
1722
+ requests.set(id, expiresAt);
1723
+ return true;
1724
+ },
1725
+ async delete(clientId) {
1726
+ clientKeys.delete(clientId);
1727
+ },
1728
+ async close() {
1729
+ clientKeys.clear();
1730
+ requests.clear();
1731
+ }
1732
+ };
1733
+ }
1734
+
1735
+ // src/transport/store-provider/crypto-transport-store-redis.ts
1736
+ var TRANSPORT_KEY_CACHE_PREFIX = "hai:crypto:transport:client";
1737
+ function normalizeTtlSeconds(ttlSeconds) {
1738
+ if (ttlSeconds === void 0)
1739
+ return void 0;
1740
+ if (!Number.isSafeInteger(ttlSeconds) || ttlSeconds <= 0 || ttlSeconds > 2147483647)
1741
+ throw new TypeError(cryptoM("crypto_transportKeyStoreTtlInvalid"));
1742
+ return ttlSeconds;
1743
+ }
1744
+ function createClientId2() {
1745
+ if (typeof crypto !== "undefined" && typeof crypto.randomUUID === "function")
1746
+ return `c_${crypto.randomUUID()}`;
1747
+ return `c_${Date.now()}_${Math.random().toString(36).slice(2, 12)}`;
1748
+ }
1749
+ function toOperationError(operation, error) {
1750
+ const wrapped = new Error(`${operation} failed: ${error.message}`, { cause: error });
1751
+ wrapped.code = error.code;
1752
+ return wrapped;
1753
+ }
1754
+ function unwrapResult(operation, result) {
1755
+ if (result.success)
1756
+ return result.data;
1757
+ throw toOperationError(operation, result.error);
1758
+ }
1759
+ function createRedisTransportKeyStore(options) {
1760
+ const ttlSeconds = normalizeTtlSeconds(options.ttlSeconds);
1761
+ function buildCacheKey(clientId) {
1762
+ return `${TRANSPORT_KEY_CACHE_PREFIX}:${clientId}`;
1763
+ }
1764
+ return {
1765
+ get stateScope() {
1766
+ return options.cache.config?.type === "redis" ? "shared" : "local";
1767
+ },
1768
+ async register(publicKey) {
1769
+ if (!isValidSM2PublicKey(publicKey))
1770
+ throw new TypeError(cryptoM("crypto_sm2PublicKeyInvalid"));
1771
+ const clientId = createClientId2();
1772
+ const applied = unwrapResult(
1773
+ "cache.kv.set",
1774
+ await options.cache.kv.set(
1775
+ buildCacheKey(clientId),
1776
+ publicKey,
1777
+ ttlSeconds === void 0 ? void 0 : { ex: ttlSeconds }
1778
+ )
1779
+ );
1780
+ if (!applied)
1781
+ throw new Error(cryptoM("crypto_transportClientNotRegistered"));
1782
+ return clientId;
1783
+ },
1784
+ async get(clientId) {
1785
+ const publicKey = unwrapResult(
1786
+ "cache.kv.get",
1787
+ await options.cache.kv.get(buildCacheKey(clientId))
1788
+ );
1789
+ return publicKey ?? void 0;
1790
+ },
1791
+ async claimRequest(clientId, requestId, expiresAt) {
1792
+ const ttl = expiresAt - Date.now();
1793
+ if (ttl <= 0)
1794
+ return false;
1795
+ return unwrapResult("cache.kv.compareAndSwap", await options.cache.kv.compareAndSwap(
1796
+ `hai:crypto:transport:request:${clientId}:${requestId}`,
1797
+ void 0,
1798
+ true,
1799
+ { px: ttl }
1800
+ ));
1801
+ },
1802
+ async delete(clientId) {
1803
+ unwrapResult("cache.kv.del", await options.cache.kv.del(buildCacheKey(clientId)));
1804
+ }
1805
+ };
1806
+ }
1807
+
1808
+ // src/transport/store-provider/crypto-transport-store-reldb.ts
1809
+ var TRANSPORT_KEY_TABLE = "hai_crypto_transport_client_keys";
1810
+ var TRANSPORT_REQUEST_TABLE = "hai_crypto_transport_requests";
1811
+ var TRANSPORT_CAPACITY_TABLE = "hai_crypto_transport_capacity";
1812
+ var PRUNE_BATCH_SIZE = 100;
1813
+ function toOperationError2(operation, error) {
1814
+ return Object.assign(new Error(cryptoM("crypto_transportKeyStoreOperationFailed")), { operation, code: error.code });
1815
+ }
1816
+ function unwrapResult2(operation, result) {
1817
+ if (result.success)
1818
+ return result.data;
1819
+ throw toOperationError2(operation, result.error);
1820
+ }
1821
+ function createReldbTransportKeyStore(options) {
1822
+ const { ttlSeconds } = options;
1823
+ if (ttlSeconds !== void 0 && (!Number.isSafeInteger(ttlSeconds) || ttlSeconds <= 0 || ttlSeconds > 2147483647))
1824
+ throw new TypeError(cryptoM("crypto_transportKeyStoreTtlInvalid"));
1825
+ const maxClients = options.maxClients ?? 1e4;
1826
+ if (!Number.isSafeInteger(maxClients) || maxClients <= 0)
1827
+ throw new TypeError(cryptoM("crypto_transportKeyStoreCapacityInvalid"));
1828
+ let ensureTablePromise = null;
1829
+ function nowSql() {
1830
+ switch (options.reldb.config?.type) {
1831
+ case "postgresql":
1832
+ return "CAST(FLOOR(EXTRACT(EPOCH FROM clock_timestamp()) * 1000) AS BIGINT)";
1833
+ case "mysql":
1834
+ return "CAST(FLOOR(UNIX_TIMESTAMP(CURRENT_TIMESTAMP(3)) * 1000) AS SIGNED)";
1835
+ default:
1836
+ return "(CAST(strftime('%s', 'now') AS INTEGER) * 1000 + CAST(substr(strftime('%f', 'now'), 4, 3) AS INTEGER))";
1837
+ }
1838
+ }
1839
+ function insertIfAbsent(table, columns, placeholders, key) {
1840
+ return options.reldb.config?.type === "mysql" ? `INSERT IGNORE INTO ${table} (${columns}) VALUES (${placeholders})` : `INSERT INTO ${table} (${columns}) VALUES (${placeholders}) ON CONFLICT (${key}) DO NOTHING`;
1841
+ }
1842
+ async function ensureTable() {
1843
+ if (!ensureTablePromise) {
1844
+ const initialization = (async () => {
1845
+ unwrapResult2(
1846
+ "reldb.ddl.createTable",
1847
+ await options.reldb.ddl.createTable(TRANSPORT_KEY_TABLE, {
1848
+ client_id: { type: "TEXT", primaryKey: true },
1849
+ public_key: { type: "TEXT", notNull: true },
1850
+ created_at_ms: { type: "BIGINT", notNull: true },
1851
+ expires_at_ms: { type: "BIGINT" }
1852
+ }, true)
1853
+ );
1854
+ unwrapResult2("reldb.ddl.createTable", await options.reldb.ddl.createTable(TRANSPORT_REQUEST_TABLE, {
1855
+ request_key: { type: "TEXT", primaryKey: true },
1856
+ expires_at: { type: "BIGINT", notNull: true }
1857
+ }, true));
1858
+ unwrapResult2("reldb.ddl.createTable", await options.reldb.ddl.createTable(TRANSPORT_CAPACITY_TABLE, {
1859
+ id: { type: "INTEGER", primaryKey: true },
1860
+ max_clients: { type: "BIGINT", notNull: true },
1861
+ revision: { type: "BIGINT", notNull: true }
1862
+ }, true));
1863
+ unwrapResult2("reldb.sql.execute", await options.reldb.sql.execute(
1864
+ insertIfAbsent(TRANSPORT_CAPACITY_TABLE, "id, max_clients, revision", "?, ?, ?", "id"),
1865
+ [1, maxClients, 0]
1866
+ ));
1867
+ const capacity = unwrapResult2("reldb.sql.get", await options.reldb.sql.get(
1868
+ `SELECT max_clients FROM ${TRANSPORT_CAPACITY_TABLE} WHERE id = ?`,
1869
+ [1]
1870
+ ));
1871
+ if (!capacity || Number(capacity.max_clients) !== maxClients)
1872
+ throw new Error(cryptoM("crypto_transportKeyStoreCapacityMismatch"));
1873
+ })();
1874
+ ensureTablePromise = initialization;
1875
+ try {
1876
+ await initialization;
1877
+ } catch (error) {
1878
+ if (ensureTablePromise === initialization)
1879
+ ensureTablePromise = null;
1880
+ throw error;
1881
+ }
1882
+ }
1883
+ return ensureTablePromise;
1884
+ }
1885
+ async function deleteClientKey(clientId) {
1886
+ unwrapResult2(
1887
+ "reldb.sql.execute",
1888
+ await options.reldb.sql.execute(
1889
+ `DELETE FROM ${TRANSPORT_KEY_TABLE} WHERE client_id = ?`,
1890
+ [clientId]
1891
+ )
1892
+ );
1893
+ }
1894
+ async function withCapacityLock(operation) {
1895
+ await ensureTable();
1896
+ const result = await options.reldb.tx.wrap(async (tx) => {
1897
+ const locked = unwrapResult2("tx.execute", await tx.execute(
1898
+ `UPDATE ${TRANSPORT_CAPACITY_TABLE} SET revision = revision + 1 WHERE id = ? AND max_clients = ?`,
1899
+ [1, maxClients]
1900
+ ));
1901
+ if (locked.changes !== 1)
1902
+ throw new Error(cryptoM("crypto_transportKeyStoreCapacityMismatch"));
1903
+ return operation(tx);
1904
+ });
1905
+ return unwrapResult2("reldb.tx.wrap", result);
1906
+ }
1907
+ async function pruneRows(tx) {
1908
+ let removed = 0;
1909
+ for (const [table, id, expiry] of [[TRANSPORT_KEY_TABLE, "client_id", "expires_at_ms"], [TRANSPORT_REQUEST_TABLE, "request_key", "expires_at"]]) {
1910
+ removed += unwrapResult2("tx.execute", await tx.execute(
1911
+ `DELETE FROM ${table} WHERE ${expiry} <= ${nowSql()} AND ${id} IN (SELECT ${id} FROM (SELECT ${id} FROM ${table} WHERE ${expiry} <= ${nowSql()} LIMIT ${PRUNE_BATCH_SIZE}) AS expired)`
1912
+ )).changes;
1913
+ }
1914
+ return removed;
1915
+ }
1916
+ async function pruneExpired() {
1917
+ return withCapacityLock(pruneRows);
1918
+ }
1919
+ return {
1920
+ get stateScope() {
1921
+ return options.reldb.config?.type && options.reldb.config.type !== "sqlite" ? "shared" : "local";
1922
+ },
1923
+ async register(publicKey) {
1924
+ if (!isValidSM2PublicKey(publicKey))
1925
+ throw new TypeError(cryptoM("crypto_sm2PublicKeyInvalid"));
1926
+ const clientId = `c_${globalThis.crypto.randomUUID()}`;
1927
+ return withCapacityLock(async (tx) => {
1928
+ await pruneRows(tx);
1929
+ const count = unwrapResult2("tx.get", await tx.get(
1930
+ `SELECT COUNT(*) AS total FROM ${TRANSPORT_KEY_TABLE} WHERE expires_at_ms IS NULL OR expires_at_ms > ${nowSql()}`
1931
+ ));
1932
+ const total = Number(count?.total);
1933
+ if (!Number.isSafeInteger(total) || total < 0)
1934
+ throw new Error(cryptoM("crypto_transportKeyStoreOperationFailed"));
1935
+ if (total >= maxClients)
1936
+ throw new Error(cryptoM("crypto_transportKeyStoreCapacityReached"));
1937
+ const inserted = unwrapResult2("tx.execute", await tx.execute(
1938
+ `INSERT INTO ${TRANSPORT_KEY_TABLE} (client_id, public_key, created_at_ms, expires_at_ms) VALUES (?, ?, ${nowSql()}, ${ttlSeconds === void 0 ? "NULL" : `${nowSql()} + ?`})`,
1939
+ ttlSeconds === void 0 ? [clientId, publicKey] : [clientId, publicKey, ttlSeconds * 1e3]
1940
+ ));
1941
+ if (inserted.changes !== 1)
1942
+ throw new Error(cryptoM("crypto_transportClientNotRegistered"));
1943
+ return clientId;
1944
+ });
1945
+ },
1946
+ async get(clientId) {
1947
+ await ensureTable();
1948
+ const row = unwrapResult2(
1949
+ "reldb.sql.get",
1950
+ await options.reldb.sql.get(
1951
+ `SELECT public_key FROM ${TRANSPORT_KEY_TABLE} WHERE client_id = ? AND (expires_at_ms IS NULL OR expires_at_ms > ${nowSql()})`,
1952
+ [clientId]
1953
+ )
1954
+ );
1955
+ return row?.public_key;
1956
+ },
1957
+ async claimRequest(clientId, requestId, expiresAt) {
1958
+ await ensureTable();
1959
+ if (!Number.isSafeInteger(expiresAt) || expiresAt <= Date.now())
1960
+ return false;
1961
+ const insertSql = options.reldb.config?.type === "mysql" ? `INSERT IGNORE INTO ${TRANSPORT_REQUEST_TABLE} (request_key, expires_at) VALUES (?, ?)` : `INSERT INTO ${TRANSPORT_REQUEST_TABLE} (request_key, expires_at) VALUES (?, ?) ON CONFLICT (request_key) DO NOTHING`;
1962
+ const inserted = unwrapResult2("reldb.sql.execute", await options.reldb.sql.execute(
1963
+ insertSql,
1964
+ [JSON.stringify([clientId, requestId]), expiresAt]
1965
+ ));
1966
+ return inserted.changes === 1;
1967
+ },
1968
+ async delete(clientId) {
1969
+ await ensureTable();
1970
+ await deleteClientKey(clientId);
1971
+ },
1972
+ pruneExpired
1973
+ };
1974
+ }
817
1975
 
818
- export { HaiCryptoError, crypto2 as crypto };
1976
+ 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 };
819
1977
  //# sourceMappingURL=index.js.map
820
1978
  //# sourceMappingURL=index.js.map