3xui-api-client 2.0.0 → 2.1.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +155 -118
- package/LICENSE +20 -20
- package/README.md +374 -317
- package/index.d.ts +26 -6
- package/index.js +361 -3
- package/index.mjs +4 -4
- package/package.json +1 -1
- package/src/builders/ProtocolBuilders.js +689 -685
- package/src/generators/CredentialGenerator.js +361 -361
- package/src/middleware/WebMiddleware.js +310 -310
- package/src/security/SecurityEnhancer.js +325 -1
- package/src/session/SessionManager.js +492 -492
|
@@ -1,362 +1,362 @@
|
|
|
1
|
-
const crypto = require('crypto');
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Comprehensive credential generator for all 3x-ui supported protocols
|
|
5
|
-
* Based on research from https://github.com/MHSanaei/3x-ui
|
|
6
|
-
*/
|
|
7
|
-
class CredentialGenerator {
|
|
8
|
-
/**
|
|
9
|
-
* Generate UUID v4 for VLESS and VMess protocols
|
|
10
|
-
* Format: ecc322b8-a458-4583-ac98-e343aefb5ac5
|
|
11
|
-
* @returns {string} UUID v4 string
|
|
12
|
-
*/
|
|
13
|
-
static generateUUID() {
|
|
14
|
-
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
15
|
-
const r = Math.random() * 16 | 0;
|
|
16
|
-
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
|
17
|
-
return v.toString(16);
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Generate secure UUID using crypto module (more secure alternative)
|
|
23
|
-
* @returns {string} Cryptographically secure UUID v4
|
|
24
|
-
*/
|
|
25
|
-
static generateSecureUUID() {
|
|
26
|
-
const bytes = crypto.randomBytes(16);
|
|
27
|
-
bytes[6] = (bytes[6] & 0x0f) | 0x40; // Version 4
|
|
28
|
-
bytes[8] = (bytes[8] & 0x3f) | 0x80; // Variant 10
|
|
29
|
-
|
|
30
|
-
const hex = bytes.toString('hex');
|
|
31
|
-
return [
|
|
32
|
-
hex.slice(0, 8),
|
|
33
|
-
hex.slice(8, 12),
|
|
34
|
-
hex.slice(12, 16),
|
|
35
|
-
hex.slice(16, 20),
|
|
36
|
-
hex.slice(20, 32)
|
|
37
|
-
].join('-');
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Generate random password for Trojan and Shadowsocks protocols
|
|
42
|
-
* @param {number} length - Password length (default: 16)
|
|
43
|
-
* @param {Object} options - Password generation options
|
|
44
|
-
* @returns {string} Random password
|
|
45
|
-
*/
|
|
46
|
-
static generatePassword(length = 16, options = {}) {
|
|
47
|
-
const {
|
|
48
|
-
includeUppercase = true,
|
|
49
|
-
includeLowercase = true,
|
|
50
|
-
includeNumbers = true,
|
|
51
|
-
includeSymbols = false,
|
|
52
|
-
excludeSimilar = true
|
|
53
|
-
} = options;
|
|
54
|
-
|
|
55
|
-
let chars = '';
|
|
56
|
-
if (includeUppercase) {
|
|
57
|
-
chars += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
58
|
-
}
|
|
59
|
-
if (includeLowercase) {
|
|
60
|
-
chars += 'abcdefghijklmnopqrstuvwxyz';
|
|
61
|
-
}
|
|
62
|
-
if (includeNumbers) {
|
|
63
|
-
chars += '0123456789';
|
|
64
|
-
}
|
|
65
|
-
if (includeSymbols) {
|
|
66
|
-
chars += '!@#$%^&*()_+-=[]{}|;:,.<>?';
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Exclude similar looking characters if requested
|
|
70
|
-
if (excludeSimilar) {
|
|
71
|
-
chars = chars.replace(/[0O1lI]/g, '');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
if (chars.length === 0) {
|
|
75
|
-
throw new Error('No character sets selected for password generation');
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
return Array.from(crypto.randomBytes(length))
|
|
79
|
-
.map(byte => chars[byte % chars.length])
|
|
80
|
-
.join('');
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
/**
|
|
84
|
-
* Generate Shadowsocks2022 PSK (Pre-Shared Key)
|
|
85
|
-
* @param {string} method - Cipher method
|
|
86
|
-
* @returns {string} Base64-encoded PSK
|
|
87
|
-
*/
|
|
88
|
-
static generateShadowsocks2022PSK(method = '2022-blake3-aes-256-gcm') {
|
|
89
|
-
const keyLengths = {
|
|
90
|
-
'2022-blake3-aes-128-gcm': 16,
|
|
91
|
-
'2022-blake3-aes-256-gcm': 32,
|
|
92
|
-
'2022-blake3-chacha20-poly1305': 32
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
const keyLength = keyLengths[method];
|
|
96
|
-
if (!keyLength) {
|
|
97
|
-
throw new Error(`Unsupported Shadowsocks2022 method: ${method}`);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const keyBytes = crypto.randomBytes(keyLength);
|
|
101
|
-
return keyBytes.toString('base64');
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
/**
|
|
105
|
-
* Generate WireGuard key pair
|
|
106
|
-
* @returns {Object} Object containing private and public keys
|
|
107
|
-
*/
|
|
108
|
-
static generateWireGuardKeys() {
|
|
109
|
-
// Generate 32 random bytes for private key
|
|
110
|
-
const privateKeyBytes = crypto.randomBytes(32);
|
|
111
|
-
|
|
112
|
-
// Clamp the private key (standard WireGuard key clamping)
|
|
113
|
-
privateKeyBytes[0] &= 248;
|
|
114
|
-
privateKeyBytes[31] &= 127;
|
|
115
|
-
privateKeyBytes[31] |= 64;
|
|
116
|
-
|
|
117
|
-
const privateKey = privateKeyBytes.toString('base64');
|
|
118
|
-
|
|
119
|
-
// For the public key, we would normally use Curve25519 scalar multiplication
|
|
120
|
-
// This is a simplified implementation - in production, use a proper crypto library
|
|
121
|
-
// like noble-curves or libsodium for accurate public key derivation
|
|
122
|
-
const publicKeyBytes = crypto.randomBytes(32); // Placeholder
|
|
123
|
-
const publicKey = publicKeyBytes.toString('base64');
|
|
124
|
-
|
|
125
|
-
return {
|
|
126
|
-
privateKey,
|
|
127
|
-
publicKey,
|
|
128
|
-
// Helper method to generate client config
|
|
129
|
-
generateClientConfig: (allowedIPs = ['10.0.0.2/32']) => ({
|
|
130
|
-
privateKey,
|
|
131
|
-
publicKey,
|
|
132
|
-
allowedIPs,
|
|
133
|
-
keepAlive: 25
|
|
134
|
-
})
|
|
135
|
-
};
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
/**
|
|
139
|
-
* Generate Reality key pair for anti-censorship
|
|
140
|
-
* @returns {Object} Object containing private and public keys
|
|
141
|
-
*/
|
|
142
|
-
static generateRealityKeys() {
|
|
143
|
-
// Reality uses X25519 key exchange
|
|
144
|
-
const privateKeyBytes = crypto.randomBytes(32);
|
|
145
|
-
const privateKey = privateKeyBytes.toString('base64').replace(/\//g, '_').replace(/\+/g, '-');
|
|
146
|
-
|
|
147
|
-
// Public key derivation (simplified - use proper X25519 in production)
|
|
148
|
-
const publicKeyBytes = crypto.randomBytes(32);
|
|
149
|
-
const publicKey = publicKeyBytes.toString('base64').replace(/\//g, '_').replace(/\+/g, '-');
|
|
150
|
-
|
|
151
|
-
return {
|
|
152
|
-
privateKey,
|
|
153
|
-
publicKey,
|
|
154
|
-
// Helper method to generate Reality config
|
|
155
|
-
generateRealityConfig: (dest = 'google.com:443', serverNames = ['google.com']) => ({
|
|
156
|
-
show: false,
|
|
157
|
-
dest,
|
|
158
|
-
xver: 0,
|
|
159
|
-
serverNames,
|
|
160
|
-
privateKey,
|
|
161
|
-
shortIds: [''],
|
|
162
|
-
settings: {
|
|
163
|
-
publicKey,
|
|
164
|
-
fingerprint: 'chrome'
|
|
165
|
-
}
|
|
166
|
-
})
|
|
167
|
-
};
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
/**
|
|
171
|
-
* Generate username for SOCKS5/HTTP protocols
|
|
172
|
-
* @param {string} prefix - Optional prefix for username
|
|
173
|
-
* @returns {string} Random username
|
|
174
|
-
*/
|
|
175
|
-
static generateUsername(prefix = 'user') {
|
|
176
|
-
const randomSuffix = Math.random().toString(36).substring(2, 8);
|
|
177
|
-
return `${prefix}_${randomSuffix}`;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Generate client identifier for client identification (not a real email)
|
|
182
|
-
* @param {string} prefix - Optional prefix for identifier (default: 'client')
|
|
183
|
-
* @returns {string} Random client identifier
|
|
184
|
-
*/
|
|
185
|
-
static generateClientIdentifier(prefix = 'client') {
|
|
186
|
-
const randomSuffix = Math.random().toString(36).substring(2, 8);
|
|
187
|
-
const timestamp = Date.now().toString().slice(-4);
|
|
188
|
-
return `${prefix}_${randomSuffix}${timestamp}`;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Get recommended cipher method for Shadowsocks
|
|
193
|
-
* @returns {string} Recommended cipher method
|
|
194
|
-
*/
|
|
195
|
-
static getRecommendedShadowsocksCipher() {
|
|
196
|
-
return 'chacha20-ietf-poly1305'; // AEAD cipher, most secure and performant
|
|
197
|
-
}
|
|
198
|
-
|
|
199
|
-
/**
|
|
200
|
-
* Get available cipher methods for Shadowsocks
|
|
201
|
-
* @returns {Array} Array of supported cipher methods
|
|
202
|
-
*/
|
|
203
|
-
static getShadowsocksCipherMethods() {
|
|
204
|
-
return [
|
|
205
|
-
'chacha20-ietf-poly1305', // Recommended
|
|
206
|
-
'aes-256-gcm',
|
|
207
|
-
'aes-128-gcm',
|
|
208
|
-
'chacha20-poly1305'
|
|
209
|
-
];
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
/**
|
|
213
|
-
* Generate port number within safe range
|
|
214
|
-
* @param {number} min - Minimum port (default: 10000)
|
|
215
|
-
* @param {number} max - Maximum port (default: 65535)
|
|
216
|
-
* @returns {number} Random port number
|
|
217
|
-
*/
|
|
218
|
-
static generatePort(min = 10000, max = 65535) {
|
|
219
|
-
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
/**
|
|
223
|
-
* Generate short ID for Reality protocol
|
|
224
|
-
* @returns {string} Short ID (hex string)
|
|
225
|
-
*/
|
|
226
|
-
static generateRealityShortId() {
|
|
227
|
-
const length = Math.floor(Math.random() * 16) + 1; // 1-16 characters
|
|
228
|
-
return crypto.randomBytes(Math.ceil(length / 2))
|
|
229
|
-
.toString('hex')
|
|
230
|
-
.substring(0, length);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Generate complete credential set for a specific protocol
|
|
235
|
-
* @param {string} protocol - Protocol name
|
|
236
|
-
* @param {Object} options - Protocol-specific options
|
|
237
|
-
* @returns {Object} Complete credential set
|
|
238
|
-
*/
|
|
239
|
-
static generateForProtocol(protocol, options = {}) {
|
|
240
|
-
const email = options.email || this.generateClientIdentifier();
|
|
241
|
-
|
|
242
|
-
switch (protocol.toLowerCase()) {
|
|
243
|
-
case 'vless':
|
|
244
|
-
return {
|
|
245
|
-
id: this.generateSecureUUID(),
|
|
246
|
-
email,
|
|
247
|
-
flow: options.flow || 'xtls-rprx-vision',
|
|
248
|
-
encryption: 'none'
|
|
249
|
-
};
|
|
250
|
-
|
|
251
|
-
case 'vmess':
|
|
252
|
-
return {
|
|
253
|
-
id: this.generateSecureUUID(),
|
|
254
|
-
email,
|
|
255
|
-
level: options.level || 0,
|
|
256
|
-
alterId: options.alterId || 0
|
|
257
|
-
};
|
|
258
|
-
|
|
259
|
-
case 'trojan':
|
|
260
|
-
return {
|
|
261
|
-
password: this.generatePassword(options.passwordLength || 16),
|
|
262
|
-
email,
|
|
263
|
-
level: options.level || 0
|
|
264
|
-
};
|
|
265
|
-
|
|
266
|
-
case 'shadowsocks':
|
|
267
|
-
return {
|
|
268
|
-
method: options.method || this.getRecommendedShadowsocksCipher(),
|
|
269
|
-
password: this.generatePassword(options.passwordLength || 16),
|
|
270
|
-
email
|
|
271
|
-
};
|
|
272
|
-
|
|
273
|
-
case 'shadowsocks2022': {
|
|
274
|
-
const method = options.method || '2022-blake3-aes-256-gcm';
|
|
275
|
-
return {
|
|
276
|
-
method,
|
|
277
|
-
password: this.generateShadowsocks2022PSK(method),
|
|
278
|
-
email
|
|
279
|
-
};
|
|
280
|
-
}
|
|
281
|
-
|
|
282
|
-
case 'wireguard': {
|
|
283
|
-
const keys = this.generateWireGuardKeys();
|
|
284
|
-
return {
|
|
285
|
-
privateKey: keys.privateKey,
|
|
286
|
-
publicKey: keys.publicKey,
|
|
287
|
-
allowedIPs: options.allowedIPs || ['10.0.0.2/32'],
|
|
288
|
-
keepAlive: options.keepAlive || 25
|
|
289
|
-
};
|
|
290
|
-
}
|
|
291
|
-
|
|
292
|
-
case 'socks5':
|
|
293
|
-
case 'http':
|
|
294
|
-
return {
|
|
295
|
-
user: options.username || this.generateUsername(),
|
|
296
|
-
pass: this.generatePassword(options.passwordLength || 12),
|
|
297
|
-
email
|
|
298
|
-
};
|
|
299
|
-
|
|
300
|
-
case 'dokodemo-door':
|
|
301
|
-
return {
|
|
302
|
-
// No authentication required
|
|
303
|
-
email
|
|
304
|
-
};
|
|
305
|
-
|
|
306
|
-
default:
|
|
307
|
-
throw new Error(`Unsupported protocol: ${protocol}`);
|
|
308
|
-
}
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
/**
|
|
312
|
-
* Generate bulk credentials for multiple clients
|
|
313
|
-
* @param {string} protocol - Protocol name
|
|
314
|
-
* @param {number} count - Number of credentials to generate
|
|
315
|
-
* @param {Object} options - Generation options
|
|
316
|
-
* @returns {Array} Array of credential objects
|
|
317
|
-
*/
|
|
318
|
-
static generateBulk(protocol, count, options = {}) {
|
|
319
|
-
return Array.from({ length: count }, () =>
|
|
320
|
-
this.generateForProtocol(protocol, options)
|
|
321
|
-
);
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
/**
|
|
325
|
-
* Validate generated credentials
|
|
326
|
-
* @param {Object} credentials - Credentials to validate
|
|
327
|
-
* @param {string} protocol - Protocol name
|
|
328
|
-
* @returns {Object} Validation result
|
|
329
|
-
*/
|
|
330
|
-
static validateCredentials(credentials, protocol) {
|
|
331
|
-
const errors = [];
|
|
332
|
-
|
|
333
|
-
switch (protocol.toLowerCase()) {
|
|
334
|
-
case 'vless':
|
|
335
|
-
case 'vmess':
|
|
336
|
-
if (!credentials.id || !/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(credentials.id)) {
|
|
337
|
-
errors.push('Invalid UUID format');
|
|
338
|
-
}
|
|
339
|
-
break;
|
|
340
|
-
|
|
341
|
-
case 'trojan':
|
|
342
|
-
case 'shadowsocks':
|
|
343
|
-
if (!credentials.password || credentials.password.length < 8) {
|
|
344
|
-
errors.push('Password too short (minimum 8 characters)');
|
|
345
|
-
}
|
|
346
|
-
break;
|
|
347
|
-
|
|
348
|
-
case 'wireguard':
|
|
349
|
-
if (!credentials.privateKey || !credentials.publicKey) {
|
|
350
|
-
errors.push('Missing key pair');
|
|
351
|
-
}
|
|
352
|
-
break;
|
|
353
|
-
}
|
|
354
|
-
|
|
355
|
-
return {
|
|
356
|
-
valid: errors.length === 0,
|
|
357
|
-
errors
|
|
358
|
-
};
|
|
359
|
-
}
|
|
360
|
-
}
|
|
361
|
-
|
|
1
|
+
const crypto = require('crypto');
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Comprehensive credential generator for all 3x-ui supported protocols
|
|
5
|
+
* Based on research from https://github.com/MHSanaei/3x-ui
|
|
6
|
+
*/
|
|
7
|
+
class CredentialGenerator {
|
|
8
|
+
/**
|
|
9
|
+
* Generate UUID v4 for VLESS and VMess protocols
|
|
10
|
+
* Format: ecc322b8-a458-4583-ac98-e343aefb5ac5
|
|
11
|
+
* @returns {string} UUID v4 string
|
|
12
|
+
*/
|
|
13
|
+
static generateUUID() {
|
|
14
|
+
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
|
|
15
|
+
const r = Math.random() * 16 | 0;
|
|
16
|
+
const v = c === 'x' ? r : (r & 0x3 | 0x8);
|
|
17
|
+
return v.toString(16);
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Generate secure UUID using crypto module (more secure alternative)
|
|
23
|
+
* @returns {string} Cryptographically secure UUID v4
|
|
24
|
+
*/
|
|
25
|
+
static generateSecureUUID() {
|
|
26
|
+
const bytes = crypto.randomBytes(16);
|
|
27
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x40; // Version 4
|
|
28
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80; // Variant 10
|
|
29
|
+
|
|
30
|
+
const hex = bytes.toString('hex');
|
|
31
|
+
return [
|
|
32
|
+
hex.slice(0, 8),
|
|
33
|
+
hex.slice(8, 12),
|
|
34
|
+
hex.slice(12, 16),
|
|
35
|
+
hex.slice(16, 20),
|
|
36
|
+
hex.slice(20, 32)
|
|
37
|
+
].join('-');
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Generate random password for Trojan and Shadowsocks protocols
|
|
42
|
+
* @param {number} length - Password length (default: 16)
|
|
43
|
+
* @param {Object} options - Password generation options
|
|
44
|
+
* @returns {string} Random password
|
|
45
|
+
*/
|
|
46
|
+
static generatePassword(length = 16, options = {}) {
|
|
47
|
+
const {
|
|
48
|
+
includeUppercase = true,
|
|
49
|
+
includeLowercase = true,
|
|
50
|
+
includeNumbers = true,
|
|
51
|
+
includeSymbols = false,
|
|
52
|
+
excludeSimilar = true
|
|
53
|
+
} = options;
|
|
54
|
+
|
|
55
|
+
let chars = '';
|
|
56
|
+
if (includeUppercase) {
|
|
57
|
+
chars += 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
|
58
|
+
}
|
|
59
|
+
if (includeLowercase) {
|
|
60
|
+
chars += 'abcdefghijklmnopqrstuvwxyz';
|
|
61
|
+
}
|
|
62
|
+
if (includeNumbers) {
|
|
63
|
+
chars += '0123456789';
|
|
64
|
+
}
|
|
65
|
+
if (includeSymbols) {
|
|
66
|
+
chars += '!@#$%^&*()_+-=[]{}|;:,.<>?';
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// Exclude similar looking characters if requested
|
|
70
|
+
if (excludeSimilar) {
|
|
71
|
+
chars = chars.replace(/[0O1lI]/g, '');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (chars.length === 0) {
|
|
75
|
+
throw new Error('No character sets selected for password generation');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return Array.from(crypto.randomBytes(length))
|
|
79
|
+
.map(byte => chars[byte % chars.length])
|
|
80
|
+
.join('');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Generate Shadowsocks2022 PSK (Pre-Shared Key)
|
|
85
|
+
* @param {string} method - Cipher method
|
|
86
|
+
* @returns {string} Base64-encoded PSK
|
|
87
|
+
*/
|
|
88
|
+
static generateShadowsocks2022PSK(method = '2022-blake3-aes-256-gcm') {
|
|
89
|
+
const keyLengths = {
|
|
90
|
+
'2022-blake3-aes-128-gcm': 16,
|
|
91
|
+
'2022-blake3-aes-256-gcm': 32,
|
|
92
|
+
'2022-blake3-chacha20-poly1305': 32
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
const keyLength = keyLengths[method];
|
|
96
|
+
if (!keyLength) {
|
|
97
|
+
throw new Error(`Unsupported Shadowsocks2022 method: ${method}`);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const keyBytes = crypto.randomBytes(keyLength);
|
|
101
|
+
return keyBytes.toString('base64');
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Generate WireGuard key pair
|
|
106
|
+
* @returns {Object} Object containing private and public keys
|
|
107
|
+
*/
|
|
108
|
+
static generateWireGuardKeys() {
|
|
109
|
+
// Generate 32 random bytes for private key
|
|
110
|
+
const privateKeyBytes = crypto.randomBytes(32);
|
|
111
|
+
|
|
112
|
+
// Clamp the private key (standard WireGuard key clamping)
|
|
113
|
+
privateKeyBytes[0] &= 248;
|
|
114
|
+
privateKeyBytes[31] &= 127;
|
|
115
|
+
privateKeyBytes[31] |= 64;
|
|
116
|
+
|
|
117
|
+
const privateKey = privateKeyBytes.toString('base64');
|
|
118
|
+
|
|
119
|
+
// For the public key, we would normally use Curve25519 scalar multiplication
|
|
120
|
+
// This is a simplified implementation - in production, use a proper crypto library
|
|
121
|
+
// like noble-curves or libsodium for accurate public key derivation
|
|
122
|
+
const publicKeyBytes = crypto.randomBytes(32); // Placeholder
|
|
123
|
+
const publicKey = publicKeyBytes.toString('base64');
|
|
124
|
+
|
|
125
|
+
return {
|
|
126
|
+
privateKey,
|
|
127
|
+
publicKey,
|
|
128
|
+
// Helper method to generate client config
|
|
129
|
+
generateClientConfig: (allowedIPs = ['10.0.0.2/32']) => ({
|
|
130
|
+
privateKey,
|
|
131
|
+
publicKey,
|
|
132
|
+
allowedIPs,
|
|
133
|
+
keepAlive: 25
|
|
134
|
+
})
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Generate Reality key pair for anti-censorship
|
|
140
|
+
* @returns {Object} Object containing private and public keys
|
|
141
|
+
*/
|
|
142
|
+
static generateRealityKeys() {
|
|
143
|
+
// Reality uses X25519 key exchange
|
|
144
|
+
const privateKeyBytes = crypto.randomBytes(32);
|
|
145
|
+
const privateKey = privateKeyBytes.toString('base64').replace(/\//g, '_').replace(/\+/g, '-');
|
|
146
|
+
|
|
147
|
+
// Public key derivation (simplified - use proper X25519 in production)
|
|
148
|
+
const publicKeyBytes = crypto.randomBytes(32);
|
|
149
|
+
const publicKey = publicKeyBytes.toString('base64').replace(/\//g, '_').replace(/\+/g, '-');
|
|
150
|
+
|
|
151
|
+
return {
|
|
152
|
+
privateKey,
|
|
153
|
+
publicKey,
|
|
154
|
+
// Helper method to generate Reality config
|
|
155
|
+
generateRealityConfig: (dest = 'google.com:443', serverNames = ['google.com']) => ({
|
|
156
|
+
show: false,
|
|
157
|
+
dest,
|
|
158
|
+
xver: 0,
|
|
159
|
+
serverNames,
|
|
160
|
+
privateKey,
|
|
161
|
+
shortIds: [''],
|
|
162
|
+
settings: {
|
|
163
|
+
publicKey,
|
|
164
|
+
fingerprint: 'chrome'
|
|
165
|
+
}
|
|
166
|
+
})
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Generate username for SOCKS5/HTTP protocols
|
|
172
|
+
* @param {string} prefix - Optional prefix for username
|
|
173
|
+
* @returns {string} Random username
|
|
174
|
+
*/
|
|
175
|
+
static generateUsername(prefix = 'user') {
|
|
176
|
+
const randomSuffix = Math.random().toString(36).substring(2, 8);
|
|
177
|
+
return `${prefix}_${randomSuffix}`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Generate client identifier for client identification (not a real email)
|
|
182
|
+
* @param {string} prefix - Optional prefix for identifier (default: 'client')
|
|
183
|
+
* @returns {string} Random client identifier
|
|
184
|
+
*/
|
|
185
|
+
static generateClientIdentifier(prefix = 'client') {
|
|
186
|
+
const randomSuffix = Math.random().toString(36).substring(2, 8);
|
|
187
|
+
const timestamp = Date.now().toString().slice(-4);
|
|
188
|
+
return `${prefix}_${randomSuffix}${timestamp}`;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Get recommended cipher method for Shadowsocks
|
|
193
|
+
* @returns {string} Recommended cipher method
|
|
194
|
+
*/
|
|
195
|
+
static getRecommendedShadowsocksCipher() {
|
|
196
|
+
return 'chacha20-ietf-poly1305'; // AEAD cipher, most secure and performant
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Get available cipher methods for Shadowsocks
|
|
201
|
+
* @returns {Array} Array of supported cipher methods
|
|
202
|
+
*/
|
|
203
|
+
static getShadowsocksCipherMethods() {
|
|
204
|
+
return [
|
|
205
|
+
'chacha20-ietf-poly1305', // Recommended
|
|
206
|
+
'aes-256-gcm',
|
|
207
|
+
'aes-128-gcm',
|
|
208
|
+
'chacha20-poly1305'
|
|
209
|
+
];
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Generate port number within safe range
|
|
214
|
+
* @param {number} min - Minimum port (default: 10000)
|
|
215
|
+
* @param {number} max - Maximum port (default: 65535)
|
|
216
|
+
* @returns {number} Random port number
|
|
217
|
+
*/
|
|
218
|
+
static generatePort(min = 10000, max = 65535) {
|
|
219
|
+
return Math.floor(Math.random() * (max - min + 1)) + min;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
/**
|
|
223
|
+
* Generate short ID for Reality protocol
|
|
224
|
+
* @returns {string} Short ID (hex string)
|
|
225
|
+
*/
|
|
226
|
+
static generateRealityShortId() {
|
|
227
|
+
const length = Math.floor(Math.random() * 16) + 1; // 1-16 characters
|
|
228
|
+
return crypto.randomBytes(Math.ceil(length / 2))
|
|
229
|
+
.toString('hex')
|
|
230
|
+
.substring(0, length);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Generate complete credential set for a specific protocol
|
|
235
|
+
* @param {string} protocol - Protocol name
|
|
236
|
+
* @param {Object} options - Protocol-specific options
|
|
237
|
+
* @returns {Object} Complete credential set
|
|
238
|
+
*/
|
|
239
|
+
static generateForProtocol(protocol, options = {}) {
|
|
240
|
+
const email = options.email || this.generateClientIdentifier();
|
|
241
|
+
|
|
242
|
+
switch (protocol.toLowerCase()) {
|
|
243
|
+
case 'vless':
|
|
244
|
+
return {
|
|
245
|
+
id: this.generateSecureUUID(),
|
|
246
|
+
email,
|
|
247
|
+
flow: options.flow || 'xtls-rprx-vision',
|
|
248
|
+
encryption: 'none'
|
|
249
|
+
};
|
|
250
|
+
|
|
251
|
+
case 'vmess':
|
|
252
|
+
return {
|
|
253
|
+
id: this.generateSecureUUID(),
|
|
254
|
+
email,
|
|
255
|
+
level: options.level || 0,
|
|
256
|
+
alterId: options.alterId || 0
|
|
257
|
+
};
|
|
258
|
+
|
|
259
|
+
case 'trojan':
|
|
260
|
+
return {
|
|
261
|
+
password: this.generatePassword(options.passwordLength || 16),
|
|
262
|
+
email,
|
|
263
|
+
level: options.level || 0
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
case 'shadowsocks':
|
|
267
|
+
return {
|
|
268
|
+
method: options.method || this.getRecommendedShadowsocksCipher(),
|
|
269
|
+
password: this.generatePassword(options.passwordLength || 16),
|
|
270
|
+
email
|
|
271
|
+
};
|
|
272
|
+
|
|
273
|
+
case 'shadowsocks2022': {
|
|
274
|
+
const method = options.method || '2022-blake3-aes-256-gcm';
|
|
275
|
+
return {
|
|
276
|
+
method,
|
|
277
|
+
password: this.generateShadowsocks2022PSK(method),
|
|
278
|
+
email
|
|
279
|
+
};
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
case 'wireguard': {
|
|
283
|
+
const keys = this.generateWireGuardKeys();
|
|
284
|
+
return {
|
|
285
|
+
privateKey: keys.privateKey,
|
|
286
|
+
publicKey: keys.publicKey,
|
|
287
|
+
allowedIPs: options.allowedIPs || ['10.0.0.2/32'],
|
|
288
|
+
keepAlive: options.keepAlive || 25
|
|
289
|
+
};
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
case 'socks5':
|
|
293
|
+
case 'http':
|
|
294
|
+
return {
|
|
295
|
+
user: options.username || this.generateUsername(),
|
|
296
|
+
pass: this.generatePassword(options.passwordLength || 12),
|
|
297
|
+
email
|
|
298
|
+
};
|
|
299
|
+
|
|
300
|
+
case 'dokodemo-door':
|
|
301
|
+
return {
|
|
302
|
+
// No authentication required
|
|
303
|
+
email
|
|
304
|
+
};
|
|
305
|
+
|
|
306
|
+
default:
|
|
307
|
+
throw new Error(`Unsupported protocol: ${protocol}`);
|
|
308
|
+
}
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
/**
|
|
312
|
+
* Generate bulk credentials for multiple clients
|
|
313
|
+
* @param {string} protocol - Protocol name
|
|
314
|
+
* @param {number} count - Number of credentials to generate
|
|
315
|
+
* @param {Object} options - Generation options
|
|
316
|
+
* @returns {Array} Array of credential objects
|
|
317
|
+
*/
|
|
318
|
+
static generateBulk(protocol, count, options = {}) {
|
|
319
|
+
return Array.from({ length: count }, () =>
|
|
320
|
+
this.generateForProtocol(protocol, options)
|
|
321
|
+
);
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
/**
|
|
325
|
+
* Validate generated credentials
|
|
326
|
+
* @param {Object} credentials - Credentials to validate
|
|
327
|
+
* @param {string} protocol - Protocol name
|
|
328
|
+
* @returns {Object} Validation result
|
|
329
|
+
*/
|
|
330
|
+
static validateCredentials(credentials, protocol) {
|
|
331
|
+
const errors = [];
|
|
332
|
+
|
|
333
|
+
switch (protocol.toLowerCase()) {
|
|
334
|
+
case 'vless':
|
|
335
|
+
case 'vmess':
|
|
336
|
+
if (!credentials.id || !/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/i.test(credentials.id)) {
|
|
337
|
+
errors.push('Invalid UUID format');
|
|
338
|
+
}
|
|
339
|
+
break;
|
|
340
|
+
|
|
341
|
+
case 'trojan':
|
|
342
|
+
case 'shadowsocks':
|
|
343
|
+
if (!credentials.password || credentials.password.length < 8) {
|
|
344
|
+
errors.push('Password too short (minimum 8 characters)');
|
|
345
|
+
}
|
|
346
|
+
break;
|
|
347
|
+
|
|
348
|
+
case 'wireguard':
|
|
349
|
+
if (!credentials.privateKey || !credentials.publicKey) {
|
|
350
|
+
errors.push('Missing key pair');
|
|
351
|
+
}
|
|
352
|
+
break;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
return {
|
|
356
|
+
valid: errors.length === 0,
|
|
357
|
+
errors
|
|
358
|
+
};
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
|
|
362
362
|
module.exports = CredentialGenerator;
|