var net = require('net') , encryption = require('./../encryption') , ursa = require('ursa') , exec = require('child_process').exec , fs = require('fs') , sh = require('execSync') var stdin = process.openStdin() var username, password, connection, privateKey, publicKey, otherPublicKey = false, userNames = [], certificate = false, myId, isNew = true privateKey = ursa.generatePrivateKey() publicKey = privateKey.toPublicPem().toString() fs.writeFileSync('private-key.pem', privateKey.toPrivatePem().toString()) /************** GENERATING CERTIFICATE REQUEST AND SENDING IT TO THE CERTIFICATE AUTHOROTY ***************/ sh.exec("openssl req -new -key private-key.pem -out request.pem -subj /C=SY/ST=Syria/L=Damascus/O=A/emailAddress=a@a.com") var request = fs.readFileSync('request.pem').toString() var caServer = net.connect(5000, function(){ caServer.write(request) caServer.on('data', function(data){ //getting certificate var res = sh.exec('openssl x509 -req -in request.pem -signkey private-key.pem -out certificate.pem') certificate = fs.readFileSync('certificate.pem').toString() //AFTER WE GET A CERTIFICATE, WE CONNECT TO THE CHAT SERVER connectServer() }) }) var receiveId = false var otherPublicKeys = [] var sessionKey = 'asdf' //THIS FUNCTION IS USED TO INITIALIZE THE CONNECTION BETWEEN THE CLIENT AND SERVER function initialization(){ //WE FIRST SEND THE PUBLIC KEY TO THE SERVER connection.write(publicKey) //THEN WE GET INPUT FROM THE USER console.log('Please enter your username!') stdin.addListener('data', function(data){ //WE RECEIVE THE USERNAME if (!username){ username = data connection.write(username) console.log('Please enter your password') //THEN WE RECEIVE THE PASSWORD AND SEND IT } else if (!password) { password = data connection.write(password) //THEN WE FINALLY START SENDING DATA ENCRYPTED WITH THE SESSION KEY, ATTACHED WITH OUR ID AND OUR SIGNITURE //FORMAT: [signiature:userId:encryptedMessage] } else { connection.write(encryption.sign(data, privateKey) + ':' + myId.toString() + ':' +encryption.encryptAES(data, sessionKey)) } }) } /* FUNCTION TO PROCESS INCOMING DATA WE HAVE 4 BASIC INCOMING 1) PUBLIC KEYS whenever a new user joins the chat group, we send his public key to all other connecting clients so that these clients can use this key to verify the digital signed messages sent by this client each public key is attached with the client id that it belongs to in addition to the username of this client Multiple keys are sent in the following format -----BEGIN PUBLIC KEY----- XXXXXXXXXXXXXXXXXXXXXXXXX -----END PUBLIC KEY----- username1 -----END PUBLIC KEY----- id1 -----BEGIN PUBLIC KEY----- YYYYYYYYYYYYYYYYYYYYYYYYYY -----END PUBLIC KEY----- username2 -----END PUBLIC KEY----- id2 ==== 2) SESSION KEY whever a new client joins the chat group, his is sent a sessionKey that he should use to encrypt that he should use to encrypt and decrypt all outgoing and incoming messages 3) CLIENT ID each client has a unique id that he receives from the server. if the client has an id of 0, then he is the first client , and he is responsible for generating and distributing the sessino key 4) CHAT MESSAGES after the chat user finally gets the session key, he starts sending and receiveing chat message. each message is composed of three parts: [signiture:userId:encryptedMessage] */ function input(data){ var parsed = data.toString() var type = parsed.substring(0, 3) var string = parsed.substring(3, parsed.length) if (type === 'pub'){ string.split('-----BEGIN PUBLIC KEY-----\n').forEach(function(message){ if (message.length > 1){ var isCertificate = true var isName = true var key, receivedIndex, receivedName message.split('-----END PUBLIC KEY-----\n').forEach(function(message){ if (isCertificate){ message = '-----BEGIN PUBLIC KEY-----\n' + message + '-----END PUBLIC KEY-----\n' key = ursa.createPublicKey(message) isCertificate = false } else if (isName) { receivedName = message isName = false } else { receivedIndex = parseInt(message) otherPublicKeys[receivedIndex] = key userNames[receivedIndex] = receivedName isCertificate = true isName = true } }) console.log('========================================') console.log('SERVER: receiving key of client ' + receivedIndex) console.log(key.toPublicPem().toString().slice(0, -1)) console.log('========================================') //IF I AM THE SESSION ADMIN, I SHOULD SEND THE SESSION KEY TO THE NEW CONNECTING CLIENT if (myId == 0 && receivedIndex != 0){ connection.write('sessionKey:' + receivedIndex + ':'+ encryption.encrypt(sessionKey, key)) } } }) } else if (parsed.indexOf('sessionKey') >= 0){ var result = string.split(':') sessionKey = encryption.decrypt(result[2], privateKey) console.log('========================================') console.log('SERVER: receiving session key: ' + sessionKey) console.log('========================================') } else if (isNew) { myId = parseInt(data.toString()) console.log('SERVER: Your ID is: '+myId) isNew = false } else { var triple = data.toString().split(':') //converting the received buffer to a string var signiture = triple[0] var userId = parseInt(triple[1]) var string = triple[2] var decryptedString = encryption.decryptAES(string, sessionKey) if (encryption.verify(decryptedString, signiture, otherPublicKeys[userId])){ //removing /n character console.log("| " + userNames[userId] + ': ' + decryptedString.substr(0, decryptedString.length-1)) } else { console.log('Error! The sent string has been changed!!!') } } } function connectServer(){ connection = net.connect(4000, "localhost", initialization) connection.on('data', input) }