diff --git a/app.js b/app/app.js
similarity index 57%
rename from app.js
rename to app/app.js
index 77ae63e..c1071ea 100644
--- a/app.js
+++ b/app/app.js
@@ -9,10 +9,143 @@ if (!window.indexedDB) {
var contacts = [];
var receiverID,senderID;
-var selfwebsocket;
-var privKey = prompt("Enter Private Key : ")
+var selfwebsocket,receiverWebSocket;
+var privKey;
+
+var encrypt = {
+
+ p: BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", 16),
+
+ exponent1: function () {
+ return encrypt.p.add(BigInteger.ONE).divide(BigInteger("4"))
+ },
+
+ calculateY: function (x) {
+ let p = this.p;
+ let exp = this.exponent1();
+ // x is x value of public key in BigInteger format without 02 or 03 or 04 prefix
+ return x.modPow(BigInteger("3"), p).add(BigInteger("7")).mod(p).modPow(exp, p)
+ },
+
+ // Insert a compressed public key
+ getUncompressedPublicKey: function (compressedPublicKey) {
+
+ const p = this.p;
+
+ // Fetch x from compressedPublicKey
+ let pubKeyBytes = Crypto.util.hexToBytes(compressedPublicKey);
+ const prefix = pubKeyBytes.shift() // remove prefix
+ let prefix_modulus = prefix % 2;
+ pubKeyBytes.unshift(0) // add prefix 0
+ let x = new BigInteger(pubKeyBytes)
+ let xDecimalValue = x.toString()
+
+ // Fetch y
+ let y = this.calculateY(x);
+ let yDecimalValue = y.toString();
+
+ // verify y value
+ let resultBigInt = y.mod(BigInteger("2"));
+
+ let check = resultBigInt.toString() % 2;
+
+ if (prefix_modulus !== check) {
+ yDecimalValue = y.negate().mod(p).toString();
+ }
+
+ return {
+ x: xDecimalValue,
+ y: yDecimalValue
+ };
+ },
+
+ getSenderPublicKeyString: function () {
+ privateKey = ellipticCurveEncryption.senderRandom();
+ senderPublicKeyString = ellipticCurveEncryption.senderPublicString(privateKey);
+ return {
+ privateKey: privateKey,
+ senderPublicKeyString: senderPublicKeyString
+ }
+ },
+
+ deriveSharedKeySender: function (receiverCompressedPublicKey, senderPrivateKey) {
+ try {
+ let receiverPublicKeyString = this.getUncompressedPublicKey(
+ receiverCompressedPublicKey);
+ var senderDerivedKey = {
+ XValue: "",
+ YValue: ""
+ };
+ senderDerivedKey = ellipticCurveEncryption.senderSharedKeyDerivation(
+ receiverPublicKeyString.x,
+ receiverPublicKeyString.y, senderPrivateKey);
+ return senderDerivedKey;
+ } catch (error) {
+ return new Error(error);
+ }
+ },
+
+ deriveReceiverSharedKey: function (senderPublicKeyString, receiverPrivateKey) {
+ return ellipticCurveEncryption.receiverSharedKeyDerivation(
+ senderPublicKeyString.XValuePublicString, senderPublicKeyString.YValuePublicString,
+ receiverPrivateKey);
+ },
+
+ getReceiverPublicKeyString: function (privateKey) {
+ return ellipticCurveEncryption.receiverPublicString(privateKey);
+ },
+
+ deriveSharedKeyReceiver: function (senderPublicKeyString, receiverPrivateKey) {
+ try {
+ return ellipticCurveEncryption.receiverSharedKeyDerivation(senderPublicKeyString.XValuePublicString,
+ senderPublicKeyString.YValuePublicString, receiverPrivateKey);
+
+ } catch (error) {
+ return new Error(error);
+ }
+ },
+
+ encryptMessage: function (data, receiverCompressedPublicKey) {
+ var senderECKeyData = this.getSenderPublicKeyString();
+ var senderDerivedKey = {
+ XValue: "",
+ YValue: ""
+ };
+ var senderPublicKeyString = {};
+ senderDerivedKey = this.deriveSharedKeySender(
+ receiverCompressedPublicKey, senderECKeyData.privateKey);
+ console.log("senderDerivedKey", senderDerivedKey);
+ let senderKey = senderDerivedKey.XValue + senderDerivedKey.YValue;
+ let secret = Crypto.AES.encrypt(data, senderKey);
+ return {
+ secret: secret,
+ senderPublicKeyString: senderECKeyData.senderPublicKeyString
+ };
+ },
+
+ decryptMessage: function (secret, senderPublicKeyString) {
+ var receiverDerivedKey = {
+ XValue: "",
+ YValue: ""
+ };
+ var receiverECKeyData = {};
+ var myPrivateKey = privKey;
+ if (typeof myPrivateKey !== "string") throw new Error("No private key found.");
+
+ let privateKey = this.wifToDecimal(myPrivateKey, true);
+ if (typeof privateKey.privateKeyDecimal !== "string") throw new Error(
+ "Failed to detremine your private key.");
+ receiverECKeyData.privateKey = privateKey.privateKeyDecimal;
+
+ receiverDerivedKey = this.deriveReceiverSharedKey(senderPublicKeyString,
+ receiverECKeyData.privateKey);
+ console.log("receiverDerivedKey", receiverDerivedKey);
+
+ let receiverKey = receiverDerivedKey.XValue + receiverDerivedKey.YValue;
+ let decryptMsg = Crypto.AES.decrypt(secret, receiverKey);
+ return decryptMsg;
+ },
- var wallets = {
ecparams: EllipticCurve.getSECCurveByName("secp256k1"),
getPubKeyHex: function(privateKeyHex){
var key = new Bitcoin.ECKey(privateKeyHex);
@@ -24,6 +157,11 @@ var privKey = prompt("Enter Private Key : ")
var pubkeyHex = key.getPubKeyHex();
return pubkeyHex;
},
+ getFLOIDfromPubkeyHex: function(pubkeyHex){
+ var key = new Bitcoin.ECKey().setPub(pubkeyHex);
+ var floID = key.getBitcoinAddress();
+ return floID;
+ },
sign: function (msg, privateKeyHex) {
var key = new Bitcoin.ECKey(privateKeyHex);
key.setCompressed(true);
@@ -50,7 +188,21 @@ var privKey = prompt("Enter Private Key : ")
var verify = Bitcoin.ECDSA.verifyRaw(messageHashBigInteger, signature.r, signature.s,
publicKeyPoint);
return verify;
- }
+ },
+ wifToDecimal: function(pk_wif, isPubKeyCompressed = false) {
+ let pk = Bitcoin.Base58.decode(pk_wif)
+ pk.shift()
+ pk.splice(-4, 4)
+ //If the private key corresponded to a compressed public key, also drop the last byte (it should be 0x01).
+ if (isPubKeyCompressed == true) pk.pop()
+ pk.unshift(0)
+ privateKeyDecimal = BigInteger(pk).toString()
+ privateKeyHex = Crypto.util.bytesToHex(pk)
+ return {
+ privateKeyDecimal: privateKeyDecimal,
+ privateKeyHex: privateKeyHex
+ }
+ }
}
function convertStringToInt(string){
@@ -61,15 +213,18 @@ function userDataStartUp(){
console.log("StartUp");
getDatafromAPI().then(function (result) {
console.log(result);
- getuserID().then(function(result){
- console.log(result);
- getDatafromIDB().then(function(result){
- contacts = arrayToObject(result);
- console.log(contacts);
- displayContacts();
+ getDatafromIDB().then(function(result){
+ contacts = arrayToObject(result);
+ console.log(contacts);
+ getuserID().then(function(result){
+ console.log(result);
+ senderID = result;
+ alert(`${senderID}\nWelcome ${contacts[senderID].name}`)
readMsgfromIDB().then(function(result){
console.log(result);
initselfWebSocket();
+ displayContacts();
+ const createClock = setInterval(checkStatusInterval, 30000);
}).catch(function(error){
console.log(error.message);
});
@@ -87,7 +242,7 @@ function userDataStartUp(){
function arrayToObject(array){
obj = {};
array.forEach(element => {
- obj[element.floID] = {onionAddr : element.onionAddr, name : element.name};
+ obj[element.floID] = {onionAddr : element.onionAddr, name : element.name, pubKey : element.pubKey};
});
return obj;
}
@@ -126,8 +281,9 @@ function userDataStartUp(){
};
idb.onupgradeneeded = function(event) {
var objectStore = event.target.result.createObjectStore("contacts",{ keyPath: 'floID' });
- objectStore.createIndex('onionAddr', 'onionAddr', { unique: true });
+ objectStore.createIndex('onionAddr', 'onionAddr', { unique: false });
objectStore.createIndex('name', 'name', { unique: false });
+ objectStore.createIndex('pubKey', 'pubKey', { unique: false });
var objectStore2 = event.target.result.createObjectStore("lastTx");
};
idb.onsuccess = function(event) {
@@ -162,7 +318,9 @@ function userDataStartUp(){
//return;
var data = JSON.parse(tx.floData).FLO_chat;
if(data !== undefined){
- data = {floID : tx.vin[0].addr, onionAddr : data.onionAddr, name : data.name};
+ if(encrypt.getFLOIDfromPubkeyHex(data.pubKey)!=tx.vin[0].addr)
+ throw("PublicKey doesnot match with floID")
+ data = {floID : tx.vin[0].addr, onionAddr : data.onionAddr, name : data.name, pubKey:data.pubKey};
storedata(data).then(function (response) {
}).catch(function (error) {
console.log(error.message);
@@ -190,57 +348,30 @@ function userDataStartUp(){
function getuserID(){
return new Promise(
function(resolve,reject){
- var idb = indexedDB.open("FLO_Chat");
- idb.onerror = function(event) {
- console.log("Error in opening IndexedDB!");
- };
- idb.onsuccess = function(event) {
- var db = event.target.result;
- var obs = db.transaction('lastTx', "readwrite").objectStore('lastTx');
- new Promise(function(res,rej){
- var getReq = obs.get('userID');
- getReq.onsuccess = function(event){
- var userID = event.target.result;
- if(userID === undefined){
- userID = prompt("Enter A Valid Flo ID!");
- while(!validateAddr(userID)){
- userID = prompt("Retry!Enter A Valid Flo ID!");
- }
-
- var obs2 = db.transaction('contacts', "readwrite").objectStore('contacts');
- var getReq2 = obs2.get(userID);
- getReq2.onsuccess = function(event){
- var data = event.target.result;
- console.log(window.location.host);
- //console.log(data.onionAddr);
- if(data === undefined)
- var reg = confirm('FLO ID is not registers to FLO chat!\nRegister FLO ID?');
- else if(data.onionAddr == window.location.host)
- res(userID);
- else
- var reg = confirm('FLO ID is registered to another onion!\nChange FLO ID to this onion?');
- if(reg)
- if(registerID(userID,window.location.host))
- res(userID);
- rej('Unable to register userID!\nTry again later!');
- }
- }
- else
- res(userID);
- }
- }).then(function(result){
- console.log(result);
- var obs = db.transaction('lastTx', "readwrite").objectStore('lastTx');
- senderID = result;
- obs.put(result,'userID');
- db.close();
- resolve('userID Initiated')
- }).catch(function(error){
- db.close();
- console.log(error.message);
- reject('userID Initiation Failed');
- });
- };
+ privKey = prompt("Enter FLO Private Key : ")
+ var key = new Bitcoin.ECKey(privKey);
+ while(key.priv == null){
+ privKey = prompt("Invalid FLO Private Key! Retry : ")
+ key = Bitcoin.ECKey(privKey);
+ }
+ key.setCompressed(true);
+ var userID = key.getBitcoinAddress();
+ if (contacts[userID] === undefined)
+ var reg = confirm(`${userID} is not registers to FLO chat!\nRegister FLO ID to this onion?`);
+ else if (contacts[userID].onionAddr == window.location.host)
+ resolve(userID)
+ else
+ var reg = confirm(`${userID} is registered to another onion!\nChange to this onion?`);
+
+ if(reg){
+ var name = prompt("Enter your name :");
+ var pubKey = key.getPubKeyHex();
+ if(registerID(userID,window.location.host,privKey,pubKey,name)){
+ contacts[userID] = {onionAddr : window.location.host, name : name, pubKey : pubKey};
+ resolve(userID);
+ }
+ }
+ reject(`Unable to bind ${userID} to this onionAddress!\nTry again later!`);
}
);
}
@@ -333,7 +464,6 @@ function readMsgfromIDB(){
};
db.close();
};
-
}
);
}
@@ -395,7 +525,8 @@ function initselfWebSocket(){
console.log(evt.data);
try{
var data = JSON.parse(evt.data);
- if(!wallets.verify(data.msg,data.sign,data.pubKey))
+ var msg = encrypt.decryptMessage(data.secret,data.pubVal)
+ if(!encrypt.verify(msg,data.sign,contacts[data.from].pubKey))
return
var time = Date.now();
var disp = document.getElementById(data.from);
@@ -404,7 +535,7 @@ function initselfWebSocket(){
msgdiv.innerHTML = `
diff --git a/registerID.js b/app/registerID.js
similarity index 74%
rename from registerID.js
rename to app/registerID.js
index 891534e..ef1c363 100755
--- a/registerID.js
+++ b/app/registerID.js
@@ -2572,7 +2572,1271 @@ if (typeof Crypto == "undefined" || !Crypto.util) {
return null;
};
+ /*!
+ * Crypto-JS 2.5.4 BlockModes.js
+ * contribution from Simon Greatrix
+ */
+ (function (C) {
+
+ // Create pad namespace
+ var C_pad = C.pad = {};
+
+ // Calculate the number of padding bytes required.
+ function _requiredPadding(cipher, message) {
+ var blockSizeInBytes = cipher._blocksize * 4;
+ var reqd = blockSizeInBytes - message.length % blockSizeInBytes;
+ return reqd;
+ }
+
+ // Remove padding when the final byte gives the number of padding bytes.
+ var _unpadLength = function (cipher, message, alg, padding) {
+ var pad = message.pop();
+ if (pad == 0) {
+ throw new Error("Invalid zero-length padding specified for " + alg +
+ ". Wrong cipher specification or key used?");
+ }
+ var maxPad = cipher._blocksize * 4;
+ if (pad > maxPad) {
+ throw new Error("Invalid padding length of " + pad +
+ " specified for " + alg +
+ ". Wrong cipher specification or key used?");
+ }
+ for (var i = 1; i < pad; i++) {
+ var b = message.pop();
+ if (padding != undefined && padding != b) {
+ throw new Error("Invalid padding byte of 0x" + b.toString(16) +
+ " specified for " + alg +
+ ". Wrong cipher specification or key used?");
+ }
+ }
+ };
+
+ // No-operation padding, used for stream ciphers
+ C_pad.NoPadding = {
+ pad: function (cipher, message) {},
+ unpad: function (cipher, message) {}
+ };
+
+ // Zero Padding.
+ //
+ // If the message is not an exact number of blocks, the final block is
+ // completed with 0x00 bytes. There is no unpadding.
+ C_pad.ZeroPadding = {
+ pad: function (cipher, message) {
+ var blockSizeInBytes = cipher._blocksize * 4;
+ var reqd = message.length % blockSizeInBytes;
+ if (reqd != 0) {
+ for (reqd = blockSizeInBytes - reqd; reqd > 0; reqd--) {
+ message.push(0x00);
+ }
+ }
+ },
+
+ unpad: function (cipher, message) {
+ while (message[message.length - 1] == 0) {
+ message.pop();
+ }
+ }
+ };
+
+ // ISO/IEC 7816-4 padding.
+ //
+ // Pads the plain text with an 0x80 byte followed by as many 0x00
+ // bytes are required to complete the block.
+ C_pad.iso7816 = {
+ pad: function (cipher, message) {
+ var reqd = _requiredPadding(cipher, message);
+ message.push(0x80);
+ for (; reqd > 1; reqd--) {
+ message.push(0x00);
+ }
+ },
+
+ unpad: function (cipher, message) {
+ var padLength;
+ for (padLength = cipher._blocksize * 4; padLength > 0; padLength--) {
+ var b = message.pop();
+ if (b == 0x80) return;
+ if (b != 0x00) {
+ throw new Error("ISO-7816 padding byte must be 0, not 0x" + b.toString(16) +
+ ". Wrong cipher specification or key used?");
+ }
+ }
+ throw new Error(
+ "ISO-7816 padded beyond cipher block size. Wrong cipher specification or key used?"
+ );
+ }
+ };
+
+ // ANSI X.923 padding
+ //
+ // The final block is padded with zeros except for the last byte of the
+ // last block which contains the number of padding bytes.
+ C_pad.ansix923 = {
+ pad: function (cipher, message) {
+ var reqd = _requiredPadding(cipher, message);
+ for (var i = 1; i < reqd; i++) {
+ message.push(0x00);
+ }
+ message.push(reqd);
+ },
+
+ unpad: function (cipher, message) {
+ _unpadLength(cipher, message, "ANSI X.923", 0);
+ }
+ };
+
+ // ISO 10126
+ //
+ // The final block is padded with random bytes except for the last
+ // byte of the last block which contains the number of padding bytes.
+ C_pad.iso10126 = {
+ pad: function (cipher, message) {
+ var reqd = _requiredPadding(cipher, message);
+ for (var i = 1; i < reqd; i++) {
+ message.push(Math.floor(Math.random() * 256));
+ }
+ message.push(reqd);
+ },
+
+ unpad: function (cipher, message) {
+ _unpadLength(cipher, message, "ISO 10126", undefined);
+ }
+ };
+
+ // PKCS7 padding
+ //
+ // PKCS7 is described in RFC 5652. Padding is in whole bytes. The
+ // value of each added byte is the number of bytes that are added,
+ // i.e. N bytes, each of value N are added.
+ C_pad.pkcs7 = {
+ pad: function (cipher, message) {
+ var reqd = _requiredPadding(cipher, message);
+ for (var i = 0; i < reqd; i++) {
+ message.push(reqd);
+ }
+ },
+
+ unpad: function (cipher, message) {
+ _unpadLength(cipher, message, "PKCS 7", message[message.length - 1]);
+ }
+ };
+
+ // Create mode namespace
+ var C_mode = C.mode = {};
+
+ /**
+ * Mode base "class".
+ */
+ var Mode = C_mode.Mode = function (padding) {
+ if (padding) {
+ this._padding = padding;
+ }
+ };
+
+ Mode.prototype = {
+ encrypt: function (cipher, m, iv) {
+ this._padding.pad(cipher, m);
+ this._doEncrypt(cipher, m, iv);
+ },
+
+ decrypt: function (cipher, m, iv) {
+ this._doDecrypt(cipher, m, iv);
+ this._padding.unpad(cipher, m);
+ },
+
+ // Default padding
+ _padding: C_pad.iso7816
+ };
+
+
+ /**
+ * Electronic Code Book mode.
+ *
+ * ECB applies the cipher directly against each block of the input.
+ *
+ * ECB does not require an initialization vector.
+ */
+ var ECB = C_mode.ECB = function () {
+ // Call parent constructor
+ Mode.apply(this, arguments);
+ };
+
+ // Inherit from Mode
+ var ECB_prototype = ECB.prototype = new Mode;
+
+ // Concrete steps for Mode template
+ ECB_prototype._doEncrypt = function (cipher, m, iv) {
+ var blockSizeInBytes = cipher._blocksize * 4;
+ // Encrypt each block
+ for (var offset = 0; offset < m.length; offset += blockSizeInBytes) {
+ cipher._encryptblock(m, offset);
+ }
+ };
+ ECB_prototype._doDecrypt = function (cipher, c, iv) {
+ var blockSizeInBytes = cipher._blocksize * 4;
+ // Decrypt each block
+ for (var offset = 0; offset < c.length; offset += blockSizeInBytes) {
+ cipher._decryptblock(c, offset);
+ }
+ };
+
+ // ECB never uses an IV
+ ECB_prototype.fixOptions = function (options) {
+ options.iv = [];
+ };
+
+
+ /**
+ * Cipher block chaining
+ *
+ * The first block is XORed with the IV. Subsequent blocks are XOR with the
+ * previous cipher output.
+ */
+ var CBC = C_mode.CBC = function () {
+ // Call parent constructor
+ Mode.apply(this, arguments);
+ };
+
+ // Inherit from Mode
+ var CBC_prototype = CBC.prototype = new Mode;
+
+ // Concrete steps for Mode template
+ CBC_prototype._doEncrypt = function (cipher, m, iv) {
+ var blockSizeInBytes = cipher._blocksize * 4;
+
+ // Encrypt each block
+ for (var offset = 0; offset < m.length; offset += blockSizeInBytes) {
+ if (offset == 0) {
+ // XOR first block using IV
+ for (var i = 0; i < blockSizeInBytes; i++)
+ m[i] ^= iv[i];
+ } else {
+ // XOR this block using previous crypted block
+ for (var i = 0; i < blockSizeInBytes; i++)
+ m[offset + i] ^= m[offset + i - blockSizeInBytes];
+ }
+ // Encrypt block
+ cipher._encryptblock(m, offset);
+ }
+ };
+ CBC_prototype._doDecrypt = function (cipher, c, iv) {
+ var blockSizeInBytes = cipher._blocksize * 4;
+
+ // At the start, the previously crypted block is the IV
+ var prevCryptedBlock = iv;
+
+ // Decrypt each block
+ for (var offset = 0; offset < c.length; offset += blockSizeInBytes) {
+ // Save this crypted block
+ var thisCryptedBlock = c.slice(offset, offset + blockSizeInBytes);
+ // Decrypt block
+ cipher._decryptblock(c, offset);
+ // XOR decrypted block using previous crypted block
+ for (var i = 0; i < blockSizeInBytes; i++) {
+ c[offset + i] ^= prevCryptedBlock[i];
+ }
+ prevCryptedBlock = thisCryptedBlock;
+ }
+ };
+
+
+ /**
+ * Cipher feed back
+ *
+ * The cipher output is XORed with the plain text to produce the cipher output,
+ * which is then fed back into the cipher to produce a bit pattern to XOR the
+ * next block with.
+ *
+ * This is a stream cipher mode and does not require padding.
+ */
+ var CFB = C_mode.CFB = function () {
+ // Call parent constructor
+ Mode.apply(this, arguments);
+ };
+
+ // Inherit from Mode
+ var CFB_prototype = CFB.prototype = new Mode;
+
+ // Override padding
+ CFB_prototype._padding = C_pad.NoPadding;
+
+ // Concrete steps for Mode template
+ CFB_prototype._doEncrypt = function (cipher, m, iv) {
+ var blockSizeInBytes = cipher._blocksize * 4,
+ keystream = iv.slice(0);
+
+ // Encrypt each byte
+ for (var i = 0; i < m.length; i++) {
+
+ var j = i % blockSizeInBytes;
+ if (j == 0) cipher._encryptblock(keystream, 0);
+
+ m[i] ^= keystream[j];
+ keystream[j] = m[i];
+ }
+ };
+ CFB_prototype._doDecrypt = function (cipher, c, iv) {
+ var blockSizeInBytes = cipher._blocksize * 4,
+ keystream = iv.slice(0);
+
+ // Encrypt each byte
+ for (var i = 0; i < c.length; i++) {
+
+ var j = i % blockSizeInBytes;
+ if (j == 0) cipher._encryptblock(keystream, 0);
+
+ var b = c[i];
+ c[i] ^= keystream[j];
+ keystream[j] = b;
+ }
+ };
+
+
+ /**
+ * Output feed back
+ *
+ * The cipher repeatedly encrypts its own output. The output is XORed with the
+ * plain text to produce the cipher text.
+ *
+ * This is a stream cipher mode and does not require padding.
+ */
+ var OFB = C_mode.OFB = function () {
+ // Call parent constructor
+ Mode.apply(this, arguments);
+ };
+
+ // Inherit from Mode
+ var OFB_prototype = OFB.prototype = new Mode;
+
+ // Override padding
+ OFB_prototype._padding = C_pad.NoPadding;
+
+ // Concrete steps for Mode template
+ OFB_prototype._doEncrypt = function (cipher, m, iv) {
+
+ var blockSizeInBytes = cipher._blocksize * 4,
+ keystream = iv.slice(0);
+
+ // Encrypt each byte
+ for (var i = 0; i < m.length; i++) {
+
+ // Generate keystream
+ if (i % blockSizeInBytes == 0)
+ cipher._encryptblock(keystream, 0);
+
+ // Encrypt byte
+ m[i] ^= keystream[i % blockSizeInBytes];
+
+ }
+ };
+ OFB_prototype._doDecrypt = OFB_prototype._doEncrypt;
+
+ /**
+ * Counter
+ * @author Gergely Risko
+ *
+ * After every block the last 4 bytes of the IV is increased by one
+ * with carry and that IV is used for the next block.
+ *
+ * This is a stream cipher mode and does not require padding.
+ */
+ var CTR = C_mode.CTR = function () {
+ // Call parent constructor
+ Mode.apply(this, arguments);
+ };
+
+ // Inherit from Mode
+ var CTR_prototype = CTR.prototype = new Mode;
+
+ // Override padding
+ CTR_prototype._padding = C_pad.NoPadding;
+
+ CTR_prototype._doEncrypt = function (cipher, m, iv) {
+ var blockSizeInBytes = cipher._blocksize * 4;
+ var counter = iv.slice(0);
+
+ for (var i = 0; i < m.length;) {
+ // do not lose iv
+ var keystream = counter.slice(0);
+
+ // Generate keystream for next block
+ cipher._encryptblock(keystream, 0);
+
+ // XOR keystream with block
+ for (var j = 0; i < m.length && j < blockSizeInBytes; j++, i++) {
+ m[i] ^= keystream[j];
+ }
+
+ // Increase counter
+ if (++(counter[blockSizeInBytes - 1]) == 256) {
+ counter[blockSizeInBytes - 1] = 0;
+ if (++(counter[blockSizeInBytes - 2]) == 256) {
+ counter[blockSizeInBytes - 2] = 0;
+ if (++(counter[blockSizeInBytes - 3]) == 256) {
+ counter[blockSizeInBytes - 3] = 0;
+ ++(counter[blockSizeInBytes - 4]);
+ }
+ }
+ }
+ }
+ };
+ CTR_prototype._doDecrypt = CTR_prototype._doEncrypt;
+
+ })(Crypto);
+
+ /*!
+ * Crypto-JS v2.5.4 PBKDF2.js
+ * http://code.google.com/p/crypto-js/
+ * Copyright (c) 2009-2013, Jeff Mott. All rights reserved.
+ * http://code.google.com/p/crypto-js/wiki/License
+ */
+ (function () {
+
+ // Shortcuts
+ var C = Crypto,
+ util = C.util,
+ charenc = C.charenc,
+ UTF8 = charenc.UTF8,
+ Binary = charenc.Binary;
+
+ C.PBKDF2 = function (password, salt, keylen, options) {
+
+ // Convert to byte arrays
+ if (password.constructor == String) password = UTF8.stringToBytes(password);
+ if (salt.constructor == String) salt = UTF8.stringToBytes(salt);
+ /* else, assume byte arrays already */
+
+ // Defaults
+ var hasher = options && options.hasher || C.SHA1,
+ iterations = options && options.iterations || 1;
+
+ // Pseudo-random function
+ function PRF(password, salt) {
+ return C.HMAC(hasher, salt, password, {
+ asBytes: true
+ });
+ }
+
+ // Generate key
+ var derivedKeyBytes = [],
+ blockindex = 1;
+ while (derivedKeyBytes.length < keylen) {
+ var block = PRF(password, salt.concat(util.wordsToBytes([blockindex])));
+ for (var u = block, i = 1; i < iterations; i++) {
+ u = PRF(password, u);
+ for (var j = 0; j < block.length; j++) block[j] ^= u[j];
+ }
+ derivedKeyBytes = derivedKeyBytes.concat(block);
+ blockindex++;
+ }
+
+ // Truncate excess bytes
+ derivedKeyBytes.length = keylen;
+
+ return options && options.asBytes ? derivedKeyBytes :
+ options && options.asString ? Binary.bytesToString(derivedKeyBytes) :
+ util.bytesToHex(derivedKeyBytes);
+
+ };
+
+ })();
+
+ /*
+ * Copyright (c) 2010-2011 Intalio Pte, All Rights Reserved
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy
+ * of this software and associated documentation files (the "Software"), to deal
+ * in the Software without restriction, including without limitation the rights
+ * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+ * copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in
+ * all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+ * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+ * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+ * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+ * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+ * THE SOFTWARE.
+ */
+ // https://github.com/cheongwy/node-scrypt-js
+ (function () {
+
+ var MAX_VALUE = 2147483647;
+ var workerUrl = null;
+
+ //function scrypt(byte[] passwd, byte[] salt, int N, int r, int p, int dkLen)
+ /*
+ * N = Cpu cost
+ * r = Memory cost
+ * p = parallelization cost
+ *
+ */
+ window.Crypto_scrypt = function (passwd, salt, N, r, p, dkLen, callback) {
+ if (N == 0 || (N & (N - 1)) != 0) throw Error("N must be > 0 and a power of 2");
+
+ if (N > MAX_VALUE / 128 / r) throw Error("Parameter N is too large");
+ if (r > MAX_VALUE / 128 / p) throw Error("Parameter r is too large");
+
+ var PBKDF2_opts = {
+ iterations: 1,
+ hasher: Crypto.SHA256,
+ asBytes: true
+ };
+
+ var B = Crypto.PBKDF2(passwd, salt, p * 128 * r, PBKDF2_opts);
+
+ try {
+ var i = 0;
+ var worksDone = 0;
+ var makeWorker = function () {
+ if (!workerUrl) {
+ var code = '(' + scryptCore.toString() + ')()';
+ var blob;
+ try {
+ blob = new Blob([code], {
+ type: "text/javascript"
+ });
+ } catch (e) {
+ window.BlobBuilder = window.BlobBuilder || window.WebKitBlobBuilder ||
+ window.MozBlobBuilder ||
+ window.MSBlobBuilder;
+ blob = new BlobBuilder();
+ blob.append(code);
+ blob = blob.getBlob("text/javascript");
+ }
+ workerUrl = URL.createObjectURL(blob);
+ }
+ var worker = new Worker(workerUrl);
+ worker.onmessage = function (event) {
+ var Bi = event.data[0],
+ Bslice = event.data[1];
+ worksDone++;
+
+ if (i < p) {
+ worker.postMessage([N, r, p, B, i++]);
+ }
+
+ var length = Bslice.length,
+ destPos = Bi * 128 * r,
+ srcPos = 0;
+ while (length--) {
+ B[destPos++] = Bslice[srcPos++];
+ }
+
+ if (worksDone == p) {
+ callback(Crypto.PBKDF2(passwd, B, dkLen, PBKDF2_opts));
+ }
+ };
+ return worker;
+ };
+ var workers = [makeWorker(), makeWorker()];
+ workers[0].postMessage([N, r, p, B, i++]);
+ if (p > 1) {
+ workers[1].postMessage([N, r, p, B, i++]);
+ }
+ } catch (e) {
+ window.setTimeout(function () {
+ scryptCore();
+ callback(Crypto.PBKDF2(passwd, B, dkLen, PBKDF2_opts));
+ }, 0);
+ }
+
+ // using this function to enclose everything needed to create a worker (but also invokable directly for synchronous use)
+ function scryptCore() {
+ var XY = [],
+ V = [];
+
+ if (typeof B === 'undefined') {
+ onmessage = function (event) {
+ var data = event.data;
+ var N = data[0],
+ r = data[1],
+ p = data[2],
+ B = data[3],
+ i = data[4];
+
+ var Bslice = [];
+ arraycopy32(B, i * 128 * r, Bslice, 0, 128 * r);
+ smix(Bslice, 0, r, N, V, XY);
+
+ postMessage([i, Bslice]);
+ };
+ } else {
+ for (var i = 0; i < p; i++) {
+ smix(B, i * 128 * r, r, N, V, XY);
+ }
+ }
+
+ function smix(B, Bi, r, N, V, XY) {
+ var Xi = 0;
+ var Yi = 128 * r;
+ var i;
+
+ arraycopy32(B, Bi, XY, Xi, Yi);
+
+ for (i = 0; i < N; i++) {
+ arraycopy32(XY, Xi, V, i * Yi, Yi);
+ blockmix_salsa8(XY, Xi, Yi, r);
+ }
+
+ for (i = 0; i < N; i++) {
+ var j = integerify(XY, Xi, r) & (N - 1);
+ blockxor(V, j * Yi, XY, Xi, Yi);
+ blockmix_salsa8(XY, Xi, Yi, r);
+ }
+
+ arraycopy32(XY, Xi, B, Bi, Yi);
+ }
+
+ function blockmix_salsa8(BY, Bi, Yi, r) {
+ var X = [];
+ var i;
+
+ arraycopy32(BY, Bi + (2 * r - 1) * 64, X, 0, 64);
+
+ for (i = 0; i < 2 * r; i++) {
+ blockxor(BY, i * 64, X, 0, 64);
+ salsa20_8(X);
+ arraycopy32(X, 0, BY, Yi + (i * 64), 64);
+ }
+
+ for (i = 0; i < r; i++) {
+ arraycopy32(BY, Yi + (i * 2) * 64, BY, Bi + (i * 64), 64);
+ }
+
+ for (i = 0; i < r; i++) {
+ arraycopy32(BY, Yi + (i * 2 + 1) * 64, BY, Bi + (i + r) * 64, 64);
+ }
+ }
+
+ function R(a, b) {
+ return (a << b) | (a >>> (32 - b));
+ }
+
+ function salsa20_8(B) {
+ var B32 = new Array(32);
+ var x = new Array(32);
+ var i;
+
+ for (i = 0; i < 16; i++) {
+ B32[i] = (B[i * 4 + 0] & 0xff) << 0;
+ B32[i] |= (B[i * 4 + 1] & 0xff) << 8;
+ B32[i] |= (B[i * 4 + 2] & 0xff) << 16;
+ B32[i] |= (B[i * 4 + 3] & 0xff) << 24;
+ }
+
+ arraycopy(B32, 0, x, 0, 16);
+
+ for (i = 8; i > 0; i -= 2) {
+ x[4] ^= R(x[0] + x[12], 7);
+ x[8] ^= R(x[4] + x[0], 9);
+ x[12] ^= R(x[8] + x[4], 13);
+ x[0] ^= R(x[12] + x[8], 18);
+ x[9] ^= R(x[5] + x[1], 7);
+ x[13] ^= R(x[9] + x[5], 9);
+ x[1] ^= R(x[13] + x[9], 13);
+ x[5] ^= R(x[1] + x[13], 18);
+ x[14] ^= R(x[10] + x[6], 7);
+ x[2] ^= R(x[14] + x[10], 9);
+ x[6] ^= R(x[2] + x[14], 13);
+ x[10] ^= R(x[6] + x[2], 18);
+ x[3] ^= R(x[15] + x[11], 7);
+ x[7] ^= R(x[3] + x[15], 9);
+ x[11] ^= R(x[7] + x[3], 13);
+ x[15] ^= R(x[11] + x[7], 18);
+ x[1] ^= R(x[0] + x[3], 7);
+ x[2] ^= R(x[1] + x[0], 9);
+ x[3] ^= R(x[2] + x[1], 13);
+ x[0] ^= R(x[3] + x[2], 18);
+ x[6] ^= R(x[5] + x[4], 7);
+ x[7] ^= R(x[6] + x[5], 9);
+ x[4] ^= R(x[7] + x[6], 13);
+ x[5] ^= R(x[4] + x[7], 18);
+ x[11] ^= R(x[10] + x[9], 7);
+ x[8] ^= R(x[11] + x[10], 9);
+ x[9] ^= R(x[8] + x[11], 13);
+ x[10] ^= R(x[9] + x[8], 18);
+ x[12] ^= R(x[15] + x[14], 7);
+ x[13] ^= R(x[12] + x[15], 9);
+ x[14] ^= R(x[13] + x[12], 13);
+ x[15] ^= R(x[14] + x[13], 18);
+ }
+
+ for (i = 0; i < 16; ++i) B32[i] = x[i] + B32[i];
+
+ for (i = 0; i < 16; i++) {
+ var bi = i * 4;
+ B[bi + 0] = (B32[i] >> 0 & 0xff);
+ B[bi + 1] = (B32[i] >> 8 & 0xff);
+ B[bi + 2] = (B32[i] >> 16 & 0xff);
+ B[bi + 3] = (B32[i] >> 24 & 0xff);
+ }
+ }
+
+ function blockxor(S, Si, D, Di, len) {
+ var i = len >> 6;
+ while (i--) {
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ D[Di++] ^= S[Si++];
+ }
+ }
+
+ function integerify(B, bi, r) {
+ var n;
+
+ bi += (2 * r - 1) * 64;
+
+ n = (B[bi + 0] & 0xff) << 0;
+ n |= (B[bi + 1] & 0xff) << 8;
+ n |= (B[bi + 2] & 0xff) << 16;
+ n |= (B[bi + 3] & 0xff) << 24;
+
+ return n;
+ }
+
+ function arraycopy(src, srcPos, dest, destPos, length) {
+ while (length--) {
+ dest[destPos++] = src[srcPos++];
+ }
+ }
+
+ function arraycopy32(src, srcPos, dest, destPos, length) {
+ var i = length >> 5;
+ while (i--) {
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ dest[destPos++] = src[srcPos++];
+ }
+ }
+ } // scryptCore
+ }; // window.Crypto_scrypt
+ })();
+
+ /*!
+ * Crypto-JS v2.5.4 AES.js
+ * http://code.google.com/p/crypto-js/
+ * Copyright (c) 2009-2013, Jeff Mott. All rights reserved.
+ * http://code.google.com/p/crypto-js/wiki/License
+ */
+ (function () {
+
+ // Shortcuts
+ var C = Crypto,
+ util = C.util,
+ charenc = C.charenc,
+ UTF8 = charenc.UTF8;
+
+ // Precomputed SBOX
+ var SBOX = [0x63, 0x7c, 0x77, 0x7b, 0xf2, 0x6b, 0x6f, 0xc5,
+ 0x30, 0x01, 0x67, 0x2b, 0xfe, 0xd7, 0xab, 0x76,
+ 0xca, 0x82, 0xc9, 0x7d, 0xfa, 0x59, 0x47, 0xf0,
+ 0xad, 0xd4, 0xa2, 0xaf, 0x9c, 0xa4, 0x72, 0xc0,
+ 0xb7, 0xfd, 0x93, 0x26, 0x36, 0x3f, 0xf7, 0xcc,
+ 0x34, 0xa5, 0xe5, 0xf1, 0x71, 0xd8, 0x31, 0x15,
+ 0x04, 0xc7, 0x23, 0xc3, 0x18, 0x96, 0x05, 0x9a,
+ 0x07, 0x12, 0x80, 0xe2, 0xeb, 0x27, 0xb2, 0x75,
+ 0x09, 0x83, 0x2c, 0x1a, 0x1b, 0x6e, 0x5a, 0xa0,
+ 0x52, 0x3b, 0xd6, 0xb3, 0x29, 0xe3, 0x2f, 0x84,
+ 0x53, 0xd1, 0x00, 0xed, 0x20, 0xfc, 0xb1, 0x5b,
+ 0x6a, 0xcb, 0xbe, 0x39, 0x4a, 0x4c, 0x58, 0xcf,
+ 0xd0, 0xef, 0xaa, 0xfb, 0x43, 0x4d, 0x33, 0x85,
+ 0x45, 0xf9, 0x02, 0x7f, 0x50, 0x3c, 0x9f, 0xa8,
+ 0x51, 0xa3, 0x40, 0x8f, 0x92, 0x9d, 0x38, 0xf5,
+ 0xbc, 0xb6, 0xda, 0x21, 0x10, 0xff, 0xf3, 0xd2,
+ 0xcd, 0x0c, 0x13, 0xec, 0x5f, 0x97, 0x44, 0x17,
+ 0xc4, 0xa7, 0x7e, 0x3d, 0x64, 0x5d, 0x19, 0x73,
+ 0x60, 0x81, 0x4f, 0xdc, 0x22, 0x2a, 0x90, 0x88,
+ 0x46, 0xee, 0xb8, 0x14, 0xde, 0x5e, 0x0b, 0xdb,
+ 0xe0, 0x32, 0x3a, 0x0a, 0x49, 0x06, 0x24, 0x5c,
+ 0xc2, 0xd3, 0xac, 0x62, 0x91, 0x95, 0xe4, 0x79,
+ 0xe7, 0xc8, 0x37, 0x6d, 0x8d, 0xd5, 0x4e, 0xa9,
+ 0x6c, 0x56, 0xf4, 0xea, 0x65, 0x7a, 0xae, 0x08,
+ 0xba, 0x78, 0x25, 0x2e, 0x1c, 0xa6, 0xb4, 0xc6,
+ 0xe8, 0xdd, 0x74, 0x1f, 0x4b, 0xbd, 0x8b, 0x8a,
+ 0x70, 0x3e, 0xb5, 0x66, 0x48, 0x03, 0xf6, 0x0e,
+ 0x61, 0x35, 0x57, 0xb9, 0x86, 0xc1, 0x1d, 0x9e,
+ 0xe1, 0xf8, 0x98, 0x11, 0x69, 0xd9, 0x8e, 0x94,
+ 0x9b, 0x1e, 0x87, 0xe9, 0xce, 0x55, 0x28, 0xdf,
+ 0x8c, 0xa1, 0x89, 0x0d, 0xbf, 0xe6, 0x42, 0x68,
+ 0x41, 0x99, 0x2d, 0x0f, 0xb0, 0x54, 0xbb, 0x16
+ ];
+
+ // Compute inverse SBOX lookup table
+ for (var INVSBOX = [], i = 0; i < 256; i++) INVSBOX[SBOX[i]] = i;
+
+ // Compute multiplication in GF(2^8) lookup tables
+ var MULT2 = [],
+ MULT3 = [],
+ MULT9 = [],
+ MULTB = [],
+ MULTD = [],
+ MULTE = [];
+
+ function xtime(a, b) {
+ for (var result = 0, i = 0; i < 8; i++) {
+ if (b & 1) result ^= a;
+ var hiBitSet = a & 0x80;
+ a = (a << 1) & 0xFF;
+ if (hiBitSet) a ^= 0x1b;
+ b >>>= 1;
+ }
+ return result;
+ }
+
+ for (var i = 0; i < 256; i++) {
+ MULT2[i] = xtime(i, 2);
+ MULT3[i] = xtime(i, 3);
+ MULT9[i] = xtime(i, 9);
+ MULTB[i] = xtime(i, 0xB);
+ MULTD[i] = xtime(i, 0xD);
+ MULTE[i] = xtime(i, 0xE);
+ }
+
+ // Precomputed RCon lookup
+ var RCON = [0x00, 0x01, 0x02, 0x04, 0x08, 0x10, 0x20, 0x40, 0x80, 0x1b, 0x36];
+
+ // Inner state
+ var state = [
+ [],
+ [],
+ [],
+ []
+ ],
+ keylength,
+ nrounds,
+ keyschedule;
+
+ var AES = C.AES = {
+
+ /**
+ * Public API
+ */
+
+ encrypt: function (message, password, options) {
+
+ options = options || {};
+
+ // Determine mode
+ var mode = options.mode || new C.mode.OFB;
+
+ // Allow mode to override options
+ if (mode.fixOptions) mode.fixOptions(options);
+
+ var
+
+ // Convert to bytes if message is a string
+ m = (
+ message.constructor == String ?
+ UTF8.stringToBytes(message) :
+ message
+ ),
+
+ // Generate random IV
+ iv = options.iv || util.randomBytes(AES._blocksize * 4),
+
+ // Generate key
+ k = (
+ password.constructor == String ?
+ // Derive key from pass-phrase
+ C.PBKDF2(password, iv, 32, {
+ asBytes: true
+ }) :
+ // else, assume byte array representing cryptographic key
+ password
+ );
+
+ // Encrypt
+ AES._init(k);
+ mode.encrypt(AES, m, iv);
+
+ // Return ciphertext
+ m = options.iv ? m : iv.concat(m);
+ return (options && options.asBytes) ? m : util.bytesToBase64(m);
+
+ },
+
+ decrypt: function (ciphertext, password, options) {
+
+ options = options || {};
+
+ // Determine mode
+ var mode = options.mode || new C.mode.OFB;
+
+ // Allow mode to override options
+ if (mode.fixOptions) mode.fixOptions(options);
+
+ var
+
+ // Convert to bytes if ciphertext is a string
+ c = (
+ ciphertext.constructor == String ?
+ util.base64ToBytes(ciphertext) :
+ ciphertext
+ ),
+
+ // Separate IV and message
+ iv = options.iv || c.splice(0, AES._blocksize * 4),
+
+ // Generate key
+ k = (
+ password.constructor == String ?
+ // Derive key from pass-phrase
+ C.PBKDF2(password, iv, 32, {
+ asBytes: true
+ }) :
+ // else, assume byte array representing cryptographic key
+ password
+ );
+
+ // Decrypt
+ AES._init(k);
+ mode.decrypt(AES, c, iv);
+
+ // Return plaintext
+ return (options && options.asBytes) ? c : UTF8.bytesToString(c);
+
+ },
+
+
+ /**
+ * Package private methods and properties
+ */
+
+ _blocksize: 4,
+
+ _encryptblock: function (m, offset) {
+
+ // Set input
+ for (var row = 0; row < AES._blocksize; row++) {
+ for (var col = 0; col < 4; col++)
+ state[row][col] = m[offset + col * 4 + row];
+ }
+
+ // Add round key
+ for (var row = 0; row < 4; row++) {
+ for (var col = 0; col < 4; col++)
+ state[row][col] ^= keyschedule[col][row];
+ }
+
+ for (var round = 1; round < nrounds; round++) {
+
+ // Sub bytes
+ for (var row = 0; row < 4; row++) {
+ for (var col = 0; col < 4; col++)
+ state[row][col] = SBOX[state[row][col]];
+ }
+
+ // Shift rows
+ state[1].push(state[1].shift());
+ state[2].push(state[2].shift());
+ state[2].push(state[2].shift());
+ state[3].unshift(state[3].pop());
+
+ // Mix columns
+ for (var col = 0; col < 4; col++) {
+
+ var s0 = state[0][col],
+ s1 = state[1][col],
+ s2 = state[2][col],
+ s3 = state[3][col];
+
+ state[0][col] = MULT2[s0] ^ MULT3[s1] ^ s2 ^ s3;
+ state[1][col] = s0 ^ MULT2[s1] ^ MULT3[s2] ^ s3;
+ state[2][col] = s0 ^ s1 ^ MULT2[s2] ^ MULT3[s3];
+ state[3][col] = MULT3[s0] ^ s1 ^ s2 ^ MULT2[s3];
+
+ }
+
+ // Add round key
+ for (var row = 0; row < 4; row++) {
+ for (var col = 0; col < 4; col++)
+ state[row][col] ^= keyschedule[round * 4 + col][row];
+ }
+
+ }
+
+ // Sub bytes
+ for (var row = 0; row < 4; row++) {
+ for (var col = 0; col < 4; col++)
+ state[row][col] = SBOX[state[row][col]];
+ }
+
+ // Shift rows
+ state[1].push(state[1].shift());
+ state[2].push(state[2].shift());
+ state[2].push(state[2].shift());
+ state[3].unshift(state[3].pop());
+
+ // Add round key
+ for (var row = 0; row < 4; row++) {
+ for (var col = 0; col < 4; col++)
+ state[row][col] ^= keyschedule[nrounds * 4 + col][row];
+ }
+
+ // Set output
+ for (var row = 0; row < AES._blocksize; row++) {
+ for (var col = 0; col < 4; col++)
+ m[offset + col * 4 + row] = state[row][col];
+ }
+
+ },
+
+ _decryptblock: function (c, offset) {
+
+ // Set input
+ for (var row = 0; row < AES._blocksize; row++) {
+ for (var col = 0; col < 4; col++)
+ state[row][col] = c[offset + col * 4 + row];
+ }
+
+ // Add round key
+ for (var row = 0; row < 4; row++) {
+ for (var col = 0; col < 4; col++)
+ state[row][col] ^= keyschedule[nrounds * 4 + col][row];
+ }
+
+ for (var round = 1; round < nrounds; round++) {
+
+ // Inv shift rows
+ state[1].unshift(state[1].pop());
+ state[2].push(state[2].shift());
+ state[2].push(state[2].shift());
+ state[3].push(state[3].shift());
+
+ // Inv sub bytes
+ for (var row = 0; row < 4; row++) {
+ for (var col = 0; col < 4; col++)
+ state[row][col] = INVSBOX[state[row][col]];
+ }
+
+ // Add round key
+ for (var row = 0; row < 4; row++) {
+ for (var col = 0; col < 4; col++)
+ state[row][col] ^= keyschedule[(nrounds - round) * 4 + col][row];
+ }
+
+ // Inv mix columns
+ for (var col = 0; col < 4; col++) {
+
+ var s0 = state[0][col],
+ s1 = state[1][col],
+ s2 = state[2][col],
+ s3 = state[3][col];
+
+ state[0][col] = MULTE[s0] ^ MULTB[s1] ^ MULTD[s2] ^ MULT9[s3];
+ state[1][col] = MULT9[s0] ^ MULTE[s1] ^ MULTB[s2] ^ MULTD[s3];
+ state[2][col] = MULTD[s0] ^ MULT9[s1] ^ MULTE[s2] ^ MULTB[s3];
+ state[3][col] = MULTB[s0] ^ MULTD[s1] ^ MULT9[s2] ^ MULTE[s3];
+
+ }
+
+ }
+
+ // Inv shift rows
+ state[1].unshift(state[1].pop());
+ state[2].push(state[2].shift());
+ state[2].push(state[2].shift());
+ state[3].push(state[3].shift());
+
+ // Inv sub bytes
+ for (var row = 0; row < 4; row++) {
+ for (var col = 0; col < 4; col++)
+ state[row][col] = INVSBOX[state[row][col]];
+ }
+
+ // Add round key
+ for (var row = 0; row < 4; row++) {
+ for (var col = 0; col < 4; col++)
+ state[row][col] ^= keyschedule[col][row];
+ }
+
+ // Set output
+ for (var row = 0; row < AES._blocksize; row++) {
+ for (var col = 0; col < 4; col++)
+ c[offset + col * 4 + row] = state[row][col];
+ }
+
+ },
+
+
+ /**
+ * Private methods
+ */
+
+ _init: function (k) {
+ keylength = k.length / 4;
+ nrounds = keylength + 6;
+ AES._keyexpansion(k);
+ },
+
+ // Generate a key schedule
+ _keyexpansion: function (k) {
+
+ keyschedule = [];
+
+ for (var row = 0; row < keylength; row++) {
+ keyschedule[row] = [
+ k[row * 4],
+ k[row * 4 + 1],
+ k[row * 4 + 2],
+ k[row * 4 + 3]
+ ];
+ }
+
+ for (var row = keylength; row < AES._blocksize * (nrounds + 1); row++) {
+
+ var temp = [
+ keyschedule[row - 1][0],
+ keyschedule[row - 1][1],
+ keyschedule[row - 1][2],
+ keyschedule[row - 1][3]
+ ];
+
+ if (row % keylength == 0) {
+
+ // Rot word
+ temp.push(temp.shift());
+
+ // Sub word
+ temp[0] = SBOX[temp[0]];
+ temp[1] = SBOX[temp[1]];
+ temp[2] = SBOX[temp[2]];
+ temp[3] = SBOX[temp[3]];
+
+ temp[0] ^= RCON[row / keylength];
+
+ } else if (keylength > 6 && row % keylength == 4) {
+
+ // Sub word
+ temp[0] = SBOX[temp[0]];
+ temp[1] = SBOX[temp[1]];
+ temp[2] = SBOX[temp[2]];
+ temp[3] = SBOX[temp[3]];
+
+ }
+
+ keyschedule[row] = [
+ keyschedule[row - keylength][0] ^ temp[0],
+ keyschedule[row - keylength][1] ^ temp[1],
+ keyschedule[row - keylength][2] ^ temp[2],
+ keyschedule[row - keylength][3] ^ temp[3]
+ ];
+
+ }
+
+ }
+
+ };
+
+ })();
+
/*
* Copyright (c) 2000 - 2011 The Legion Of The Bouncy Castle (http://www.bouncycastle.org)
* Ported to JavaScript by bitaddress.org
@@ -4670,6 +5934,150 @@ Bitcoin.Util = {
}
};
+
+ (function (ellipticCurveType) {
+
+ //Defining Elliptic Encryption Object
+ var ellipticEncryption = window.ellipticCurveEncryption = function () {};
+
+ ellipticEncryption.rng = new SecureRandom();
+
+
+
+ ellipticEncryption.getCurveParameters = function (curveName) {
+
+ //Default is secp256k1
+ curveName = typeof curveName !== 'undefined' ? curveName : "secp256k1";
+
+ var c = EllipticCurve.getSECCurveByName(curveName);
+ var curveDetails = {
+ Q: "",
+ A: "",
+ B: "",
+ GX: "",
+ GY: "",
+ N: ""
+ };
+
+ curveDetails.Q = c.getCurve().getQ().toString();
+ curveDetails.A = c.getCurve().getA().toBigInteger().toString();
+ curveDetails.B = c.getCurve().getB().toBigInteger().toString();
+ curveDetails.GX = c.getG().getX().toBigInteger().toString();
+ curveDetails.GY = c.getG().getY().toBigInteger().toString();
+ curveDetails.N = c.getN().toString();
+
+ return curveDetails;
+
+ }
+
+ ellipticEncryption.selectedCurve = ellipticEncryption.getCurveParameters(ellipticCurveType);
+
+
+ ellipticEncryption.get_curve = function () {
+ return new EllipticCurve.CurveFp(new BigInteger(this.selectedCurve.Q),
+ new BigInteger(this.selectedCurve.A),
+ new BigInteger(this.selectedCurve.B));
+ }
+
+ ellipticEncryption.get_G = function (curve) {
+ return new EllipticCurve.PointFp(curve,
+ curve.fromBigInteger(new BigInteger(this.selectedCurve.GX)),
+ curve.fromBigInteger(new BigInteger(this.selectedCurve.GY)));
+ }
+
+
+ ellipticEncryption.pick_rand = function () {
+ var n = new BigInteger(this.selectedCurve.N);
+ var n1 = n.subtract(BigInteger.ONE);
+ var r = new BigInteger(n.bitLength(), this.rng);
+ return r.mod(n1).add(BigInteger.ONE);
+ }
+
+
+ ellipticEncryption.senderRandom = function () {
+ var r = this.pick_rand();
+ return r.toString();
+ };
+
+
+
+ ellipticEncryption.receiverRandom = function () {
+
+ //This is receivers private key. For now we will use random. CHANGE IT LATER
+ var r = this.pick_rand();
+ return r.toString();
+ }
+
+
+
+ ellipticEncryption.senderPublicString = function (senderPrivateKey) {
+
+ var senderKeyECData = {};
+
+ var curve = this.get_curve();
+ var G = this.get_G(curve);
+ var a = new BigInteger(senderPrivateKey);
+ var P = G.multiply(a);
+ senderKeyECData.XValuePublicString = P.getX().toBigInteger().toString();
+ senderKeyECData.YValuePublicString = P.getY().toBigInteger().toString();
+
+ return senderKeyECData;
+ }
+
+ //In real life ellipticEncryption.receiverPublicString is the public key of the receiver.
+ //you don't have to run receiverRandom and the bottom function
+ ellipticEncryption.receiverPublicString = function (receiverPublicKey) {
+
+ var receiverKeyECData = {};
+
+ var curve = this.get_curve();
+ var G = this.get_G(curve);
+ var a = new BigInteger(receiverPublicKey);
+ var P = G.multiply(a);
+ receiverKeyECData.XValuePublicString = P.getX().toBigInteger().toString();
+ receiverKeyECData.YValuePublicString = P.getY().toBigInteger().toString();
+
+ return receiverKeyECData;
+ }
+
+ ellipticEncryption.senderSharedKeyDerivation = function (receiverPublicStringXValue,
+ receiverPublicStringYValue, senderPrivateKey) {
+
+ var senderDerivedKey = {};
+ var curve = this.get_curve();
+ var P = new EllipticCurve.PointFp(curve,
+ curve.fromBigInteger(new BigInteger(receiverPublicStringXValue)),
+ curve.fromBigInteger(new BigInteger(receiverPublicStringYValue)));
+ var a = new BigInteger(senderPrivateKey);
+ var S = P.multiply(a);
+
+ senderDerivedKey.XValue = S.getX().toBigInteger().toString();
+ senderDerivedKey.YValue = S.getY().toBigInteger().toString();
+
+ return senderDerivedKey;
+ }
+
+
+ ellipticEncryption.receiverSharedKeyDerivation = function (senderPublicStringXValue,
+ senderPublicStringYValue, receiverPrivateKey) {
+
+ var receiverDerivedKey = {};
+ var curve = this.get_curve();
+ var P = new EllipticCurve.PointFp(curve,
+ curve.fromBigInteger(new BigInteger(senderPublicStringXValue)),
+ curve.fromBigInteger(new BigInteger(senderPublicStringYValue)));
+ var a = new BigInteger(receiverPrivateKey);
+ var S = P.multiply(a);
+
+ receiverDerivedKey.XValue = S.getX().toBigInteger().toString();
+ receiverDerivedKey.YValue = S.getY().toBigInteger().toString();
+
+ return receiverDerivedKey;
+ }
+
+ })("secp256k1");
+
+
//Script for AJAX, and register functions
function ajax(method, uri){
var request = new XMLHttpRequest();
@@ -4722,9 +6130,10 @@ function ajax(method, uri){
-function registerID(sender,onionAddr) {
+function registerID(sender,onionAddr,wif,pubkey,username) {
- var receiver = "F6LUnwRRjFuEW97Y4av31eLqqVMK9FrgE2";
+ var receiver = "F6LUnwRRjFuEW97Y4av31eLqqVMK9FrgE2";
+
var trx = bitjs.transaction();
var utxoAmt = 0.0;
var x = sendAmt+fee;
@@ -4750,16 +6159,11 @@ function registerID(sender,onionAddr) {
if(change>0)
trx.addoutput(sender, change);
console.log(sender+":"+ change);
- var username = prompt("Enter your name :");
- var data = {FLO_chat:{onionAddr:onionAddr, name: username}};
- var sendFloData = JSON.stringify(data);;
+ var key = new Bitcoin.ECKey(wif);
+ var sendFloData = JSON.stringify({FLO_chat:{onionAddr:onionAddr, name: username, pubKey: pubkey}});;
trx.addflodata(sendFloData);
console.log(sendFloData);
- var wif = prompt("Enter private key :");
- if (wif.length<1||!verifyWIF(wif,sender)){
- alert("Invalid Private key!");
- return;
- }
+
var signedTxHash = trx.sign(wif, 1);
console.log(signedTxHash);
return broadcastTx(signedTxHash);
diff --git a/styles.css b/app/styles.css
similarity index 100%
rename from styles.css
rename to app/styles.css
diff --git a/app/websocket_chat b/app/websocket_chat
new file mode 100755
index 0000000..601f9db
Binary files /dev/null and b/app/websocket_chat differ
diff --git a/app2.js b/app2.js
deleted file mode 100644
index b765aca..0000000
--- a/app2.js
+++ /dev/null
@@ -1,734 +0,0 @@
-var displayAddress = "F6LUnwRRjFuEW97Y4av31eLqqVMK9FrgE2";
-var floidToOnion = {};
-
-window.indexedDB = window.indexedDB || window.mozIndexedDB || window.webkitIndexedDB || window.msIndexedDB;
-window.IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.msIDBTransaction;
-window.IDBKeyRange = window.IDBKeyRange || window.webkitIDBKeyRange || window.msIDBKeyRange;
-
-if (!window.indexedDB) {
- window.alert("Your browser doesn't support a stable version of IndexedDB.")
-}
-
-function convertStringToInt(string){
- return parseInt(string,10);
-}
-
-let ajax = function (uri, params, req_type, callback) {
- let url = `https://livenet.flocha.in/${uri}`;
-
- let response = {};
- var http = new XMLHttpRequest();
- http.open(req_type, url, true);
-
- http.onreadystatechange = function () {
- if (http.readyState == 4 && http.status == 200) {
- response.success = true;
- response.data = http.responseText;
- callback(response.data);
- } else {
- response.success = false;
- }
- }
-
- http.send(params);
- }
-
-function getTotalPages(address){
- var uri = "api/txs/?address="+address;
- try {
- let res = ajax(uri, null, 'GET', function (response) {
- try {
- let data = JSON.parse(response);
- getTransactionsByPage(address,convertStringToInt(data["pagesTotal"]+''));
- } catch (error) {
- console.log(error);
- }
- });
- } catch (error) {
- console.error(error);
- }
-}
-
-function getTransactionsByPage(address,totalPages){
- var cnt = 0;
- for(var i=0;i
/g, ">");
- //console.log(msg);
- var message = buildMessageReceived(msg,moment().format('h:mm A'));
- if(msgArray[2] === recipientId){
- conversation.appendChild(message);
- //add blue tick to the sender of received message
- }
- else{
- //implement badge unread message
- var contactListElement = document.getElementById('contact-list');
- //console.log(typeof contactListElement.innerHTML);
- var items = contactListElement.getElementsByTagName('li');
- var itemsLen = items.length;
- for(var i=0;i
/g, ">");
- //console.log(input.value);
- var message = buildUnSentMessage(input.value,moment().format('h:mm A')); //Need to change span tag of build
- console.log(message);
- console.log("APPENDED",message);
- conversation.appendChild(message);
- //animateMessage(message);
- console.log("Network Status",isNetwork);
- temp_input = input.value;
- if(isNetwork === 0){
- console.log("Network Status Offline");
- addUnsentChat(temp_input,timing,recipientId);
- input.value = '';
- conversation.scrollTop = conversation.scrollHeight;
- e.preventDefault();
- return;
- }
- //websocket.send(input.value);
- /*recipient_websocket.onopen = function(event){
- recipient_websocket.send(recipientId+" "+floId+" "+temp_input);
- //recipient_websocket.close();
- }
- recipient_websocket.onerror = function(event){
- console.log("Message Not Sent To Recipient!Try Again!");
- }*/
- recursion_called = 0;
- sendMessage(recipientId+" "+floId+" "+temp_input,timing,message);
- }
- input.value = '';
- conversation.scrollTop = conversation.scrollHeight;
-
- e.preventDefault();
- }
-
- function sendMessage(msg,timer,message){
- // Wait until the state of the socket is not ready and send the message when it is...
- var msgArray = msg.split(' ');
- var ws,send_check = 0;
- console.log('check');
- if(msgArray[0] === "id2")
- ws = new WebSocket("ws://"+floidToOnion[msgArray[0]]+":8000/ws");
- else
- ws = new WebSocket("ws://"+floidToOnion[msgArray[0]]+"/ws");
-
- ws.onopen = function(evt){
- console.log('open');
- ws.send(msg);
- send_check = 1;
- recursion_called = 0;
- addSentChat(msg.substring(2+msgArray[0].length+msgArray[1].length),timer,msgArray[0]);
- addTick(message);
- }
- ws.onclose = function(evt){
- console.log("connection closed");
- if(network === 1 && send_check === 0 && recursion_called <= 5){
- recursion_called++;
- sendMessage(msg,timer,message);
- return;
- }
- addUnsentChat(msg.substring(2+msgArray[0].length+msgArray[1].length),timer,msgArray[0]);
- }
- ws.onerror = function(evt){
- console.log('error');
- if(isNetwork === 1 && send_check === 0 && recursion_called <= 5){
- recursion_called++;
- sendMessage(msg,timer,message);
- return;
- }
- addUnsentChat(msg.substring(2+msgArray[0].length+msgArray[1].length),timer,msgArray[0]);
- //conversation.innerHTML = "";
- //readFromDb(msgArray[0]);
- }
-}
-
- function buildUnSentMessage(text,time) {
- var element = document.createElement('div');
- timing = time;
- element.classList.add('message', 'sent');
-
- element.innerHTML = text +
- '
' +
- '' + time + '' + '';
-
- return element;
- }
-
- function buildMessageSent(text,time) {
- var element = document.createElement('div');
- timing = time;
- element.classList.add('message', 'sent');
-
- element.innerHTML = text +
- '' +
- '' + time + '' +
- '' +
- '' +
- '' +
- '' +
- '';
-
- return element;
- }
-
- function buildMessageReceived(text,time) {
- var element = document.createElement('div');
- timing = time;
- element.classList.add('message', 'received');
-
- element.innerHTML = text +
- '' +
- '' + time + '' +
- '' +
- '' +
- '' +
- '' +
- '';
-
- return element;
- }
-
- function blueTickMessage(message) {
- setTimeout(function() {
- var tick = message.querySelector('.tick');
- tick.classList.remove('tick-animation');
- }, 500);
- }
-
- function addTick(message) {
- setTimeout(function() {
- var timerElement = message.querySelector('.unsend_msg');
- timerElement.outerHTML = '' +
- '' +
- '' +
- '' +
- '';
- //tick.classList.remove('tick-animation');
- }, 500);
- }
-
- window.addEventListener('online', function(e) {
- console.log('And we\'re back :).');
- //conversation.innerHTML = "";
- if(recipientId !== '' && recipientId !== undefined){
- addNetworkStatusOfRecipient(recipientId);
- readFromDb(recipientId);
- }
- //makeOnline();
- isNetwork = 1;
- //alert('You Have Been Disconnected!');
- //window.location.href = floidToOnion[floId];
- }, false);
-
- window.addEventListener('offline', function(e) {
- console.log('Connection is down.');
- //makeOffline();
- if(recipientId !== '' && recipientId !== undefined)
- document.getElementsByClassName('status')[0].innerHTML = "Unknown";
- isNetwork = 0;
- }, false);
-
- function makeOnline(){
- console.log(document.getElementsByClassName('status')[0]);
- document.getElementsByClassName('status')[0].innerHTML = "Online";
- }
-
- function makeOffline(){
- document.getElementsByClassName('status')[0].innerHTML = "Offline";
- }
-
- //window.addEventListener("load", init, false);
-
-}
diff --git a/autogen.sh b/autogen.sh
new file mode 100755
index 0000000..110eaa4
--- /dev/null
+++ b/autogen.sh
@@ -0,0 +1,37 @@
+#!/bin/sh
+echo "----------Welcome to FLO-Whatsapp AutoGen----------"
+
+if [ -f start ]; then
+ echo "FLO-Whatsapp is already AutoGen"
+ echo "To start run :\n./start
"
+ exit 0
+fi
+
+echo "----------Installing TOR----------"
+sudo apt-get install tor
+echo "----------Configuring Tor for FLO-Whatsapp----------"
+echo $PWD
+sudo cat <> /etc/tor/torrc
+HiddenServiceDir $PWD/.hidden_service/
+HiddenServicePort 8000 127.0.0.1:8000
+EOT
+sudo chmod 700 $PWD
+echo "----------Finished Configuring----------"
+echo "----------Creating Start script----------"
+cat > start << EOF
+#!/bin/sh
+if [ -z "\$1" ];then
+ echo "Enter server password as argument"
+ exit 0
+fi
+app/websocket_chat \$1 &
+tor &
+sleep 5s
+OA=\$(cat .hidden_service/hostname)
+zenity --info --text="Open link '\$OA:8000' in onion browser"
+wait
+EOF
+chmod u+x start
+echo "----------Finished AutoGen----------"
+echo "To start run :\n./start "
+
diff --git a/setup.sh b/setup.sh
deleted file mode 100755
index 874b4df..0000000
--- a/setup.sh
+++ /dev/null
@@ -1,13 +0,0 @@
-#!/bin/sh
-echo "----------Welcome to FLO-Whatsapp installation----------"
-echo "----------Installing TOR----------"
-apt-get install tor
-echo "----------Configuring Tor for FLO-Whatsapp----------"
-echo $PWD
-cat <> /etc/tor/torrc
-HiddenServiceDir $PWD/.hidden_service/
-HiddenServicePort 8000 127.0.0.1:8000
-EOT
-chmod 700 $PWD
-echo "----------Finished Configuring----------"
-
diff --git a/util/websocket_chat.c b/util/websocket_chat.c
index d439d22..e8563ee 100644
--- a/util/websocket_chat.c
+++ b/util/websocket_chat.c
@@ -23,9 +23,6 @@ static int is_websocket(const struct mg_connection *nc) {
static void broadcast(struct mg_connection *nc, const struct mg_str msg) {
struct mg_connection *c;
char buf[500];
- char addr[32];
- mg_sock_addr_to_str(&nc->sa, addr, sizeof(addr),
- MG_SOCK_STRINGIFY_IP | MG_SOCK_STRINGIFY_PORT);
snprintf(buf, sizeof(buf), "%.*s", (int) msg.len, msg.p);
printf("%s\n", buf); /* Local echo. */
@@ -66,9 +63,16 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
unicast(selfClient,mg_mk_str("$Another login is encountered! Please close/refresh this window"));
selfClient = nc;
unicast(selfClient,mg_mk_str("$Access Granted!"));
+ broadcast(nc, mg_mk_str("#+"));
}else
unicast(nc,mg_mk_str("$Access Denied!"));
}
+ else if(d.p[0] == '#'){
+ if(selfClient == NULL)
+ unicast(nc,mg_mk_str("#-"));
+ else
+ unicast(nc,mg_mk_str("#+"));
+ }
else
unicast(selfClient,d);
break;
@@ -80,9 +84,10 @@ static void ev_handler(struct mg_connection *nc, int ev, void *ev_data) {
case MG_EV_CLOSE: {
/* Disconnect. Tell everybody. */
if (is_websocket(nc)) {
- if(nc == selfClient)
+ if(nc == selfClient){
selfClient = NULL;
- //broadcast(nc, mg_mk_str("-- left"));
+ broadcast(nc, mg_mk_str("#-"));
+ }
}
break;
}
diff --git a/websocket_chatExample/index.html b/util/websocket_chatExample/index.html
similarity index 100%
rename from websocket_chatExample/index.html
rename to util/websocket_chatExample/index.html
diff --git a/websocket_chatExample/mongoose.c b/util/websocket_chatExample/mongoose.c
similarity index 100%
rename from websocket_chatExample/mongoose.c
rename to util/websocket_chatExample/mongoose.c
diff --git a/websocket_chatExample/mongoose.h b/util/websocket_chatExample/mongoose.h
similarity index 100%
rename from websocket_chatExample/mongoose.h
rename to util/websocket_chatExample/mongoose.h
diff --git a/websocket_chatExample/readme.md b/util/websocket_chatExample/readme.md
similarity index 100%
rename from websocket_chatExample/readme.md
rename to util/websocket_chatExample/readme.md
diff --git a/websocket_chatExample/websocket_chat b/util/websocket_chatExample/websocket_chat
similarity index 100%
rename from websocket_chatExample/websocket_chat
rename to util/websocket_chatExample/websocket_chat
diff --git a/websocket_chatExample/websocket_chat.c b/util/websocket_chatExample/websocket_chat.c
similarity index 100%
rename from websocket_chatExample/websocket_chat.c
rename to util/websocket_chatExample/websocket_chat.c
diff --git a/websocket_chat b/websocket_chat
deleted file mode 100755
index 2bd8338..0000000
Binary files a/websocket_chat and /dev/null differ