@omindu/yaksha 1.0.0 → 1.0.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/README.md +597 -0
- package/package.json +1 -1
- package/src/core/protocol.js +154 -135
- package/src/core/protocol.js.backup +268 -0
package/README.md
ADDED
|
@@ -0,0 +1,597 @@
|
|
|
1
|
+
# Yaksha VPN Protocol
|
|
2
|
+
|
|
3
|
+
<div align="center">
|
|
4
|
+
|
|
5
|
+
⚡️ **Lightning-Fast** • 🔒 **Military-Grade Security** • 🚀 **Ultra-Lightweight** • 🛡️ **Firewall Bypass**
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/yaksha)
|
|
8
|
+
[](LICENSE)
|
|
9
|
+
[](https://nodejs.org)
|
|
10
|
+
[](https://www.npmjs.com/package/yaksha)
|
|
11
|
+
|
|
12
|
+
A high-performance, lightweight VPN protocol library with advanced firewall bypass capabilities, built for Node.js.
|
|
13
|
+
|
|
14
|
+
[Features](#-features) • [Installation](#-installation) • [Quick Start](#-quick-start) • [Documentation](#-documentation) • [Examples](#-examples)
|
|
15
|
+
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 🎯 Overview
|
|
21
|
+
|
|
22
|
+
**Yaksha** is a modern VPN protocol implementation designed for maximum performance and security. It combines military-grade encryption with advanced traffic obfuscation techniques to bypass even the most restrictive firewalls and Deep Packet Inspection (DPI) systems.
|
|
23
|
+
|
|
24
|
+
### Why Yaksha?
|
|
25
|
+
|
|
26
|
+
- ⚡️ **Blazing Fast**: >1 Gbps throughput, <5ms latency overhead
|
|
27
|
+
- 🪶 **Ultra-Lightweight**: <2000 lines of core code, <50MB memory per 1000 connections
|
|
28
|
+
- 🔒 **Military-Grade Security**: AES-256-GCM, ChaCha20-Poly1305, double encryption
|
|
29
|
+
- 🛡️ **Firewall Bypass**: SNI spoofing, traffic obfuscation, DPI evasion
|
|
30
|
+
- 🌐 **DNS Security**: DNS-over-HTTPS, DNS-over-TLS with DNSSEC
|
|
31
|
+
- 🚦 **Multi-Path Routing**: Aggregate bandwidth across multiple connections
|
|
32
|
+
- 🎭 **TLS Camouflage**: Traffic looks like legitimate HTTPS
|
|
33
|
+
- 📦 **Easy to Use**: Simple API, comprehensive documentation
|
|
34
|
+
- 🔧 **Highly Configurable**: Four security levels (Low/Medium/High/Custom)
|
|
35
|
+
- 🌍 **Cross-Platform**: Pure JavaScript, works everywhere Node.js runs
|
|
36
|
+
|
|
37
|
+
---
|
|
38
|
+
|
|
39
|
+
## ✨ Features
|
|
40
|
+
|
|
41
|
+
### Core Features
|
|
42
|
+
|
|
43
|
+
| Feature | Description |
|
|
44
|
+
|---------|-------------|
|
|
45
|
+
| **Custom Protocol** | Lightweight, efficient protocol optimized for VPN traffic |
|
|
46
|
+
| **Multiple Encryption** | ChaCha20-Poly1305, AES-256-GCM, double encryption |
|
|
47
|
+
| **Perfect Forward Secrecy** | X25519 key exchange with automatic key rotation |
|
|
48
|
+
| **Authentication** | Password, token, and certificate-based auth with 2FA |
|
|
49
|
+
| **TCP + UDP** | Control channel (TCP) + high-speed data channel (UDP) |
|
|
50
|
+
| **Connection Pooling** | Efficient resource management for 10,000+ connections |
|
|
51
|
+
|
|
52
|
+
### Advanced Features
|
|
53
|
+
|
|
54
|
+
| Feature | Description |
|
|
55
|
+
|---------|-------------|
|
|
56
|
+
| **SNI Spoofing** | Replace Server Name Indication with fake domains |
|
|
57
|
+
| **Bug Host Support** | Use specific domains to bypass network restrictions |
|
|
58
|
+
| **Traffic Obfuscation** | Randomize patterns to evade detection |
|
|
59
|
+
| **Multi-Path Routing** | Split traffic across 2-5 simultaneous connections |
|
|
60
|
+
| **DNS Override** | Tunnel all DNS queries through encrypted VPN |
|
|
61
|
+
| **TLS Camouflage** | Mimic TLS 1.3 handshake and traffic patterns |
|
|
62
|
+
| **Firewall Evasion** | Port hopping, packet fragmentation, timing manipulation |
|
|
63
|
+
| **DPI Resistance** | Encrypted payload, randomized structure, protocol masquerading |
|
|
64
|
+
| **Anti-Replay Protection** | Nonce + timestamp validation |
|
|
65
|
+
| **Rate Limiting** | Prevent brute force attacks |
|
|
66
|
+
|
|
67
|
+
### Security Levels
|
|
68
|
+
|
|
69
|
+
#### 🟢 **LOW** (Speed Priority)
|
|
70
|
+
- **Encryption**: ChaCha20-Poly1305
|
|
71
|
+
- **Throughput**: ~2000 MB/s
|
|
72
|
+
- **Latency**: <2ms overhead
|
|
73
|
+
- **Use Cases**: Streaming, gaming, general browsing
|
|
74
|
+
|
|
75
|
+
#### 🟡 **MEDIUM** (Balanced)
|
|
76
|
+
- **Encryption**: AES-256-GCM
|
|
77
|
+
- **Throughput**: ~1000 MB/s
|
|
78
|
+
- **Latency**: <5ms overhead
|
|
79
|
+
- **Use Cases**: Daily use, business applications
|
|
80
|
+
|
|
81
|
+
#### 🔴 **HIGH** (Security Priority)
|
|
82
|
+
- **Encryption**: Double (AES-256-GCM + ChaCha20-Poly1305)
|
|
83
|
+
- **Throughput**: ~500 MB/s
|
|
84
|
+
- **Latency**: <10ms overhead
|
|
85
|
+
- **Use Cases**: Banking, sensitive data, high-security environments
|
|
86
|
+
|
|
87
|
+
#### ⚙️ **CUSTOM** (User-Defined)
|
|
88
|
+
- **Encryption**: Configurable (ChaCha20, AES-256-GCM, or Double)
|
|
89
|
+
- **Throughput**: Varies based on configuration
|
|
90
|
+
- **Latency**: Varies based on configuration
|
|
91
|
+
- **Use Cases**: Specific requirements, fine-tuned security/performance balance
|
|
92
|
+
- **Flexibility**: Mix and match features from any level
|
|
93
|
+
- **Base Levels**: Start from LOW, MEDIUM, or HIGH and customize
|
|
94
|
+
|
|
95
|
+
**Custom Level Features:**
|
|
96
|
+
```javascript
|
|
97
|
+
// Create custom configuration based on medium level
|
|
98
|
+
const customConfig = SecurityLevels.createCustomConfig({
|
|
99
|
+
encryption: 'double', // Override encryption
|
|
100
|
+
keyExchange: 'x25519-rotate', // Override key exchange
|
|
101
|
+
obfuscation: 'aes-256-gcm', // Override obfuscation
|
|
102
|
+
multiPath: 3, // Custom path count
|
|
103
|
+
keyRotation: true, // Enable key rotation
|
|
104
|
+
keyRotationInterval: 2000 // Custom rotation interval
|
|
105
|
+
}, 'medium'); // Base level
|
|
106
|
+
|
|
107
|
+
// Use with server/client
|
|
108
|
+
const server = createServer({
|
|
109
|
+
securityLevel: 'custom',
|
|
110
|
+
customSecurityConfig: customConfig
|
|
111
|
+
});
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 📦 Installation
|
|
117
|
+
|
|
118
|
+
```bash
|
|
119
|
+
npm install @omindu/yaksha
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Requirements
|
|
123
|
+
|
|
124
|
+
- **Node.js**: >=14.0.0
|
|
125
|
+
- **OS**: Windows, Linux, macOS
|
|
126
|
+
- **Dependencies**: `tweetnacl` (automatically installed)
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## 🚀 Quick Start
|
|
131
|
+
|
|
132
|
+
### Server
|
|
133
|
+
|
|
134
|
+
```javascript
|
|
135
|
+
const yaksha = require('@omindu/yaksha');
|
|
136
|
+
|
|
137
|
+
// Create server
|
|
138
|
+
const server = yaksha.createServer({
|
|
139
|
+
port: 8443,
|
|
140
|
+
securityLevel: 'medium',
|
|
141
|
+
authMethod: 'token'
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
// Register client
|
|
145
|
+
server.auth.registerToken('client1', 'your-secret-token');
|
|
146
|
+
|
|
147
|
+
// Start server
|
|
148
|
+
await server.start();
|
|
149
|
+
console.log('VPN server running on port 8443');
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### Client
|
|
153
|
+
|
|
154
|
+
```javascript
|
|
155
|
+
const yaksha = require('@omindu/yaksha');
|
|
156
|
+
|
|
157
|
+
// Create client
|
|
158
|
+
const client = yaksha.createClient({
|
|
159
|
+
server: 'vpn.example.com',
|
|
160
|
+
port: 8443,
|
|
161
|
+
securityLevel: 'medium',
|
|
162
|
+
authCredentials: {
|
|
163
|
+
identifier: 'client1',
|
|
164
|
+
token: 'your-secret-token'
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
// Connect
|
|
169
|
+
await client.connect();
|
|
170
|
+
console.log('Connected to VPN server');
|
|
171
|
+
|
|
172
|
+
// Send data
|
|
173
|
+
await client.send(Buffer.from('Hello, VPN!'));
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### CLI Usage
|
|
177
|
+
|
|
178
|
+
```bash
|
|
179
|
+
# Start server
|
|
180
|
+
yaksha server --port 8443 --security high
|
|
181
|
+
|
|
182
|
+
# Connect client
|
|
183
|
+
yaksha client --server vpn.example.com --token abc123
|
|
184
|
+
|
|
185
|
+
# Generate keys
|
|
186
|
+
yaksha keygen
|
|
187
|
+
|
|
188
|
+
# Run benchmarks
|
|
189
|
+
yaksha benchmark
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
---
|
|
193
|
+
|
|
194
|
+
## 📖 Documentation
|
|
195
|
+
|
|
196
|
+
### Server API
|
|
197
|
+
|
|
198
|
+
```javascript
|
|
199
|
+
const server = yaksha.createServer(options);
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Options:**
|
|
203
|
+
- `port` (number): Server port (default: 8443)
|
|
204
|
+
- `host` (string): Bind address (default: '0.0.0.0')
|
|
205
|
+
- `securityLevel` (string): 'low', 'medium', 'high', or 'custom' (default: 'medium')
|
|
206
|
+
- `customSecurityConfig` (object): Custom security configuration (required if securityLevel is 'custom')
|
|
207
|
+
- `authMethod` (string): 'password', 'token', or 'certificate' (default: 'token')
|
|
208
|
+
- `maxConnections` (number): Maximum concurrent connections (default: 10000)
|
|
209
|
+
- `logLevel` (string): 'debug', 'info', 'warn', 'error' (default: 'info')
|
|
210
|
+
|
|
211
|
+
**Methods:**
|
|
212
|
+
- `await server.start()`: Start the server
|
|
213
|
+
- `await server.stop()`: Stop the server
|
|
214
|
+
- `server.sendToClient(sessionId, data, protocol)`: Send data to specific client
|
|
215
|
+
- `server.getStats()`: Get server statistics
|
|
216
|
+
|
|
217
|
+
**Events:**
|
|
218
|
+
- `listening`: Server started
|
|
219
|
+
- `connection`: New client connected
|
|
220
|
+
- `disconnection`: Client disconnected
|
|
221
|
+
- `data`: Data received from client
|
|
222
|
+
- `error`: Error occurred
|
|
223
|
+
|
|
224
|
+
### Client API
|
|
225
|
+
|
|
226
|
+
```javascript
|
|
227
|
+
const client = yaksha.createClient(options);
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
**Options:**
|
|
231
|
+
- `server` (string): Server address (required)
|
|
232
|
+
- `port` (number): Server port (default: 8443)
|
|
233
|
+
- `securityLevel` (string): 'low', 'medium', 'high', or 'custom' (default: 'medium')
|
|
234
|
+
- `customSecurityConfig` (object): Custom security configuration (required if securityLevel is 'custom')
|
|
235
|
+
- `autoReconnect` (boolean): Auto-reconnect on disconnect (default: true)
|
|
236
|
+
- `authCredentials` (object): Authentication credentials
|
|
237
|
+
- `features` (object): Enable/disable features
|
|
238
|
+
|
|
239
|
+
**Methods:**
|
|
240
|
+
- `await client.connect()`: Connect to server
|
|
241
|
+
- `await client.disconnect()`: Disconnect from server
|
|
242
|
+
- `await client.send(data, protocol)`: Send data through VPN
|
|
243
|
+
- `await client.resolveDNS(domain, type)`: Resolve DNS through VPN
|
|
244
|
+
- `client.getStats()`: Get client statistics
|
|
245
|
+
|
|
246
|
+
**Events:**
|
|
247
|
+
- `connected`: Connected to server
|
|
248
|
+
- `disconnected`: Disconnected from server
|
|
249
|
+
- `reconnecting`: Attempting reconnection
|
|
250
|
+
- `data`: Data received from server
|
|
251
|
+
- `error`: Error occurred
|
|
252
|
+
|
|
253
|
+
---
|
|
254
|
+
|
|
255
|
+
## 💡 Examples
|
|
256
|
+
|
|
257
|
+
### Basic Server
|
|
258
|
+
|
|
259
|
+
```javascript
|
|
260
|
+
const yaksha = require('@omindu/yaksha');
|
|
261
|
+
|
|
262
|
+
const server = yaksha.createServer({
|
|
263
|
+
port: 8443,
|
|
264
|
+
securityLevel: 'medium',
|
|
265
|
+
authMethod: 'token'
|
|
266
|
+
});
|
|
267
|
+
|
|
268
|
+
server.auth.registerToken('client1', 'secret-token-123');
|
|
269
|
+
|
|
270
|
+
server.on('connection', (sessionId, address) => {
|
|
271
|
+
console.log(`Client connected: ${address}`);
|
|
272
|
+
});
|
|
273
|
+
|
|
274
|
+
await server.start();
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Advanced Configuration
|
|
278
|
+
|
|
279
|
+
```javascript
|
|
280
|
+
const client = yaksha.createClient({
|
|
281
|
+
server: 'vpn.example.com',
|
|
282
|
+
port: 8443,
|
|
283
|
+
securityLevel: 'high',
|
|
284
|
+
features: {
|
|
285
|
+
sniSpoofing: true,
|
|
286
|
+
trafficObfuscation: true,
|
|
287
|
+
multiPath: true,
|
|
288
|
+
dnsOverride: true,
|
|
289
|
+
tlsCamouflage: true,
|
|
290
|
+
firewallEvasion: true
|
|
291
|
+
},
|
|
292
|
+
authCredentials: {
|
|
293
|
+
identifier: 'secure-client',
|
|
294
|
+
certificate: 'base64-cert',
|
|
295
|
+
totpToken: '123456'
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
|
|
299
|
+
await client.connect();
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
### Custom Security Level
|
|
303
|
+
|
|
304
|
+
```javascript
|
|
305
|
+
const { createServer, createClient, SecurityLevels } = require('@omindu/yaksha');
|
|
306
|
+
|
|
307
|
+
// Create custom configuration
|
|
308
|
+
const customConfig = SecurityLevels.createCustomConfig({
|
|
309
|
+
encryption: 'double',
|
|
310
|
+
keyExchange: 'x25519-rotate',
|
|
311
|
+
obfuscation: 'aes-256-gcm',
|
|
312
|
+
multiPath: 3,
|
|
313
|
+
keyRotation: true,
|
|
314
|
+
keyRotationInterval: 2000,
|
|
315
|
+
tlsCamouflage: 'full'
|
|
316
|
+
}, 'medium'); // Base level
|
|
317
|
+
|
|
318
|
+
// Server with custom security
|
|
319
|
+
const server = createServer({
|
|
320
|
+
port: 8443,
|
|
321
|
+
securityLevel: 'custom',
|
|
322
|
+
customSecurityConfig: customConfig
|
|
323
|
+
});
|
|
324
|
+
|
|
325
|
+
// Client with custom security
|
|
326
|
+
const client = createClient({
|
|
327
|
+
server: 'localhost',
|
|
328
|
+
port: 8443,
|
|
329
|
+
securityLevel: 'custom',
|
|
330
|
+
customSecurityConfig: customConfig
|
|
331
|
+
});
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### DNS Resolution
|
|
335
|
+
|
|
336
|
+
```javascript
|
|
337
|
+
const addresses = await client.resolveDNS('example.com', 'A');
|
|
338
|
+
console.log('IP addresses:', addresses);
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
### Bug Host Configuration
|
|
342
|
+
|
|
343
|
+
Bug Host allows bypassing network restrictions by setting SNI to an allowed domain:
|
|
344
|
+
|
|
345
|
+
```javascript
|
|
346
|
+
// Client with bug host
|
|
347
|
+
const client = createClient({
|
|
348
|
+
server: 'vpn.example.com',
|
|
349
|
+
port: 8443,
|
|
350
|
+
bugHost: 'cloudflare.com', // SNI appears as cloudflare.com
|
|
351
|
+
securityLevel: 'custom',
|
|
352
|
+
customSecurityConfig: {
|
|
353
|
+
sniSpoofing: 'static',
|
|
354
|
+
bugHost: 'cloudflare.com'
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
|
|
358
|
+
// Popular bug hosts
|
|
359
|
+
const bugHosts = {
|
|
360
|
+
cdn: 'cloudflare.com',
|
|
361
|
+
social: 'facebook.com',
|
|
362
|
+
tech: 'google.com',
|
|
363
|
+
education: 'wikipedia.org'
|
|
364
|
+
};
|
|
365
|
+
```
|
|
366
|
+
|
|
367
|
+
**See [examples/bug-host-example.js](examples/bug-host-example.js) for comprehensive bug host usage.**
|
|
368
|
+
|
|
369
|
+
---
|
|
370
|
+
|
|
371
|
+
## 🔧 Configuration
|
|
372
|
+
|
|
373
|
+
### Environment Variables
|
|
374
|
+
|
|
375
|
+
```bash
|
|
376
|
+
YAKSHA_HOST=0.0.0.0
|
|
377
|
+
YAKSHA_PORT=8443
|
|
378
|
+
YAKSHA_SECURITY_LEVEL=medium
|
|
379
|
+
YAKSHA_AUTH_METHOD=token
|
|
380
|
+
YAKSHA_LOG_LEVEL=info
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
### Configuration File
|
|
384
|
+
|
|
385
|
+
```json
|
|
386
|
+
{
|
|
387
|
+
"server": {
|
|
388
|
+
"host": "0.0.0.0",
|
|
389
|
+
"port": 8443,
|
|
390
|
+
"maxConnections": 10000
|
|
391
|
+
},
|
|
392
|
+
"security": {
|
|
393
|
+
"level": "high",
|
|
394
|
+
"authMethod": "certificate"
|
|
395
|
+
},
|
|
396
|
+
"features": {
|
|
397
|
+
"sniSpoofing": true,
|
|
398
|
+
"trafficObfuscation": true,
|
|
399
|
+
"multiPath": true,
|
|
400
|
+
"dnsOverride": true,
|
|
401
|
+
"tlsCamouflage": true,
|
|
402
|
+
"firewallEvasion": true
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
Load with: `const config = new yaksha.Config(); config.loadFromFile('config.json');`
|
|
408
|
+
|
|
409
|
+
---
|
|
410
|
+
|
|
411
|
+
## 📊 Performance
|
|
412
|
+
|
|
413
|
+
### Benchmarks
|
|
414
|
+
|
|
415
|
+
| Metric | Value |
|
|
416
|
+
|--------|-------|
|
|
417
|
+
| Throughput | >1 Gbps on gigabit connection |
|
|
418
|
+
| Latency Overhead | <5ms (medium security) |
|
|
419
|
+
| Memory Usage | <50MB per 1000 connections |
|
|
420
|
+
| CPU Usage | <10% on 4-core modern CPU |
|
|
421
|
+
| Concurrent Connections | 10,000+ |
|
|
422
|
+
| Packet Processing | ~100,000 packets/second |
|
|
423
|
+
|
|
424
|
+
### Optimization Tips
|
|
425
|
+
|
|
426
|
+
1. **Use UDP for data**: Lower latency than TCP
|
|
427
|
+
2. **Enable buffer pooling**: Reduces GC pressure
|
|
428
|
+
3. **Choose appropriate security level**: Balance security vs performance
|
|
429
|
+
4. **Enable multi-path**: Aggregate bandwidth
|
|
430
|
+
5. **Adjust MTU**: Optimize for your network
|
|
431
|
+
|
|
432
|
+
---
|
|
433
|
+
|
|
434
|
+
## 🔒 Security
|
|
435
|
+
|
|
436
|
+
### Encryption Algorithms
|
|
437
|
+
|
|
438
|
+
- **ChaCha20-Poly1305**: Fast, secure stream cipher
|
|
439
|
+
- **AES-256-GCM**: Industry standard, hardware-accelerated
|
|
440
|
+
- **Double Encryption**: Maximum security (AES + ChaCha20)
|
|
441
|
+
|
|
442
|
+
### Key Exchange
|
|
443
|
+
|
|
444
|
+
- **X25519**: Elliptic curve Diffie-Hellman
|
|
445
|
+
- **Perfect Forward Secrecy**: New keys for each session
|
|
446
|
+
- **Key Rotation**: Automatic rekeying (high security mode)
|
|
447
|
+
|
|
448
|
+
### Authentication
|
|
449
|
+
|
|
450
|
+
- **Password**: bcrypt hashing, rate limiting
|
|
451
|
+
- **Token**: Time-based, automatic rotation
|
|
452
|
+
- **Certificate**: X.509 with optional 2FA
|
|
453
|
+
|
|
454
|
+
### Security Best Practices
|
|
455
|
+
|
|
456
|
+
1. Always use TLS/SSL in production
|
|
457
|
+
2. Enable all firewall bypass features
|
|
458
|
+
3. Use high security level for sensitive data
|
|
459
|
+
4. Rotate authentication tokens regularly
|
|
460
|
+
5. Use custom security level for specific requirements
|
|
461
|
+
6. Monitor for unusual traffic patterns
|
|
462
|
+
7. Keep dependencies updated
|
|
463
|
+
|
|
464
|
+
### Custom Security Configurations
|
|
465
|
+
|
|
466
|
+
Yaksha supports custom security configurations through the `createCustomConfig` method:
|
|
467
|
+
|
|
468
|
+
```javascript
|
|
469
|
+
const { SecurityLevels } = require('@omindu/yaksha');
|
|
470
|
+
|
|
471
|
+
// IoT Device (Low resource, moderate security)
|
|
472
|
+
const iotConfig = SecurityLevels.createCustomConfig({
|
|
473
|
+
encryption: 'chacha20-poly1305',
|
|
474
|
+
obfuscation: 'xor',
|
|
475
|
+
multiPath: 1,
|
|
476
|
+
keyRotation: false
|
|
477
|
+
}, 'low');
|
|
478
|
+
|
|
479
|
+
// Corporate VPN (Balanced + Compliance)
|
|
480
|
+
const corporateConfig = SecurityLevels.createCustomConfig({
|
|
481
|
+
encryption: 'aes-256-gcm',
|
|
482
|
+
certificateValidation: true,
|
|
483
|
+
keyRotation: true,
|
|
484
|
+
multiPath: 4
|
|
485
|
+
}, 'medium');
|
|
486
|
+
|
|
487
|
+
// Maximum Security (Government/Banking)
|
|
488
|
+
const maxSecurityConfig = SecurityLevels.createCustomConfig({
|
|
489
|
+
encryption: 'double',
|
|
490
|
+
twoFactorAuth: true,
|
|
491
|
+
keyRotationInterval: 250,
|
|
492
|
+
multiPath: 8
|
|
493
|
+
}, 'high');
|
|
494
|
+
```
|
|
495
|
+
|
|
496
|
+
**See [examples/custom-level-example.js](examples/custom-level-example.js) for more custom configurations.**
|
|
497
|
+
|
|
498
|
+
---
|
|
499
|
+
|
|
500
|
+
## 🧪 Testing
|
|
501
|
+
|
|
502
|
+
```bash
|
|
503
|
+
# Run all tests
|
|
504
|
+
npm test
|
|
505
|
+
|
|
506
|
+
# Run specific test suite
|
|
507
|
+
npm test -- core.test.js
|
|
508
|
+
|
|
509
|
+
# Run with coverage
|
|
510
|
+
npm run test:coverage
|
|
511
|
+
|
|
512
|
+
# Run benchmarks
|
|
513
|
+
npm run benchmark
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
---
|
|
517
|
+
|
|
518
|
+
## 🤝 Contributing
|
|
519
|
+
|
|
520
|
+
Contributions are welcome! Please read our [Contributing Guidelines](CONTRIBUTING.md) first.
|
|
521
|
+
|
|
522
|
+
1. Fork the repository
|
|
523
|
+
2. Create your feature branch (`git checkout -b feature/AmazingFeature`)
|
|
524
|
+
3. Commit your changes (`git commit -m 'Add some AmazingFeature'`)
|
|
525
|
+
4. Push to the branch (`git push origin feature/AmazingFeature`)
|
|
526
|
+
5. Open a Pull Request
|
|
527
|
+
|
|
528
|
+
---
|
|
529
|
+
|
|
530
|
+
## 📝 License
|
|
531
|
+
|
|
532
|
+
This project is licensed under the MIT License - see the [LICENSE](LICENSE) file for details.
|
|
533
|
+
|
|
534
|
+
---
|
|
535
|
+
|
|
536
|
+
## 👤 Author
|
|
537
|
+
|
|
538
|
+
**Omindu Dissanayaka** (SE U. G)
|
|
539
|
+
|
|
540
|
+
- GitHub: [@OminduDissanayaka](https://github.com/OminduDissanayaka)
|
|
541
|
+
- Email: contact@example.com
|
|
542
|
+
|
|
543
|
+
---
|
|
544
|
+
|
|
545
|
+
## 🙏 Acknowledgments
|
|
546
|
+
|
|
547
|
+
- Built with Node.js
|
|
548
|
+
- Uses TweetNaCl for some cryptographic operations
|
|
549
|
+
- Inspired by modern VPN protocols (WireGuard, OpenVPN)
|
|
550
|
+
|
|
551
|
+
---
|
|
552
|
+
|
|
553
|
+
## 📚 Additional Documentation
|
|
554
|
+
|
|
555
|
+
- [API Reference](docs/API.md)
|
|
556
|
+
- [Security Model](docs/SECURITY.md)
|
|
557
|
+
- [Protocol Specification](docs/PROTOCOL.md)
|
|
558
|
+
- [Performance Tuning](docs/PERFORMANCE.md)
|
|
559
|
+
- [Troubleshooting](docs/TROUBLESHOOTING.md)
|
|
560
|
+
|
|
561
|
+
---
|
|
562
|
+
|
|
563
|
+
## 🗺️ Roadmap
|
|
564
|
+
|
|
565
|
+
### Version 1.1
|
|
566
|
+
- [ ] WebSocket support
|
|
567
|
+
- [ ] QUIC protocol support
|
|
568
|
+
- [ ] IPv6 support
|
|
569
|
+
- [ ] Plugin system
|
|
570
|
+
|
|
571
|
+
### Version 1.2
|
|
572
|
+
- [ ] GUI client
|
|
573
|
+
- [ ] Mobile support (React Native)
|
|
574
|
+
- [ ] Traffic statistics dashboard
|
|
575
|
+
- [ ] Web admin panel
|
|
576
|
+
|
|
577
|
+
### Version 2.0
|
|
578
|
+
- [ ] P2P mode
|
|
579
|
+
- [ ] Mesh networking
|
|
580
|
+
- [ ] Blockchain-based authentication
|
|
581
|
+
- [ ] Quantum-resistant encryption
|
|
582
|
+
|
|
583
|
+
---
|
|
584
|
+
|
|
585
|
+
## ⚠️ Disclaimer
|
|
586
|
+
|
|
587
|
+
This software is provided for educational and research purposes. Ensure compliance with local laws and regulations when using VPN technology. The authors are not responsible for misuse of this software.
|
|
588
|
+
|
|
589
|
+
---
|
|
590
|
+
|
|
591
|
+
<div align="center">
|
|
592
|
+
|
|
593
|
+
Made with ❤️ by Omindu Dissanayaka
|
|
594
|
+
|
|
595
|
+
**If you find this project useful, please give it a ⭐️**
|
|
596
|
+
|
|
597
|
+
</div>
|
package/package.json
CHANGED
package/src/core/protocol.js
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
3
|
/**
|
|
4
|
-
* Yaksha Protocol
|
|
5
|
-
*
|
|
4
|
+
* Yaksha Protocol Implementation (v1.1.0 - Cross-Platform Fixed)
|
|
5
|
+
* Fixed for Linux server <-> Windows client compatibility
|
|
6
|
+
* - Explicit unsigned integer operations
|
|
7
|
+
* - Platform-independent CRC32 checksum
|
|
8
|
+
* - Robust buffer handling with proper byte ordering
|
|
6
9
|
*/
|
|
7
10
|
|
|
8
11
|
const crypto = require('crypto');
|
|
9
|
-
|
|
12
|
+
|
|
13
|
+
// Protocol constants
|
|
14
|
+
const PROTOCOL_VERSION = 0x01;
|
|
15
|
+
const HEADER_SIZE = 16; // bytes
|
|
10
16
|
|
|
11
17
|
// Packet types
|
|
12
18
|
const PACKET_TYPES = {
|
|
@@ -17,12 +23,6 @@ const PACKET_TYPES = {
|
|
|
17
23
|
KEEPALIVE: 0x05
|
|
18
24
|
};
|
|
19
25
|
|
|
20
|
-
// Protocol version
|
|
21
|
-
const PROTOCOL_VERSION = 0x01;
|
|
22
|
-
|
|
23
|
-
// Header size (fixed 16 bytes)
|
|
24
|
-
const HEADER_SIZE = 16;
|
|
25
|
-
|
|
26
26
|
class Protocol {
|
|
27
27
|
constructor(options = {}) {
|
|
28
28
|
this.version = PROTOCOL_VERSION;
|
|
@@ -31,56 +31,59 @@ class Protocol {
|
|
|
31
31
|
}
|
|
32
32
|
|
|
33
33
|
/**
|
|
34
|
-
* Create
|
|
35
|
-
*
|
|
36
|
-
* - Version (1 byte)
|
|
37
|
-
* - Type (1 byte)
|
|
38
|
-
* - Length (2 bytes) - payload length
|
|
39
|
-
* - Session ID (4 bytes)
|
|
40
|
-
* - Sequence (4 bytes)
|
|
41
|
-
* - Checksum (4 bytes)
|
|
34
|
+
* Create protocol header with explicit byte ordering
|
|
35
|
+
* CRITICAL: Uses big-endian for all multi-byte values for cross-platform consistency
|
|
42
36
|
*/
|
|
43
|
-
createHeader(type,
|
|
44
|
-
const header =
|
|
45
|
-
|
|
46
|
-
//
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
//
|
|
53
|
-
header.
|
|
54
|
-
|
|
55
|
-
// Session ID (
|
|
56
|
-
header.writeUInt32BE(
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
header.writeUInt32BE(sequence, 8);
|
|
60
|
-
|
|
61
|
-
// Checksum placeholder (will be filled later)
|
|
62
|
-
header.writeUInt32BE(0, 12);
|
|
63
|
-
|
|
37
|
+
createHeader(type, length, sessionId, sequence) {
|
|
38
|
+
const header = Buffer.alloc(HEADER_SIZE); // Zero-filled for consistency
|
|
39
|
+
|
|
40
|
+
// Force unsigned integers to prevent sign issues across platforms
|
|
41
|
+
const safeType = (type & 0xFF) >>> 0;
|
|
42
|
+
const safeLength = (length & 0xFFFF) >>> 0;
|
|
43
|
+
const safeSessionId = (sessionId >>> 0);
|
|
44
|
+
const safeSequence = (sequence >>> 0);
|
|
45
|
+
|
|
46
|
+
header.writeUInt8(this.version, 0); // Version (1 byte)
|
|
47
|
+
header.writeUInt8(safeType, 1); // Type (1 byte)
|
|
48
|
+
header.writeUInt16BE(safeLength, 2); // Length (2 bytes, big-endian)
|
|
49
|
+
header.writeUInt32BE(safeSessionId, 4); // Session ID (4 bytes, big-endian)
|
|
50
|
+
header.writeUInt32BE(safeSequence, 8); // Sequence (4 bytes, big-endian)
|
|
51
|
+
header.writeUInt32BE(0, 12); // Checksum placeholder (4 bytes)
|
|
52
|
+
|
|
64
53
|
return header;
|
|
65
54
|
}
|
|
66
55
|
|
|
67
56
|
/**
|
|
68
|
-
* Calculate CRC32 checksum
|
|
57
|
+
* Calculate CRC32 checksum with explicit unsigned operations
|
|
58
|
+
* CRITICAL FIX: Ensures same result on Linux and Windows
|
|
69
59
|
*/
|
|
70
60
|
calculateChecksum(data) {
|
|
71
|
-
//
|
|
72
|
-
|
|
61
|
+
// Ensure we're working with a Buffer
|
|
62
|
+
if (!Buffer.isBuffer(data)) {
|
|
63
|
+
data = Buffer.from(data);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
let crc = 0xFFFFFFFF >>> 0; // Force unsigned
|
|
67
|
+
|
|
73
68
|
for (let i = 0; i < data.length; i++) {
|
|
74
|
-
|
|
69
|
+
const byte = data[i] & 0xFF; // Ensure byte is 0-255
|
|
70
|
+
crc = (crc ^ byte) >>> 0;
|
|
71
|
+
|
|
75
72
|
for (let j = 0; j < 8; j++) {
|
|
76
|
-
|
|
73
|
+
// Explicit unsigned right shift and bitwise operations
|
|
74
|
+
if ((crc & 1) !== 0) {
|
|
75
|
+
crc = ((crc >>> 1) ^ 0xEDB88320) >>> 0;
|
|
76
|
+
} else {
|
|
77
|
+
crc = (crc >>> 1) >>> 0;
|
|
78
|
+
}
|
|
77
79
|
}
|
|
78
80
|
}
|
|
79
|
-
|
|
81
|
+
|
|
82
|
+
return ((crc ^ 0xFFFFFFFF) >>> 0);
|
|
80
83
|
}
|
|
81
84
|
|
|
82
85
|
/**
|
|
83
|
-
* Add random padding for
|
|
86
|
+
* Add random padding for obfuscation
|
|
84
87
|
*/
|
|
85
88
|
addPadding(data, maxPaddingSize = 255) {
|
|
86
89
|
if (!this.enablePadding) {
|
|
@@ -102,27 +105,48 @@ class Protocol {
|
|
|
102
105
|
* Remove padding from data
|
|
103
106
|
*/
|
|
104
107
|
removePadding(data, paddingSize) {
|
|
105
|
-
if (paddingSize === 0) {
|
|
108
|
+
if (paddingSize === 0 || !paddingSize) {
|
|
106
109
|
return data;
|
|
107
110
|
}
|
|
108
111
|
return data.slice(0, -paddingSize);
|
|
109
112
|
}
|
|
110
113
|
|
|
111
114
|
/**
|
|
112
|
-
* Serialize packet
|
|
115
|
+
* Serialize packet (Linux/Windows compatible)
|
|
116
|
+
* CRITICAL FIX: Proper buffer construction and checksum calculation
|
|
113
117
|
*/
|
|
114
118
|
serialize(type, payload, sessionId, sequence) {
|
|
119
|
+
// Ensure payload is a proper Buffer
|
|
120
|
+
if (!Buffer.isBuffer(payload)) {
|
|
121
|
+
if (typeof payload === 'string') {
|
|
122
|
+
payload = Buffer.from(payload, 'utf8');
|
|
123
|
+
} else {
|
|
124
|
+
payload = Buffer.from(payload);
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
115
128
|
// Add padding to payload
|
|
116
129
|
const { data: paddedPayload, paddingSize } = this.addPadding(payload);
|
|
117
130
|
|
|
118
|
-
//
|
|
119
|
-
const
|
|
131
|
+
// Force safe unsigned integers
|
|
132
|
+
const safeSessionId = (sessionId >>> 0);
|
|
133
|
+
const safeSequence = (sequence >>> 0);
|
|
134
|
+
|
|
135
|
+
// Create header with zero checksum
|
|
136
|
+
const header = this.createHeader(type, paddedPayload.length, safeSessionId, safeSequence);
|
|
120
137
|
|
|
121
|
-
//
|
|
122
|
-
const
|
|
138
|
+
// Create complete packet buffer (avoid Buffer.concat for consistency)
|
|
139
|
+
const totalSize = header.length + paddedPayload.length;
|
|
140
|
+
const packet = Buffer.allocUnsafe(totalSize);
|
|
141
|
+
|
|
142
|
+
// Copy header and payload explicitly
|
|
143
|
+
header.copy(packet, 0, 0, header.length);
|
|
144
|
+
paddedPayload.copy(packet, header.length, 0, paddedPayload.length);
|
|
123
145
|
|
|
124
|
-
// Calculate
|
|
125
|
-
const checksum = this.calculateChecksum(packet);
|
|
146
|
+
// Calculate checksum over entire packet (with checksum field zeroed)
|
|
147
|
+
const checksum = this.calculateChecksum(packet) >>> 0;
|
|
148
|
+
|
|
149
|
+
// Write checksum to header
|
|
126
150
|
packet.writeUInt32BE(checksum, 12);
|
|
127
151
|
|
|
128
152
|
return { packet, paddingSize };
|
|
@@ -132,20 +156,25 @@ class Protocol {
|
|
|
132
156
|
* Parse packet header
|
|
133
157
|
*/
|
|
134
158
|
parseHeader(buffer) {
|
|
159
|
+
if (!Buffer.isBuffer(buffer)) {
|
|
160
|
+
throw new Error('Invalid buffer: not a Buffer object');
|
|
161
|
+
}
|
|
162
|
+
|
|
135
163
|
if (buffer.length < HEADER_SIZE) {
|
|
136
|
-
throw new Error(
|
|
164
|
+
throw new Error(`Buffer too small for header: ${buffer.length} bytes (need ${HEADER_SIZE})`);
|
|
137
165
|
}
|
|
138
166
|
|
|
139
|
-
|
|
140
|
-
const
|
|
141
|
-
const
|
|
142
|
-
const
|
|
143
|
-
const
|
|
144
|
-
const
|
|
167
|
+
// Read with explicit unsigned operations
|
|
168
|
+
const version = buffer.readUInt8(0) & 0xFF;
|
|
169
|
+
const type = buffer.readUInt8(1) & 0xFF;
|
|
170
|
+
const length = buffer.readUInt16BE(2) >>> 0;
|
|
171
|
+
const sessionId = buffer.readUInt32BE(4) >>> 0;
|
|
172
|
+
const sequence = buffer.readUInt32BE(8) >>> 0;
|
|
173
|
+
const checksum = buffer.readUInt32BE(12) >>> 0;
|
|
145
174
|
|
|
146
175
|
// Validate version
|
|
147
176
|
if (version !== this.version) {
|
|
148
|
-
throw new Error(`Unsupported protocol version: ${version}`);
|
|
177
|
+
throw new Error(`Unsupported protocol version: ${version} (expected ${this.version})`);
|
|
149
178
|
}
|
|
150
179
|
|
|
151
180
|
// Validate type
|
|
@@ -155,7 +184,7 @@ class Protocol {
|
|
|
155
184
|
|
|
156
185
|
// Validate length
|
|
157
186
|
if (length > this.maxPayloadSize) {
|
|
158
|
-
throw new Error(`Payload too large: ${length}`);
|
|
187
|
+
throw new Error(`Payload too large: ${length} (max ${this.maxPayloadSize})`);
|
|
159
188
|
}
|
|
160
189
|
|
|
161
190
|
return {
|
|
@@ -169,99 +198,89 @@ class Protocol {
|
|
|
169
198
|
}
|
|
170
199
|
|
|
171
200
|
/**
|
|
172
|
-
* Deserialize packet
|
|
201
|
+
* Deserialize packet (Linux/Windows compatible)
|
|
202
|
+
* CRITICAL FIX: Robust validation and error reporting
|
|
173
203
|
*/
|
|
174
204
|
deserialize(buffer, paddingSize = 0) {
|
|
175
|
-
|
|
176
|
-
|
|
205
|
+
if (!Buffer.isBuffer(buffer)) {
|
|
206
|
+
throw new Error('Invalid buffer: not a Buffer object');
|
|
207
|
+
}
|
|
177
208
|
|
|
178
|
-
//
|
|
179
|
-
const
|
|
180
|
-
|
|
181
|
-
|
|
209
|
+
// Parse header first
|
|
210
|
+
const header = this.parseHeader(buffer);
|
|
211
|
+
|
|
212
|
+
const totalExpectedSize = HEADER_SIZE + header.length;
|
|
213
|
+
if (buffer.length < totalExpectedSize) {
|
|
214
|
+
throw new Error(
|
|
215
|
+
`Incomplete packet: got ${buffer.length} bytes, expected ${totalExpectedSize} ` +
|
|
216
|
+
`(header=${HEADER_SIZE}, payload=${header.length})`
|
|
217
|
+
);
|
|
182
218
|
}
|
|
183
219
|
|
|
184
|
-
//
|
|
185
|
-
const
|
|
220
|
+
// Extract payload (create new buffer to avoid reference issues)
|
|
221
|
+
const payloadStart = HEADER_SIZE;
|
|
222
|
+
const payloadEnd = payloadStart + header.length;
|
|
223
|
+
const payload = Buffer.allocUnsafe(header.length);
|
|
224
|
+
buffer.copy(payload, 0, payloadStart, payloadEnd);
|
|
225
|
+
|
|
226
|
+
// Verify checksum - recalculate with checksum field zeroed
|
|
227
|
+
const verifyBuffer = Buffer.allocUnsafe(buffer.length);
|
|
228
|
+
buffer.copy(verifyBuffer, 0, 0, buffer.length);
|
|
229
|
+
verifyBuffer.writeUInt32BE(0, 12); // Zero out checksum field
|
|
186
230
|
|
|
187
|
-
|
|
188
|
-
const
|
|
189
|
-
packetCopy.writeUInt32BE(0, 12);
|
|
231
|
+
const calculatedChecksum = this.calculateChecksum(verifyBuffer) >>> 0;
|
|
232
|
+
const receivedChecksum = header.checksum >>> 0;
|
|
190
233
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
234
|
+
if (receivedChecksum !== calculatedChecksum) {
|
|
235
|
+
// Detailed error for debugging cross-platform issues
|
|
236
|
+
const debugInfo = {
|
|
237
|
+
received: '0x' + receivedChecksum.toString(16).padStart(8, '0').toUpperCase(),
|
|
238
|
+
calculated: '0x' + calculatedChecksum.toString(16).padStart(8, '0').toUpperCase(),
|
|
239
|
+
packetSize: buffer.length,
|
|
240
|
+
headerSize: HEADER_SIZE,
|
|
241
|
+
payloadSize: header.length,
|
|
242
|
+
type: header.type,
|
|
243
|
+
sequence: header.sequence,
|
|
244
|
+
platform: process.platform,
|
|
245
|
+
nodeVersion: process.version
|
|
246
|
+
};
|
|
247
|
+
|
|
248
|
+
throw new Error(
|
|
249
|
+
`Checksum verification failed!\n` +
|
|
250
|
+
` Received: ${debugInfo.received}\n` +
|
|
251
|
+
` Calculated: ${debugInfo.calculated}\n` +
|
|
252
|
+
` Packet: ${debugInfo.packetSize} bytes (header=${debugInfo.headerSize}, payload=${debugInfo.payloadSize})\n` +
|
|
253
|
+
` Type: ${debugInfo.type}, Sequence: ${debugInfo.sequence}\n` +
|
|
254
|
+
` Platform: ${debugInfo.platform}, Node: ${debugInfo.nodeVersion}`
|
|
255
|
+
);
|
|
195
256
|
}
|
|
196
257
|
|
|
197
|
-
//
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
// Remove padding if present
|
|
201
|
-
payload = this.removePadding(payload, paddingSize);
|
|
258
|
+
// Remove padding from payload
|
|
259
|
+
const unpaddedPayload = this.removePadding(payload, paddingSize);
|
|
202
260
|
|
|
203
261
|
return {
|
|
204
|
-
header,
|
|
205
|
-
payload,
|
|
206
|
-
|
|
262
|
+
type: header.type,
|
|
263
|
+
payload: unpaddedPayload,
|
|
264
|
+
sessionId: header.sessionId,
|
|
265
|
+
sequence: header.sequence,
|
|
266
|
+
checksum: header.checksum
|
|
207
267
|
};
|
|
208
268
|
}
|
|
209
269
|
|
|
210
|
-
/**
|
|
211
|
-
* Create HANDSHAKE packet
|
|
212
|
-
*/
|
|
213
|
-
createHandshake(sessionId, sequence, data) {
|
|
214
|
-
return this.serialize(PACKET_TYPES.HANDSHAKE, data, sessionId, sequence);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
/**
|
|
218
|
-
* Create DATA packet
|
|
219
|
-
*/
|
|
220
|
-
createData(sessionId, sequence, data) {
|
|
221
|
-
return this.serialize(PACKET_TYPES.DATA, data, sessionId, sequence);
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Create ACK packet
|
|
226
|
-
*/
|
|
227
|
-
createAck(sessionId, sequence, ackSequence) {
|
|
228
|
-
const ackBuffer = Buffer.allocUnsafe(4);
|
|
229
|
-
ackBuffer.writeUInt32BE(ackSequence, 0);
|
|
230
|
-
return this.serialize(PACKET_TYPES.ACK, ackBuffer, sessionId, sequence);
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
/**
|
|
234
|
-
* Create CLOSE packet
|
|
235
|
-
*/
|
|
236
|
-
createClose(sessionId, sequence, reason = '') {
|
|
237
|
-
const reasonBuffer = Buffer.from(reason, 'utf8');
|
|
238
|
-
return this.serialize(PACKET_TYPES.CLOSE, reasonBuffer, sessionId, sequence);
|
|
239
|
-
}
|
|
240
|
-
|
|
241
|
-
/**
|
|
242
|
-
* Create KEEPALIVE packet
|
|
243
|
-
*/
|
|
244
|
-
createKeepalive(sessionId, sequence) {
|
|
245
|
-
const timestamp = Buffer.allocUnsafe(8);
|
|
246
|
-
timestamp.writeBigUInt64BE(BigInt(Date.now()), 0);
|
|
247
|
-
return this.serialize(PACKET_TYPES.KEEPALIVE, timestamp, sessionId, sequence);
|
|
248
|
-
}
|
|
249
|
-
|
|
250
270
|
/**
|
|
251
271
|
* Get packet type name
|
|
252
272
|
*/
|
|
253
|
-
|
|
254
|
-
const
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
};
|
|
261
|
-
return typeNames[type] || 'UNKNOWN';
|
|
273
|
+
getPacketTypeName(type) {
|
|
274
|
+
for (const [name, value] of Object.entries(PACKET_TYPES)) {
|
|
275
|
+
if (value === type) {
|
|
276
|
+
return name;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
return `UNKNOWN(${type})`;
|
|
262
280
|
}
|
|
263
281
|
}
|
|
264
282
|
|
|
283
|
+
// Export
|
|
265
284
|
module.exports = Protocol;
|
|
266
285
|
module.exports.PACKET_TYPES = PACKET_TYPES;
|
|
267
286
|
module.exports.HEADER_SIZE = HEADER_SIZE;
|
|
@@ -0,0 +1,268 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Yaksha Protocol Handler
|
|
5
|
+
* Custom protocol for packet parsing and handling
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
const crypto = require('crypto');
|
|
9
|
+
const { globalPool } = require('../utils/buffer-pool');
|
|
10
|
+
|
|
11
|
+
// Packet types
|
|
12
|
+
const PACKET_TYPES = {
|
|
13
|
+
HANDSHAKE: 0x01,
|
|
14
|
+
DATA: 0x02,
|
|
15
|
+
ACK: 0x03,
|
|
16
|
+
CLOSE: 0x04,
|
|
17
|
+
KEEPALIVE: 0x05
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// Protocol version
|
|
21
|
+
const PROTOCOL_VERSION = 0x01;
|
|
22
|
+
|
|
23
|
+
// Header size (fixed 16 bytes)
|
|
24
|
+
const HEADER_SIZE = 16;
|
|
25
|
+
|
|
26
|
+
class Protocol {
|
|
27
|
+
constructor(options = {}) {
|
|
28
|
+
this.version = PROTOCOL_VERSION;
|
|
29
|
+
this.maxPayloadSize = options.maxPayloadSize || 65535;
|
|
30
|
+
this.enablePadding = options.enablePadding !== false;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Create a packet header
|
|
35
|
+
* Header structure (16 bytes):
|
|
36
|
+
* - Version (1 byte)
|
|
37
|
+
* - Type (1 byte)
|
|
38
|
+
* - Length (2 bytes) - payload length
|
|
39
|
+
* - Session ID (4 bytes)
|
|
40
|
+
* - Sequence (4 bytes)
|
|
41
|
+
* - Checksum (4 bytes)
|
|
42
|
+
*/
|
|
43
|
+
createHeader(type, payloadLength, sessionId, sequence) {
|
|
44
|
+
const header = globalPool.acquire(HEADER_SIZE, true);
|
|
45
|
+
|
|
46
|
+
// Version
|
|
47
|
+
header.writeUInt8(this.version, 0);
|
|
48
|
+
|
|
49
|
+
// Type
|
|
50
|
+
header.writeUInt8(type, 1);
|
|
51
|
+
|
|
52
|
+
// Length (uint16, max 65535)
|
|
53
|
+
header.writeUInt16BE(payloadLength, 2);
|
|
54
|
+
|
|
55
|
+
// Session ID (uint32)
|
|
56
|
+
header.writeUInt32BE(sessionId, 4);
|
|
57
|
+
|
|
58
|
+
// Sequence (uint32)
|
|
59
|
+
header.writeUInt32BE(sequence, 8);
|
|
60
|
+
|
|
61
|
+
// Checksum placeholder (will be filled later)
|
|
62
|
+
header.writeUInt32BE(0, 12);
|
|
63
|
+
|
|
64
|
+
return header;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Calculate CRC32 checksum
|
|
69
|
+
*/
|
|
70
|
+
calculateChecksum(data) {
|
|
71
|
+
// Simple CRC32 implementation
|
|
72
|
+
let crc = 0xFFFFFFFF;
|
|
73
|
+
for (let i = 0; i < data.length; i++) {
|
|
74
|
+
crc ^= data[i];
|
|
75
|
+
for (let j = 0; j < 8; j++) {
|
|
76
|
+
crc = (crc >>> 1) ^ (0xEDB88320 & -(crc & 1));
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
return (crc ^ 0xFFFFFFFF) >>> 0;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Add random padding for traffic obfuscation
|
|
84
|
+
*/
|
|
85
|
+
addPadding(data, maxPaddingSize = 255) {
|
|
86
|
+
if (!this.enablePadding) {
|
|
87
|
+
return { data, paddingSize: 0 };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const paddingSize = crypto.randomInt(0, Math.min(maxPaddingSize + 1, 256));
|
|
91
|
+
if (paddingSize === 0) {
|
|
92
|
+
return { data, paddingSize: 0 };
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const padding = crypto.randomBytes(paddingSize);
|
|
96
|
+
const paddedData = Buffer.concat([data, padding]);
|
|
97
|
+
|
|
98
|
+
return { data: paddedData, paddingSize };
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Remove padding from data
|
|
103
|
+
*/
|
|
104
|
+
removePadding(data, paddingSize) {
|
|
105
|
+
if (paddingSize === 0) {
|
|
106
|
+
return data;
|
|
107
|
+
}
|
|
108
|
+
return data.slice(0, -paddingSize);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Serialize packet
|
|
113
|
+
*/
|
|
114
|
+
serialize(type, payload, sessionId, sequence) {
|
|
115
|
+
// Add padding to payload
|
|
116
|
+
const { data: paddedPayload, paddingSize } = this.addPadding(payload);
|
|
117
|
+
|
|
118
|
+
// Create header
|
|
119
|
+
const header = this.createHeader(type, paddedPayload.length, sessionId, sequence);
|
|
120
|
+
|
|
121
|
+
// Combine header and payload
|
|
122
|
+
const packet = Buffer.concat([header, paddedPayload]);
|
|
123
|
+
|
|
124
|
+
// Calculate and update checksum (checksum covers header + payload)
|
|
125
|
+
const checksum = this.calculateChecksum(packet);
|
|
126
|
+
packet.writeUInt32BE(checksum, 12);
|
|
127
|
+
|
|
128
|
+
return { packet, paddingSize };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Parse packet header
|
|
133
|
+
*/
|
|
134
|
+
parseHeader(buffer) {
|
|
135
|
+
if (buffer.length < HEADER_SIZE) {
|
|
136
|
+
throw new Error('Buffer too small for header');
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const version = buffer.readUInt8(0);
|
|
140
|
+
const type = buffer.readUInt8(1);
|
|
141
|
+
const length = buffer.readUInt16BE(2);
|
|
142
|
+
const sessionId = buffer.readUInt32BE(4);
|
|
143
|
+
const sequence = buffer.readUInt32BE(8);
|
|
144
|
+
const checksum = buffer.readUInt32BE(12);
|
|
145
|
+
|
|
146
|
+
// Validate version
|
|
147
|
+
if (version !== this.version) {
|
|
148
|
+
throw new Error(`Unsupported protocol version: ${version}`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// Validate type
|
|
152
|
+
if (!Object.values(PACKET_TYPES).includes(type)) {
|
|
153
|
+
throw new Error(`Invalid packet type: ${type}`);
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Validate length
|
|
157
|
+
if (length > this.maxPayloadSize) {
|
|
158
|
+
throw new Error(`Payload too large: ${length}`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
return {
|
|
162
|
+
version,
|
|
163
|
+
type,
|
|
164
|
+
length,
|
|
165
|
+
sessionId,
|
|
166
|
+
sequence,
|
|
167
|
+
checksum
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Deserialize packet
|
|
173
|
+
*/
|
|
174
|
+
deserialize(buffer, paddingSize = 0) {
|
|
175
|
+
// Parse header
|
|
176
|
+
const header = this.parseHeader(buffer);
|
|
177
|
+
|
|
178
|
+
// Check if we have complete packet
|
|
179
|
+
const expectedSize = HEADER_SIZE + header.length;
|
|
180
|
+
if (buffer.length < expectedSize) {
|
|
181
|
+
throw new Error(`Incomplete packet: expected ${expectedSize}, got ${buffer.length}`);
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// Verify checksum
|
|
185
|
+
const storedChecksum = header.checksum;
|
|
186
|
+
|
|
187
|
+
// Create a copy of packet and zero out checksum field for verification
|
|
188
|
+
const packetCopy = Buffer.from(buffer.slice(0, expectedSize));
|
|
189
|
+
packetCopy.writeUInt32BE(0, 12);
|
|
190
|
+
|
|
191
|
+
const calculatedChecksum = this.calculateChecksum(packetCopy);
|
|
192
|
+
|
|
193
|
+
if (storedChecksum !== calculatedChecksum) {
|
|
194
|
+
throw new Error('Checksum verification failed');
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
// Extract payload
|
|
198
|
+
let payload = buffer.slice(HEADER_SIZE, expectedSize);
|
|
199
|
+
|
|
200
|
+
// Remove padding if present
|
|
201
|
+
payload = this.removePadding(payload, paddingSize);
|
|
202
|
+
|
|
203
|
+
return {
|
|
204
|
+
header,
|
|
205
|
+
payload,
|
|
206
|
+
totalSize: expectedSize
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
/**
|
|
211
|
+
* Create HANDSHAKE packet
|
|
212
|
+
*/
|
|
213
|
+
createHandshake(sessionId, sequence, data) {
|
|
214
|
+
return this.serialize(PACKET_TYPES.HANDSHAKE, data, sessionId, sequence);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/**
|
|
218
|
+
* Create DATA packet
|
|
219
|
+
*/
|
|
220
|
+
createData(sessionId, sequence, data) {
|
|
221
|
+
return this.serialize(PACKET_TYPES.DATA, data, sessionId, sequence);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Create ACK packet
|
|
226
|
+
*/
|
|
227
|
+
createAck(sessionId, sequence, ackSequence) {
|
|
228
|
+
const ackBuffer = Buffer.allocUnsafe(4);
|
|
229
|
+
ackBuffer.writeUInt32BE(ackSequence, 0);
|
|
230
|
+
return this.serialize(PACKET_TYPES.ACK, ackBuffer, sessionId, sequence);
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Create CLOSE packet
|
|
235
|
+
*/
|
|
236
|
+
createClose(sessionId, sequence, reason = '') {
|
|
237
|
+
const reasonBuffer = Buffer.from(reason, 'utf8');
|
|
238
|
+
return this.serialize(PACKET_TYPES.CLOSE, reasonBuffer, sessionId, sequence);
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Create KEEPALIVE packet
|
|
243
|
+
*/
|
|
244
|
+
createKeepalive(sessionId, sequence) {
|
|
245
|
+
const timestamp = Buffer.allocUnsafe(8);
|
|
246
|
+
timestamp.writeBigUInt64BE(BigInt(Date.now()), 0);
|
|
247
|
+
return this.serialize(PACKET_TYPES.KEEPALIVE, timestamp, sessionId, sequence);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* Get packet type name
|
|
252
|
+
*/
|
|
253
|
+
getTypeName(type) {
|
|
254
|
+
const typeNames = {
|
|
255
|
+
[PACKET_TYPES.HANDSHAKE]: 'HANDSHAKE',
|
|
256
|
+
[PACKET_TYPES.DATA]: 'DATA',
|
|
257
|
+
[PACKET_TYPES.ACK]: 'ACK',
|
|
258
|
+
[PACKET_TYPES.CLOSE]: 'CLOSE',
|
|
259
|
+
[PACKET_TYPES.KEEPALIVE]: 'KEEPALIVE'
|
|
260
|
+
};
|
|
261
|
+
return typeNames[type] || 'UNKNOWN';
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
module.exports = Protocol;
|
|
266
|
+
module.exports.PACKET_TYPES = PACKET_TYPES;
|
|
267
|
+
module.exports.HEADER_SIZE = HEADER_SIZE;
|
|
268
|
+
module.exports.PROTOCOL_VERSION = PROTOCOL_VERSION;
|