@omindu/yaksha 1.0.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/bin/yaksha.js +329 -0
- package/package.json +28 -0
- package/src/bypass/firewall-evasion.js +354 -0
- package/src/client/index.js +544 -0
- package/src/core/connection.js +393 -0
- package/src/core/encryption.js +299 -0
- package/src/core/protocol.js +268 -0
- package/src/features/dns-override.js +403 -0
- package/src/features/multi-path.js +394 -0
- package/src/features/sni-spoof.js +355 -0
- package/src/features/tls-camouflage.js +369 -0
- package/src/features/traffic-obfuscation.js +338 -0
- package/src/index.js +106 -0
- package/src/security/auth.js +441 -0
- package/src/security/levels.js +316 -0
- package/src/server/index.js +551 -0
- package/src/utils/buffer-pool.js +150 -0
- package/src/utils/config.js +205 -0
- package/src/utils/logger.js +105 -0
|
@@ -0,0 +1,441 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Yaksha Authentication System
|
|
5
|
+
* Supports multiple authentication methods: password, token, certificate
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const crypto = require('crypto');
|
|
9
|
+
const EventEmitter = require('events');
|
|
10
|
+
|
|
11
|
+
// Authentication methods
|
|
12
|
+
const AUTH_METHODS = {
|
|
13
|
+
PASSWORD: 'password',
|
|
14
|
+
TOKEN: 'token',
|
|
15
|
+
CERTIFICATE: 'certificate'
|
|
16
|
+
};
|
|
17
|
+
|
|
18
|
+
// Authentication states
|
|
19
|
+
const AUTH_STATES = {
|
|
20
|
+
PENDING: 'pending',
|
|
21
|
+
AUTHENTICATED: 'authenticated',
|
|
22
|
+
FAILED: 'failed',
|
|
23
|
+
LOCKED: 'locked'
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
class Authentication extends EventEmitter {
|
|
27
|
+
constructor(method = 'token', options = {}) {
|
|
28
|
+
super();
|
|
29
|
+
|
|
30
|
+
this.method = method;
|
|
31
|
+
this.maxAttempts = options.maxAttempts || 10;
|
|
32
|
+
this.lockoutDuration = options.lockoutDuration || 300000; // 5 minutes
|
|
33
|
+
this.rateLimitWindow = options.rateLimitWindow || 60000; // 1 minute
|
|
34
|
+
this.maxAttemptsPerWindow = options.maxAttemptsPerWindow || 5;
|
|
35
|
+
this.sessionTimeout = options.sessionTimeout || 3600000; // 1 hour
|
|
36
|
+
this.tokenRotationInterval = options.tokenRotationInterval || 86400000; // 24 hours
|
|
37
|
+
|
|
38
|
+
// Storage for credentials and sessions
|
|
39
|
+
this.credentials = new Map(); // username/id -> credential data
|
|
40
|
+
this.sessions = new Map(); // sessionId -> session data
|
|
41
|
+
this.attempts = new Map(); // identifier -> attempt data
|
|
42
|
+
this.lockouts = new Map(); // identifier -> lockout timestamp
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Hash password using PBKDF2
|
|
47
|
+
*/
|
|
48
|
+
_hashPassword(password, salt) {
|
|
49
|
+
salt = salt || crypto.randomBytes(32);
|
|
50
|
+
const hash = crypto.pbkdf2Sync(password, salt, 10000, 64, 'sha512');
|
|
51
|
+
return { hash, salt };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Verify password (constant-time comparison)
|
|
56
|
+
*/
|
|
57
|
+
_verifyPassword(password, storedHash, salt) {
|
|
58
|
+
const { hash } = this._hashPassword(password, salt);
|
|
59
|
+
return crypto.timingSafeEqual(hash, storedHash);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Generate random token
|
|
64
|
+
*/
|
|
65
|
+
_generateToken() {
|
|
66
|
+
return crypto.randomBytes(32).toString('hex');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Generate challenge for challenge-response authentication
|
|
71
|
+
*/
|
|
72
|
+
_generateChallenge() {
|
|
73
|
+
return crypto.randomBytes(32);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Verify TOTP (Time-based One-Time Password) for 2FA
|
|
78
|
+
*/
|
|
79
|
+
_verifyTOTP(token, secret, window = 1) {
|
|
80
|
+
// Simplified TOTP verification
|
|
81
|
+
// In production, use a proper TOTP library
|
|
82
|
+
const time = Math.floor(Date.now() / 30000); // 30-second window
|
|
83
|
+
|
|
84
|
+
for (let i = -window; i <= window; i++) {
|
|
85
|
+
const expectedToken = this._generateTOTP(secret, time + i);
|
|
86
|
+
if (token === expectedToken) {
|
|
87
|
+
return true;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Generate TOTP token
|
|
96
|
+
*/
|
|
97
|
+
_generateTOTP(secret, time) {
|
|
98
|
+
// Simplified TOTP generation
|
|
99
|
+
const hmac = crypto.createHmac('sha1', Buffer.from(secret, 'hex'));
|
|
100
|
+
hmac.update(Buffer.from([0, 0, 0, 0, 0, 0, 0, time]));
|
|
101
|
+
const hash = hmac.digest();
|
|
102
|
+
|
|
103
|
+
const offset = hash[hash.length - 1] & 0xf;
|
|
104
|
+
const code = ((hash[offset] & 0x7f) << 24) |
|
|
105
|
+
((hash[offset + 1] & 0xff) << 16) |
|
|
106
|
+
((hash[offset + 2] & 0xff) << 8) |
|
|
107
|
+
(hash[offset + 3] & 0xff);
|
|
108
|
+
|
|
109
|
+
return String(code % 1000000).padStart(6, '0');
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Check if identifier is rate limited
|
|
114
|
+
*/
|
|
115
|
+
_isRateLimited(identifier) {
|
|
116
|
+
const now = Date.now();
|
|
117
|
+
const attemptData = this.attempts.get(identifier);
|
|
118
|
+
|
|
119
|
+
if (!attemptData) {
|
|
120
|
+
return false;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// Remove old attempts outside the window
|
|
124
|
+
attemptData.timestamps = attemptData.timestamps.filter(
|
|
125
|
+
ts => now - ts < this.rateLimitWindow
|
|
126
|
+
);
|
|
127
|
+
|
|
128
|
+
if (attemptData.timestamps.length >= this.maxAttemptsPerWindow) {
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Check if identifier is locked out
|
|
137
|
+
*/
|
|
138
|
+
_isLockedOut(identifier) {
|
|
139
|
+
const lockoutTime = this.lockouts.get(identifier);
|
|
140
|
+
if (!lockoutTime) {
|
|
141
|
+
return false;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
const now = Date.now();
|
|
145
|
+
if (now - lockoutTime > this.lockoutDuration) {
|
|
146
|
+
this.lockouts.delete(identifier);
|
|
147
|
+
return false;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/**
|
|
154
|
+
* Record authentication attempt
|
|
155
|
+
*/
|
|
156
|
+
_recordAttempt(identifier, success) {
|
|
157
|
+
const now = Date.now();
|
|
158
|
+
|
|
159
|
+
if (!this.attempts.has(identifier)) {
|
|
160
|
+
this.attempts.set(identifier, {
|
|
161
|
+
total: 0,
|
|
162
|
+
failed: 0,
|
|
163
|
+
timestamps: []
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const attemptData = this.attempts.get(identifier);
|
|
168
|
+
attemptData.total++;
|
|
169
|
+
attemptData.timestamps.push(now);
|
|
170
|
+
|
|
171
|
+
if (!success) {
|
|
172
|
+
attemptData.failed++;
|
|
173
|
+
|
|
174
|
+
// Check if should lock out
|
|
175
|
+
if (attemptData.failed >= this.maxAttempts) {
|
|
176
|
+
this.lockouts.set(identifier, now);
|
|
177
|
+
this.emit('lockout', identifier);
|
|
178
|
+
}
|
|
179
|
+
} else {
|
|
180
|
+
// Reset on successful authentication
|
|
181
|
+
attemptData.failed = 0;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Register user with password
|
|
187
|
+
*/
|
|
188
|
+
registerPassword(username, password) {
|
|
189
|
+
if (this.method !== AUTH_METHODS.PASSWORD) {
|
|
190
|
+
throw new Error('Authentication method is not password');
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const { hash, salt } = this._hashPassword(password);
|
|
194
|
+
|
|
195
|
+
this.credentials.set(username, {
|
|
196
|
+
method: AUTH_METHODS.PASSWORD,
|
|
197
|
+
hash,
|
|
198
|
+
salt,
|
|
199
|
+
createdAt: Date.now()
|
|
200
|
+
});
|
|
201
|
+
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
/**
|
|
206
|
+
* Register user with token
|
|
207
|
+
*/
|
|
208
|
+
registerToken(identifier, token) {
|
|
209
|
+
if (this.method !== AUTH_METHODS.TOKEN) {
|
|
210
|
+
throw new Error('Authentication method is not token');
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
token = token || this._generateToken();
|
|
214
|
+
|
|
215
|
+
this.credentials.set(identifier, {
|
|
216
|
+
method: AUTH_METHODS.TOKEN,
|
|
217
|
+
token,
|
|
218
|
+
createdAt: Date.now(),
|
|
219
|
+
expiresAt: Date.now() + this.tokenRotationInterval
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
return token;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Register user with certificate
|
|
227
|
+
*/
|
|
228
|
+
registerCertificate(identifier, certificate, options = {}) {
|
|
229
|
+
if (this.method !== AUTH_METHODS.CERTIFICATE) {
|
|
230
|
+
throw new Error('Authentication method is not certificate');
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
this.credentials.set(identifier, {
|
|
234
|
+
method: AUTH_METHODS.CERTIFICATE,
|
|
235
|
+
certificate,
|
|
236
|
+
twoFactorSecret: options.twoFactorSecret || null,
|
|
237
|
+
createdAt: Date.now()
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
return true;
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
/**
|
|
244
|
+
* Authenticate with password
|
|
245
|
+
*/
|
|
246
|
+
authenticatePassword(username, password) {
|
|
247
|
+
if (this._isLockedOut(username)) {
|
|
248
|
+
return { success: false, state: AUTH_STATES.LOCKED, reason: 'Account locked' };
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
if (this._isRateLimited(username)) {
|
|
252
|
+
return { success: false, state: AUTH_STATES.FAILED, reason: 'Rate limit exceeded' };
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const credential = this.credentials.get(username);
|
|
256
|
+
|
|
257
|
+
if (!credential || credential.method !== AUTH_METHODS.PASSWORD) {
|
|
258
|
+
this._recordAttempt(username, false);
|
|
259
|
+
return { success: false, state: AUTH_STATES.FAILED, reason: 'Invalid credentials' };
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
const isValid = this._verifyPassword(password, credential.hash, credential.salt);
|
|
263
|
+
this._recordAttempt(username, isValid);
|
|
264
|
+
|
|
265
|
+
if (!isValid) {
|
|
266
|
+
return { success: false, state: AUTH_STATES.FAILED, reason: 'Invalid credentials' };
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
// Create session
|
|
270
|
+
const sessionId = this._generateToken();
|
|
271
|
+
this.sessions.set(sessionId, {
|
|
272
|
+
identifier: username,
|
|
273
|
+
createdAt: Date.now(),
|
|
274
|
+
expiresAt: Date.now() + this.sessionTimeout
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
this.emit('authenticated', username, sessionId);
|
|
278
|
+
|
|
279
|
+
return {
|
|
280
|
+
success: true,
|
|
281
|
+
state: AUTH_STATES.AUTHENTICATED,
|
|
282
|
+
sessionId,
|
|
283
|
+
expiresAt: Date.now() + this.sessionTimeout
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Authenticate with token
|
|
289
|
+
*/
|
|
290
|
+
authenticateToken(identifier, token) {
|
|
291
|
+
if (this._isLockedOut(identifier)) {
|
|
292
|
+
return { success: false, state: AUTH_STATES.LOCKED, reason: 'Account locked' };
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
if (this._isRateLimited(identifier)) {
|
|
296
|
+
return { success: false, state: AUTH_STATES.FAILED, reason: 'Rate limit exceeded' };
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
const credential = this.credentials.get(identifier);
|
|
300
|
+
|
|
301
|
+
if (!credential || credential.method !== AUTH_METHODS.TOKEN) {
|
|
302
|
+
this._recordAttempt(identifier, false);
|
|
303
|
+
return { success: false, state: AUTH_STATES.FAILED, reason: 'Invalid token' };
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
// Check token expiration
|
|
307
|
+
if (credential.expiresAt && Date.now() > credential.expiresAt) {
|
|
308
|
+
return { success: false, state: AUTH_STATES.FAILED, reason: 'Token expired' };
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
// Constant-time comparison
|
|
312
|
+
const isValid = crypto.timingSafeEqual(
|
|
313
|
+
Buffer.from(token),
|
|
314
|
+
Buffer.from(credential.token)
|
|
315
|
+
);
|
|
316
|
+
|
|
317
|
+
this._recordAttempt(identifier, isValid);
|
|
318
|
+
|
|
319
|
+
if (!isValid) {
|
|
320
|
+
return { success: false, state: AUTH_STATES.FAILED, reason: 'Invalid token' };
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
// Create session
|
|
324
|
+
const sessionId = this._generateToken();
|
|
325
|
+
this.sessions.set(sessionId, {
|
|
326
|
+
identifier,
|
|
327
|
+
createdAt: Date.now(),
|
|
328
|
+
expiresAt: Date.now() + this.sessionTimeout
|
|
329
|
+
});
|
|
330
|
+
|
|
331
|
+
this.emit('authenticated', identifier, sessionId);
|
|
332
|
+
|
|
333
|
+
return {
|
|
334
|
+
success: true,
|
|
335
|
+
state: AUTH_STATES.AUTHENTICATED,
|
|
336
|
+
sessionId,
|
|
337
|
+
expiresAt: Date.now() + this.sessionTimeout
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* Authenticate with certificate
|
|
343
|
+
*/
|
|
344
|
+
authenticateCertificate(identifier, certificate, totpToken = null) {
|
|
345
|
+
if (this._isLockedOut(identifier)) {
|
|
346
|
+
return { success: false, state: AUTH_STATES.LOCKED, reason: 'Account locked' };
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const credential = this.credentials.get(identifier);
|
|
350
|
+
|
|
351
|
+
if (!credential || credential.method !== AUTH_METHODS.CERTIFICATE) {
|
|
352
|
+
this._recordAttempt(identifier, false);
|
|
353
|
+
return { success: false, state: AUTH_STATES.FAILED, reason: 'Invalid certificate' };
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
// In production, perform proper certificate validation
|
|
357
|
+
const isValid = certificate === credential.certificate;
|
|
358
|
+
|
|
359
|
+
if (!isValid) {
|
|
360
|
+
this._recordAttempt(identifier, false);
|
|
361
|
+
return { success: false, state: AUTH_STATES.FAILED, reason: 'Invalid certificate' };
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// Check 2FA if enabled
|
|
365
|
+
if (credential.twoFactorSecret) {
|
|
366
|
+
if (!totpToken || !this._verifyTOTP(totpToken, credential.twoFactorSecret)) {
|
|
367
|
+
this._recordAttempt(identifier, false);
|
|
368
|
+
return { success: false, state: AUTH_STATES.FAILED, reason: '2FA verification failed' };
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
this._recordAttempt(identifier, true);
|
|
373
|
+
|
|
374
|
+
// Create session
|
|
375
|
+
const sessionId = this._generateToken();
|
|
376
|
+
this.sessions.set(sessionId, {
|
|
377
|
+
identifier,
|
|
378
|
+
createdAt: Date.now(),
|
|
379
|
+
expiresAt: Date.now() + this.sessionTimeout
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
this.emit('authenticated', identifier, sessionId);
|
|
383
|
+
|
|
384
|
+
return {
|
|
385
|
+
success: true,
|
|
386
|
+
state: AUTH_STATES.AUTHENTICATED,
|
|
387
|
+
sessionId,
|
|
388
|
+
expiresAt: Date.now() + this.sessionTimeout
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
/**
|
|
393
|
+
* Validate session
|
|
394
|
+
*/
|
|
395
|
+
validateSession(sessionId) {
|
|
396
|
+
const session = this.sessions.get(sessionId);
|
|
397
|
+
|
|
398
|
+
if (!session) {
|
|
399
|
+
return { valid: false, reason: 'Invalid session' };
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
if (Date.now() > session.expiresAt) {
|
|
403
|
+
this.sessions.delete(sessionId);
|
|
404
|
+
return { valid: false, reason: 'Session expired' };
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
return {
|
|
408
|
+
valid: true,
|
|
409
|
+
identifier: session.identifier,
|
|
410
|
+
expiresAt: session.expiresAt
|
|
411
|
+
};
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Revoke session
|
|
416
|
+
*/
|
|
417
|
+
revokeSession(sessionId) {
|
|
418
|
+
return this.sessions.delete(sessionId);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
/**
|
|
422
|
+
* Cleanup expired sessions
|
|
423
|
+
*/
|
|
424
|
+
cleanupSessions() {
|
|
425
|
+
const now = Date.now();
|
|
426
|
+
let cleaned = 0;
|
|
427
|
+
|
|
428
|
+
for (const [sessionId, session] of this.sessions.entries()) {
|
|
429
|
+
if (now > session.expiresAt) {
|
|
430
|
+
this.sessions.delete(sessionId);
|
|
431
|
+
cleaned++;
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
return cleaned;
|
|
436
|
+
}
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
module.exports = Authentication;
|
|
440
|
+
module.exports.AUTH_METHODS = AUTH_METHODS;
|
|
441
|
+
module.exports.AUTH_STATES = AUTH_STATES;
|