@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.
@@ -0,0 +1,355 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Yaksha SNI Spoofing
5
+ * Intercept and replace Server Name Indication in TLS ClientHello
6
+ */
7
+
8
+ const crypto = require('crypto');
9
+
10
+ // Popular domains for SNI spoofing (categorized)
11
+ const DOMAIN_LISTS = {
12
+ tech: [
13
+ 'google.com', 'microsoft.com', 'apple.com', 'amazon.com',
14
+ 'facebook.com', 'twitter.com', 'linkedin.com', 'github.com'
15
+ ],
16
+ cdn: [
17
+ 'cloudflare.com', 'fastly.net', 'akamai.net', 'cloudfront.net',
18
+ 'cdn77.com', 'stackpath.com', 'bunny.net', 'jsdelivr.net'
19
+ ],
20
+ popular: [
21
+ 'youtube.com', 'netflix.com', 'spotify.com', 'instagram.com',
22
+ 'reddit.com', 'wikipedia.org', 'twitch.tv', 'zoom.us'
23
+ ],
24
+ news: [
25
+ 'nytimes.com', 'bbc.com', 'cnn.com', 'reuters.com',
26
+ 'theguardian.com', 'wsj.com', 'bloomberg.com', 'forbes.com'
27
+ ]
28
+ };
29
+
30
+ // Flatten all domains into single array
31
+ const ALL_DOMAINS = Object.values(DOMAIN_LISTS).flat();
32
+
33
+ class SNISpoofing {
34
+ constructor(securityLevel = 'medium', options = {}) {
35
+ this.securityLevel = securityLevel;
36
+ this.mode = this._selectMode(securityLevel);
37
+ this.customDomains = options.customDomains || [];
38
+ this.bugHost = options.bugHost || null; // Custom bug host for network bypass
39
+ this.sniMap = new Map(); // original -> fake mapping
40
+ this.enabled = options.enabled !== false;
41
+ }
42
+
43
+ /**
44
+ * Select SNI spoofing mode based on security level
45
+ */
46
+ _selectMode(level) {
47
+ switch (level) {
48
+ case 'low':
49
+ return 'static';
50
+ case 'medium':
51
+ return 'random';
52
+ case 'high':
53
+ return 'context-aware';
54
+ default:
55
+ return 'random';
56
+ }
57
+ }
58
+
59
+ /**
60
+ * Get static fake SNI (always returns the same domain)
61
+ */
62
+ _getStaticSNI() {
63
+ // Use bug host if specified, otherwise use default
64
+ return this.bugHost || 'cloudflare.com';
65
+ }
66
+
67
+ /**
68
+ * Get random fake SNI from domain list
69
+ */
70
+ _getRandomSNI() {
71
+ const domains = this.customDomains.length > 0
72
+ ? this.customDomains
73
+ : ALL_DOMAINS;
74
+
75
+ const randomIndex = crypto.randomInt(0, domains.length);
76
+ return domains[randomIndex];
77
+ }
78
+
79
+ /**
80
+ * Get context-aware fake SNI based on time and other factors
81
+ */
82
+ _getContextAwareSNI() {
83
+ const hour = new Date().getHours();
84
+
85
+ // Select domain category based on time of day
86
+ let category;
87
+ if (hour >= 9 && hour < 17) {
88
+ // Business hours: use tech/professional domains
89
+ category = Math.random() < 0.7 ? 'tech' : 'news';
90
+ } else if (hour >= 17 && hour < 23) {
91
+ // Evening: use entertainment domains
92
+ category = Math.random() < 0.7 ? 'popular' : 'cdn';
93
+ } else {
94
+ // Night: mixed
95
+ category = Math.random() < 0.5 ? 'popular' : 'tech';
96
+ }
97
+
98
+ const domains = DOMAIN_LISTS[category];
99
+ const randomIndex = crypto.randomInt(0, domains.length);
100
+
101
+ // Add timing jitter to avoid patterns
102
+ const jitter = crypto.randomInt(0, 100);
103
+ setTimeout(() => {}, jitter);
104
+
105
+ return domains[randomIndex];
106
+ }
107
+
108
+ /**
109
+ * Get fake SNI based on mode
110
+ */
111
+ getFakeSNI(originalSNI) {
112
+ if (!this.enabled) {
113
+ return originalSNI;
114
+ }
115
+
116
+ // If bug host is specified, always use it (highest priority)
117
+ if (this.bugHost) {
118
+ this.sniMap.set(originalSNI, this.bugHost);
119
+ return this.bugHost;
120
+ }
121
+
122
+ // Check if we already have a mapping
123
+ if (this.sniMap.has(originalSNI)) {
124
+ return this.sniMap.get(originalSNI);
125
+ }
126
+
127
+ let fakeSNI;
128
+
129
+ switch (this.mode) {
130
+ case 'static':
131
+ fakeSNI = this._getStaticSNI();
132
+ break;
133
+ case 'random':
134
+ fakeSNI = this._getRandomSNI();
135
+ break;
136
+ case 'context-aware':
137
+ fakeSNI = this._getContextAwareSNI();
138
+ break;
139
+ default:
140
+ fakeSNI = this._getRandomSNI();
141
+ }
142
+
143
+ // Store mapping
144
+ this.sniMap.set(originalSNI, fakeSNI);
145
+
146
+ return fakeSNI;
147
+ }
148
+
149
+ /**
150
+ * Get original SNI from fake SNI
151
+ */
152
+ getOriginalSNI(fakeSNI) {
153
+ for (const [original, fake] of this.sniMap.entries()) {
154
+ if (fake === fakeSNI) {
155
+ return original;
156
+ }
157
+ }
158
+ return null;
159
+ }
160
+
161
+ /**
162
+ * Parse TLS ClientHello to extract SNI
163
+ */
164
+ parseSNI(clientHello) {
165
+ try {
166
+ // TLS record header: 5 bytes
167
+ // Handshake header: 4 bytes
168
+ // Client version: 2 bytes
169
+ // Random: 32 bytes
170
+ // Session ID length: 1 byte
171
+
172
+ let offset = 0;
173
+
174
+ // Skip TLS record header
175
+ offset += 5;
176
+
177
+ // Skip handshake header
178
+ offset += 4;
179
+
180
+ // Skip client version
181
+ offset += 2;
182
+
183
+ // Skip random
184
+ offset += 32;
185
+
186
+ // Read session ID length and skip session ID
187
+ const sessionIdLength = clientHello[offset];
188
+ offset += 1 + sessionIdLength;
189
+
190
+ // Read cipher suites length and skip cipher suites
191
+ const cipherSuitesLength = clientHello.readUInt16BE(offset);
192
+ offset += 2 + cipherSuitesLength;
193
+
194
+ // Read compression methods length and skip compression methods
195
+ const compressionMethodsLength = clientHello[offset];
196
+ offset += 1 + compressionMethodsLength;
197
+
198
+ // Read extensions length
199
+ if (offset + 2 > clientHello.length) {
200
+ return null; // No extensions
201
+ }
202
+
203
+ const extensionsLength = clientHello.readUInt16BE(offset);
204
+ offset += 2;
205
+
206
+ const extensionsEnd = offset + extensionsLength;
207
+
208
+ // Parse extensions to find SNI (type 0x0000)
209
+ while (offset + 4 <= extensionsEnd) {
210
+ const extensionType = clientHello.readUInt16BE(offset);
211
+ const extensionLength = clientHello.readUInt16BE(offset + 2);
212
+ offset += 4;
213
+
214
+ if (extensionType === 0x0000) { // SNI extension
215
+ // SNI extension format:
216
+ // - Server Name List Length: 2 bytes
217
+ // - Server Name Type: 1 byte (0 = host_name)
218
+ // - Server Name Length: 2 bytes
219
+ // - Server Name: variable
220
+
221
+ offset += 2; // Skip list length
222
+ const nameType = clientHello[offset];
223
+ offset += 1;
224
+
225
+ if (nameType === 0) { // host_name
226
+ const nameLength = clientHello.readUInt16BE(offset);
227
+ offset += 2;
228
+ const serverName = clientHello.toString('utf8', offset, offset + nameLength);
229
+ return serverName;
230
+ }
231
+ } else {
232
+ offset += extensionLength;
233
+ }
234
+ }
235
+
236
+ return null;
237
+ } catch (error) {
238
+ return null;
239
+ }
240
+ }
241
+
242
+ /**
243
+ * Replace SNI in TLS ClientHello
244
+ */
245
+ replaceSNI(clientHello, newSNI) {
246
+ try {
247
+ // Create a mutable copy
248
+ const modified = Buffer.from(clientHello);
249
+
250
+ // Find SNI extension and replace
251
+ // This is a simplified implementation
252
+ // In production, use a proper TLS parser
253
+
254
+ const originalSNI = this.parseSNI(clientHello);
255
+ if (!originalSNI) {
256
+ return modified; // No SNI to replace
257
+ }
258
+
259
+ // Find and replace SNI string
260
+ const originalSNIBuffer = Buffer.from(originalSNI, 'utf8');
261
+ const newSNIBuffer = Buffer.from(newSNI, 'utf8');
262
+
263
+ const sniIndex = modified.indexOf(originalSNIBuffer);
264
+ if (sniIndex === -1) {
265
+ return modified;
266
+ }
267
+
268
+ // If new SNI is same length, simple replacement
269
+ if (originalSNIBuffer.length === newSNIBuffer.length) {
270
+ newSNIBuffer.copy(modified, sniIndex);
271
+ return modified;
272
+ }
273
+
274
+ // Different lengths require rebuilding the packet
275
+ // For simplicity, we'll pad/truncate to same length
276
+ if (newSNIBuffer.length < originalSNIBuffer.length) {
277
+ // Pad with zeros
278
+ newSNIBuffer.copy(modified, sniIndex);
279
+ modified.fill(0, sniIndex + newSNIBuffer.length, sniIndex + originalSNIBuffer.length);
280
+ } else {
281
+ // Truncate
282
+ newSNIBuffer.copy(modified, sniIndex, 0, originalSNIBuffer.length);
283
+ }
284
+
285
+ // Update SNI length field (2 bytes before SNI)
286
+ modified.writeUInt16BE(Math.min(newSNIBuffer.length, originalSNIBuffer.length), sniIndex - 2);
287
+
288
+ return modified;
289
+ } catch (error) {
290
+ // On error, return original
291
+ return clientHello;
292
+ }
293
+ }
294
+
295
+ /**
296
+ * Spoof SNI in ClientHello packet
297
+ */
298
+ spoof(clientHello, originalSNI = null) {
299
+ if (!this.enabled) {
300
+ return clientHello;
301
+ }
302
+
303
+ // Extract original SNI if not provided
304
+ if (!originalSNI) {
305
+ originalSNI = this.parseSNI(clientHello);
306
+ }
307
+
308
+ if (!originalSNI) {
309
+ return clientHello; // No SNI found
310
+ }
311
+
312
+ // Get fake SNI
313
+ const fakeSNI = this.getFakeSNI(originalSNI);
314
+
315
+ // Replace SNI in ClientHello
316
+ return this.replaceSNI(clientHello, fakeSNI);
317
+ }
318
+
319
+ /**
320
+ * Enable SNI spoofing
321
+ */
322
+ enable() {
323
+ this.enabled = true;
324
+ }
325
+
326
+ /**
327
+ * Disable SNI spoofing
328
+ */
329
+ disable() {
330
+ this.enabled = false;
331
+ }
332
+
333
+ /**
334
+ * Clear SNI mapping cache
335
+ */
336
+ clearCache() {
337
+ this.sniMap.clear();
338
+ }
339
+
340
+ /**
341
+ * Get statistics
342
+ */
343
+ getStats() {
344
+ return {
345
+ enabled: this.enabled,
346
+ mode: this.mode,
347
+ mappings: this.sniMap.size,
348
+ domains: this.customDomains.length || ALL_DOMAINS.length
349
+ };
350
+ }
351
+ }
352
+
353
+ module.exports = SNISpoofing;
354
+ module.exports.DOMAIN_LISTS = DOMAIN_LISTS;
355
+ module.exports.ALL_DOMAINS = ALL_DOMAINS;
@@ -0,0 +1,369 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Yaksha TLS 1.3 Camouflage
5
+ * Make VPN traffic look like legitimate HTTPS/TLS traffic
6
+ */
7
+
8
+ const crypto = require('crypto');
9
+ const Logger = require('../utils/logger');
10
+
11
+ // TLS 1.3 cipher suites
12
+ const CIPHER_SUITES = [
13
+ 0x1301, // TLS_AES_128_GCM_SHA256
14
+ 0x1302, // TLS_AES_256_GCM_SHA384
15
+ 0x1303, // TLS_CHACHA20_POLY1305_SHA256
16
+ ];
17
+
18
+ // TLS extensions
19
+ const EXTENSIONS = {
20
+ SERVER_NAME: 0x0000,
21
+ MAX_FRAGMENT_LENGTH: 0x0001,
22
+ STATUS_REQUEST: 0x0005,
23
+ SUPPORTED_GROUPS: 0x000a,
24
+ EC_POINT_FORMATS: 0x000b,
25
+ SIGNATURE_ALGORITHMS: 0x000d,
26
+ ALPN: 0x0010,
27
+ SIGNED_CERTIFICATE_TIMESTAMP: 0x0012,
28
+ EXTENDED_MASTER_SECRET: 0x0017,
29
+ SESSION_TICKET: 0x0023,
30
+ SUPPORTED_VERSIONS: 0x002b,
31
+ PSK_KEY_EXCHANGE_MODES: 0x002d,
32
+ KEY_SHARE: 0x0033
33
+ };
34
+
35
+ // ALPN protocols
36
+ const ALPN_PROTOCOLS = ['h2', 'http/1.1'];
37
+
38
+ class TLSCamouflage {
39
+ constructor(securityLevel = 'medium', options = {}) {
40
+ this.securityLevel = securityLevel;
41
+ this.mode = this._selectMode(securityLevel);
42
+ this.enabled = options.enabled !== false;
43
+ this.serverName = options.serverName || 'cloudflare.com';
44
+ this.alpnProtocols = options.alpnProtocols || ALPN_PROTOCOLS;
45
+
46
+ this.logger = new Logger({ prefix: 'TLS-Camouflage' });
47
+ }
48
+
49
+ /**
50
+ * Select camouflage mode based on security level
51
+ */
52
+ _selectMode(level) {
53
+ switch (level) {
54
+ case 'low':
55
+ return 'basic';
56
+ case 'medium':
57
+ return 'full';
58
+ case 'high':
59
+ return 'nested';
60
+ default:
61
+ return 'full';
62
+ }
63
+ }
64
+
65
+ /**
66
+ * Build TLS ClientHello packet
67
+ */
68
+ buildClientHello(options = {}) {
69
+ const serverName = options.serverName || this.serverName;
70
+ const sessionId = options.sessionId || crypto.randomBytes(32);
71
+
72
+ // TLS Record Header (5 bytes)
73
+ const recordHeader = Buffer.allocUnsafe(5);
74
+ recordHeader.writeUInt8(0x16, 0); // Content Type: Handshake
75
+ recordHeader.writeUInt16BE(0x0301, 1); // Legacy Version: TLS 1.0
76
+ // Length will be filled later
77
+
78
+ // Handshake Header (4 bytes)
79
+ const handshakeHeader = Buffer.allocUnsafe(4);
80
+ handshakeHeader.writeUInt8(0x01, 0); // Handshake Type: ClientHello
81
+ // Length will be filled later
82
+
83
+ // ClientHello
84
+ const clientHello = [];
85
+
86
+ // Client Version (2 bytes) - TLS 1.2 for compatibility
87
+ const clientVersion = Buffer.allocUnsafe(2);
88
+ clientVersion.writeUInt16BE(0x0303, 0);
89
+ clientHello.push(clientVersion);
90
+
91
+ // Random (32 bytes)
92
+ const random = crypto.randomBytes(32);
93
+ clientHello.push(random);
94
+
95
+ // Session ID Length (1 byte) + Session ID
96
+ const sessionIdLength = Buffer.allocUnsafe(1);
97
+ sessionIdLength.writeUInt8(sessionId.length, 0);
98
+ clientHello.push(sessionIdLength, sessionId);
99
+
100
+ // Cipher Suites Length (2 bytes) + Cipher Suites
101
+ const cipherSuitesLength = Buffer.allocUnsafe(2);
102
+ cipherSuitesLength.writeUInt16BE(CIPHER_SUITES.length * 2, 0);
103
+ clientHello.push(cipherSuitesLength);
104
+
105
+ for (const suite of CIPHER_SUITES) {
106
+ const suiteBuffer = Buffer.allocUnsafe(2);
107
+ suiteBuffer.writeUInt16BE(suite, 0);
108
+ clientHello.push(suiteBuffer);
109
+ }
110
+
111
+ // Compression Methods Length (1 byte) + Compression Methods
112
+ const compressionMethods = Buffer.from([0x01, 0x00]); // No compression
113
+ clientHello.push(compressionMethods);
114
+
115
+ // Extensions
116
+ const extensions = this._buildExtensions(serverName);
117
+ clientHello.push(extensions);
118
+
119
+ // Combine ClientHello
120
+ const clientHelloBuffer = Buffer.concat(clientHello);
121
+
122
+ // Update handshake length
123
+ handshakeHeader.writeUIntBE(clientHelloBuffer.length, 1, 3);
124
+
125
+ // Combine handshake
126
+ const handshake = Buffer.concat([handshakeHeader, clientHelloBuffer]);
127
+
128
+ // Update record length
129
+ recordHeader.writeUInt16BE(handshake.length, 3);
130
+
131
+ // Complete TLS record
132
+ return Buffer.concat([recordHeader, handshake]);
133
+ }
134
+
135
+ /**
136
+ * Build TLS extensions
137
+ */
138
+ _buildExtensions(serverName) {
139
+ const extensions = [];
140
+
141
+ // Server Name Indication (SNI)
142
+ const sniExtension = this._buildSNIExtension(serverName);
143
+ extensions.push(sniExtension);
144
+
145
+ // Supported Versions
146
+ const supportedVersions = Buffer.from([
147
+ 0x00, 0x2b, // Extension Type: supported_versions
148
+ 0x00, 0x03, // Length: 3
149
+ 0x02, // Supported Versions Length: 2
150
+ 0x03, 0x04 // TLS 1.3
151
+ ]);
152
+ extensions.push(supportedVersions);
153
+
154
+ // Supported Groups (Elliptic Curves)
155
+ const supportedGroups = Buffer.from([
156
+ 0x00, 0x0a, // Extension Type: supported_groups
157
+ 0x00, 0x08, // Length: 8
158
+ 0x00, 0x06, // Supported Groups Length: 6
159
+ 0x00, 0x1d, // x25519
160
+ 0x00, 0x17, // secp256r1
161
+ 0x00, 0x18 // secp384r1
162
+ ]);
163
+ extensions.push(supportedGroups);
164
+
165
+ // Signature Algorithms
166
+ const signatureAlgorithms = Buffer.from([
167
+ 0x00, 0x0d, // Extension Type: signature_algorithms
168
+ 0x00, 0x08, // Length: 8
169
+ 0x00, 0x06, // Signature Algorithms Length: 6
170
+ 0x04, 0x03, // ecdsa_secp256r1_sha256
171
+ 0x05, 0x03, // ecdsa_secp384r1_sha384
172
+ 0x06, 0x03 // ecdsa_secp521r1_sha512
173
+ ]);
174
+ extensions.push(signatureAlgorithms);
175
+
176
+ // ALPN
177
+ const alpnExtension = this._buildALPNExtension();
178
+ extensions.push(alpnExtension);
179
+
180
+ // Key Share
181
+ const keyShareExtension = this._buildKeyShareExtension();
182
+ extensions.push(keyShareExtension);
183
+
184
+ // Extended Master Secret
185
+ const extendedMasterSecret = Buffer.from([
186
+ 0x00, 0x17, // Extension Type: extended_master_secret
187
+ 0x00, 0x00 // Length: 0
188
+ ]);
189
+ extensions.push(extendedMasterSecret);
190
+
191
+ // Session Ticket
192
+ const sessionTicket = Buffer.from([
193
+ 0x00, 0x23, // Extension Type: session_ticket
194
+ 0x00, 0x00 // Length: 0
195
+ ]);
196
+ extensions.push(sessionTicket);
197
+
198
+ // Combine all extensions
199
+ const extensionsBuffer = Buffer.concat(extensions);
200
+
201
+ // Extensions length prefix
202
+ const extensionsLength = Buffer.allocUnsafe(2);
203
+ extensionsLength.writeUInt16BE(extensionsBuffer.length, 0);
204
+
205
+ return Buffer.concat([extensionsLength, extensionsBuffer]);
206
+ }
207
+
208
+ /**
209
+ * Build SNI extension
210
+ */
211
+ _buildSNIExtension(serverName) {
212
+ const serverNameBuffer = Buffer.from(serverName, 'utf8');
213
+
214
+ const extension = Buffer.allocUnsafe(9 + serverNameBuffer.length);
215
+ extension.writeUInt16BE(EXTENSIONS.SERVER_NAME, 0); // Extension Type
216
+ extension.writeUInt16BE(5 + serverNameBuffer.length, 2); // Extension Length
217
+ extension.writeUInt16BE(3 + serverNameBuffer.length, 4); // Server Name List Length
218
+ extension.writeUInt8(0x00, 6); // Server Name Type: host_name
219
+ extension.writeUInt16BE(serverNameBuffer.length, 7); // Server Name Length
220
+ serverNameBuffer.copy(extension, 9);
221
+
222
+ return extension;
223
+ }
224
+
225
+ /**
226
+ * Build ALPN extension
227
+ */
228
+ _buildALPNExtension() {
229
+ const protocols = [];
230
+ let totalLength = 0;
231
+
232
+ for (const protocol of this.alpnProtocols) {
233
+ const protocolBuffer = Buffer.from(protocol, 'utf8');
234
+ const protocolEntry = Buffer.allocUnsafe(1 + protocolBuffer.length);
235
+ protocolEntry.writeUInt8(protocolBuffer.length, 0);
236
+ protocolBuffer.copy(protocolEntry, 1);
237
+ protocols.push(protocolEntry);
238
+ totalLength += protocolEntry.length;
239
+ }
240
+
241
+ const protocolsBuffer = Buffer.concat(protocols);
242
+
243
+ const extension = Buffer.allocUnsafe(6 + totalLength);
244
+ extension.writeUInt16BE(EXTENSIONS.ALPN, 0); // Extension Type
245
+ extension.writeUInt16BE(2 + totalLength, 2); // Extension Length
246
+ extension.writeUInt16BE(totalLength, 4); // ALPN List Length
247
+ protocolsBuffer.copy(extension, 6);
248
+
249
+ return extension;
250
+ }
251
+
252
+ /**
253
+ * Build Key Share extension
254
+ */
255
+ _buildKeyShareExtension() {
256
+ // Generate ephemeral X25519 key pair
257
+ const keyPair = crypto.generateKeyPairSync('x25519');
258
+ const publicKey = keyPair.publicKey.export({ type: 'spki', format: 'der' });
259
+
260
+ // Extract raw public key (last 32 bytes of SPKI)
261
+ const rawPublicKey = publicKey.slice(-32);
262
+
263
+ const extension = Buffer.allocUnsafe(40);
264
+ extension.writeUInt16BE(EXTENSIONS.KEY_SHARE, 0); // Extension Type
265
+ extension.writeUInt16BE(36, 2); // Extension Length
266
+ extension.writeUInt16BE(34, 4); // Key Share List Length
267
+ extension.writeUInt16BE(0x001d, 6); // Group: x25519
268
+ extension.writeUInt16BE(32, 8); // Key Exchange Length
269
+ rawPublicKey.copy(extension, 10);
270
+
271
+ return extension;
272
+ }
273
+
274
+ /**
275
+ * Wrap data in TLS Application Data record
276
+ */
277
+ wrapApplicationData(data) {
278
+ // TLS Record Header
279
+ const recordHeader = Buffer.allocUnsafe(5);
280
+ recordHeader.writeUInt8(0x17, 0); // Content Type: Application Data
281
+ recordHeader.writeUInt16BE(0x0303, 1); // Version: TLS 1.2
282
+ recordHeader.writeUInt16BE(data.length, 3); // Length
283
+
284
+ return Buffer.concat([recordHeader, data]);
285
+ }
286
+
287
+ /**
288
+ * Camouflage VPN data
289
+ */
290
+ camouflage(data, options = {}) {
291
+ if (!this.enabled) {
292
+ return data;
293
+ }
294
+
295
+ switch (this.mode) {
296
+ case 'basic':
297
+ // Just wrap in TLS record
298
+ return this.wrapApplicationData(data);
299
+
300
+ case 'full':
301
+ // Add realistic TLS patterns
302
+ if (options.isHandshake) {
303
+ return this.buildClientHello(options);
304
+ }
305
+ return this.wrapApplicationData(data);
306
+
307
+ case 'nested':
308
+ // Real TLS tunnel with nested VPN encryption
309
+ // This would require actual TLS implementation
310
+ // For now, use full mode
311
+ return this.wrapApplicationData(data);
312
+
313
+ default:
314
+ return data;
315
+ }
316
+ }
317
+
318
+ /**
319
+ * Extract data from TLS record
320
+ */
321
+ extract(tlsRecord) {
322
+ if (!this.enabled || tlsRecord.length < 5) {
323
+ return tlsRecord;
324
+ }
325
+
326
+ // Check if this is a TLS record
327
+ const contentType = tlsRecord.readUInt8(0);
328
+
329
+ if (contentType !== 0x17) { // Application Data
330
+ return tlsRecord;
331
+ }
332
+
333
+ // Extract payload (skip 5-byte header)
334
+ return tlsRecord.slice(5);
335
+ }
336
+
337
+ /**
338
+ * Enable camouflage
339
+ */
340
+ enable() {
341
+ this.enabled = true;
342
+ this.logger.info('TLS camouflage enabled');
343
+ }
344
+
345
+ /**
346
+ * Disable camouflage
347
+ */
348
+ disable() {
349
+ this.enabled = false;
350
+ this.logger.info('TLS camouflage disabled');
351
+ }
352
+
353
+ /**
354
+ * Get statistics
355
+ */
356
+ getStats() {
357
+ return {
358
+ enabled: this.enabled,
359
+ mode: this.mode,
360
+ serverName: this.serverName,
361
+ alpnProtocols: this.alpnProtocols
362
+ };
363
+ }
364
+ }
365
+
366
+ module.exports = TLSCamouflage;
367
+ module.exports.CIPHER_SUITES = CIPHER_SUITES;
368
+ module.exports.EXTENSIONS = EXTENSIONS;
369
+ module.exports.ALPN_PROTOCOLS = ALPN_PROTOCOLS;