secure-crypto-top-sdk 0.1.0
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/LICENSE +21 -0
- package/README.md +238 -0
- package/dist/base-v1-7dHNy.d.mts +183 -0
- package/dist/base-v1-7dHNy.d.ts +183 -0
- package/dist/client.d.mts +72 -0
- package/dist/client.d.ts +72 -0
- package/dist/client.js +1436 -0
- package/dist/client.js.map +1 -0
- package/dist/client.mjs +1406 -0
- package/dist/client.mjs.map +1 -0
- package/dist/index.d.mts +162 -0
- package/dist/index.d.ts +162 -0
- package/dist/index.js +1393 -0
- package/dist/index.js.map +1 -0
- package/dist/index.mjs +1335 -0
- package/dist/index.mjs.map +1 -0
- package/dist/key-store-BCmOK04m.d.ts +49 -0
- package/dist/key-store-BF0_3do0.d.mts +49 -0
- package/dist/server.d.mts +60 -0
- package/dist/server.d.ts +60 -0
- package/dist/server.js +1369 -0
- package/dist/server.js.map +1 -0
- package/dist/server.mjs +1338 -0
- package/dist/server.mjs.map +1 -0
- package/package.json +70 -0
package/dist/server.js
ADDED
|
@@ -0,0 +1,1369 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/server/fastify-plugin.ts
|
|
31
|
+
var fastify_plugin_exports = {};
|
|
32
|
+
__export(fastify_plugin_exports, {
|
|
33
|
+
default: () => fastify_plugin_default
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(fastify_plugin_exports);
|
|
36
|
+
var import_fastify_plugin = __toESM(require("fastify-plugin"));
|
|
37
|
+
|
|
38
|
+
// src/errors/index.ts
|
|
39
|
+
var ErrorMessages = {
|
|
40
|
+
[1e3 /* ENCRYPTION_FAILED */]: "\u52A0\u5BC6\u5931\u8D25",
|
|
41
|
+
[1001 /* DECRYPTION_FAILED */]: "\u89E3\u5BC6\u5931\u8D25",
|
|
42
|
+
[1002 /* INVALID_AUTH_TAG */]: "\u8BA4\u8BC1\u6807\u7B7E\u65E0\u6548",
|
|
43
|
+
[1003 /* UNSUPPORTED_CIPHER */]: "\u4E0D\u652F\u6301\u7684\u52A0\u5BC6\u7B97\u6CD5",
|
|
44
|
+
[1004 /* UNSUPPORTED_DIGEST */]: "\u4E0D\u652F\u6301\u7684\u6458\u8981/\u7B7E\u540D\u7B97\u6CD5",
|
|
45
|
+
[1005 /* INVALID_KEY_LENGTH */]: "\u5BC6\u94A5\u957F\u5EA6\u65E0\u6548",
|
|
46
|
+
[1006 /* INVALID_IV_LENGTH */]: "IV \u957F\u5EA6\u65E0\u6548",
|
|
47
|
+
[1100 /* HMAC_MISMATCH */]: "HMAC \u9A8C\u8BC1\u5931\u8D25",
|
|
48
|
+
[1101 /* SIGNATURE_MISMATCH */]: "\u7B7E\u540D\u4E0D\u5339\u914D",
|
|
49
|
+
[1102 /* TIMESTAMP_EXPIRED */]: "\u8BF7\u6C42\u5DF2\u8FC7\u671F",
|
|
50
|
+
[1103 /* NONCE_REUSED */]: "\u8BF7\u6C42\u5DF2\u88AB\u5904\u7406\uFF0C\u8BF7\u52FF\u91CD\u653E",
|
|
51
|
+
[1104 /* INVALID_TIMESTAMP */]: "\u65E0\u6548\u7684\u65F6\u95F4\u6233",
|
|
52
|
+
[1105 /* SIGN_METHOD_NOT_ALLOWED */]: "\u8BE5\u7B7E\u540D\u65B9\u6CD5\u672A\u88AB\u8BE5 app \u5141\u8BB8",
|
|
53
|
+
[1200 /* APP_NOT_FOUND */]: "\u672A\u627E\u5230\u5BF9\u5E94\u7684 app \u914D\u7F6E",
|
|
54
|
+
[1201 /* APP_DISABLED */]: "\u8BE5 app \u5DF2\u88AB\u7981\u7528",
|
|
55
|
+
[1202 /* PROTOCOL_NOT_FOUND */]: "\u672A\u6CE8\u518C\u5BF9\u5E94\u534F\u8BAE",
|
|
56
|
+
[1203 /* MISSING_APP_KEY */]: "\u7F3A\u5C11 app_key \u53C2\u6570",
|
|
57
|
+
[1204 /* MISSING_METHOD */]: "\u7F3A\u5C11 method \u53C2\u6570",
|
|
58
|
+
[1300 /* VALIDATION_ERROR */]: "\u6570\u636E\u6821\u9A8C\u5931\u8D25",
|
|
59
|
+
[1301 /* INVALID_REQUEST */]: "\u8BF7\u6C42\u683C\u5F0F\u9519\u8BEF",
|
|
60
|
+
[1302 /* INVALID_PAYLOAD */]: "\u8BF7\u6C42\u8F7D\u8377\u65E0\u6548"
|
|
61
|
+
};
|
|
62
|
+
var SecureError = class extends Error {
|
|
63
|
+
constructor(code, details, statusCode) {
|
|
64
|
+
super(typeof details === "string" ? details : ErrorMessages[code]);
|
|
65
|
+
this.name = "SecureError";
|
|
66
|
+
this.code = code;
|
|
67
|
+
this.statusCode = statusCode || this.getDefaultStatusCode(code);
|
|
68
|
+
this.details = details;
|
|
69
|
+
Error.captureStackTrace(this, this.constructor);
|
|
70
|
+
}
|
|
71
|
+
getDefaultStatusCode(code) {
|
|
72
|
+
if (code >= 1e3 && code < 1100) return 400;
|
|
73
|
+
if (code >= 1100 && code < 1200) return 401;
|
|
74
|
+
if (code >= 1200 && code < 1300) return 403;
|
|
75
|
+
if (code >= 1300 && code < 1400) return 400;
|
|
76
|
+
return 500;
|
|
77
|
+
}
|
|
78
|
+
toJSON() {
|
|
79
|
+
return { success: false, code: this.code, message: this.message, details: this.details };
|
|
80
|
+
}
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// src/config/app-registry.ts
|
|
84
|
+
var AppRegistry = class {
|
|
85
|
+
constructor(apps = []) {
|
|
86
|
+
this.map = /* @__PURE__ */ new Map();
|
|
87
|
+
for (const a of apps) this.register(a);
|
|
88
|
+
}
|
|
89
|
+
register(app) {
|
|
90
|
+
if (!app.appKey) throw new SecureError(1200 /* APP_NOT_FOUND */, "appKey \u4E0D\u80FD\u4E3A\u7A7A");
|
|
91
|
+
if (!app.appSecret) throw new SecureError(1200 /* APP_NOT_FOUND */, `app ${app.appKey} \u7F3A\u5C11 appSecret`);
|
|
92
|
+
this.map.set(app.appKey, app);
|
|
93
|
+
return this;
|
|
94
|
+
}
|
|
95
|
+
get(appKey) {
|
|
96
|
+
const app = this.map.get(appKey);
|
|
97
|
+
if (!app) throw new SecureError(1200 /* APP_NOT_FOUND */, `Unknown app: ${appKey}`);
|
|
98
|
+
if (app.enabled === false) throw new SecureError(1201 /* APP_DISABLED */, `App disabled: ${appKey}`);
|
|
99
|
+
return app;
|
|
100
|
+
}
|
|
101
|
+
has(appKey) {
|
|
102
|
+
return this.map.has(appKey);
|
|
103
|
+
}
|
|
104
|
+
list() {
|
|
105
|
+
return Array.from(this.map.values());
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
// src/config/key-store.ts
|
|
110
|
+
var nodeCrypto7 = __toESM(require("crypto"));
|
|
111
|
+
|
|
112
|
+
// src/algorithms/cipher/aes-gcm.ts
|
|
113
|
+
var nodeCrypto = __toESM(require("crypto"));
|
|
114
|
+
var META = {
|
|
115
|
+
name: "aes-256-gcm",
|
|
116
|
+
keyLength: 32,
|
|
117
|
+
ivLength: 12,
|
|
118
|
+
hasAuthTag: true,
|
|
119
|
+
blockSize: 16
|
|
120
|
+
};
|
|
121
|
+
var aesGcm = {
|
|
122
|
+
name: "aes-256-gcm",
|
|
123
|
+
meta: META,
|
|
124
|
+
generateKey() {
|
|
125
|
+
return nodeCrypto.randomBytes(META.keyLength);
|
|
126
|
+
},
|
|
127
|
+
generateIv() {
|
|
128
|
+
return nodeCrypto.randomBytes(META.ivLength);
|
|
129
|
+
},
|
|
130
|
+
encrypt(plain, params = {}) {
|
|
131
|
+
if (!params.key) {
|
|
132
|
+
throw new SecureError(1005 /* INVALID_KEY_LENGTH */, "AES-256-GCM \u9700\u8981\u4F20\u5165 32 \u5B57\u8282\u5BF9\u79F0\u5BC6\u94A5");
|
|
133
|
+
}
|
|
134
|
+
const key = params.key;
|
|
135
|
+
if (key.length !== META.keyLength) {
|
|
136
|
+
throw new SecureError(1005 /* INVALID_KEY_LENGTH */, `AES-256 \u5BC6\u94A5\u9700 ${META.keyLength} \u5B57\u8282`);
|
|
137
|
+
}
|
|
138
|
+
const iv = params.rsaPublicKeyPem ? nodeCrypto.randomBytes(META.ivLength) : params.iv ?? this.generateIv();
|
|
139
|
+
const cipher = nodeCrypto.createCipheriv("aes-256-gcm", key, iv, { authTagLength: 16 });
|
|
140
|
+
const enc = Buffer.concat([cipher.update(plain), cipher.final()]);
|
|
141
|
+
return { data: enc, iv, tag: cipher.getAuthTag() };
|
|
142
|
+
},
|
|
143
|
+
decrypt(cipherBuf, params) {
|
|
144
|
+
if (!params.key) throw new SecureError(1005 /* INVALID_KEY_LENGTH */, "AES-256-GCM \u9700\u8981\u5BC6\u94A5");
|
|
145
|
+
if (!params.tag) throw new SecureError(1002 /* INVALID_AUTH_TAG */, "AES-256-GCM \u9700\u8981 authTag");
|
|
146
|
+
const decipher = nodeCrypto.createDecipheriv("aes-256-gcm", params.key, params.iv, { authTagLength: 16 });
|
|
147
|
+
decipher.setAuthTag(params.tag);
|
|
148
|
+
try {
|
|
149
|
+
return Buffer.concat([decipher.update(cipherBuf), decipher.final()]);
|
|
150
|
+
} catch {
|
|
151
|
+
throw new SecureError(1002 /* INVALID_AUTH_TAG */, "AES-GCM authTag \u6821\u9A8C\u5931\u8D25,\u6570\u636E\u53EF\u80FD\u5DF2\u88AB\u7BE1\u6539");
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
};
|
|
155
|
+
|
|
156
|
+
// src/algorithms/cipher/aes-cbc.ts
|
|
157
|
+
var nodeCrypto2 = __toESM(require("crypto"));
|
|
158
|
+
var META_256 = {
|
|
159
|
+
name: "aes-256-cbc",
|
|
160
|
+
keyLength: 32,
|
|
161
|
+
ivLength: 16,
|
|
162
|
+
hasAuthTag: false,
|
|
163
|
+
blockSize: 16
|
|
164
|
+
};
|
|
165
|
+
var META_128 = {
|
|
166
|
+
name: "aes-128-cbc",
|
|
167
|
+
keyLength: 16,
|
|
168
|
+
ivLength: 16,
|
|
169
|
+
hasAuthTag: false,
|
|
170
|
+
blockSize: 16
|
|
171
|
+
};
|
|
172
|
+
function pkcs7Pad(data, blockSize) {
|
|
173
|
+
const pad = blockSize - data.length % blockSize;
|
|
174
|
+
return Buffer.concat([data, Buffer.alloc(pad, pad)]);
|
|
175
|
+
}
|
|
176
|
+
function pkcs7Unpad(data) {
|
|
177
|
+
if (data.length === 0) return data;
|
|
178
|
+
const pad = data[data.length - 1];
|
|
179
|
+
if (pad < 1 || pad > data.length) {
|
|
180
|
+
throw new SecureError(1001 /* DECRYPTION_FAILED */, "PKCS#7 padding \u65E0\u6548");
|
|
181
|
+
}
|
|
182
|
+
return data.subarray(0, data.length - pad);
|
|
183
|
+
}
|
|
184
|
+
function makeAesCbc(meta, openSslName) {
|
|
185
|
+
return {
|
|
186
|
+
name: meta.name,
|
|
187
|
+
meta,
|
|
188
|
+
generateKey() {
|
|
189
|
+
return nodeCrypto2.randomBytes(meta.keyLength);
|
|
190
|
+
},
|
|
191
|
+
generateIv() {
|
|
192
|
+
return nodeCrypto2.randomBytes(meta.ivLength);
|
|
193
|
+
},
|
|
194
|
+
encrypt(plain, params = {}) {
|
|
195
|
+
if (!params.key || params.key.length !== meta.keyLength) {
|
|
196
|
+
throw new SecureError(1005 /* INVALID_KEY_LENGTH */, `${meta.name} \u5BC6\u94A5\u9700 ${meta.keyLength} \u5B57\u8282`);
|
|
197
|
+
}
|
|
198
|
+
const iv = nodeCrypto2.randomBytes(meta.ivLength);
|
|
199
|
+
const cipher = nodeCrypto2.createCipheriv(openSslName, params.key, iv);
|
|
200
|
+
cipher.setAutoPadding(false);
|
|
201
|
+
const padded = pkcs7Pad(plain, meta.blockSize);
|
|
202
|
+
const enc = Buffer.concat([cipher.update(padded), cipher.final()]);
|
|
203
|
+
return { data: enc, iv };
|
|
204
|
+
},
|
|
205
|
+
decrypt(cipherBuf, params) {
|
|
206
|
+
if (!params.key || params.key.length !== meta.keyLength) {
|
|
207
|
+
throw new SecureError(1005 /* INVALID_KEY_LENGTH */, `${meta.name} \u5BC6\u94A5\u9700 ${meta.keyLength} \u5B57\u8282`);
|
|
208
|
+
}
|
|
209
|
+
const decipher = nodeCrypto2.createDecipheriv(openSslName, params.key, params.iv);
|
|
210
|
+
decipher.setAutoPadding(false);
|
|
211
|
+
const dec = Buffer.concat([decipher.update(cipherBuf), decipher.final()]);
|
|
212
|
+
return pkcs7Unpad(dec);
|
|
213
|
+
}
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
var aesCbc256 = makeAesCbc(META_256, "aes-256-cbc");
|
|
217
|
+
var aesCbc128 = makeAesCbc(META_128, "aes-128-cbc");
|
|
218
|
+
|
|
219
|
+
// src/algorithms/cipher/sm4-cbc.ts
|
|
220
|
+
var nodeCrypto3 = __toESM(require("crypto"));
|
|
221
|
+
var META2 = {
|
|
222
|
+
name: "sm4-cbc",
|
|
223
|
+
keyLength: 16,
|
|
224
|
+
ivLength: 16,
|
|
225
|
+
hasAuthTag: false,
|
|
226
|
+
blockSize: 16
|
|
227
|
+
};
|
|
228
|
+
var SBOX = [
|
|
229
|
+
214,
|
|
230
|
+
144,
|
|
231
|
+
233,
|
|
232
|
+
254,
|
|
233
|
+
204,
|
|
234
|
+
225,
|
|
235
|
+
61,
|
|
236
|
+
183,
|
|
237
|
+
22,
|
|
238
|
+
182,
|
|
239
|
+
20,
|
|
240
|
+
194,
|
|
241
|
+
40,
|
|
242
|
+
251,
|
|
243
|
+
44,
|
|
244
|
+
5,
|
|
245
|
+
43,
|
|
246
|
+
103,
|
|
247
|
+
154,
|
|
248
|
+
118,
|
|
249
|
+
42,
|
|
250
|
+
190,
|
|
251
|
+
4,
|
|
252
|
+
195,
|
|
253
|
+
170,
|
|
254
|
+
68,
|
|
255
|
+
19,
|
|
256
|
+
38,
|
|
257
|
+
73,
|
|
258
|
+
134,
|
|
259
|
+
6,
|
|
260
|
+
153,
|
|
261
|
+
156,
|
|
262
|
+
66,
|
|
263
|
+
80,
|
|
264
|
+
244,
|
|
265
|
+
145,
|
|
266
|
+
239,
|
|
267
|
+
152,
|
|
268
|
+
122,
|
|
269
|
+
51,
|
|
270
|
+
84,
|
|
271
|
+
11,
|
|
272
|
+
67,
|
|
273
|
+
237,
|
|
274
|
+
207,
|
|
275
|
+
172,
|
|
276
|
+
98,
|
|
277
|
+
228,
|
|
278
|
+
179,
|
|
279
|
+
28,
|
|
280
|
+
169,
|
|
281
|
+
201,
|
|
282
|
+
8,
|
|
283
|
+
232,
|
|
284
|
+
149,
|
|
285
|
+
128,
|
|
286
|
+
223,
|
|
287
|
+
148,
|
|
288
|
+
250,
|
|
289
|
+
117,
|
|
290
|
+
143,
|
|
291
|
+
63,
|
|
292
|
+
166,
|
|
293
|
+
71,
|
|
294
|
+
7,
|
|
295
|
+
167,
|
|
296
|
+
252,
|
|
297
|
+
243,
|
|
298
|
+
115,
|
|
299
|
+
23,
|
|
300
|
+
186,
|
|
301
|
+
131,
|
|
302
|
+
89,
|
|
303
|
+
60,
|
|
304
|
+
25,
|
|
305
|
+
230,
|
|
306
|
+
133,
|
|
307
|
+
79,
|
|
308
|
+
168,
|
|
309
|
+
104,
|
|
310
|
+
107,
|
|
311
|
+
129,
|
|
312
|
+
178,
|
|
313
|
+
113,
|
|
314
|
+
100,
|
|
315
|
+
218,
|
|
316
|
+
139,
|
|
317
|
+
248,
|
|
318
|
+
235,
|
|
319
|
+
15,
|
|
320
|
+
75,
|
|
321
|
+
112,
|
|
322
|
+
86,
|
|
323
|
+
157,
|
|
324
|
+
53,
|
|
325
|
+
30,
|
|
326
|
+
36,
|
|
327
|
+
14,
|
|
328
|
+
94,
|
|
329
|
+
99,
|
|
330
|
+
88,
|
|
331
|
+
209,
|
|
332
|
+
162,
|
|
333
|
+
37,
|
|
334
|
+
34,
|
|
335
|
+
124,
|
|
336
|
+
59,
|
|
337
|
+
1,
|
|
338
|
+
33,
|
|
339
|
+
120,
|
|
340
|
+
135,
|
|
341
|
+
212,
|
|
342
|
+
0,
|
|
343
|
+
70,
|
|
344
|
+
87,
|
|
345
|
+
159,
|
|
346
|
+
211,
|
|
347
|
+
39,
|
|
348
|
+
82,
|
|
349
|
+
76,
|
|
350
|
+
54,
|
|
351
|
+
2,
|
|
352
|
+
231,
|
|
353
|
+
160,
|
|
354
|
+
196,
|
|
355
|
+
200,
|
|
356
|
+
158,
|
|
357
|
+
234,
|
|
358
|
+
191,
|
|
359
|
+
138,
|
|
360
|
+
210,
|
|
361
|
+
64,
|
|
362
|
+
199,
|
|
363
|
+
56,
|
|
364
|
+
181,
|
|
365
|
+
163,
|
|
366
|
+
247,
|
|
367
|
+
242,
|
|
368
|
+
206,
|
|
369
|
+
249,
|
|
370
|
+
97,
|
|
371
|
+
21,
|
|
372
|
+
161,
|
|
373
|
+
224,
|
|
374
|
+
174,
|
|
375
|
+
93,
|
|
376
|
+
164,
|
|
377
|
+
155,
|
|
378
|
+
52,
|
|
379
|
+
26,
|
|
380
|
+
85,
|
|
381
|
+
173,
|
|
382
|
+
147,
|
|
383
|
+
50,
|
|
384
|
+
48,
|
|
385
|
+
245,
|
|
386
|
+
140,
|
|
387
|
+
177,
|
|
388
|
+
227,
|
|
389
|
+
29,
|
|
390
|
+
246,
|
|
391
|
+
226,
|
|
392
|
+
46,
|
|
393
|
+
130,
|
|
394
|
+
102,
|
|
395
|
+
202,
|
|
396
|
+
96,
|
|
397
|
+
192,
|
|
398
|
+
41,
|
|
399
|
+
35,
|
|
400
|
+
171,
|
|
401
|
+
13,
|
|
402
|
+
83,
|
|
403
|
+
78,
|
|
404
|
+
111,
|
|
405
|
+
213,
|
|
406
|
+
219,
|
|
407
|
+
55,
|
|
408
|
+
69,
|
|
409
|
+
222,
|
|
410
|
+
253,
|
|
411
|
+
142,
|
|
412
|
+
47,
|
|
413
|
+
3,
|
|
414
|
+
255,
|
|
415
|
+
106,
|
|
416
|
+
114,
|
|
417
|
+
109,
|
|
418
|
+
108,
|
|
419
|
+
91,
|
|
420
|
+
81,
|
|
421
|
+
141,
|
|
422
|
+
27,
|
|
423
|
+
175,
|
|
424
|
+
146,
|
|
425
|
+
187,
|
|
426
|
+
221,
|
|
427
|
+
188,
|
|
428
|
+
127,
|
|
429
|
+
17,
|
|
430
|
+
217,
|
|
431
|
+
92,
|
|
432
|
+
65,
|
|
433
|
+
31,
|
|
434
|
+
16,
|
|
435
|
+
90,
|
|
436
|
+
216,
|
|
437
|
+
10,
|
|
438
|
+
193,
|
|
439
|
+
49,
|
|
440
|
+
136,
|
|
441
|
+
165,
|
|
442
|
+
205,
|
|
443
|
+
123,
|
|
444
|
+
189,
|
|
445
|
+
45,
|
|
446
|
+
116,
|
|
447
|
+
208,
|
|
448
|
+
18,
|
|
449
|
+
184,
|
|
450
|
+
229,
|
|
451
|
+
180,
|
|
452
|
+
176,
|
|
453
|
+
137,
|
|
454
|
+
105,
|
|
455
|
+
151,
|
|
456
|
+
74,
|
|
457
|
+
12,
|
|
458
|
+
150,
|
|
459
|
+
119,
|
|
460
|
+
126,
|
|
461
|
+
101,
|
|
462
|
+
185,
|
|
463
|
+
241,
|
|
464
|
+
9,
|
|
465
|
+
197,
|
|
466
|
+
110,
|
|
467
|
+
198,
|
|
468
|
+
132,
|
|
469
|
+
24,
|
|
470
|
+
240,
|
|
471
|
+
125,
|
|
472
|
+
236,
|
|
473
|
+
58,
|
|
474
|
+
220,
|
|
475
|
+
77,
|
|
476
|
+
32,
|
|
477
|
+
121,
|
|
478
|
+
238,
|
|
479
|
+
95,
|
|
480
|
+
62,
|
|
481
|
+
215,
|
|
482
|
+
203,
|
|
483
|
+
57,
|
|
484
|
+
72
|
|
485
|
+
];
|
|
486
|
+
var CK = [
|
|
487
|
+
462357,
|
|
488
|
+
472066609,
|
|
489
|
+
943670861,
|
|
490
|
+
1415275113,
|
|
491
|
+
1886879365,
|
|
492
|
+
2358483617,
|
|
493
|
+
2830087869,
|
|
494
|
+
3301692121,
|
|
495
|
+
3773296373,
|
|
496
|
+
4228057617,
|
|
497
|
+
404694573,
|
|
498
|
+
876298825,
|
|
499
|
+
1347903077,
|
|
500
|
+
1819507329,
|
|
501
|
+
2291111581,
|
|
502
|
+
2762715833,
|
|
503
|
+
3234320085,
|
|
504
|
+
3705924337,
|
|
505
|
+
4177462797,
|
|
506
|
+
337322537,
|
|
507
|
+
808926789,
|
|
508
|
+
1280531041,
|
|
509
|
+
1752135293,
|
|
510
|
+
2223739545,
|
|
511
|
+
2695343797,
|
|
512
|
+
3166948049,
|
|
513
|
+
3638552301,
|
|
514
|
+
4110090761,
|
|
515
|
+
269950501,
|
|
516
|
+
741554753,
|
|
517
|
+
1213159005,
|
|
518
|
+
1684763257
|
|
519
|
+
];
|
|
520
|
+
function rotl(x, n) {
|
|
521
|
+
return (x << n | x >>> 32 - n) >>> 0;
|
|
522
|
+
}
|
|
523
|
+
function bytesToU32(b, off) {
|
|
524
|
+
return (b[off] << 24 | b[off + 1] << 16 | b[off + 2] << 8 | b[off + 3]) >>> 0;
|
|
525
|
+
}
|
|
526
|
+
function u32ToBytes(x, b, off) {
|
|
527
|
+
b[off] = x >>> 24 & 255;
|
|
528
|
+
b[off + 1] = x >>> 16 & 255;
|
|
529
|
+
b[off + 2] = x >>> 8 & 255;
|
|
530
|
+
b[off + 3] = x & 255;
|
|
531
|
+
}
|
|
532
|
+
function tau(a) {
|
|
533
|
+
return SBOX[a >>> 24 & 255] << 24 | SBOX[a >>> 16 & 255] << 16 | SBOX[a >>> 8 & 255] << 8 | SBOX[a & 255];
|
|
534
|
+
}
|
|
535
|
+
function L(b) {
|
|
536
|
+
return b ^ rotl(b, 2) ^ rotl(b, 10) ^ rotl(b, 18) ^ rotl(b, 24);
|
|
537
|
+
}
|
|
538
|
+
function Lp(b) {
|
|
539
|
+
return b ^ rotl(b, 13) ^ rotl(b, 23);
|
|
540
|
+
}
|
|
541
|
+
function T(x) {
|
|
542
|
+
return L(tau(x));
|
|
543
|
+
}
|
|
544
|
+
function Tp(x) {
|
|
545
|
+
return Lp(tau(x));
|
|
546
|
+
}
|
|
547
|
+
function expandKey(key) {
|
|
548
|
+
const MK = new Array(4);
|
|
549
|
+
for (let i = 0; i < 4; i++) MK[i] = bytesToU32(key, i * 4);
|
|
550
|
+
const rk = new Array(32);
|
|
551
|
+
for (let i = 0; i < 32; i++) {
|
|
552
|
+
const k1 = MK[i] ^ CK[i];
|
|
553
|
+
rk[i] = k1 ^ Tp(k1 ^ MK[(i + 1) % 4] ^ MK[(i + 2) % 4] ^ MK[(i + 3) % 4]);
|
|
554
|
+
}
|
|
555
|
+
return rk;
|
|
556
|
+
}
|
|
557
|
+
function cryptBlock(input, off, rk) {
|
|
558
|
+
const X = new Array(36);
|
|
559
|
+
for (let i = 0; i < 4; i++) X[i] = bytesToU32(input, off + i * 4);
|
|
560
|
+
for (let i = 0; i < 32; i++) {
|
|
561
|
+
X[i + 4] = X[i] ^ T(X[i + 1] ^ X[i + 2] ^ X[i + 3] ^ rk[i]);
|
|
562
|
+
}
|
|
563
|
+
return [X[35], X[34], X[33], X[32]];
|
|
564
|
+
}
|
|
565
|
+
function pkcs7Pad2(data, blockSize) {
|
|
566
|
+
const pad = blockSize - data.length % blockSize;
|
|
567
|
+
return Buffer.concat([data, Buffer.alloc(pad, pad)]);
|
|
568
|
+
}
|
|
569
|
+
function pkcs7Unpad2(data) {
|
|
570
|
+
if (data.length === 0) return data;
|
|
571
|
+
const pad = data[data.length - 1];
|
|
572
|
+
if (pad < 1 || pad > data.length) throw new SecureError(1001 /* DECRYPTION_FAILED */, "PKCS#7 padding \u65E0\u6548");
|
|
573
|
+
return data.subarray(0, data.length - pad);
|
|
574
|
+
}
|
|
575
|
+
function xorBlock(a, b) {
|
|
576
|
+
const out = Buffer.alloc(a.length);
|
|
577
|
+
for (let i = 0; i < a.length; i++) out[i] = a[i] ^ b[i];
|
|
578
|
+
return out;
|
|
579
|
+
}
|
|
580
|
+
var sm4Cbc = {
|
|
581
|
+
name: "sm4-cbc",
|
|
582
|
+
meta: META2,
|
|
583
|
+
generateKey() {
|
|
584
|
+
return nodeCrypto3.randomBytes(META2.keyLength);
|
|
585
|
+
},
|
|
586
|
+
generateIv() {
|
|
587
|
+
return nodeCrypto3.randomBytes(META2.ivLength);
|
|
588
|
+
},
|
|
589
|
+
encrypt(plain, params = {}) {
|
|
590
|
+
if (!params.key || params.key.length !== 16) {
|
|
591
|
+
throw new SecureError(1005 /* INVALID_KEY_LENGTH */, "SM4 \u5BC6\u94A5\u9700 16 \u5B57\u8282");
|
|
592
|
+
}
|
|
593
|
+
const rk = expandKey(params.key);
|
|
594
|
+
const iv = nodeCrypto3.randomBytes(META2.ivLength);
|
|
595
|
+
const padded = pkcs7Pad2(plain, META2.blockSize);
|
|
596
|
+
const out = Buffer.alloc(padded.length);
|
|
597
|
+
let prev = iv;
|
|
598
|
+
for (let off = 0; off < padded.length; off += 16) {
|
|
599
|
+
const block = xorBlock(padded.subarray(off, off + 16), prev);
|
|
600
|
+
const r = cryptBlock(block, 0, rk);
|
|
601
|
+
const enc = Buffer.alloc(16);
|
|
602
|
+
for (let i = 0; i < 4; i++) u32ToBytes(r[i], enc, i * 4);
|
|
603
|
+
out.set(enc, off);
|
|
604
|
+
prev = enc;
|
|
605
|
+
}
|
|
606
|
+
return { data: out, iv };
|
|
607
|
+
},
|
|
608
|
+
decrypt(cipherBuf, params) {
|
|
609
|
+
if (!params.key || params.key.length !== 16) {
|
|
610
|
+
throw new SecureError(1005 /* INVALID_KEY_LENGTH */, "SM4 \u5BC6\u94A5\u9700 16 \u5B57\u8282");
|
|
611
|
+
}
|
|
612
|
+
if (params.iv.length !== 16) {
|
|
613
|
+
throw new SecureError(1006 /* INVALID_IV_LENGTH */, "SM4 IV \u9700 16 \u5B57\u8282");
|
|
614
|
+
}
|
|
615
|
+
const rk = expandKey(params.key).slice().reverse();
|
|
616
|
+
const out = Buffer.alloc(cipherBuf.length);
|
|
617
|
+
let prev = params.iv;
|
|
618
|
+
for (let off = 0; off < cipherBuf.length; off += 16) {
|
|
619
|
+
const enc = cipherBuf.subarray(off, off + 16);
|
|
620
|
+
const r = cryptBlock(enc, 0, rk);
|
|
621
|
+
const dec = Buffer.alloc(16);
|
|
622
|
+
for (let i = 0; i < 4; i++) u32ToBytes(r[i], dec, i * 4);
|
|
623
|
+
const plain = xorBlock(dec, prev);
|
|
624
|
+
out.set(plain, off);
|
|
625
|
+
prev = enc;
|
|
626
|
+
}
|
|
627
|
+
return pkcs7Unpad2(out);
|
|
628
|
+
}
|
|
629
|
+
};
|
|
630
|
+
|
|
631
|
+
// src/algorithms/cipher/rsa-oaep-aes-gcm.ts
|
|
632
|
+
var nodeCrypto4 = __toESM(require("crypto"));
|
|
633
|
+
var META3 = {
|
|
634
|
+
name: "rsa-oaep-aes-256-gcm",
|
|
635
|
+
keyLength: 32,
|
|
636
|
+
// 对称密钥长度
|
|
637
|
+
ivLength: 12,
|
|
638
|
+
// GCM IV 长度
|
|
639
|
+
hasAuthTag: true,
|
|
640
|
+
blockSize: 16
|
|
641
|
+
};
|
|
642
|
+
var rsaOaepAesGcm = {
|
|
643
|
+
name: "rsa-oaep-aes-256-gcm",
|
|
644
|
+
meta: META3,
|
|
645
|
+
generateKey() {
|
|
646
|
+
return nodeCrypto4.randomBytes(META3.keyLength);
|
|
647
|
+
},
|
|
648
|
+
generateIv() {
|
|
649
|
+
return nodeCrypto4.randomBytes(META3.ivLength);
|
|
650
|
+
},
|
|
651
|
+
encrypt(plain, params = {}) {
|
|
652
|
+
if (!params.rsaPublicKeyPem) {
|
|
653
|
+
throw new SecureError(1e3 /* ENCRYPTION_FAILED */, "rsa-oaep-aes-256-gcm \u9700\u8981 rsaPublicKeyPem");
|
|
654
|
+
}
|
|
655
|
+
const aesKey = nodeCrypto4.randomBytes(32);
|
|
656
|
+
const iv = nodeCrypto4.randomBytes(12);
|
|
657
|
+
const r = aesGcm.encrypt(plain, { key: aesKey, iv });
|
|
658
|
+
const encryptedKey = nodeCrypto4.publicEncrypt(
|
|
659
|
+
{ key: params.rsaPublicKeyPem, padding: nodeCrypto4.constants.RSA_PKCS1_OAEP_PADDING, oaepHash: "sha256" },
|
|
660
|
+
aesKey
|
|
661
|
+
);
|
|
662
|
+
return { data: r.data, iv: r.iv, tag: r.tag, encryptedKey };
|
|
663
|
+
},
|
|
664
|
+
decrypt(cipherBuf, params) {
|
|
665
|
+
if (!params.rsaPrivateKeyPem) {
|
|
666
|
+
throw new SecureError(1001 /* DECRYPTION_FAILED */, "rsa-oaep-aes-256-gcm \u9700\u8981 rsaPrivateKeyPem");
|
|
667
|
+
}
|
|
668
|
+
if (!params.encryptedKey) {
|
|
669
|
+
throw new SecureError(1001 /* DECRYPTION_FAILED */, "\u7F3A\u5C11 encryptedKey");
|
|
670
|
+
}
|
|
671
|
+
const aesKey = nodeCrypto4.privateDecrypt(
|
|
672
|
+
{ key: params.rsaPrivateKeyPem, padding: nodeCrypto4.constants.RSA_PKCS1_OAEP_PADDING, oaepHash: "sha256" },
|
|
673
|
+
params.encryptedKey
|
|
674
|
+
);
|
|
675
|
+
return aesGcm.decrypt(cipherBuf, { key: aesKey, iv: params.iv, tag: params.tag });
|
|
676
|
+
}
|
|
677
|
+
};
|
|
678
|
+
|
|
679
|
+
// src/algorithms/digest/hmac.ts
|
|
680
|
+
var nodeCrypto5 = __toESM(require("crypto"));
|
|
681
|
+
var META_2562 = {
|
|
682
|
+
name: "hmac-sha256",
|
|
683
|
+
outputLength: 32,
|
|
684
|
+
keyed: true,
|
|
685
|
+
minKeyLength: 16
|
|
686
|
+
};
|
|
687
|
+
var META_512 = {
|
|
688
|
+
name: "hmac-sha512",
|
|
689
|
+
outputLength: 64,
|
|
690
|
+
keyed: true,
|
|
691
|
+
minKeyLength: 16
|
|
692
|
+
};
|
|
693
|
+
function makeHmac(meta, algo) {
|
|
694
|
+
return {
|
|
695
|
+
name: meta.name,
|
|
696
|
+
meta,
|
|
697
|
+
sign(data, key) {
|
|
698
|
+
if (!key) throw new SecureError(1005 /* INVALID_KEY_LENGTH */, `${meta.name} \u9700\u8981\u5BC6\u94A5`);
|
|
699
|
+
const keyBuf = typeof key === "string" ? Buffer.from(key, "utf-8") : key;
|
|
700
|
+
if (keyBuf.length < meta.minKeyLength) {
|
|
701
|
+
throw new SecureError(1005 /* INVALID_KEY_LENGTH */, `${meta.name} \u5BC6\u94A5\u81F3\u5C11 ${meta.minKeyLength} \u5B57\u8282`);
|
|
702
|
+
}
|
|
703
|
+
const dataBuf = typeof data === "string" ? Buffer.from(data, "utf-8") : data;
|
|
704
|
+
return nodeCrypto5.createHmac(algo, keyBuf).update(dataBuf).digest("hex");
|
|
705
|
+
},
|
|
706
|
+
verify(sig, data, key) {
|
|
707
|
+
const expected = this.sign(data, key);
|
|
708
|
+
try {
|
|
709
|
+
return nodeCrypto5.timingSafeEqual(Buffer.from(sig, "hex"), Buffer.from(expected, "hex"));
|
|
710
|
+
} catch {
|
|
711
|
+
return false;
|
|
712
|
+
}
|
|
713
|
+
}
|
|
714
|
+
};
|
|
715
|
+
}
|
|
716
|
+
var hmacSha256 = makeHmac(META_2562, "sha256");
|
|
717
|
+
var hmacSha512 = makeHmac(META_512, "sha512");
|
|
718
|
+
|
|
719
|
+
// src/algorithms/digest/hash.ts
|
|
720
|
+
var nodeCrypto6 = __toESM(require("crypto"));
|
|
721
|
+
var META_MD5 = {
|
|
722
|
+
name: "md5",
|
|
723
|
+
outputLength: 16,
|
|
724
|
+
keyed: false,
|
|
725
|
+
minKeyLength: 0
|
|
726
|
+
};
|
|
727
|
+
var META_SHA256 = {
|
|
728
|
+
name: "sha256",
|
|
729
|
+
outputLength: 32,
|
|
730
|
+
keyed: false,
|
|
731
|
+
minKeyLength: 0
|
|
732
|
+
};
|
|
733
|
+
function makeHash(meta, algo) {
|
|
734
|
+
return {
|
|
735
|
+
name: meta.name,
|
|
736
|
+
meta,
|
|
737
|
+
sign(data) {
|
|
738
|
+
const dataBuf = typeof data === "string" ? Buffer.from(data, "utf-8") : data;
|
|
739
|
+
return nodeCrypto6.createHash(algo).update(dataBuf).digest("hex");
|
|
740
|
+
},
|
|
741
|
+
// 非密钥散列的"verify"是直接比较
|
|
742
|
+
verify(sig, data) {
|
|
743
|
+
try {
|
|
744
|
+
return this.sign(data) === sig.toLowerCase();
|
|
745
|
+
} catch {
|
|
746
|
+
return false;
|
|
747
|
+
}
|
|
748
|
+
}
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
var md5 = makeHash(META_MD5, "md5");
|
|
752
|
+
var sha256 = makeHash(META_SHA256, "sha256");
|
|
753
|
+
|
|
754
|
+
// src/algorithms/digest/sm3.ts
|
|
755
|
+
var META4 = {
|
|
756
|
+
name: "sm3",
|
|
757
|
+
outputLength: 32,
|
|
758
|
+
keyed: false,
|
|
759
|
+
minKeyLength: 0
|
|
760
|
+
};
|
|
761
|
+
var IV = new Uint32Array([
|
|
762
|
+
1937774191,
|
|
763
|
+
1226093241,
|
|
764
|
+
388252375,
|
|
765
|
+
3666478592,
|
|
766
|
+
2842636476,
|
|
767
|
+
372324522,
|
|
768
|
+
3817729613,
|
|
769
|
+
2969243214
|
|
770
|
+
]);
|
|
771
|
+
function rotl2(x, n) {
|
|
772
|
+
return (x << n | x >>> 32 - n) >>> 0;
|
|
773
|
+
}
|
|
774
|
+
function P0(x) {
|
|
775
|
+
return x ^ rotl2(x, 9) ^ rotl2(x, 17);
|
|
776
|
+
}
|
|
777
|
+
function P1(x) {
|
|
778
|
+
return x ^ rotl2(x, 15) ^ rotl2(x, 23);
|
|
779
|
+
}
|
|
780
|
+
function FF(x, y, z, j) {
|
|
781
|
+
return j < 16 ? x ^ y ^ z : x & y | x & z | y & z;
|
|
782
|
+
}
|
|
783
|
+
function GG(x, y, z, j) {
|
|
784
|
+
return j < 16 ? x ^ y ^ z : x & y | (~x & z) >>> 0;
|
|
785
|
+
}
|
|
786
|
+
function Tj(j) {
|
|
787
|
+
return j < 16 ? 2043430169 : 2055708042;
|
|
788
|
+
}
|
|
789
|
+
function CF(V, B) {
|
|
790
|
+
const W = new Uint32Array(68);
|
|
791
|
+
const W1 = new Uint32Array(64);
|
|
792
|
+
for (let i = 0; i < 16; i++) W[i] = B[i];
|
|
793
|
+
for (let i = 16; i < 68; i++) {
|
|
794
|
+
W[i] = P1(W[i - 16] ^ W[i - 9] ^ rotl2(W[i - 3], 15)) ^ rotl2(W[i - 13], 7) ^ W[i - 6];
|
|
795
|
+
}
|
|
796
|
+
for (let i = 0; i < 64; i++) W1[i] = W[i] ^ W[i + 4];
|
|
797
|
+
let A = V[0], Bb = V[1], C = V[2], D = V[3];
|
|
798
|
+
let E = V[4], F = V[5], G = V[6], H = V[7];
|
|
799
|
+
for (let j = 0; j < 64; j++) {
|
|
800
|
+
const SS1 = rotl2(rotl2(A, 12) + E + rotl2(Tj(j), j), 7);
|
|
801
|
+
const SS2 = SS1 ^ rotl2(A, 12);
|
|
802
|
+
const TT1 = FF(A, Bb, C, j) + D + SS2 + W1[j] >>> 0;
|
|
803
|
+
const TT2 = GG(E, F, G, j) + H + SS1 + W[j] >>> 0;
|
|
804
|
+
D = C;
|
|
805
|
+
C = rotl2(Bb, 9);
|
|
806
|
+
Bb = A;
|
|
807
|
+
A = TT1;
|
|
808
|
+
H = G;
|
|
809
|
+
G = rotl2(F, 19);
|
|
810
|
+
F = E;
|
|
811
|
+
E = P0(TT2);
|
|
812
|
+
}
|
|
813
|
+
return new Uint32Array([
|
|
814
|
+
V[0] ^ A,
|
|
815
|
+
V[1] ^ Bb,
|
|
816
|
+
V[2] ^ C,
|
|
817
|
+
V[3] ^ D,
|
|
818
|
+
V[4] ^ E,
|
|
819
|
+
V[5] ^ F,
|
|
820
|
+
V[6] ^ G,
|
|
821
|
+
V[7] ^ H
|
|
822
|
+
]);
|
|
823
|
+
}
|
|
824
|
+
function sm3Hash(data) {
|
|
825
|
+
const bitLen = BigInt(data.length) * 8n;
|
|
826
|
+
const padLen = (56 - (data.length + 1) % 64 + 64) % 64;
|
|
827
|
+
const padded = Buffer.alloc(data.length + 1 + padLen + 8);
|
|
828
|
+
data.copy(padded, 0);
|
|
829
|
+
padded[data.length] = 128;
|
|
830
|
+
for (let i = 0; i < 8; i++) {
|
|
831
|
+
padded[padded.length - 1 - i] = Number(bitLen >> BigInt(i * 8) & 0xffn);
|
|
832
|
+
}
|
|
833
|
+
const V = new Uint32Array(IV);
|
|
834
|
+
for (let off = 0; off < padded.length; off += 64) {
|
|
835
|
+
const B = new Uint32Array(16);
|
|
836
|
+
for (let i = 0; i < 16; i++) {
|
|
837
|
+
B[i] = padded.readUInt32BE(off + i * 4);
|
|
838
|
+
}
|
|
839
|
+
const Vn = CF(V, B);
|
|
840
|
+
for (let i = 0; i < 8; i++) V[i] = Vn[i];
|
|
841
|
+
}
|
|
842
|
+
const out = Buffer.alloc(32);
|
|
843
|
+
for (let i = 0; i < 8; i++) V[i] = V[i] >>> 0, out.writeUInt32BE(V[i], i * 4);
|
|
844
|
+
return out;
|
|
845
|
+
}
|
|
846
|
+
var sm3 = {
|
|
847
|
+
name: "sm3",
|
|
848
|
+
meta: META4,
|
|
849
|
+
sign(data) {
|
|
850
|
+
const buf = typeof data === "string" ? Buffer.from(data, "utf-8") : data;
|
|
851
|
+
return sm3Hash(buf).toString("hex");
|
|
852
|
+
},
|
|
853
|
+
verify(sig, data) {
|
|
854
|
+
try {
|
|
855
|
+
return this.sign(data) === sig.toLowerCase();
|
|
856
|
+
} catch {
|
|
857
|
+
return false;
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
};
|
|
861
|
+
|
|
862
|
+
// src/algorithms/registry.ts
|
|
863
|
+
var cipherRegistry = /* @__PURE__ */ new Map([
|
|
864
|
+
["aes-256-gcm", aesGcm],
|
|
865
|
+
["aes-256-cbc", aesCbc256],
|
|
866
|
+
["aes-128-cbc", aesCbc128],
|
|
867
|
+
["sm4-cbc", sm4Cbc],
|
|
868
|
+
["rsa-oaep-aes-256-gcm", rsaOaepAesGcm]
|
|
869
|
+
]);
|
|
870
|
+
function getCipher(name) {
|
|
871
|
+
const c = cipherRegistry.get(name);
|
|
872
|
+
if (!c) {
|
|
873
|
+
throw new SecureError(1003 /* UNSUPPORTED_CIPHER */, `Unsupported cipher: ${name}`);
|
|
874
|
+
}
|
|
875
|
+
return c;
|
|
876
|
+
}
|
|
877
|
+
var digestRegistry = /* @__PURE__ */ new Map([
|
|
878
|
+
["hmac-sha256", hmacSha256],
|
|
879
|
+
["hmac-sha512", hmacSha512],
|
|
880
|
+
["md5", md5],
|
|
881
|
+
["sha256", sha256],
|
|
882
|
+
["sm3", sm3]
|
|
883
|
+
]);
|
|
884
|
+
function getDigest(name) {
|
|
885
|
+
const d = digestRegistry.get(name);
|
|
886
|
+
if (!d) {
|
|
887
|
+
throw new SecureError(1004 /* UNSUPPORTED_DIGEST */, `Unsupported digest: ${name}`);
|
|
888
|
+
}
|
|
889
|
+
return d;
|
|
890
|
+
}
|
|
891
|
+
function parseKeyHex(hex, expectedLength = 32) {
|
|
892
|
+
if (!hex || hex.length !== expectedLength * 2) {
|
|
893
|
+
throw new SecureError(
|
|
894
|
+
1005 /* INVALID_KEY_LENGTH */,
|
|
895
|
+
`Key hex \u957F\u5EA6\u9700\u4E3A ${expectedLength * 2} \u5B57\u7B26(\u5BF9\u5E94 ${expectedLength} \u5B57\u8282)`
|
|
896
|
+
);
|
|
897
|
+
}
|
|
898
|
+
return Buffer.from(hex, "hex");
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
// src/config/key-store.ts
|
|
902
|
+
var KeyStore = class _KeyStore {
|
|
903
|
+
constructor() {
|
|
904
|
+
this.keys = /* @__PURE__ */ new Map();
|
|
905
|
+
}
|
|
906
|
+
/** 从 apps 推导默认密钥材料:对称密钥 = SHA-256(appSecret),签名密钥 = appSecret */
|
|
907
|
+
static deriveFromApp(app) {
|
|
908
|
+
const hmacSecret = app.appSecret;
|
|
909
|
+
const encryptionKey = nodeCrypto7.createHash("sha256").update(app.appSecret).digest();
|
|
910
|
+
return { encryptionKey, hmacSecret };
|
|
911
|
+
}
|
|
912
|
+
set(appKey, key) {
|
|
913
|
+
this.keys.set(appKey, key);
|
|
914
|
+
}
|
|
915
|
+
/** 懒加载:未显式 set 时,自动 derive */
|
|
916
|
+
get(app) {
|
|
917
|
+
let k = this.keys.get(app.appKey);
|
|
918
|
+
if (!k) {
|
|
919
|
+
k = _KeyStore.deriveFromApp(app);
|
|
920
|
+
this.keys.set(app.appKey, k);
|
|
921
|
+
}
|
|
922
|
+
return k;
|
|
923
|
+
}
|
|
924
|
+
/** 用 hex 显式注入对称密钥(给"对称密钥"和"签名密钥"分离的场景) */
|
|
925
|
+
setHex(appKey, encryptionKeyHex, hmacSecret, rsa) {
|
|
926
|
+
const k = {
|
|
927
|
+
encryptionKey: parseKeyHex(encryptionKeyHex),
|
|
928
|
+
hmacSecret,
|
|
929
|
+
rsaPublicKeyPem: rsa?.publicKey,
|
|
930
|
+
rsaPrivateKeyPem: rsa?.privateKey
|
|
931
|
+
};
|
|
932
|
+
this.keys.set(appKey, k);
|
|
933
|
+
}
|
|
934
|
+
};
|
|
935
|
+
|
|
936
|
+
// src/protocol/custom/strategy.ts
|
|
937
|
+
var nodeCrypto8 = __toESM(require("crypto"));
|
|
938
|
+
var PROTOCOL = "custom";
|
|
939
|
+
var customProtocol = {
|
|
940
|
+
kind: PROTOCOL,
|
|
941
|
+
async buildRequest(input, ctx) {
|
|
942
|
+
const cipherName = input.cipher || "aes-256-gcm";
|
|
943
|
+
const cipher = getCipher(cipherName);
|
|
944
|
+
const digest = getDigest(input.digest || "hmac-sha256");
|
|
945
|
+
const payload = JSON.stringify({
|
|
946
|
+
path: input.path,
|
|
947
|
+
method: input.method,
|
|
948
|
+
body: input.body,
|
|
949
|
+
query: input.query
|
|
950
|
+
});
|
|
951
|
+
const r = cipher.encrypt(Buffer.from(payload, "utf-8"), { key: ctx.encryptionKey });
|
|
952
|
+
const timestamp = Date.now();
|
|
953
|
+
const nonce = nodeCrypto8.randomUUID();
|
|
954
|
+
const signData = `${timestamp}.${nonce}.${r.data.toString("base64")}.${r.iv.toString("base64")}.${(r.tag ?? Buffer.alloc(0)).toString("base64")}`;
|
|
955
|
+
const signature = digest.sign(signData, ctx.hmacSecret);
|
|
956
|
+
const envelope = {
|
|
957
|
+
protocol: PROTOCOL,
|
|
958
|
+
app_key: ctx.appKey,
|
|
959
|
+
cipher: cipherName,
|
|
960
|
+
encryptedPayload: r.data.toString("base64"),
|
|
961
|
+
iv: r.iv.toString("base64"),
|
|
962
|
+
timestamp,
|
|
963
|
+
nonce,
|
|
964
|
+
signature
|
|
965
|
+
};
|
|
966
|
+
if (r.tag) envelope.authTag = r.tag.toString("base64");
|
|
967
|
+
if (r.encryptedKey) envelope.encryptedKey = r.encryptedKey.toString("base64");
|
|
968
|
+
return envelope;
|
|
969
|
+
},
|
|
970
|
+
async parseAndVerify(raw, ctx) {
|
|
971
|
+
const encryptedPayload = raw.encryptedPayload;
|
|
972
|
+
const iv = raw.iv;
|
|
973
|
+
const authTag = raw.authTag;
|
|
974
|
+
const signature = raw.signature;
|
|
975
|
+
const timestamp = raw.timestamp;
|
|
976
|
+
const nonce = raw.nonce;
|
|
977
|
+
const encryptedKey = raw.encryptedKey;
|
|
978
|
+
const cipherName = raw.cipher || "aes-256-gcm";
|
|
979
|
+
if (!encryptedPayload || !iv || !signature || !timestamp || !nonce) {
|
|
980
|
+
throw new SecureError(1301 /* INVALID_REQUEST */, "Custom \u534F\u8BAE\u5B57\u6BB5\u7F3A\u5931");
|
|
981
|
+
}
|
|
982
|
+
validateTimestamp(timestamp, ctx.requestWindowMs);
|
|
983
|
+
await checkNonce(ctx.nonceStore, nonce, ctx.requestWindowMs);
|
|
984
|
+
const digest = getDigest("hmac-sha256");
|
|
985
|
+
const signData = `${timestamp}.${nonce}.${encryptedPayload}.${iv}.${authTag ?? ""}`;
|
|
986
|
+
if (!digest.verify(signature, signData, ctx.app.appSecret)) {
|
|
987
|
+
throw new SecureError(1101 /* SIGNATURE_MISMATCH */, "Custom \u534F\u8BAE\u7B7E\u540D\u9A8C\u8BC1\u5931\u8D25");
|
|
988
|
+
}
|
|
989
|
+
const cipher = getCipher(cipherName);
|
|
990
|
+
const plain = cipher.decrypt(Buffer.from(encryptedPayload, "base64"), {
|
|
991
|
+
key: ctx.encryptionKey,
|
|
992
|
+
iv: Buffer.from(iv, "base64"),
|
|
993
|
+
tag: authTag ? Buffer.from(authTag, "base64") : void 0
|
|
994
|
+
});
|
|
995
|
+
const json = JSON.parse(plain.toString("utf-8"));
|
|
996
|
+
return {
|
|
997
|
+
appKey: ctx.app.appKey,
|
|
998
|
+
method: json.method,
|
|
999
|
+
path: json.path,
|
|
1000
|
+
body: json.body,
|
|
1001
|
+
query: json.query,
|
|
1002
|
+
biz: json
|
|
1003
|
+
};
|
|
1004
|
+
},
|
|
1005
|
+
async buildResponse(biz, ctx) {
|
|
1006
|
+
const cipher = getCipher("aes-256-gcm");
|
|
1007
|
+
const digest = getDigest("hmac-sha256");
|
|
1008
|
+
const r = cipher.encrypt(Buffer.from(JSON.stringify(biz), "utf-8"), { key: ctx.encryptionKey });
|
|
1009
|
+
const timestamp = Date.now();
|
|
1010
|
+
const nonce = ctx.nonce || nodeCrypto8.randomUUID();
|
|
1011
|
+
const signData = `${timestamp}.${nonce}.${r.data.toString("base64")}.${r.iv.toString("base64")}.${(r.tag ?? Buffer.alloc(0)).toString("base64")}`;
|
|
1012
|
+
const signature = digest.sign(signData, ctx.app.appSecret);
|
|
1013
|
+
return {
|
|
1014
|
+
protocol: PROTOCOL,
|
|
1015
|
+
encryptedPayload: r.data.toString("base64"),
|
|
1016
|
+
iv: r.iv.toString("base64"),
|
|
1017
|
+
authTag: r.tag?.toString("base64"),
|
|
1018
|
+
signature,
|
|
1019
|
+
timestamp,
|
|
1020
|
+
nonce
|
|
1021
|
+
};
|
|
1022
|
+
}
|
|
1023
|
+
};
|
|
1024
|
+
function validateTimestamp(ts, windowMs) {
|
|
1025
|
+
const now = Date.now();
|
|
1026
|
+
if (ts > now + 5e3) throw new SecureError(1104 /* INVALID_TIMESTAMP */, "\u65F6\u95F4\u6233\u6765\u81EA\u672A\u6765");
|
|
1027
|
+
if (now - ts > windowMs) throw new SecureError(1102 /* TIMESTAMP_EXPIRED */, "\u8BF7\u6C42\u5DF2\u8FC7\u671F");
|
|
1028
|
+
}
|
|
1029
|
+
async function checkNonce(store, nonce, windowMs) {
|
|
1030
|
+
if (!store) return;
|
|
1031
|
+
const ttl = Math.ceil(windowMs * 2 / 1e3);
|
|
1032
|
+
const r = await store.set(`nonce:${nonce}`, "1", "EX", ttl, "NX");
|
|
1033
|
+
if (r === null) throw new SecureError(1103 /* NONCE_REUSED */, "nonce \u5DF2\u88AB\u4F7F\u7528");
|
|
1034
|
+
}
|
|
1035
|
+
|
|
1036
|
+
// src/protocol/top/signer.ts
|
|
1037
|
+
var SYSTEM_PARAMS = /* @__PURE__ */ new Set([
|
|
1038
|
+
"protocol",
|
|
1039
|
+
"method",
|
|
1040
|
+
"app_key",
|
|
1041
|
+
"session",
|
|
1042
|
+
"timestamp",
|
|
1043
|
+
"v",
|
|
1044
|
+
"sign_method",
|
|
1045
|
+
"sign",
|
|
1046
|
+
"format",
|
|
1047
|
+
"simplify",
|
|
1048
|
+
"target_app_key",
|
|
1049
|
+
"session_auth_token",
|
|
1050
|
+
"cipher",
|
|
1051
|
+
"digest",
|
|
1052
|
+
"biz_content",
|
|
1053
|
+
// TOP 把整个业务内容 base64 后放在这个字段
|
|
1054
|
+
"encrypted_key"
|
|
1055
|
+
// 混合加密场景
|
|
1056
|
+
]);
|
|
1057
|
+
function buildSortedParams(input, includeSystem = false) {
|
|
1058
|
+
const filtered = {};
|
|
1059
|
+
for (const [k, v] of Object.entries(input)) {
|
|
1060
|
+
if (v === void 0 || v === null) continue;
|
|
1061
|
+
if (!includeSystem && SYSTEM_PARAMS.has(k)) continue;
|
|
1062
|
+
filtered[k] = v;
|
|
1063
|
+
}
|
|
1064
|
+
const keys = Object.keys(filtered).sort();
|
|
1065
|
+
return keys.map((k) => k + stringifyVal(filtered[k])).join("");
|
|
1066
|
+
}
|
|
1067
|
+
function stringifyVal(v) {
|
|
1068
|
+
if (v === null || v === void 0) return "";
|
|
1069
|
+
if (typeof v === "object") return JSON.stringify(v);
|
|
1070
|
+
return String(v);
|
|
1071
|
+
}
|
|
1072
|
+
function signTop(opts) {
|
|
1073
|
+
const bizStr = buildSortedParams(opts.params, false);
|
|
1074
|
+
const source = bizStr + opts.appSecret + (opts.cipherText ?? "");
|
|
1075
|
+
return opts.digest.sign(source, opts.appSecret);
|
|
1076
|
+
}
|
|
1077
|
+
function verifyTop(opts) {
|
|
1078
|
+
return opts.digest.verify(opts.signature, (() => {
|
|
1079
|
+
const bizStr = buildSortedParams(opts.params, false);
|
|
1080
|
+
return bizStr + opts.appSecret + (opts.cipherText ?? "");
|
|
1081
|
+
})(), opts.appSecret);
|
|
1082
|
+
}
|
|
1083
|
+
|
|
1084
|
+
// src/protocol/top/strategy.ts
|
|
1085
|
+
var PROTOCOL2 = "top";
|
|
1086
|
+
var topProtocol = {
|
|
1087
|
+
kind: PROTOCOL2,
|
|
1088
|
+
async buildRequest(input, ctx) {
|
|
1089
|
+
const cipherName = input.cipher || "aes-256-gcm";
|
|
1090
|
+
const digestName = input.digest || "hmac-sha256";
|
|
1091
|
+
const cipher = getCipher(cipherName);
|
|
1092
|
+
const digest = getDigest(digestName);
|
|
1093
|
+
const bizJson = JSON.stringify(input.biz);
|
|
1094
|
+
const r = cipher.encrypt(Buffer.from(bizJson, "utf-8"), {
|
|
1095
|
+
key: ctx.encryptionKey,
|
|
1096
|
+
rsaPublicKeyPem: ctx.rsaPublicKeyPem
|
|
1097
|
+
});
|
|
1098
|
+
const cipherText = r.data.toString("base64");
|
|
1099
|
+
const iv = r.iv.toString("base64");
|
|
1100
|
+
const tag = r.tag?.toString("base64");
|
|
1101
|
+
const encryptedKey = r.encryptedKey?.toString("base64");
|
|
1102
|
+
const sysParams = {
|
|
1103
|
+
method: input.path,
|
|
1104
|
+
// TOP 风格:path 实际是 method 名
|
|
1105
|
+
app_key: ctx.appKey,
|
|
1106
|
+
timestamp: Date.now(),
|
|
1107
|
+
v: "2.0",
|
|
1108
|
+
sign_method: digestName,
|
|
1109
|
+
cipher: cipherName,
|
|
1110
|
+
format: "json"
|
|
1111
|
+
};
|
|
1112
|
+
if (input.session) sysParams.session = input.session;
|
|
1113
|
+
const paramsForSign = {
|
|
1114
|
+
...input.biz,
|
|
1115
|
+
method: sysParams.method,
|
|
1116
|
+
app_key: sysParams.appKey,
|
|
1117
|
+
timestamp: sysParams.timestamp,
|
|
1118
|
+
v: sysParams.v,
|
|
1119
|
+
sign_method: sysParams.sign_method,
|
|
1120
|
+
cipher: sysParams.cipher
|
|
1121
|
+
};
|
|
1122
|
+
if (input.session) paramsForSign.session = input.session;
|
|
1123
|
+
const signature = signTop({
|
|
1124
|
+
appSecret: ctx.appSecret,
|
|
1125
|
+
params: paramsForSign,
|
|
1126
|
+
cipherText,
|
|
1127
|
+
digest
|
|
1128
|
+
});
|
|
1129
|
+
const envelope = {
|
|
1130
|
+
protocol: PROTOCOL2,
|
|
1131
|
+
...sysParams,
|
|
1132
|
+
biz_content: cipherText,
|
|
1133
|
+
sign: signature
|
|
1134
|
+
};
|
|
1135
|
+
if (iv) envelope.iv = iv;
|
|
1136
|
+
if (tag) envelope.authTag = tag;
|
|
1137
|
+
if (encryptedKey) envelope.encrypted_key = encryptedKey;
|
|
1138
|
+
return envelope;
|
|
1139
|
+
},
|
|
1140
|
+
async parseAndVerify(raw, ctx) {
|
|
1141
|
+
const appKey = raw.app_key || ctx.app.appKey;
|
|
1142
|
+
if (!appKey) throw new SecureError(1203 /* MISSING_APP_KEY */, "TOP \u534F\u8BAE\u7F3A\u5C11 app_key");
|
|
1143
|
+
const method = raw.method;
|
|
1144
|
+
if (!method) throw new SecureError(1204 /* MISSING_METHOD */, "TOP \u534F\u8BAE\u7F3A\u5C11 method");
|
|
1145
|
+
const cipherName = raw.cipher || "aes-256-gcm";
|
|
1146
|
+
const digestName = raw.sign_method || "hmac-sha256";
|
|
1147
|
+
if (ctx.app.allowedCiphers?.length && !ctx.app.allowedCiphers.includes(cipherName)) {
|
|
1148
|
+
throw new SecureError(1003 /* UNSUPPORTED_CIPHER */, `app ${appKey} \u4E0D\u5141\u8BB8 cipher: ${cipherName}`);
|
|
1149
|
+
}
|
|
1150
|
+
if (ctx.app.allowedDigests?.length && !ctx.app.allowedDigests.includes(digestName)) {
|
|
1151
|
+
throw new SecureError(1105 /* SIGN_METHOD_NOT_ALLOWED */, `app ${appKey} \u4E0D\u5141\u8BB8 sign_method: ${digestName}`);
|
|
1152
|
+
}
|
|
1153
|
+
const timestamp = Number(raw.timestamp);
|
|
1154
|
+
validateTimestamp2(timestamp, ctx.requestWindowMs);
|
|
1155
|
+
await checkNonce2(ctx.nonceStore, raw.nonce || `top:${method}:${timestamp}`, ctx.requestWindowMs);
|
|
1156
|
+
const bizContent = raw.biz_content;
|
|
1157
|
+
if (!bizContent) throw new SecureError(1302 /* INVALID_PAYLOAD */, "TOP \u534F\u8BAE\u7F3A\u5C11 biz_content");
|
|
1158
|
+
const iv = raw.iv;
|
|
1159
|
+
const tag = raw.authTag;
|
|
1160
|
+
const encryptedKey = raw.encrypted_key;
|
|
1161
|
+
const cipher = getCipher(cipherName);
|
|
1162
|
+
const plain = cipher.decrypt(Buffer.from(bizContent, "base64"), {
|
|
1163
|
+
key: ctx.encryptionKey,
|
|
1164
|
+
iv: iv ? Buffer.from(iv, "base64") : Buffer.alloc(0),
|
|
1165
|
+
tag: tag ? Buffer.from(tag, "base64") : void 0,
|
|
1166
|
+
encryptedKey: encryptedKey ? Buffer.from(encryptedKey, "base64") : void 0,
|
|
1167
|
+
rsaPrivateKeyPem: ctx.rsaPrivateKeyPem
|
|
1168
|
+
});
|
|
1169
|
+
const biz = JSON.parse(plain.toString("utf-8"));
|
|
1170
|
+
const digest = getDigest(digestName);
|
|
1171
|
+
const paramsForSign = {
|
|
1172
|
+
...biz,
|
|
1173
|
+
method,
|
|
1174
|
+
app_key: appKey,
|
|
1175
|
+
timestamp,
|
|
1176
|
+
v: raw.v,
|
|
1177
|
+
sign_method: digestName,
|
|
1178
|
+
cipher: cipherName
|
|
1179
|
+
};
|
|
1180
|
+
if (raw.session) paramsForSign.session = raw.session;
|
|
1181
|
+
const ok = verifyTop({
|
|
1182
|
+
appSecret: ctx.app.appSecret,
|
|
1183
|
+
params: paramsForSign,
|
|
1184
|
+
cipherText: bizContent,
|
|
1185
|
+
digest,
|
|
1186
|
+
signature: raw.sign
|
|
1187
|
+
});
|
|
1188
|
+
if (!ok) throw new SecureError(1101 /* SIGNATURE_MISMATCH */, "TOP \u534F\u8BAE\u7B7E\u540D\u9A8C\u8BC1\u5931\u8D25");
|
|
1189
|
+
return {
|
|
1190
|
+
appKey,
|
|
1191
|
+
method,
|
|
1192
|
+
path: method,
|
|
1193
|
+
session: raw.session,
|
|
1194
|
+
biz
|
|
1195
|
+
};
|
|
1196
|
+
},
|
|
1197
|
+
async buildResponse(biz, ctx) {
|
|
1198
|
+
const cipherName = "aes-256-gcm";
|
|
1199
|
+
const digestName = "hmac-sha256";
|
|
1200
|
+
const cipher = getCipher(cipherName);
|
|
1201
|
+
const digest = getDigest(digestName);
|
|
1202
|
+
const r = cipher.encrypt(Buffer.from(JSON.stringify(biz), "utf-8"), { key: ctx.encryptionKey });
|
|
1203
|
+
const cipherText = r.data.toString("base64");
|
|
1204
|
+
const iv = r.iv.toString("base64");
|
|
1205
|
+
const tag = r.tag?.toString("base64");
|
|
1206
|
+
const timestamp = Date.now();
|
|
1207
|
+
const paramsForSign = {
|
|
1208
|
+
app_key: ctx.app.appKey,
|
|
1209
|
+
timestamp,
|
|
1210
|
+
sign_method: digestName,
|
|
1211
|
+
cipher: cipherName
|
|
1212
|
+
};
|
|
1213
|
+
const sign = signTop({ appSecret: ctx.app.appSecret, params: paramsForSign, cipherText, digest });
|
|
1214
|
+
return {
|
|
1215
|
+
protocol: PROTOCOL2,
|
|
1216
|
+
app_key: ctx.app.appKey,
|
|
1217
|
+
timestamp,
|
|
1218
|
+
v: "2.0",
|
|
1219
|
+
sign_method: digestName,
|
|
1220
|
+
cipher: cipherName,
|
|
1221
|
+
biz_content: cipherText,
|
|
1222
|
+
iv,
|
|
1223
|
+
authTag: tag,
|
|
1224
|
+
sign
|
|
1225
|
+
};
|
|
1226
|
+
}
|
|
1227
|
+
};
|
|
1228
|
+
function validateTimestamp2(ts, windowMs) {
|
|
1229
|
+
const now = Date.now();
|
|
1230
|
+
if (!ts) throw new SecureError(1104 /* INVALID_TIMESTAMP */, "\u65F6\u95F4\u6233\u65E0\u6548");
|
|
1231
|
+
if (ts > now + 5e3) throw new SecureError(1104 /* INVALID_TIMESTAMP */, "\u65F6\u95F4\u6233\u6765\u81EA\u672A\u6765");
|
|
1232
|
+
if (now - ts > windowMs) throw new SecureError(1102 /* TIMESTAMP_EXPIRED */, "\u8BF7\u6C42\u5DF2\u8FC7\u671F");
|
|
1233
|
+
}
|
|
1234
|
+
async function checkNonce2(store, nonce, windowMs) {
|
|
1235
|
+
if (!store) return;
|
|
1236
|
+
const ttl = Math.ceil(windowMs * 2 / 1e3);
|
|
1237
|
+
const r = await store.set(`nonce:${nonce}`, "1", "EX", ttl, "NX");
|
|
1238
|
+
if (r === null) throw new SecureError(1103 /* NONCE_REUSED */, "nonce \u5DF2\u88AB\u4F7F\u7528");
|
|
1239
|
+
}
|
|
1240
|
+
|
|
1241
|
+
// src/protocol/registry.ts
|
|
1242
|
+
var protocolRegistry = /* @__PURE__ */ new Map([
|
|
1243
|
+
["custom", customProtocol],
|
|
1244
|
+
["top", topProtocol]
|
|
1245
|
+
]);
|
|
1246
|
+
function getProtocol(kind) {
|
|
1247
|
+
const p = protocolRegistry.get(kind);
|
|
1248
|
+
if (!p) throw new SecureError(1202 /* PROTOCOL_NOT_FOUND */, `Unsupported protocol: ${kind}`);
|
|
1249
|
+
return p;
|
|
1250
|
+
}
|
|
1251
|
+
function pickProtocol(envelope, defaultKind = "top") {
|
|
1252
|
+
const k = envelope.protocol || defaultKind;
|
|
1253
|
+
return getProtocol(k);
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
// src/server/fastify-plugin.ts
|
|
1257
|
+
var DEFAULT_BYPASS_PATHS = ["/health", "/docs", "/docs/", "/favicon.ico", "/"];
|
|
1258
|
+
var DEFAULT_BYPASS_METHODS = ["OPTIONS", "HEAD"];
|
|
1259
|
+
var DEFAULT_REQUEST_WINDOW_MS = 5 * 60 * 1e3;
|
|
1260
|
+
function isBypass(path, methods, m, bypassPaths) {
|
|
1261
|
+
if (methods.includes(m.toUpperCase())) return true;
|
|
1262
|
+
if (path === "/") return true;
|
|
1263
|
+
return bypassPaths.some((p) => p !== "/" && (path === p || path.startsWith(p)));
|
|
1264
|
+
}
|
|
1265
|
+
async function secureCryptoPluginImpl(app, options) {
|
|
1266
|
+
if (!options.apps || options.apps.length === 0) {
|
|
1267
|
+
throw new Error("[secure-crypto-top-sdk] apps \u4E0D\u80FD\u4E3A\u7A7A");
|
|
1268
|
+
}
|
|
1269
|
+
const registry = new AppRegistry(options.apps);
|
|
1270
|
+
const keyStore = new KeyStore();
|
|
1271
|
+
const defaultProtocol = options.defaultProtocol ?? "top";
|
|
1272
|
+
app.decorate("secureCrypto", { appRegistry: registry, keyStore, defaultProtocol });
|
|
1273
|
+
const bypassPaths = options.bypassPaths ?? DEFAULT_BYPASS_PATHS;
|
|
1274
|
+
const bypassMethods = options.bypassMethods ?? DEFAULT_BYPASS_METHODS;
|
|
1275
|
+
const requestWindowMs = options.requestWindowMs ?? DEFAULT_REQUEST_WINDOW_MS;
|
|
1276
|
+
app.addHook("preValidation", async (request, reply) => {
|
|
1277
|
+
const path = request.url.split("?")[0];
|
|
1278
|
+
if (isBypass(path, bypassMethods, request.method, bypassPaths)) return;
|
|
1279
|
+
const body = request.body ?? {};
|
|
1280
|
+
if (!body || typeof body !== "object") return;
|
|
1281
|
+
try {
|
|
1282
|
+
const protocol = pickProtocol(body, defaultProtocol);
|
|
1283
|
+
const appKey = body.app_key || body.appKey;
|
|
1284
|
+
if (!appKey) {
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
1287
|
+
const appConfig = registry.get(appKey);
|
|
1288
|
+
if (appConfig.allowedProtocols?.length && !appConfig.allowedProtocols.includes(protocol.kind)) {
|
|
1289
|
+
throw new SecureError(1202 /* PROTOCOL_NOT_FOUND */, `app ${appKey} \u4E0D\u5141\u8BB8 protocol: ${protocol.kind}`);
|
|
1290
|
+
}
|
|
1291
|
+
const key = keyStore.get(appConfig);
|
|
1292
|
+
const ctx = {
|
|
1293
|
+
app: appConfig,
|
|
1294
|
+
encryptionKey: key.encryptionKey,
|
|
1295
|
+
hmacSecret: appConfig.appSecret,
|
|
1296
|
+
// 签名密钥用 appSecret
|
|
1297
|
+
rsaPrivateKeyPem: options.rsaPrivateKeyPem,
|
|
1298
|
+
nonceStore: options.redis,
|
|
1299
|
+
requestWindowMs
|
|
1300
|
+
};
|
|
1301
|
+
const parsed = await protocol.parseAndVerify(body, ctx);
|
|
1302
|
+
request.secureRequest = {
|
|
1303
|
+
appKey: parsed.appKey,
|
|
1304
|
+
method: parsed.method,
|
|
1305
|
+
path: parsed.path,
|
|
1306
|
+
body: parsed.body,
|
|
1307
|
+
query: parsed.query,
|
|
1308
|
+
session: parsed.session,
|
|
1309
|
+
biz: parsed.biz,
|
|
1310
|
+
protocol: protocol.kind
|
|
1311
|
+
};
|
|
1312
|
+
if (protocol.kind === "top") {
|
|
1313
|
+
request.body = parsed.biz;
|
|
1314
|
+
} else {
|
|
1315
|
+
request.body = parsed.body ?? parsed.biz;
|
|
1316
|
+
}
|
|
1317
|
+
if (parsed.query) {
|
|
1318
|
+
request.query = { ...request.query, ...parsed.query };
|
|
1319
|
+
}
|
|
1320
|
+
} catch (err) {
|
|
1321
|
+
if (err instanceof SecureError) {
|
|
1322
|
+
app.log.warn({ code: err.code, message: err.message }, "\u26A0\uFE0F \u52A0\u5BC6\u9A8C\u8BC1\u5931\u8D25");
|
|
1323
|
+
return reply.status(err.statusCode).send({ success: false, code: err.code, message: err.message });
|
|
1324
|
+
}
|
|
1325
|
+
app.log.error({ err }, "\u274C \u89E3\u5BC6\u8FC7\u7A0B\u5F02\u5E38");
|
|
1326
|
+
return reply.status(400).send({ success: false, code: 1001 /* DECRYPTION_FAILED */, message: "\u8BF7\u6C42\u89E3\u5BC6\u5931\u8D25" });
|
|
1327
|
+
}
|
|
1328
|
+
});
|
|
1329
|
+
app.addHook("onSend", async (request, reply, payload) => {
|
|
1330
|
+
const path = request.url.split("?")[0];
|
|
1331
|
+
if (isBypass(path, bypassMethods, request.method, bypassPaths)) return payload;
|
|
1332
|
+
if (!request.secureRequest) return payload;
|
|
1333
|
+
if (typeof payload === "string" && payload.startsWith("<?xml")) return payload;
|
|
1334
|
+
try {
|
|
1335
|
+
const protocol = getProtocol(request.secureRequest.protocol);
|
|
1336
|
+
const appConfig = registry.get(request.secureRequest.appKey);
|
|
1337
|
+
const key = keyStore.get(appConfig);
|
|
1338
|
+
const env = await protocol.buildResponse(safeJsonParse(payload), {
|
|
1339
|
+
app: appConfig,
|
|
1340
|
+
encryptionKey: key.encryptionKey,
|
|
1341
|
+
hmacSecret: appConfig.appSecret,
|
|
1342
|
+
rsaPrivateKeyPem: options.rsaPrivateKeyPem,
|
|
1343
|
+
nonceStore: options.redis,
|
|
1344
|
+
requestWindowMs
|
|
1345
|
+
});
|
|
1346
|
+
reply.header("Content-Type", "application/json; charset=utf-8");
|
|
1347
|
+
return JSON.stringify(env);
|
|
1348
|
+
} catch (err) {
|
|
1349
|
+
app.log.error({ err }, "\u274C \u54CD\u5E94\u52A0\u5BC6\u5931\u8D25");
|
|
1350
|
+
return payload;
|
|
1351
|
+
}
|
|
1352
|
+
});
|
|
1353
|
+
app.log.info(`\u{1F510} secure-crypto-top-sdk \u63D2\u4EF6\u5DF2\u6CE8\u518C (default protocol: ${defaultProtocol}, ${options.apps.length} apps)`);
|
|
1354
|
+
}
|
|
1355
|
+
function safeJsonParse(payload) {
|
|
1356
|
+
if (typeof payload === "string") {
|
|
1357
|
+
try {
|
|
1358
|
+
return JSON.parse(payload);
|
|
1359
|
+
} catch {
|
|
1360
|
+
return payload;
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
return payload;
|
|
1364
|
+
}
|
|
1365
|
+
var fastify_plugin_default = (0, import_fastify_plugin.default)(secureCryptoPluginImpl, {
|
|
1366
|
+
name: "secure-crypto-top-sdk",
|
|
1367
|
+
fastify: "4.x"
|
|
1368
|
+
});
|
|
1369
|
+
//# sourceMappingURL=server.js.map
|