Node.js, 638638 607 bytes
R=require,P=process,s=R('net'),y=R('crypto'),W='write'w=0,C='create',S='toString',G='generateKeys'W='write',F=C+'DiffieHellman'D='data',B='hex',D='data'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[S]x=>(x+'').split(GB).map(p=>p&&(w?Y('Dec','utf8',c=>c[W](p,B),console.log):(p=p.split(S),a||(a=y[F](p[1],B,p[2],B),s[W](a[G](B))),w=a.computeSecret(p[0],B),P.stdin.on(D,m=>Y('C',B,c=>c[W](m),r=>s[W](r+G)))))r+B)),a=w=0,Y=(t,m[p,gq,fr]=p.split(D)=>g,r&&s[W](G(c=y[C+t+'ipher']a=y[F]('aes192'q,wB,k='')).on('readable'r,_=>k+=(c.read()||''B)[S](m)),w=a.oncomputeSecret('end'p,_=>f(kB)))+c.end()));(R=P.argv)[3]?X(s=ss.Socket()).connect(R[3],R[2])+X(s):(s=s[C+'Server']s[C+'Server'](s=>s=>X(s,a=y[F](2<<9),X(s),s[W][W](a[G]G(B)+S+a+D+a.getPrime(B)+S+a+D+a.getGenerator(B)+G)+B)).listen(R[2])) R=require,P=process,s=R('net'),y=R('crypto'),W='write'w=0,C='create',S='toString'W='write',GD='data',B ='generateKeys'='hex',F=C+'DiffieHellman'G=_=>a.generateKeys(B),B='hex'Y=(t,D='data'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[S]x=>(x+'').split (GB).map(p=>p&&(w?Y('Dec','utf8',c=>c[W]c=>c[ W](p,B),console.log):(p=p.split(S),a||(a=y[ F](p[1],B,p[2],B),s[W](a[G](B))),w=a.computeSecret(p[0],B),P.stdin.on(D,m=>Y('C' ,B,c=>c[W](m),r=>s[W](r+G)))))r+B)),a=w=0,Y=(t,m[p,gq,fr]=p .split(D)=>g,r&&s[W](G(c=y[C+t+'ipher']a=y[F]('aes192' q,wB,k='')).on('readable'r,_=>k+=(c.read()||''B)[S](m)),w=a.oncomputeSecret('end'p,_=>f(kB)))+c.end()));( R=P.argv)[3]? X(s=ss.Socket()).connect(R[3],R[2])+X(s):(s=s[C+'Server']s[C+'Server'](s=>s=>X(s,a=y[F]( 2<<9),X(s),s[W][W](a[G]G(B)+S+a+D+a. getPrime(B)+S+a+D+a.getGenerator(B)+G)+B)).listen(R[2])) s=require('net'), y=require('crypto'), X=s=>sw=0, // Shared secret starts unknown Y=(t,m,g,f)=>g( // Helper for encryption & decryption (c=y['create'+t+'ipher']('aes192',w,k='')) .on('data''readable',x=>x_=>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=> p=p // Prepare to encrypt + send input Y('C','hex',c=>c.splitwrite('TOKEN1'm),r=>s.write(r+'TOKEN2')),( [p,q,r]=p.split('TOKEN1'), // Split up DH data sent to us a||( r&& // No keyGiven pairDH yetdetails? (client) a=y s.createDiffieHellmanwrite( (a=y.createDiffieHellman( // Compute onekey pair... p[1] q,'hex',p[2]r,'hex'), // ...using the received params s.write(a ).generateKeys('hex'))), // And send the public key w=a.computeSecret(p[0]p,'hex'), // Compute shared secret //,console.log(w.toString('hex')); // Print if you want to verify no MITM process.stdin.on('data',m=> // Encrypt + send input Y('C','hex',c=>c.write(m),r=>s.write(r+'TOKEN2'))) ) ) )), a=w=0, // Agent & Shared secret start unknown Y=(t,m,g,f)=>g( // Helper function for encryption & decryption (c=y['create'+t+'ipher']('aes192',w,k='')) .on('readable',_=>k+=(c.read()||'').toString(m)) .on('end',_=>f(k)))+c.end(); (R=process.argv)[3] // Are we running as a client? ?X(s=ss.Socket()).connect(R[3],R[2])+X(s) // Connect & start chat :(s=ss.createServer(s=>( // Start server. On connection: a=y.createDiffieHellmanX(1024)s, // Calc DiffieHellman, X(s), // Start chat, a=y.createDiffieHellman(1024)) // StartCalc chatDiffieHellman, s.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 (toStringdata and generateKeyshex).