11const fs = require ( "fs" ) ;
22const path = require ( "path" ) ;
33const { createConnection } = require ( 'net' ) ;
4- const { createDiffieHellman, createDiffieHellmanGroup } = require ( 'crypto' ) ;
4+ const { createHash, createECDH } = require ( 'crypto' ) ;
5+ const { Transform } = require ( 'stream' ) ;
56
7+ // Application settings
68const port = process . argv [ 3 ] || 23 ;
79const hostname = process . argv [ 2 ] || ( process . pkg ? "dom.ht-dev.de" : "localhost" ) ;
8- const delayMs = process . argv [ 4 ] || 100 ; // Command delay in milliseconds
10+ let delayMs = process . argv [ 4 ] || 100 ; // Command delay in milliseconds
11+
12+ // Diffie-Hellman parameters
13+ const keyCurve = "prime256v1" ; // key exchange curve, make this negotiable in the future
14+
15+ class MemoryStream extends Transform {
16+ constructor ( options = { } ) {
17+ super ( options ) ;
18+ }
19+
20+ _transform ( chunk , encoding , callback ) {
21+ this . push ( chunk ) ;
22+ callback ( ) ;
23+ }
24+ }
25+
26+ const writer = new MemoryStream ( ) ;
27+ let encrypted = false ; // Encryption status, do not modify directly, runtime only
28+ let privateKey ; // Private key, do not modify directly, runtime only
29+ const key = createECDH ( keyCurve ) ;
30+
31+ const delay = ( ms ) => new Promise ( resolve => setTimeout ( resolve , ms ) ) ;
32+
33+ const send = async ( command ) => {
34+ await delay ( delayMs ) ;
35+ writer . write ( command ) ; // Command
36+ await delay ( delayMs ) ;
37+ writer . write ( "\r\n" ) ; // Line Feed
38+ } ;
939
1040// Numeric Buffer values
1141const NUL = Buffer . from ( [ 0x00 ] ) ; // Null
@@ -65,13 +95,15 @@ if (process.pkg) {
6595}
6696
6797if ( process . pkg ) {
68- console . debug = ( ) => { } ; // Disable debug logging when run as an executable
98+ console . debug = ( ) => { } ; // Disable debug logging when run as an executable
6999 fs . writeFileSync ( path . join ( project_folder , "README.md" ) , fs . readFileSync ( path . join ( __dirname , "README.md" ) ) ) ; // copy newest readme to folder
70100 fs . writeFileSync ( path . join ( project_folder , "LICENSE" ) , fs . readFileSync ( path . join ( __dirname , "LICENSE" ) ) ) ; // copy newest license to folder
71101}
72102
73103const socket = createConnection ( port , hostname ) ;
74104
105+ writer . pipe ( socket ) ;
106+
75107let hold = false ; // Hold input
76108
77109process . stdin . setEncoding ( "ascii" ) ;
@@ -123,26 +155,46 @@ process.stdin.on('data', async (key) => {
123155} ) ;
124156
125157socket . on ( "data" , ( data ) => {
126- if ( data . equals ( PAUSE ) ) {
127- process . stdin . pause ( ) ;
128- } else if ( data . equals ( RESUME ) ) {
129- process . stdin . resume ( ) ;
130- } else if ( data . equals ( IP ) ) {
131- process . exit ( ) ;
132- } else if ( data . equals ( Buffer . concat ( [ IAC , DO , CUSTOM_CLIENT_INIT ] ) ) ) {
133- // server supports custom client features
134- socket . write ( Buffer . concat ( [ IAC , WILL , KEY_EXCHANGE ] ) ) ; // Start Key Exchange
135- } else if ( data . includes ( Buffer . concat ( [ IAC , DO , KEY_EXCHANGE ] ) ) ) {
136- // generate keys
137- } else if ( data . equals ( Buffer . concat ( [ IAC , SB , KEY_EXCHANGE , ONE /* value required */ , IAC , SE ] ) ) ) {
138- // send key to server
139- } else {
140- process . stdout . write ( data ) ;
158+ if ( ! encrypted ) {
159+ if ( data . equals ( PAUSE ) ) {
160+ process . stdin . pause ( ) ;
161+ } else if ( data . equals ( RESUME ) ) {
162+ process . stdin . resume ( ) ;
163+ } else if ( data . equals ( IP ) ) {
164+ process . exit ( ) ;
165+ } else if ( data . equals ( Buffer . concat ( [ IAC , DO , CUSTOM_CLIENT_INIT ] ) ) ) {
166+ // server supports custom client features
167+ socket . write ( Buffer . concat ( [ IAC , WILL , KEY_EXCHANGE ] ) ) ; // Start Key Exchange
168+ } else if ( data . includes ( Buffer . concat ( [ IAC , DO , KEY_EXCHANGE ] ) ) ) {
169+ // generate keys
170+ const publicKey = key . generateKeys ( ) ;
171+ console . debug ( "Generated Key: " + publicKey . toString ( "hex" ) ) ;
172+ } else if ( data . equals ( Buffer . concat ( [ IAC , SB , KEY_EXCHANGE , ONE /* value required */ , IAC , SE ] ) ) ) {
173+ socket . write ( Buffer . concat ( [ IAC , SB , KEY_EXCHANGE , NUL , key . getPublicKey ( ) , IAC , SE ] ) ) ;
174+ // send key to server
175+ } else if ( data . includes ( Buffer . concat ( [ IAC , SB , KEY_EXCHANGE , NUL /* value provided */ ] ) ) ) {
176+ // server sent its key, generate secret
177+ console . debug ( "Key exchange received" ) ;
178+ const offsetBegin = data . indexOf ( SB ) + 2 ;
179+ const offsetEnd = data . lastIndexOf ( SE ) - 1 ;
180+ const keyData = data . subarray ( offsetBegin + 1 , offsetEnd ) ; // client public key
181+ console . log ( "Extracted key:" , keyData . toString ( "hex" ) ) ;
182+ privateKey = key . computeSecret ( keyData ) ;
183+ socket . write ( Buffer . concat ( [ IAC , WILL , ENCRYPTION ] ) ) ; // Enable Encryption
184+ } else if ( data . equals ( Buffer . concat ( [ IAC , DO , ENCRYPTION ] ) ) ) {
185+ // enable encryption
186+ encrypted = true ;
187+ console . debug ( "Encryption enabled" ) ;
188+ console . debug ( "Private Key: " + privateKey . toString ( "hex" ) ) ;
189+ } else {
190+ process . stdout . write ( data ) ;
191+ }
141192 }
142193} ) ;
143194
144195socket . on ( "connect" , async ( ) => {
145196 console . log ( "Connected to server" ) ;
197+ process . stdin . pause ( ) ; // Pause input
146198
147199 // initialization
148200 socket . write ( Buffer . concat ( [ IAC , WILL , NAWS , SB , NAWS , Buffer . from ( [ /* 24x80 */ 0x00 , 0x18 , 0x00 , 0x50 ] ) , SE ] ) ) ; // Negotiate About Window Size
@@ -152,14 +204,20 @@ socket.on("connect", async () => {
152204 socket . write ( Buffer . concat ( [ IAC , DO , ECHO ] ) ) ; // Echo
153205 await delay ( delayMs ) ;
154206 socket . write ( Buffer . concat ( [ IAC , WILL , CUSTOM_CLIENT_INIT ] ) ) ; // Custom Client Initialization
155- await delay ( delayMs * 10 ) ;
156- socket . write ( Buffer . from ( [ 0x0d , 0x0a ] ) ) ; // Line Feed
207+ await delay ( delayMs ) ;
208+ // socket.write(Buffer.from([0x0d, 0x0a])); // Line Feed
209+ if ( encrypted ) {
210+ // increase delay for encryption
211+ delayMs += 500 ;
212+ }
213+ // from here on encryption is enabled, do not use socket.write() directly anymore
157214 await delay ( delayMs ) ;
158215 // initialization complete
159216
160- await send ( "help" ) ;
217+ // await send("help");
161218 await delay ( 500 ) ;
162219 process . stdout . write ( "\rCtrl+X for client side commands\r\nCtrl+C to exit, Ctrl+D to force close\r\n> " ) ;
220+ process . stdin . resume ( ) ; // Resume input
163221 // more commands can be added here
164222
165223
@@ -170,15 +228,6 @@ socket.on("end", () => {
170228 process . exit ( ) ;
171229} ) ;
172230
173- const delay = ( ms ) => new Promise ( resolve => setTimeout ( resolve , ms ) ) ;
174-
175- const send = async ( command ) => {
176- await delay ( delayMs ) ;
177- socket . write ( command ) ; // Command
178- await delay ( delayMs ) ;
179- socket . write ( "\r\n" ) ; // Line Feed
180- } ;
181-
182231module . exports = {
183232 delay,
184233 send,
0 commit comments