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 = `
- ${data.msg} + ${msg} ${getTime(time)} @@ -412,7 +543,7 @@ function initselfWebSocket(){
`; disp.appendChild(msgdiv); - storeMsg({time:time,floID:data.from,text:data.msg,type:"R"}); + storeMsg({time:time,floID:data.from,text:msg,type:"R"}); }catch(err){ if(evt.data[0]=='$') alert(evt.data); @@ -425,13 +556,63 @@ function initselfWebSocket(){ }; } +function checkStatusInterval(){ + try{ + if(receiverWebSocket !== undefined && receiverWebSocket.readyState !== WebSocket.OPEN){ + receiverWebSocket.close() + receiverWebSocket = new WebSocket("ws://"+contacts[receiverID].onionAddr+"/ws"); + receiverWebSocket.onopen = function(evt){ receiverWebSocket.send('#') }; + receiverWebSocket.onerror = function(ev) { receiverStatus(false); }; + receiverWebSocket.onclose = function(ev) { receiverStatus(false); }; + receiverWebSocket.onmessage = function(evt){ + console.log(evt.data); + if(evt.data[0]=='#'){ + if (evt.data[1]=='+') + receiverStatus(true); + else if(evt.data[1]=='-') + receiverStatus(false); + } + } + } + }catch(e){ + console.log(e); + } +} + function changeReceiver(param){ if(receiverID !== undefined) document.getElementById(receiverID).style.display = 'none'; console.log(param.getAttribute("name")); receiverID = param.getAttribute("name"); document.getElementById('recipient-floID').innerHTML = receiverID; + receiverStatus(false) document.getElementById(receiverID).style.display = 'block'; + try{ + if(receiverWebSocket !== undefined && receiverWebSocket.readyState === WebSocket.OPEN) + receiverWebSocket.close() + receiverWebSocket = new WebSocket("ws://"+contacts[receiverID].onionAddr+"/ws"); + receiverWebSocket.onopen = function(ev){ receiverWebSocket.send('#'); }; + receiverWebSocket.onerror = function(ev) { receiverStatus(false); }; + receiverWebSocket.onclose = function(ev) { receiverStatus(false); }; + receiverWebSocket.onmessage = function(evt){ + console.log(evt.data); + if(evt.data[0]=='#'){ + if (evt.data[1]=='+') + receiverStatus(true); + else if(evt.data[1]=='-') + receiverStatus(false); + } + } + }catch(e){ + console.log(e); + } +} + +function receiverStatus(status){ + if(status) + document.getElementById('recipient-floID').style.backgroundColor = "#00b300"; + else + document.getElementById('recipient-floID').style.backgroundColor = "#ff4d4d"; } function getTime(time){ @@ -452,15 +633,19 @@ function sendMsg(){ alert("Select a contact and send message"); return; } - var msg = document.getElementById('sendMsgInput').value; + if(receiverWebSocket.readyState !== WebSocket.OPEN){ + alert("Recipient is offline! Try again later") + return + } + var inp = document.getElementById('sendMsgInput') + var msg = inp.value; + inp.value = ""; console.log(msg); - var ws = new WebSocket("ws://"+contacts[receiverID].onionAddr+"/ws"); - ws.onopen = function(evt){ - var sign = wallets.sign(msg,privKey) - var pubkeyHex = wallets.getPubKeyHex(privKey) - var data = JSON.stringify({from:senderID,msg:msg,sign:sign,pubKey:pubkeyHex}); - ws.send(data); - console.log(`sentMsg : ${data}`); + var sign = encrypt.sign(msg,privKey) + var msgEncrypt = encrypt.encryptMessage(msg,contacts[receiverID].pubKey) + var data = JSON.stringify({from:senderID,secret:msgEncrypt.secret,sign:sign,pubVal:msgEncrypt.senderPublicKeyString}); + receiverWebSocket.send(data); + console.log(`sentMsg : ${data}`); time = Date.now(); var disp = document.getElementById(receiverID); var msgdiv = document.createElement('div'); @@ -476,11 +661,4 @@ function sendMsg(){ `; disp.appendChild(msgdiv); storeMsg({time:time,floID:receiverID,text:msg,type:"S"}); - //send_check = 1; - //recursion_called = 0; - //addSentChat(msg.substring(2+msgArray[0].length+msgArray[1].length),timer,msgArray[0]); - //addTick(message); - } - ws.onerror = function(ev) { console.log(ev); }; - ws.onclose = function(ev) { console.log(ev); }; } \ No newline at end of file diff --git a/index.html b/app/index.html similarity index 97% rename from index.html rename to app/index.html index 316e655..6c25824 100644 --- a/index.html +++ b/app/index.html @@ -59,7 +59,6 @@
Select Contact - Unknown
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 + - ''; - //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