diff --git a/bower.json b/bower.json index 950d050b1..ef00f8bc7 100644 --- a/bower.json +++ b/bower.json @@ -15,6 +15,7 @@ "mocha": "~1.18.2", "chai": "~1.9.1", "crypto-js": "http://crypto-js.googlecode.com/files/CryptoJS%20v3.1.2.zip", + "sjcl":"1.0.0", "file-saver": "*" } } diff --git a/config.template.js b/config.template.js index ea2f712b9..793c7d940 100644 --- a/config.template.js +++ b/config.template.js @@ -10,12 +10,18 @@ var config = { //port: 10009, //path: '/', // - key: 'g23ihfh82h35rf', - host:'162.242.219.26', + key: 'g23ihfh82h35rf', // api key for the peerjs server + host:'162.242.219.26', // peerjs server port:10009, path: '/', maxPeers: 15, - debug: 3 +// debug: 3, + sjclParams: { + salt: 'mjuBtGybi/4=', // choose your own salt (base64) + iter:1000, + mode:'ccm', + ts:parseInt(64), + } }, limits: { totalCopayers: 10, diff --git a/css/main.css b/css/main.css index 80677f7e7..3133d44fd 100644 --- a/css/main.css +++ b/css/main.css @@ -241,7 +241,7 @@ hr { margin: 2.25rem 0;} .br100 {border-radius: 100%;} .lh {line-height: 0;} .oh {overflow:hidden;} - +.lh {line-height: 0;} .signin input.ng-dirty.ng-invalid { border: 2px red solid; } @@ -265,6 +265,11 @@ hr { margin: 2.25rem 0;} padding: 0.5rem; } +.tx-copayers { + overflow: hidden; + padding: 0.5rem; +} + .box-copayers { padding: 0.5rem 2rem 0.5rem 1rem; float: left; diff --git a/index.html b/index.html index 776c6d96c..c442c602d 100644 --- a/index.html +++ b/index.html @@ -84,7 +84,7 @@ Share this secret with your other copayers for them to join your wallet
- {{$root.wallet.getMyCopayerId()}} + {{$root.wallet.getSecret()}}
@@ -512,6 +512,7 @@ + diff --git a/js/controllers/signin.js b/js/controllers/signin.js index f1e8b2fe4..052179478 100644 --- a/js/controllers/signin.js +++ b/js/controllers/signin.js @@ -18,24 +18,26 @@ angular.module('copay.signin').controller('SigninController', }; $scope.join = function(secret) { - if (!secret || secret.length !==66 || !secret.match(/^[0-9a-f]*$/) ) { - $rootScope.flashMessage = { message: 'Bad secret secret string', type: 'error'}; - return; - } $scope.loading = true; - walletFactory.network.on('joinError', function() { - controllerUtils.onErrorDigest($scope); + walletFactory.network.on('badSecret', function() { }); - walletFactory.joinCreateSession(secret, function(w) { - if (w) { - controllerUtils.startNetwork(w); - } - else { - $scope.loading = false; - controllerUtils.onErrorDigest(); + walletFactory.joinCreateSession(secret, function(err,w) { + $scope.loading = false; +console.log('[signin.js.27:err:]',err,w); //TODO + + if (err || !w) { + if (err === 'joinError') + $rootScope.flashMessage = { message: 'Can not find peer'}; + else if (err === 'badSecret') + $rootScope.flashMessage = { message: 'Bad secret secret string', type: 'error'}; + else + $rootScope.flashMessage = { message: 'Unknown error', type: 'error'}; + controllerUtils.onErrorDigest(); } + else + controllerUtils.startNetwork(w); }); }; }); diff --git a/js/models/core/Wallet.js b/js/models/core/Wallet.js index 02646d3c9..1a9898993 100644 --- a/js/models/core/Wallet.js +++ b/js/models/core/Wallet.js @@ -9,6 +9,8 @@ var Builder = bitcore.TransactionBuilder; var http = require('http'); var EventEmitter = imports.EventEmitter || require('events').EventEmitter; var copay = copay || require('../../../copay'); +var SecureRandom = bitcore.SecureRandom; +var Base58Check = bitcore.Base58.base58Check; function Wallet(opts) { var self = this; @@ -26,6 +28,8 @@ function Wallet(opts) { this.id = opts.id || Wallet.getRandomId(); this.name = opts.name; + this.netKey = opts.netKey || SecureRandom.getRandomBuffer(8).toString('base64'); + this.verbose = opts.verbose; this.publicKeyRing.walletId = this.id; this.txProposals.walletId = this.id; @@ -124,6 +128,7 @@ Wallet.prototype._optsToObj = function() { requiredCopayers: this.requiredCopayers, totalCopayers: this.totalCopayers, name: this.name, + netKey: this.netKey, }; return obj; @@ -139,6 +144,26 @@ Wallet.prototype.getMyCopayerId = function() { return this.getCopayerId(0); }; + +Wallet.prototype.getSecret = function() { + var i = new Buffer(this.getMyCopayerId(),'hex'); + var k = new Buffer(this.netKey,'base64'); + var b = Buffer.concat([i,k]); + var str = Base58Check.encode(b); + return str; +}; + + +Wallet.decodeSecret = function(secretB) { + var secret = Base58Check.decode(secretB); + var netKeyBuf = secret.slice(-8); + var pubKeyBuf = secret.slice(0,33); + return { + pubKey: pubKeyBuf.toString('hex'), + netKey: netKeyBuf.toString('base64'), + } +}; + Wallet.prototype._lockIncomming = function() { this.network.lockIncommingConnections(this.publicKeyRing.getAllCopayerIds()); }; @@ -162,6 +187,7 @@ Wallet.prototype.netStart = function() { var startOpts = { copayerId: myId, maxPeers: self.totalCopayers, + netKey: this.netKey, }; if (this.publicKeyRing.isComplete()) { diff --git a/js/models/core/WalletFactory.js b/js/models/core/WalletFactory.js index a8dd80f84..cb6974120 100644 --- a/js/models/core/WalletFactory.js +++ b/js/models/core/WalletFactory.js @@ -149,27 +149,35 @@ WalletFactory.prototype.remove = function(walletId) { }; -WalletFactory.prototype.joinCreateSession = function(copayerId, cb) { +WalletFactory.prototype.joinCreateSession = function(secret, cb) { var self = this; + var s; + try { + s=Wallet.decodeSecret(secret); + } catch (e) { + return cb('badSecret'); + } + //Create our PrivateK var privateKey = new PrivateKey({ networkName: this.networkName }); this.log('\t### PrivateKey Initialized'); var opts = { copayerId: privateKey.getId(), + netKey: s.netKey, }; self.network.cleanUp(); self.network.start(opts, function() { - self.network.connectTo(copayerId); + self.network.connectTo(s.pubKey); self.network.on('onlyYou', function(sender, data) { - return cb(); + return cb('joinError'); }); self.network.on('data', function(sender, data) { if (data.type ==='walletId') { data.opts.privateKey = privateKey; var w = self.open(data.walletId, data.opts); - w.firstCopayerId = copayerId; - return cb(w); + w.firstCopayerId = s.pubKey; + return cb(null, w); } }); }); diff --git a/js/models/network/WebRTC.js b/js/models/network/WebRTC.js index cff75bb48..baeabcda8 100644 --- a/js/models/network/WebRTC.js +++ b/js/models/network/WebRTC.js @@ -3,7 +3,6 @@ var imports = require('soop').imports(); var EventEmitter= imports.EventEmitter || require('events').EventEmitter; var bitcore = require('bitcore'); var util = bitcore.util; -var Key = bitcore.Key; /* * Emits * 'networkChange' @@ -18,15 +17,22 @@ var Key = bitcore.Key; */ function Network(opts) { - var self = this; + var self = this; opts = opts || {}; this.apiKey = opts.apiKey || 'lwjd5qra8257b9'; this.debug = opts.debug || 3; this.maxPeers = opts.maxPeers || 10; - this.opts = { key: opts.key }; + this.sjclParams = opts.sjclParams || { + salt: 'f28bfb49ef70573c', + iter:500, + mode:'ccm', + ts:parseInt(64), + }; + // For using your own peerJs server - ['port', 'host', 'path', 'debug'].forEach(function(k) { + self.opts = {}; + ['port', 'host', 'path', 'debug', 'key'].forEach(function(k) { if (opts[k]) self.opts[k] = opts[k]; }); this.cleanUp(); @@ -38,6 +44,7 @@ Network.prototype.cleanUp = function() { this.started = false; this.connectedPeers = []; this.peerId = null; + this.netKey = null; this.copayerId = null; this.signingKey = null; this.allowedCopayerIds=null; @@ -152,10 +159,11 @@ Network.prototype._addCopayer = function(copayerId, isInbound) { -Network.prototype._onData = function(data, isInbound, peerId) { +Network.prototype._onData = function(encStr, isInbound, peerId) { var sig, payload; try { + var data = this._decrypt(encStr); payload= JSON.parse(data); } catch (e) { console.log('### ERROR IN DATA: "%s" ', data, isInbound, e); @@ -166,13 +174,15 @@ Network.prototype._onData = function(data, isInbound, peerId) { console.log('### RECEIVED INBOUND?:%s TYPE: %s FROM %s', isInbound, payload.type, peerId, payload); - if(payload.type === 'hello' && !this.authenticatedPeers[peerId]) { - var payloadStr = JSON.stringify(payload); - if (this.allowedCopayerIds && !this.allowedCopayerIds[payload.copayerId]) { - console.log('#### Peer is not on the allowedCopayerIds. Closing connection', - this.allowedCopayerIds, payload.copayerId); - this._deletePeer(peerId); - return; + if(payload.type === 'hello' ) { + if (!this.authenticatedPeers[peerId]) { + var payloadStr = JSON.stringify(payload); + if (this.allowedCopayerIds && !this.allowedCopayerIds[payload.copayerId]) { + console.log('#### Peer is not on the allowedCopayerIds. Closing connection', + this.allowedCopayerIds, payload.copayerId); + this._deletePeer(peerId); + return; + } } console.log('#### Peer sent hello. Setting it up.'); //TODO this._setPeerAuthenticated(peerId); @@ -330,6 +340,7 @@ Network.prototype.start = function(opts, openCallback) { if (this.started) return openCallback(); + this.netKey = opts.netKey; this.maxPeers = opts.maxPeers || this.maxPeers; if (!this.copayerId) @@ -355,13 +366,35 @@ Network.prototype.getPeer = function() { return this.peer; }; +Network.prototype._encrypt = function(payloadStr) { + var plainText = sjcl.codec.utf8String.toBits(payloadStr); + var p = this.sjclParams; + ct = sjcl.encrypt(this.netKey, plainText, p);//,p, rp); + var c = JSON.parse(ct); + var toSend = { + iv: c.iv, + ct: c.ct, + }; + return JSON.stringify(toSend); +}; -Network.prototype._sendToOne = function(copayerId, payloadStr, sig, cb) { + +Network.prototype._decrypt = function(encStr) { + var i = JSON.parse(encStr); + for (var k in this.sjclParams) { + i[k] = this.sjclParams[k]; + } + var str= JSON.stringify(i); + var pt = sjcl.decrypt(this.netKey, str); + return pt; +}; + +Network.prototype._sendToOne = function(copayerId, payload, sig, cb) { var peerId = this.peerFromCopayer(copayerId); if (peerId !== this.peerId) { var dataConn = this.connections[peerId]; if (dataConn) { - dataConn.send(payloadStr); + dataConn.send(payload); } else { console.log('[WebRTC.js.255] WARN: NO CONNECTION TO:', peerId); //TODO @@ -379,17 +412,18 @@ Network.prototype.send = function(copayerIds, payload, cb) { var sig; var payloadStr = JSON.stringify(payload); + var encPayload = this._encrypt(payloadStr); if (Array.isArray(copayerIds)) { var l = copayerIds.length; var i = 0; copayerIds.forEach(function(copayerId) { - self._sendToOne(copayerId, payloadStr, sig, function () { + self._sendToOne(copayerId, encPayload, sig, function () { if (++i === l && typeof cb === 'function') cb(); }); }); } else if (typeof copayerIds === 'string') - self._sendToOne(copayerIds, payloadStr, sig, cb); + self._sendToOne(copayerIds, encPayload, sig, cb); }; Network.prototype.connectTo = function(copayerId) { diff --git a/js/services/controllerUtils.js b/js/services/controllerUtils.js index bfdc3909e..c2d47f2ac 100644 --- a/js/services/controllerUtils.js +++ b/js/services/controllerUtils.js @@ -29,11 +29,6 @@ angular.module('copay.controllerUtils') root.onError = function(scope) { if (scope) scope.loading = false; - $rootScope.flashMessage = { - type: 'error', - message: 'Could not connect to peer: ' + - scope - }; root.logout(); } @@ -93,6 +88,7 @@ angular.module('copay.controllerUtils') root.setSocketHandlers = function() { Socket.removeAllListeners(); + if (!$rootScope.wallet) return; var addrs = $rootScope.wallet.getAddressesStr(); for (var i = 0; i < addrs.length; i++) { diff --git a/js/services/video.js b/js/services/video.js index 367296934..3902a04bc 100644 --- a/js/services/video.js +++ b/js/services/video.js @@ -7,7 +7,7 @@ var Video = function() { this.mediaConnections = {}; this.localStream = null; - this.onlineSound = new Audio('../../sound/online.wav'); + this.onlineSound = new Audio('sound/online.wav'); }; Video.prototype.setOwnPeer = function(peer, wallet, cb) { @@ -72,11 +72,13 @@ Video.prototype._addCall = function(mediaConnection, cb) { } Video.prototype.close = function() { - this.localStream.stop(); - this.localStream.mozSrcObject = null; - this.localStream.src = ""; - this.localStream.src = null; - this.localStream = null; + if (this.localStream){ + this.localStream.stop(); + this.localStream.mozSrcObject = null; + this.localStream.src = ""; + this.localStream.src = null; + this.localStream = null; + } for (var i = 0; this.mediaConnections.length; i++) { this.mediaConnections[i].close(); } diff --git a/lib/bitcore.js b/lib/bitcore.js deleted file mode 120000 index 890de75e6..000000000 --- a/lib/bitcore.js +++ /dev/null @@ -1 +0,0 @@ -../../bitcore/browser/bundle.js \ No newline at end of file diff --git a/lib/sjcl.js b/lib/sjcl.js new file mode 100644 index 000000000..c02b7132a --- /dev/null +++ b/lib/sjcl.js @@ -0,0 +1,54 @@ +"use strict";function q(a){throw a;}var t=void 0,u=!1;var sjcl={cipher:{},hash:{},keyexchange:{},mode:{},misc:{},codec:{},exception:{corrupt:function(a){this.toString=function(){return"CORRUPT: "+this.message};this.message=a},invalid:function(a){this.toString=function(){return"INVALID: "+this.message};this.message=a},bug:function(a){this.toString=function(){return"BUG: "+this.message};this.message=a},notReady:function(a){this.toString=function(){return"NOT READY: "+this.message};this.message=a}}}; +"undefined"!==typeof module&&module.exports&&(module.exports=sjcl); +sjcl.cipher.aes=function(a){this.k[0][0][0]||this.D();var b,c,d,e,f=this.k[0][4],g=this.k[1];b=a.length;var h=1;4!==b&&(6!==b&&8!==b)&&q(new sjcl.exception.invalid("invalid aes key size"));this.b=[d=a.slice(0),e=[]];for(a=b;a<4*b+28;a++){c=d[a-1];if(0===a%b||8===b&&4===a%b)c=f[c>>>24]<<24^f[c>>16&255]<<16^f[c>>8&255]<<8^f[c&255],0===a%b&&(c=c<<8^c>>>24^h<<24,h=h<<1^283*(h>>7));d[a]=d[a-b]^c}for(b=0;a;b++,a--)c=d[b&3?a:a-4],e[b]=4>=a||4>b?c:g[0][f[c>>>24]]^g[1][f[c>>16&255]]^g[2][f[c>>8&255]]^g[3][f[c& +255]]}; +sjcl.cipher.aes.prototype={encrypt:function(a){return y(this,a,0)},decrypt:function(a){return y(this,a,1)},k:[[[],[],[],[],[]],[[],[],[],[],[]]],D:function(){var a=this.k[0],b=this.k[1],c=a[4],d=b[4],e,f,g,h=[],l=[],k,n,m,p;for(e=0;0x100>e;e++)l[(h[e]=e<<1^283*(e>>7))^e]=e;for(f=g=0;!c[f];f^=k||1,g=l[g]||1){m=g^g<<1^g<<2^g<<3^g<<4;m=m>>8^m&255^99;c[f]=m;d[m]=f;n=h[e=h[k=h[f]]];p=0x1010101*n^0x10001*e^0x101*k^0x1010100*f;n=0x101*h[m]^0x1010100*m;for(e=0;4>e;e++)a[e][f]=n=n<<24^n>>>8,b[e][m]=p=p<<24^p>>>8}for(e= +0;5>e;e++)a[e]=a[e].slice(0),b[e]=b[e].slice(0)}}; +function y(a,b,c){4!==b.length&&q(new sjcl.exception.invalid("invalid aes block size"));var d=a.b[c],e=b[0]^d[0],f=b[c?3:1]^d[1],g=b[2]^d[2];b=b[c?1:3]^d[3];var h,l,k,n=d.length/4-2,m,p=4,s=[0,0,0,0];h=a.k[c];a=h[0];var r=h[1],v=h[2],w=h[3],x=h[4];for(m=0;m>>24]^r[f>>16&255]^v[g>>8&255]^w[b&255]^d[p],l=a[f>>>24]^r[g>>16&255]^v[b>>8&255]^w[e&255]^d[p+1],k=a[g>>>24]^r[b>>16&255]^v[e>>8&255]^w[f&255]^d[p+2],b=a[b>>>24]^r[e>>16&255]^v[f>>8&255]^w[g&255]^d[p+3],p+=4,e=h,f=l,g=k;for(m=0;4> +m;m++)s[c?3&-m:m]=x[e>>>24]<<24^x[f>>16&255]<<16^x[g>>8&255]<<8^x[b&255]^d[p++],h=e,e=f,f=g,g=b,b=h;return s} +sjcl.bitArray={bitSlice:function(a,b,c){a=sjcl.bitArray.P(a.slice(b/32),32-(b&31)).slice(1);return c===t?a:sjcl.bitArray.clamp(a,c-b)},extract:function(a,b,c){var d=Math.floor(-b-c&31);return((b+c-1^b)&-32?a[b/32|0]<<32-d^a[b/32+1|0]>>>d:a[b/32|0]>>>d)&(1<>b-1,1));return a},partial:function(a,b,c){return 32===a?b:(c?b|0:b<<32-a)+0x10000000000*a},getPartial:function(a){return Math.round(a/0x10000000000)||32},equal:function(a,b){if(sjcl.bitArray.bitLength(a)!==sjcl.bitArray.bitLength(b))return u;var c=0,d;for(d=0;d>>b),c=a[e]<<32-b;e=a.length?a[a.length-1]:0;a=sjcl.bitArray.getPartial(e);d.push(sjcl.bitArray.partial(b+a&31,32>>24),e<<=8;return decodeURIComponent(escape(b))},toBits:function(a){a=unescape(encodeURIComponent(a));var b=[],c,d=0;for(c=0;c>>e)>>>26),6>e?(g=a[c]<<6-e,e+=26,c++):(g<<=6,e-=6);for(;d.length&3&&!b;)d+="=";return d},toBits:function(a,b){a=a.replace(/\s|=/g,"");var c=[],d,e=0,f=sjcl.codec.base64.J,g=0,h;b&&(f=f.substr(0,62)+"-_");for(d=0;dh&&q(new sjcl.exception.invalid("this isn't base64!")),26>>e),g=h<<32-e):(e+=6,g^=h<<32-e);e&56&&c.push(sjcl.bitArray.partial(e&56,g,1));return c}};sjcl.codec.base64url={fromBits:function(a){return sjcl.codec.base64.fromBits(a,1,1)},toBits:function(a){return sjcl.codec.base64.toBits(a,1)}};sjcl.hash.sha256=function(a){this.b[0]||this.D();a?(this.r=a.r.slice(0),this.o=a.o.slice(0),this.h=a.h):this.reset()};sjcl.hash.sha256.hash=function(a){return(new sjcl.hash.sha256).update(a).finalize()}; +sjcl.hash.sha256.prototype={blockSize:512,reset:function(){this.r=this.N.slice(0);this.o=[];this.h=0;return this},update:function(a){"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));var b,c=this.o=sjcl.bitArray.concat(this.o,a);b=this.h;a=this.h=b+sjcl.bitArray.bitLength(a);for(b=512+b&-512;b<=a;b+=512)z(this,c.splice(0,16));return this},finalize:function(){var a,b=this.o,c=this.r,b=sjcl.bitArray.concat(b,[sjcl.bitArray.partial(1,1)]);for(a=b.length+2;a&15;a++)b.push(0);b.push(Math.floor(this.h/ +4294967296));for(b.push(this.h|0);b.length;)z(this,b.splice(0,16));this.reset();return c},N:[],b:[],D:function(){function a(a){return 0x100000000*(a-Math.floor(a))|0}var b=0,c=2,d;a:for(;64>b;c++){for(d=2;d*d<=c;d++)if(0===c%d)continue a;8>b&&(this.N[b]=a(Math.pow(c,0.5)));this.b[b]=a(Math.pow(c,1/3));b++}}}; +function z(a,b){var c,d,e,f=b.slice(0),g=a.r,h=a.b,l=g[0],k=g[1],n=g[2],m=g[3],p=g[4],s=g[5],r=g[6],v=g[7];for(c=0;64>c;c++)16>c?d=f[c]:(d=f[c+1&15],e=f[c+14&15],d=f[c&15]=(d>>>7^d>>>18^d>>>3^d<<25^d<<14)+(e>>>17^e>>>19^e>>>10^e<<15^e<<13)+f[c&15]+f[c+9&15]|0),d=d+v+(p>>>6^p>>>11^p>>>25^p<<26^p<<21^p<<7)+(r^p&(s^r))+h[c],v=r,r=s,s=p,p=m+d|0,m=n,n=k,k=l,l=d+(k&n^m&(k^n))+(k>>>2^k>>>13^k>>>22^k<<30^k<<19^k<<10)|0;g[0]=g[0]+l|0;g[1]=g[1]+k|0;g[2]=g[2]+n|0;g[3]=g[3]+m|0;g[4]=g[4]+p|0;g[5]=g[5]+s|0;g[6]= +g[6]+r|0;g[7]=g[7]+v|0} +sjcl.mode.ccm={name:"ccm",encrypt:function(a,b,c,d,e){var f,g=b.slice(0),h=sjcl.bitArray,l=h.bitLength(c)/8,k=h.bitLength(g)/8;e=e||64;d=d||[];7>l&&q(new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"));for(f=2;4>f&&k>>>8*f;f++);f<15-l&&(f=15-l);c=h.clamp(c,8*(15-f));b=sjcl.mode.ccm.L(a,b,c,d,e,f);g=sjcl.mode.ccm.p(a,g,c,b,e,f);return h.concat(g.data,g.tag)},decrypt:function(a,b,c,d,e){e=e||64;d=d||[];var f=sjcl.bitArray,g=f.bitLength(c)/8,h=f.bitLength(b),l=f.clamp(b,h-e),k=f.bitSlice(b, +h-e),h=(h-e)/8;7>g&&q(new sjcl.exception.invalid("ccm: iv must be at least 7 bytes"));for(b=2;4>b&&h>>>8*b;b++);b<15-g&&(b=15-g);c=f.clamp(c,8*(15-b));l=sjcl.mode.ccm.p(a,l,c,k,e,b);a=sjcl.mode.ccm.L(a,l.data,c,d,e,b);f.equal(l.tag,a)||q(new sjcl.exception.corrupt("ccm: tag doesn't match"));return l.data},L:function(a,b,c,d,e,f){var g=[],h=sjcl.bitArray,l=h.l;e/=8;(e%2||4>e||16=c?g=[h.partial(16,c)]:0xffffffff>=c&&(g=h.concat([h.partial(16,65534)],[c]));g=h.concat(g,d);for(d=0;de.bitLength(c)&&(h=f(h,d(h)),c=e.concat(c,[-2147483648,0,0,0]));g=f(g,c);return a.encrypt(f(d(f(h, +d(h))),g))},H:function(a){return[a[0]<<1^a[1]>>>31,a[1]<<1^a[2]>>>31,a[2]<<1^a[3]>>>31,a[3]<<1^135*(a[0]>>>31)]}}; +sjcl.mode.gcm={name:"gcm",encrypt:function(a,b,c,d,e){var f=b.slice(0);b=sjcl.bitArray;d=d||[];a=sjcl.mode.gcm.p(!0,a,f,d,c,e||128);return b.concat(a.data,a.tag)},decrypt:function(a,b,c,d,e){var f=b.slice(0),g=sjcl.bitArray,h=g.bitLength(f);e=e||128;d=d||[];e<=h?(b=g.bitSlice(f,h-e),f=g.bitSlice(f,0,h-e)):(b=f,f=[]);a=sjcl.mode.gcm.p(u,a,f,d,c,e);g.equal(a.tag,b)||q(new sjcl.exception.corrupt("gcm: tag doesn't match"));return a.data},Z:function(a,b){var c,d,e,f,g,h=sjcl.bitArray.l;e=[0,0,0,0];f=b.slice(0); +for(c=0;128>c;c++){(d=0!==(a[Math.floor(c/32)]&1<<31-c%32))&&(e=h(e,f));g=0!==(f[3]&1);for(d=3;0>>1|(f[d-1]&1)<<31;f[0]>>>=1;g&&(f[0]^=-0x1f000000)}return e},g:function(a,b,c){var d,e=c.length;b=b.slice(0);for(d=0;de&&(a=b.hash(a));for(d=0;dd||0>c)&&q(sjcl.exception.invalid("invalid params to pbkdf2"));"string"===typeof a&&(a=sjcl.codec.utf8String.toBits(a));"string"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));e=e||sjcl.misc.hmac;a=new e(a);var f,g,h,l,k=[],n=sjcl.bitArray;for(l=1;32*k.length<(d||1);l++){e=f=a.encrypt(n.concat(b,[l]));for(g=1;gg;g++)e.push(0x100000000*Math.random()|0);for(g=0;g=1<this.j&&(this.j=f);this.F++; +this.b=sjcl.hash.sha256.hash(this.b.concat(e));this.A=new sjcl.cipher.aes(this.b);for(d=0;4>d&&!(this.f[d]=this.f[d]+1|0,this.f[d]);d++);}for(d=0;d>>=1;this.c[g].update([d,this.C++,2,b,f,a.length].concat(a))}break;case "string":b===t&&(b=a.length);this.c[g].update([d,this.C++,3,b,f,a.length]);this.c[g].update(a);break;default:l=1}l&&q(new sjcl.exception.bug("random: addEntropy only supports number, array of numbers or string"));this.i[g]+=b;this.d+=b;h===this.m&&(this.isReady()!==this.m&&C("seeded",Math.max(this.j,this.d)),C("progress",this.getProgress()))},isReady:function(a){a=this.I[a!==t?a:this.B];return this.j&&this.j>=a?this.i[0]>this.R&& +(new Date).valueOf()>this.O?this.u|this.t:this.t:this.d>=a?this.u|this.m:this.m},getProgress:function(a){a=this.I[a?a:this.B];return this.j>=a?1:this.d>a?1:this.d/a},startCollectors:function(){this.q||(this.a={loadTimeCollector:D(this,this.aa),mouseCollector:D(this,this.ba),keyboardCollector:D(this,this.$),accelerometerCollector:D(this,this.U)},window.addEventListener?(window.addEventListener("load",this.a.loadTimeCollector,u),window.addEventListener("mousemove",this.a.mouseCollector,u),window.addEventListener("keypress", +this.a.keyboardCollector,u),window.addEventListener("devicemotion",this.a.accelerometerCollector,u)):document.attachEvent?(document.attachEvent("onload",this.a.loadTimeCollector),document.attachEvent("onmousemove",this.a.mouseCollector),document.attachEvent("keypress",this.a.keyboardCollector)):q(new sjcl.exception.bug("can't attach event")),this.q=!0)},stopCollectors:function(){this.q&&(window.removeEventListener?(window.removeEventListener("load",this.a.loadTimeCollector,u),window.removeEventListener("mousemove", +this.a.mouseCollector,u),window.removeEventListener("keypress",this.a.keyboardCollector,u),window.removeEventListener("devicemotion",this.a.accelerometerCollector,u)):document.detachEvent&&(document.detachEvent("onload",this.a.loadTimeCollector),document.detachEvent("onmousemove",this.a.mouseCollector),document.detachEvent("keypress",this.a.keyboardCollector)),this.q=u)},addEventListener:function(a,b){this.w[a][this.V++]=b},removeEventListener:function(a,b){var c,d,e=this.w[a],f=[];for(d in e)e.hasOwnProperty(d)&& +e[d]===b&&f.push(d);for(c=0;cb&&!(a.f[b]=a.f[b]+1|0,a.f[b]);b++);return a.A.encrypt(a.f)} +function D(a,b){return function(){b.apply(a,arguments)}}sjcl.random=new sjcl.prng(6); +a:try{var F,G,H;if("undefined"!==typeof module&&module.exports)G=require("crypto"),F=G.randomBytes(128),sjcl.random.addEntropy(F,1024,"crypto['randomBytes']");else if(window&&Uint32Array){H=new Uint32Array(32);if(window.crypto&&window.crypto.getRandomValues)window.crypto.getRandomValues(H);else if(window.msCrypto&&window.msCrypto.getRandomValues)window.msCrypto.getRandomValues(H);else break a;sjcl.random.addEntropy(H,1024,"crypto['getRandomValues']")}}catch(I){console.log("There was an error collecting entropy from the browser:"), +console.log(I)} +sjcl.json={defaults:{v:1,iter:1E3,ks:128,ts:64,mode:"ccm",adata:"",cipher:"aes"},Y:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json,f=e.e({iv:sjcl.random.randomWords(4,0)},e.defaults),g;e.e(f,c);c=f.adata;"string"===typeof f.salt&&(f.salt=sjcl.codec.base64.toBits(f.salt));"string"===typeof f.iv&&(f.iv=sjcl.codec.base64.toBits(f.iv));(!sjcl.mode[f.mode]||!sjcl.cipher[f.cipher]||"string"===typeof a&&100>=f.iter||64!==f.ts&&96!==f.ts&&128!==f.ts||128!==f.ks&&192!==f.ks&&0x100!==f.ks||2>f.iv.length||4< +f.iv.length)&&q(new sjcl.exception.invalid("json encrypt: invalid parameters"));"string"===typeof a?(g=sjcl.misc.cachedPbkdf2(a,f),a=g.key.slice(0,f.ks/32),f.salt=g.salt):sjcl.ecc&&a instanceof sjcl.ecc.elGamal.publicKey&&(g=a.kem(),f.kemtag=g.tag,a=g.key.slice(0,f.ks/32));"string"===typeof b&&(b=sjcl.codec.utf8String.toBits(b));"string"===typeof c&&(c=sjcl.codec.utf8String.toBits(c));g=new sjcl.cipher[f.cipher](a);e.e(d,f);d.key=a;f.ct=sjcl.mode[f.mode].encrypt(g,b,f.iv,c,f.ts);return f},encrypt:function(a, +b,c,d){var e=sjcl.json,f=e.Y.apply(e,arguments);return e.encode(f)},X:function(a,b,c,d){c=c||{};d=d||{};var e=sjcl.json;b=e.e(e.e(e.e({},e.defaults),b),c,!0);var f;c=b.adata;"string"===typeof b.salt&&(b.salt=sjcl.codec.base64.toBits(b.salt));"string"===typeof b.iv&&(b.iv=sjcl.codec.base64.toBits(b.iv));(!sjcl.mode[b.mode]||!sjcl.cipher[b.cipher]||"string"===typeof a&&100>=b.iter||64!==b.ts&&96!==b.ts&&128!==b.ts||128!==b.ks&&192!==b.ks&&0x100!==b.ks||!b.iv||2>b.iv.length||4