# 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`).