# Node.js, <s>638</s> 607 bytes

Now that it's been well and truly beaten (and in the same language), here's my test answer:

<!-- language: lang-js -->

 R=require,P=process,s=R('net'),y=R('crypto'),w=0,C='create',W='write',D='data',B='hex',G=_=>a.generateKeys(B),Y=(t,m,g,f)=>g((c=y[C+t+'ipher']('aes192',w,k='')).on('readable',_=>k+=(c.read()||'').toString(m)).on('end',_=>f(k)))+c.end(),F=C+'DiffieHellman',X=s=>s.on(D,x=>(x+'').split(B).map(p=>p&&(w?Y('Dec','utf8',c=>c[W](p,B),console.log):P.stdin.on(D,m=>Y('C',B,c=>c[W](m),r=>s[W](r+B)),([p,q,r]=p.split(D),r&&s[W](G(a=y[F](q,B,r,B))),w=a.computeSecret(p,B))))));(R=P.argv)[3]?X(s.Socket()).connect(R[3],R[2]):s[C+'Server'](s=>X(s,a=y[F](2<<9))[W](G()+D+a.getPrime(B)+D+a.getGenerator(B)+B)).listen(R[2])

Or with wrapping:

<!-- language: lang-js -->

 R=require,P=process,s=R('net'),y=R('crypto'),w=0,C='create',W='write',D='data',B
 ='hex',G=_=>a.generateKeys(B),Y=(t,m,g,f)=>g((c=y[C+t+'ipher']('aes192',w,k=''))
 .on('readable',_=>k+=(c.read()||'').toString(m)).on('end',_=>f(k)))+c.end(),F=C+
 'DiffieHellman',X=s=>s.on(D,x=>(x+'').split(B).map(p=>p&&(w?Y('Dec','utf8',c=>c[
 W](p,B),console.log):P.stdin.on(D,m=>Y('C',B,c=>c[W](m),r=>s[W](r+B)),([p,q,r]=p
 .split(D),r&&s[W](G(a=y[F](q,B,r,B))),w=a.computeSecret(p,B))))));(R=P.argv)[3]?
 X(s.Socket()).connect(R[3],R[2]):s[C+'Server'](s=>X(s,a=y[F](2<<9))[W](G()+D+a.
 getPrime(B)+D+a.getGenerator(B)+B)).listen(R[2])

### Usage

This is a server/client implementation; one instantiation will be the server, and the other the client. The server is launched with a specific port, then the client is pointed to the server's port. DH can take a few seconds to set-up if your machine is low on entropy, so the first messages may be delayed a little.

 MACHINE 1 MACHINE 2
 $ node e2e.js <port> :
 : $ node e2e.js <address> <port>
 $ hello :
 : : hello
 : $ hi
 : hi :

### Breakdown

<!-- language: lang-js -->

 s=require('net'),
 y=require('crypto'),
 w=0, // Shared secret starts unknown
 Y=(t,m,g,f)=>g( // Helper for encryption & decryption
 (c=y['create'+t+'ipher']('aes192',w,k=''))
 .on('readable',_=>k+=(c.read()||'').toString(m))
 .on('end',_=>f(k)))+c.end();
 X=s=>s.on('data',x=>(x+'').split('TOKEN2').map(p=>
 p&&(w // Have we completed handshake?
 ?Y('Dec','utf8',c=>c.write(p,'hex'),console.log) // Decrypt + print messages
 : // Haven't completed handshake:
 process.stdin.on('data',m=> // Prepare to encrypt + send input
 Y('C','hex',c=>c.write(m),r=>s.write(r+'TOKEN2')),(
 [p,q,r]=p.split('TOKEN1'), // Split up DH data sent to us
 r&& // Given DH details? (client)
 s.write(
 (a=y.createDiffieHellman( // Compute key pair...
 q,'hex',r,'hex') // ...using the received params
 ).generateKeys('hex')), // And send the public key
 w=a.computeSecret(p,'hex') // Compute shared secret
 //,console.log(w.toString('hex')) // Print if you want to verify no MITM
 ))))),
 (R=process.argv)[3] // Are we running as a client?
 ?X(s.Socket()).connect(R[3],R[2]) // Connect & start chat
 :s.createServer(s=> // Start server. On connection:
 X(s, // Start chat,
 a=y.createDiffieHellman(1024)) // Calc DiffieHellman,
 .write( // Send public key & public DH details
 a.generateKeys('hex')+'TOKEN1'+
 a.getPrime('hex')+'TOKEN1'+
 a.getGenerator('hex')+'TOKEN2')
 ).listen(R[2]) // Listen on requested port

The only requirement for the tokens is that they contain at least one non-hex character, so in the minified code other string constants are used (`data` and `hex`).