Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /*
- Comamnd prompt IRC client with end-to-end encryption built in. Now with lots of comments on it!
- */
- var net = require('net');
- var Base64 = require('js-base64').Base64;
- var chan = ['#Tanan']; //Keep track of all channels the user has been connected.
- /*
- Declare variables necessary for the key exchange.
- */
- var prime = 23;
- var generator = 5;
- var privateKey = generateKey(prime);
- var receivedKey;
- var key;
- var starter;
- console.log(privateKey);
- var nick;
- var client = new net.Socket();
- /*
- Socket/core functions.
- */
- function send(data) {
- client.write(data + '\r\n'); //IRC messages need to have "\r\n" at the end of them when sent.
- console.log(data);
- }
- client.on('data', function(data) {
- word = (data.toString('ascii')).split(" ");
- /*
- Message handling.
- */
- if (key == null) {
- console.log(data.toString('ascii'));
- } else {
- /*
- Whenever you send a encrypted message,
- the program automatically sends a hint that the message is encrypted.
- So we have to check wether or not it's a message sent by other user,
- and if it has the encryption hint.
- */
- //Check if the message has been sent by other user and if it has the encryption hint.
- if (word[1] == "PRIVMSG" && word[4] == "en") {
- //Make sure it only decrypts where it's necesary.
- var tempWord = (data.toString('ascii')).split(" ");
- tempWord.splice(0, 5);
- tempWord = tempWord.join(" ");
- var result = b64(tempWord, key, false); //Decrypt the message.
- /*
- Now that the message is decrypted, we want the user to know who sent the message.
- So what we do is that we take the message
- Eleminate everything surrounding the encryption of it
- And output it with the now, decrypted message.
- That way the message will look just like a normal irc message, but only you can read it.
- */
- tempWord = (data.toString('ascii')).split(" "); //"we take the message"
- //"Eleminate everything surrounding the encryption of it"
- tempWord.splice(tempWord.length-2, 2);
- tempWord = tempWord.join(" ");
- console.log(tempWord + result); //And output it with the now, decrypted message.
- } else {
- //If it isn't, just print what has been sent.
- console.log(data.toString('ascii'));
- }
- }
- for (var i = 0; i < word.length; i++) {
- //Handle pings.
- if (word[i].match(/PING/)) {
- send('PONG ' + word[i+1]);
- }
- //Check if the user has connected to the IRC server correctly already.
- if (word[i].match(/iwx/)) {
- console.log("Type /j #<channel> to join a channel and #<channel> <message> to send a message.");
- console.log("Type /s #<channel> to start a end-to-end connection, \nand type /en #<channel> <message> to send a encrypted message.");
- console.log("Type /q <nickname> to send a private message to that person. \n");
- //Check if it's just a person trying to glitch the client.
- if (word[1] != 'PRIVMSG') {
- //If it isn't, connect to all default channels.
- for (k in chan) {
- send('JOIN ' + chan[k]);
- }
- }
- }
- if (word[i].match(/generateKey/)) {
- //If anything goes wrong on the key exchange, do it all over again.
- starter = true;
- console.log("----------------------------------------------Connection failed, retrying..---------------------------------------------");
- privateKey = generateKey(prime);
- send('PRIVMSG ' + word[2] + ' :publicKey ' + ((Math.pow(generator, privateKey)) % prime));
- }
- if (word[i] == ":publicKey") {
- if (!starter) {
- //Generating a new key whenever possible is very good for security.
- privateKey = generateKey(prime);
- }
- receivedKey = parseInt(word[i+1]); //Store the received key.
- key = (Math.pow(receivedKey, privateKey)) % prime; //Store the shared key. (k = rK^pK mod p)
- console.log('Type "/en #<channel> <message>" to send a encrypted message.');
- if (!starter) {
- //Handle the connection request.
- send('PRIVMSG ' + word[2] + ' :publicKey ' + ((Math.pow(generator, privateKey)) % prime));
- } else {
- if (key > 7 || key == 0) {
- /*
- If we get a key that is way too big, we will send unreversable encryption.
- Which is definetly not what we want.
- */
- console.log("----------------------------------------------Connection failed, retrying..---------------------------------------------");
- startConnection(word[2]); //Restart the key exchange.
- return;
- }
- }
- starter = false;
- }
- }
- });
- client.on('close', function() {
- console.log('------Connection closed------');
- });
- /*
- User interface functions.
- */
- var stdin = process.openStdin();
- stdin.addListener("data", function(d) {
- if (nick) {
- var word = (d.toString().trim()).split(" ");
- /*
- Debugging commands.
- */
- if (word[0] == "varList") {
- console.log(privateKey + " " + receivedKey + " " + key + " " + ((Math.pow(generator, privateKey)) % prime));
- return;
- }
- if (word[0] == "key") {
- console.log(key);
- return;
- }
- if (word[0] == "privateKey") {
- console.log(privateKey);
- return;
- }
- if (word[0] == "receivedKey") {
- console.log(receivedKey);
- return;
- }
- if(word[0] == "publicKey") {
- console.log(((Math.pow(generator, privateKey)) % prime));
- return;
- }
- /*
- Socket commands.
- */
- if (word[0] == "generateKey") {
- privateKey = generateKey(prime);
- console.log(privateKey);
- return;
- }
- if (word[0] == "startConnection" || word[0] == "secureConnection" || word[0] == "/secure" || word[0] == "/s" || word[0] == "secure") {
- startConnection(word[1]);
- return;
- }
- /*
- Basic IRC commands.
- */
- if (word[0] == "/j" || word[0] == "/join" || word[0] == "join" || word[0] == "JOIN") {
- send('JOIN ' + word[1]);
- chan[chan.length+1] = word[1]; //Keep track of the channels the user is connected to.
- return;
- }
- if (word[0] == "/k" || word[0] == "/kick" || word[0] == "kick") {
- /*
- Handling kick commands need to have extra care,
- since you want it to have multiple words as a reason for the kick.
- What we do is that we take both,
- the nickname of the person the user wants to kick and the channel that the person is in
- and then we store it on a temporary array (tempArr).
- After we have done that, we eleminate the nickname and the channel from the original array,
- so we can finally make a string out of the kick reason and send it to the server.
- */
- var tempArr = [word[1], word[2]]; //Store the nickname and channel on a temporary array.
- //Check wether or not the user has typed in a reason or not.
- if (word[3]) {
- word.splice(0, 3); //Eleminate both, the nickname and the channel from the original array
- //Perform the kick with the reason the user has gave us.
- send("KICK " + tempArr[0] + " " + tempArr[1] + " " + word.join(" "));
- } else {
- /*
- If the user hasn't tiped in the reason why he is kicking the other person,
- kick him without giving reasoning.
- */
- send("KICK " + tempArr[0] + " " + tempArr[1]);
- }
- return;
- }
- /*
- Basic IRC channel mode commands.
- */
- if (word[0] == '/o' || word[0] == '/O' || word[0] == '/op' || word[0] == '/OP' || word[0] == 'op' || word[0] == 'OP') {
- send('MODE ' + word[1] + ' +o ' + word[2]);
- return;
- }
- if (word[0] == '/v' || word[0] == '/V' || word[0] == '/voice' || word[0] == '/VOICE' || word[0] == 'voice' || word[0] == 'VOICE') {
- send('MODE ' + word[1] + ' +v ' + word[2]);
- return;
- }
- if (word[0] == '/h' || word[0] == '/H' || word[0] == '/hop' || word[0] == '/HOP' || word[0] == 'hop' || word[0] == 'HOP') {
- send('MODE ' + word[1] + ' +h ' + word[2]);
- return;
- }
- /*
- Actual user output.
- */
- if (word[0] == '/q' || word[0] == '/Q' || word[0] == '/query' || word[0] == '/QUERY' || word[0] == 'query' || word[0] == 'QUERY') {
- //Let's not send "/q <nick>", nobody does that.
- var tempWord = (d.toString().trim()).split(" ");
- tempWord.splice(0, 2);
- tempWord = tempWord.join(" ");
- send('PRIVMSG ' + word[1] + ' :' + tempWord);
- return;
- }
- //Check wether or not the user has made a key exchange and if he wants his message to me encrypted.
- if (key && word[0] == '/en') {
- //We don't want to send /en <channel>, that would look kind of weird.
- var tempWord = (d.toString().trim()).split(" ");
- tempWord.splice(0, 2);
- tempWord = tempWord.join(" ");
- //Send the encrypted message with a hint that the message is encrypted so the other user can decrypt it.
- send('PRIVMSG ' + word[1] + ' : en ' + b64(tempWord, key, true));
- } else {
- if (!starter) {
- //Check if the user is sending a message to a channel.
- for (i in chan) {
- if (word[0] == chan[i]) {
- //Sending #<channel> after every message would look weird, so let's not do it.
- var tempWord = (d.toString().trim()).split(" ");
- tempWord.splice(0, 1);
- tempWord = tempWord.join(" ");
- send('PRIVMSG ' + word[0] + ' :' + d.toString().trim()); //Send the message.
- return;
- }
- }
- send(d.toString().trim()); //If the user didn't type in a channel, just send the message anyway.
- }
- }
- } else {
- nick = d.toString().trim(); //Set the nickname.
- //Connect to the IRC server.
- client.connect(6667, 'irc.toribash.com', function() {
- send('NICK ' + nick);
- send('USER ' + nick + ' 8 * : ' + nick);
- });
- }
- });
- /*
- Cryptography functions.
- */
- function cipher(input, key, op, bool, boolX, boolY, bool2) {
- /*
- This function does something similar to a ceaser cipher.
- In fact, the only difference is that it can eleminate some few words
- and it's not limited to just shifting by 3, it can shift as much as you desire.
- */
- //Create a temporary array in case it's necessary to eleminate some few words.
- var tempWord = input.split(" ");
- if (bool == true) {
- tempWord.splice(boolX, boolY);
- }
- tempWord = tempWord.join(" "); //Transform the array into a string.
- var charString = tempWord.split(""); //Transfor the string we have just created into a array of characters.
- var charCypher = []; //Create array that will store the cypher.
- //Check if the user wants to encrypt or decrypt the array.
- if (op == '-') {
- //Decrypt the array.
- for (i in charString) {
- charCypher[i] = String.fromCharCode((charString[i].charCodeAt())-key);
- }
- } else {
- //Encrypt the array.
- for (i in charString) {
- charCypher[i] = String.fromCharCode((charString[i].charCodeAt())+key);
- }
- }
- var result = charCypher.join(""); //Turn array into a now, decrypted or encrypted string.
- return result; //I think this is pretty obvious, isn't it?
- }
- function b64(input, key, bool) {
- /*
- This is the encryption algorithm that is actually used in the end-to-end encryption.
- */
- var a;
- //Check if the function has to encrypt or decrypt the message.
- if (bool == true) {
- a = cipher(input, key, '+', false, null, null, false); //Encrypt the input with the cipher.
- a = Base64.encode(a); //Encrypt with base64 the input. Pretty obvious, but anyway.
- a = cipher(a, key, '+', false, null, null, false); //Encrypt the input again with the cipher.
- } else {
- a = cipher(input, key, '-', false, null, null, false); //Decipher the base64 encryption.
- a = Base64.decode(a); //Decrypt the base64 encryption.
- a = cipher(a, key, '-', false, null, null, false); //Decipher the actual message.
- }
- return a;
- }
- /*
- Key exchange functions.
- */
- //This function generates a number that can be used for the key exchange.
- function generateKey(prime) {
- var a;
- while(true) {
- a = Math.floor(((Math.random() * (prime*20)) + 1)/prime);
- if (a > 1) {
- break;
- }
- }
- return a;
- }
- //This function is used to start the key exchange. (Public key = g^pK mod p)
- function startConnection(channel) {
- //Generating a new private key when possible is always good for security.
- privateKey = generateKey(prime);
- send('PRIVMSG ' + channel + ' :publicKey ' + ((Math.pow(generator, privateKey)) % prime));
- starter = true;
- }
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement