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 1c7475cb9..509164d34 100644
--- a/css/main.css
+++ b/css/main.css
@@ -261,7 +261,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;
}
@@ -285,6 +285,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 5d99e22d8..c9397ec28 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()}}
@@ -508,6 +508,7 @@
+
diff --git a/js/controllers/signin.js b/js/controllers/signin.js
index 6b472e143..23197d8a6 100644
--- a/js/controllers/signin.js
+++ b/js/controllers/signin.js
@@ -19,24 +19,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 4f59a5e67..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