libves 1.0.4 → 1.9.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/libVES.node.js CHANGED
@@ -1,13 +1,39 @@
1
- /**
1
+ /***************************************************************************
2
+ * ___ ___
3
+ * / \ / \ VESvault
4
+ * \__ / \ __/ Encrypt Everything without fear of losing the Key
5
+ * \\ // https://vesvault.com https://ves.host
6
+ * \\ //
7
+ * ___ \\_//
8
+ * / \ / \ libVES: VESvault API library
9
+ * \__ / \ __/
10
+ * \\ //
11
+ * \\ //
12
+ * \\_// - Key Management and Exchange
13
+ * / \ - Item Encryption and Sharing
14
+ * \___/ - VESrecovery (TM)
15
+ *
16
+ *
17
+ * (c) 2017 - 2020 VESvault Corp
18
+ * Jim Zubov <jz@vesvault.com>
19
+ *
20
+ * GNU General Public License v3
21
+ * You may opt to use, copy, modify, merge, publish, distribute and/or sell
22
+ * copies of the Software, and permit persons to whom the Software is
23
+ * furnished to do so, under the terms of the COPYING file.
24
+ *
25
+ * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
26
+ * KIND, either express or implied.
27
+ *
2
28
  * @title libVES
3
29
  * @dev A JavaScript end-to-end encryption interface to VESvault REST API
4
- * @version 1.01b
30
+ * @version 1.99
5
31
  *
6
32
  * @dev Official source code: https://github.com/vesvault/libVES
7
33
  *
8
- * @author Jim Zubov <jz@vesvault.com> (VESvault)
9
- * GPL license, http://www.gnu.org/licenses/
10
- */
34
+ * @author Jim Zubov <jz@vesvault.com> (VESvault Corp)
35
+ *
36
+ ***************************************************************************/
11
37
 
12
38
  if (!window.libVES) window.libVES = function(optns) {
13
39
  try {
@@ -26,8 +52,8 @@ if (!window.libVES) window.libVES = function(optns) {
26
52
  libVES.prototype = {
27
53
  apiUrl: 'https://api.ves.host/v1/',
28
54
  wwwUrl: 'https://www.vesvault.com/',
29
- keyAlgo: 'RSA',
30
- textCipher: 'AES256CBC',
55
+ keyAlgo: 'ECDH',
56
+ textCipher: 'AES256GCMp',
31
57
  defaultHash: 'SHA256',
32
58
 
33
59
  request: function(method,uri,body,optns) {
@@ -62,7 +88,7 @@ libVES.prototype = {
62
88
  if (body != null) xhr.setRequestHeader('Content-Type','application/json');
63
89
  xhr.setRequestHeader('Accept','application/json');
64
90
  if (this.user && optns.password) xhr.setRequestHeader('Authorization','Basic ' + btoa(this.user + ':' + optns.password));
65
- else if (this.token) xhr.setRequestHeader('Authorization','Bearer ' + this.token);
91
+ else if (optns.token || this.token) xhr.setRequestHeader('Authorization','Bearer ' + (optns.token ? optns.token : this.token));
66
92
  xhr.responseType = 'json';
67
93
  xhr.send(body);
68
94
  }.bind(this));
@@ -87,6 +113,18 @@ libVES.prototype = {
87
113
  }
88
114
  return '';
89
115
  },
116
+ elevateAuth: function(optns) {
117
+ var self = this;
118
+ if (optns && (optns.password || optns.token)) return Promise.resolve(optns);
119
+ return (optns && optns.authVaultKey ? Promise.resolve(optns.authVaultKey) : self.getVaultKey()).then(function(vkey) {
120
+ return vkey.getSessionToken().then(function(tkn) {
121
+ if (!tkn) return optns;
122
+ var o = {token: tkn};
123
+ if (optns) for (var k in optns) o[k] = optns[k];
124
+ return o;
125
+ });
126
+ });
127
+ },
90
128
  login: function(passwd) {
91
129
  if (this.token) return this.me();
92
130
  var self = this;
@@ -102,11 +140,82 @@ libVES.prototype = {
102
140
  this.token = undefined;
103
141
  return this.lock();
104
142
  },
105
- delegate: function() {
143
+ delegate: function(optns) {
106
144
  var self = this;
107
145
  return libVES.getModule(libVES,'Delegate').then(function(dlg) {
108
- return dlg.login(self);
146
+ return dlg.login(self,null,optns).then(function() {
147
+ return self;
148
+ });
149
+ });
150
+ },
151
+ getVESflow: function() {
152
+ var self = this;
153
+ if (!this.VESflow) this.VESflow = libVES.getModule(libVES, 'Flow').then(function(fw) {
154
+ var flow = new fw('VES');
155
+ var authf = self.authorize.bind(self);
156
+ self.authorize = function(msg) {
157
+ flow.setValue(msg);
158
+ return authf(msg);
159
+ };
160
+ var outf = self.logout.bind(self);
161
+ self.logout = function() {
162
+ flow.logout();
163
+ return outf();
164
+ };
165
+ return flow;
109
166
  });
167
+ return this.VESflow;
168
+ },
169
+ flow: function(start, optns) {
170
+ var self = this;
171
+ return self.getVESflow().then(function(flow) {
172
+ return flow.get().then(function(auth) {
173
+ return self.authorize(auth);
174
+ }).catch(function(e) {
175
+ if (!start || (e && e.code == 'Reload')) throw e;
176
+ var url = self.wwwUrl + 'vv/unlock?url=' + encodeURIComponent(document.location.href) + '&domain=' + encodeURIComponent(self.domain);
177
+ if (optns) for (var k in optns) if (optns[k] != null) url += '&' + encodeURIComponent(k) + '=' + encodeURIComponent(optns[k]);
178
+ flow.url = undefined;
179
+ return flow.setValue(undefined).then(function() {
180
+ return flow.addToken(url).then(function(url) {
181
+ document.location.replace(url);
182
+ throw {code: 'Redirect', message: 'Starting VES Authorization...'};
183
+ });
184
+ });
185
+ });
186
+ });
187
+ },
188
+ authorize: function(msg) {
189
+ var self = this;
190
+ if (msg.token) this.token = msg.token;
191
+ if (msg.domain) this.domain = msg.domain;
192
+ if (msg.externalId) this.externalId = msg.externalId;
193
+ return (msg.VESkey ? this.unlock(msg.VESkey) : this.me()).then(function() {
194
+ return self;
195
+ });
196
+ },
197
+ carry: function(optns) {
198
+ var cr = {};
199
+ if (this.externalId) cr.externalId = this.externalId;
200
+ if (this.domain) cr.domain = this.domain;
201
+ if (this.token) cr.token = this.token;
202
+ if (this.VESkey) cr.VESkey = this.VESkey;
203
+ try {
204
+ sessionStorage['libVES_carry'] = JSON.stringify(cr);
205
+ } catch (e) {
206
+ return Promise.reject(e);
207
+ }
208
+ return Promise.resolve(true);
209
+ },
210
+ pick: function(optns) {
211
+ try {
212
+ if (!sessionStorage['libVES_carry']) throw new libVES.Error('NotFound','No libVES_carry in sessionStorage');
213
+ var cr = JSON.parse(sessionStorage['libVES_carry']);
214
+ delete(sessionStorage['libVES_carry']);
215
+ return this.authorize(cr);
216
+ } catch (e) {
217
+ return Promise.reject(e);
218
+ }
110
219
  },
111
220
  me: function() {
112
221
  var self = this;
@@ -119,16 +228,17 @@ libVES.prototype = {
119
228
  var self = this;
120
229
  return this.getVaultKey().then(function(vkey) {
121
230
  return vkey.unlock(Promise.resolve(veskey)).then(function(cryptoKey) {
231
+ if (self.VESkey === null) self.VESkey = veskey;
122
232
  if (!self.token && self.type == 'secondary') return vkey.getSessionToken().then(function(tkn) {
123
233
  self.token = tkn;
124
- return Promise.resolve(veskey).then(function(veskey) {
125
- if (veskey) return vkey.reshareVESkey(veskey).catch(function(){});
126
- }).then(function() {
127
- return cryptoKey;
128
- });
234
+ return cryptoKey;
129
235
  });
130
236
  return cryptoKey;
131
237
  });
238
+ }).then(function(ck) {
239
+ return self.handleAttn().catch(function() {}).then(function() {
240
+ return ck;
241
+ });
132
242
  });
133
243
  },
134
244
  lock: function() {
@@ -162,7 +272,7 @@ libVES.prototype = {
162
272
  });
163
273
  },
164
274
  getVaultKeysById: function() {
165
- if (!this.vaultKeysById) this.vaultKeysById = this.me().then(function(me) {
275
+ return this.me().then(function(me) {
166
276
  return me.getVaultKeys().then(function(vaultKeys) {
167
277
  return Promise.all(vaultKeys.map(function(e,i) {
168
278
  return e.getId();
@@ -173,7 +283,6 @@ libVES.prototype = {
173
283
  });
174
284
  });
175
285
  });
176
- return this.vaultKeysById;
177
286
  },
178
287
  getItems: function() {
179
288
  var self = this;
@@ -211,7 +320,7 @@ libVES.prototype = {
211
320
  if (u instanceof libVES.VaultKey) return [u];
212
321
  else if (u instanceof libVES.External) return [new libVES.VaultKey({externals:[u]},self)];
213
322
  else if (u instanceof libVES.User) return self.getUserKeys(u);
214
- else if (u instanceof Array || u.domain != null || u.externalId != null) return self._matchSecondaryKey(u,u.user).then(function(vkey) {
323
+ else if (u instanceof Array || u.domain != null || u.externalId != null) return self._matchSecondaryKey(u, u.user, u.appUrl).then(function(vkey) {
215
324
  return [vkey];
216
325
  });
217
326
  }
@@ -229,10 +338,21 @@ libVES.prototype = {
229
338
  } else if (typeof(u) == 'string' && u.match(/^\S+\@\S+$/)) return new libVES.User({email: u},this);
230
339
  throw new libVES.Error('BadUser',"Cannot match user: " + u,{value: u});
231
340
  },
232
- _matchSecondaryKey: function(ext,user) {
341
+ _matchSecondaryKey: function(ext, user, appUrl) {
233
342
  var self = this;
234
343
  var m = function() {
235
- return libVES.getModule(libVES.Domain,ext.domain);
344
+ return libVES.getModule(libVES.Domain,ext.domain).catch(function(e) {
345
+ return {
346
+ userToVaultRef: function(u) {
347
+ return u.getEmail().then(function(email) {
348
+ return email.toLowerCase();
349
+ });
350
+ },
351
+ vaultRefToUser: function(ext) {
352
+ return new libVES.User({email: ext.externalId}, self);
353
+ }
354
+ };
355
+ });
236
356
  };
237
357
  return (ext.externalId ? self.prepareExternals(ext) : m().then(function(dom) {
238
358
  return Promise.resolve(user || self.me()).then(function(u) {
@@ -257,10 +377,11 @@ libVES.prototype = {
257
377
  return Promise.all([me.getId(),u.getId()]).then(function(ids) {
258
378
  if (ids[0] == ids[1]) return self.getSecondaryKey(exts,true);
259
379
  }).catch(function(e) {
260
- if (e != 'NotFound') throw e;
380
+ if (e.code != 'NotFound') throw e;
261
381
  }).then(function(rs) {
262
382
  return rs || self.createTempKey(self._matchUser(u)).then(function(vkey) {
263
383
  return vkey.setField('externals',exts).then(function() {
384
+ if (appUrl) vkey.setField('appUrl', appUrl);
264
385
  return vkey;
265
386
  });
266
387
  });
@@ -292,21 +413,21 @@ libVES.prototype = {
292
413
  return keys;
293
414
  });
294
415
  },
295
- createTempKey: function(usr,optns) {
416
+ createTempKey: function(usr, optns) {
296
417
  var self = this;
297
- var key = new libVES.VaultKey({type: 'temp', algo: this.keyAlgo, user: usr},self);
418
+ var key = new libVES.VaultKey({type: 'temp', algo: this.keyAlgo, user: usr}, self);
298
419
  var veskey = this.generateVESkey(usr);
299
- return key.generate(veskey,optns).then(function(k) {
300
- if (self.e2e && self.e2e.length) usr.e2e = self.getVESkeyE2E(veskey,usr);
301
- key.setField('vaultItems',veskey.then(function(v) {
302
- var vi = new libVES.VaultItem({type: 'password'},self);
303
- return self.me().then(function(me) {
304
- return vi.shareWith([me],v,false).then(function() {
420
+ return key.generate(veskey, optns).then(function(k) {
421
+ if (self.e2e && self.e2e.length) usr.e2e = self.getVESkeyE2E(veskey, usr);
422
+ key.setField('vaultItems', veskey.then(function(v) {
423
+ var vi = new libVES.VaultItem({type: 'password'}, self);
424
+ return Promise.all(self.type == 'secondary' ? [self.me(), self.getVaultKey()] : [self.me()]).then(function(sh) {
425
+ return vi.shareWith(sh, v, false).then(function() {
305
426
  return [vi];
306
427
  });
307
428
  });
308
429
  }));
309
- key.setField('creator',self.me());
430
+ key.setField('creator', self.me());
310
431
  return key;
311
432
  });
312
433
  },
@@ -331,22 +452,31 @@ libVES.prototype = {
331
452
  var self = this;
332
453
  return this.me().then(function(me) {
333
454
  return (new libVES.VaultKey({type: 'current', algo: self.keyAlgo, user: me},self)).generate(Promise.resolve(veskey),options).then(function(k) {
334
- return self.getVaultKey().then(function(cur) {
335
- var r;
336
- if (cur) {
337
- if (lost) r = cur.setField('type','lost').then(function() {
455
+ return me.getCurrentVaultKey().then(function(cur) {
456
+ return (cur ? cur.unlock().then(function() {
457
+ return k.rekeyFrom(cur);
458
+ }).catch(function(e) {
459
+ if (!lost) throw e;
460
+ }) : Promise.resolve(null)).then(function() {
461
+ var r;
462
+ if (cur && lost) r = cur.setField('type','lost').then(function() {
338
463
  k.user = undefined;
339
464
  return me.setField('vaultKeys',[cur,k]).then(function() {
340
465
  return me;
341
466
  });
342
467
  });
343
- else r = k.rekeyFrom(cur);
344
- } else r = k;
345
- me.currentVaultKey = me.activeVaultKeys = undefined;
346
- if (!cur || !lost) me.vaultKeys = undefined;
347
- return r;
468
+ else r = k;
469
+ me.currentVaultKey = me.activeVaultKeys = me.shadowVaultKey = undefined;
470
+ if (!cur || !lost) me.vaultKeys = undefined;
471
+ return r;
472
+ });
348
473
  }).then(function(r) {
349
- return r.post(undefined,undefined,options);
474
+ return self.elevateAuth(options).then(function(optns) {
475
+ return r.post(undefined, undefined, optns);
476
+ });
477
+ }).catch(function(e) {
478
+ self.reset();
479
+ throw e;
350
480
  }).then(function(post) {
351
481
  return self.reset(post);
352
482
  }).then(function() {
@@ -370,21 +500,21 @@ libVES.prototype = {
370
500
  return rs;
371
501
  });
372
502
  },
373
- getSecondaryKey: function(ext,force) {
503
+ getSecondaryKey: function(ext, force) {
374
504
  var self = this;
375
505
  return this.prepareExternals(Promise.resolve(ext).then(function(e) {
376
- if (e.domain && !e.externalId) return libVES.getModule(libVES.Domain,e.domain).then(function(mod) {
506
+ if (e.domain && !e.externalId) return libVES.getModule(libVES.Domain, e.domain).then(function(mod) {
377
507
  return self.me().then(function(me) {
378
- return mod.userToVaultRef(me,self);
508
+ return mod.userToVaultRef(me, self);
379
509
  });
380
510
  });
381
511
  return e;
382
512
  })).then(function(ext) {
383
- var vkey = new libVES.VaultKey({externals: ext},self);
513
+ var vkey = new libVES.VaultKey({externals: ext}, self);
384
514
  return vkey.getId().then(function(id) {
385
515
  return vkey;
386
516
  }).catch(function(e) {
387
- if (!force) throw e;
517
+ if (!force || e.code != 'NotFound') throw e;
388
518
  return self.setSecondaryKey(ext);
389
519
  });
390
520
  });
@@ -395,14 +525,25 @@ libVES.prototype = {
395
525
  if (!veskey) veskey = self.generateVESkey();
396
526
  return self.me().then(function(me) {
397
527
  return (new libVES.VaultKey({type: 'secondary', algo: self.keyAlgo, user: me, externals: ext},self)).generate(veskey,optns).then(function(k) {
528
+ return self.getSecondaryKey(ext).then(function(k2) {
529
+ return k.rekeyFrom(k2);
530
+ }).catch(function(e) {
531
+ delete(k.vaultEntries);
532
+ return k;
533
+ });
534
+ }).then(function(k) {
398
535
  var vi = new libVES.VaultItem({type: "password"},self);
399
536
  k.setField('vaultItems',[vi]);
400
537
  return Promise.resolve(veskey).then(function(v) {
401
538
  if (!v) throw new libVES.Error('InvalidValue','VESkey cannot be empty');
402
539
  return vi.shareWith([me],v,false).then(function() {
403
- return k.post().then(function(post) {
404
- self.reset(post);
405
- return k;
540
+ return self.elevateAuth(optns);
541
+ }).then(function(optns) {
542
+ return k.post(undefined, undefined, optns).then(function(post) {
543
+ return k.setField('id', post.id, false).then(function() {
544
+ k.fieldUpdate = {id: true};
545
+ return k;
546
+ });
406
547
  });
407
548
  });
408
549
  });
@@ -411,14 +552,26 @@ libVES.prototype = {
411
552
  });
412
553
  },
413
554
  setShadow: function(usrs,optns) {
414
- if (!optns || !optns.n) return Promise.reject(new libVES.Error('InvalidValue','optns.n must be an integer'));
415
555
  var self = this;
556
+ if (!(usrs instanceof Array)) return Promise.reject(new libVES.Error('InvalidValue', 'usrs must be an array'));
557
+ if (!usrs.length) {
558
+ return self.getShadowKey().then(function(sh) {
559
+ return (sh ? sh.getId().then(function(id) {
560
+ return self.elevateAuth(optns).then(function(optns) {
561
+ return self.post('vaultKeys/' + id, {'$op': 'delete'}, undefined, optns). then(function() {
562
+ return null;
563
+ });
564
+ });
565
+ }) : null);
566
+ });
567
+ }
568
+ if (!optns || !optns.n) return Promise.reject(new libVES.Error('InvalidValue','optns.n must be an integer'));
416
569
  var rkey = new Uint8Array(32);
417
570
  window.crypto.getRandomValues(rkey);
418
571
  var algo = optns.v ? libVES.Scramble.algo[optns.v] : libVES.Scramble.RDX;
419
572
  if (!algo) return Promise.reject(new libVES.Error('InvalidValue','Unknown scramble algorithm: ' + optns.v));
420
573
  var s = new algo(optns.n);
421
- return s.explode(rkey,usrs.length).then(function(tkns) {
574
+ return s.explode(rkey,usrs.length,optns).then(function(tkns) {
422
575
  return self.me().then(function(me) {
423
576
  me.activeVaultKeys = undefined;
424
577
  return me.setField('shadowVaultKey',new libVES.VaultKey({type: 'shadow', user: me, algo: self.keyAlgo},self).generate(rkey,optns),false).then(function(k) {
@@ -434,14 +587,16 @@ libVES.prototype = {
434
587
  delete(libVES.Object._refs);
435
588
  return vis;
436
589
  }));
437
- return k.post();
590
+ return self.elevateAuth(optns).then(function(optns) {
591
+ return k.post(undefined, undefined, optns);
592
+ });
438
593
  });
439
594
  });
440
595
  }).catch(function(e) {
441
596
  me.shadowVaultKey = undefined;
442
597
  throw e;
443
598
  }).then(function() {
444
- me.currentVaultKey = me.activeVaultKeys = undefined;
599
+ me.currentVaultKey = me.shadowVaultKey = me.activeVaultKeys = undefined;
445
600
  return me.getShadowVaultKey();
446
601
  });
447
602
  });
@@ -467,30 +622,11 @@ libVES.prototype = {
467
622
  putValue: function(fileRef,value,shareWith) {
468
623
  var self = this;
469
624
  return this.getFileItem(fileRef).then(function(vaultItem) {
470
- return vaultItem.setField('type',libVES.VaultItem.Type._detect(value)).then(function(type) {
471
- return vaultItem.getFile().then(function(file) {
472
- return file.getExternals().then(function(exts) {
473
- return exts[0].getDomain().then(function(domain) {
474
- var m = libVES.getModule(libVES.Domain,domain);
475
- file.setField('path',m.then(function(mod) {
476
- return mod.defaultFilePath(file,exts[0]);
477
- }).catch(function() {
478
- return '';
479
- }));
480
- file.setField('name',m.then(function(mod) {
481
- return mod.defaultFileName(file,exts[0]);
482
- }).catch(function() {
483
- return null;
484
- }));
485
- return type;
486
- });
487
- });
488
- });
489
- }).then(function() {
625
+ return vaultItem.setField('type',libVES.VaultItem.Type._detect(value)).then(function() {
490
626
  return Promise.resolve(shareWith || self.getFileItem(fileRef).then(function(vi) {
491
- return vi.getShareList();
492
- }).catch(function(e) {
493
- return self.usersToKeys([{domain: fileRef.domain}]);
627
+ return vi.getShareList();
628
+ }).catch(function(e) {
629
+ return self.usersToKeys([{domain: (fileRef.domain || VES.domain)}]);
494
630
  })).then(function(shareWith) {
495
631
  return vaultItem.shareWith(shareWith,value);
496
632
  });
@@ -502,6 +638,16 @@ libVES.prototype = {
502
638
  return vaultItem.shareWith(shareWith);
503
639
  });
504
640
  },
641
+ fileExists: function(fileRef) {
642
+ return this.getFileItem(fileRef).then(function(vaultItem) {
643
+ return vaultItem.getId().then(function(id) {
644
+ return true;
645
+ }).catch(function(e) {
646
+ if (e.code == 'NotFound') return false;
647
+ throw e;
648
+ });
649
+ });
650
+ },
505
651
  deleteFile: function(fileRef) {
506
652
  return this.getFile(fileRef).then(function(file) {
507
653
  return file.delete();
@@ -539,8 +685,8 @@ libVES.prototype = {
539
685
  hashText: function(text,cls) {
540
686
  if (cls) cls = cls.split('.')[0];
541
687
  else cls = this.defaultHash;
542
- return libVES.getModule(libVES.Util,['Hash',cls]).then(function(fn) {
543
- return fn(libVES.Util.StringToByteArray(text)).then(function(buf) {
688
+ return libVES.getModule(libVES.Util,['Hash',cls]).then(function(mod) {
689
+ return mod.hash(libVES.Util.StringToByteArray(text)).then(function(buf) {
544
690
  return cls + '.' + libVES.Util.ByteArrayToB64W(buf);
545
691
  });
546
692
  });
@@ -579,8 +725,6 @@ libVES.prototype = {
579
725
  }).then(function() {
580
726
  chain = chain.then(function(ct) {
581
727
  return vaultKey.rekey().then(function() {
582
- if (self.onRekey) return self.onRekey(vaultKey);
583
- }).then(function() {
584
728
  return ct + 1;
585
729
  });
586
730
  }).catch(function(e) {console.log(e);});;
@@ -597,35 +741,104 @@ libVES.prototype = {
597
741
  var self = this;
598
742
  return self.me().then(function(me) {
599
743
  return me.getVaultKeys().then(function(vaultKeys) {
600
- return Promise.all(vaultKeys.map(function(e,i) {
601
- return e.getType();
602
- })).then(function(types) {
603
- var rs = [];
604
- for (var i = 0; i < types.length; i++) switch (types[i]) {
605
- case 'recovery': case 'shadow':
606
- rs.push(vaultKeys[i].getRecovery());
607
- }
608
- return Promise.all(rs);
609
- });
744
+ return Promise.all(vaultKeys.map(function(e,i) {
745
+ return e.getType();
746
+ })).then(function(types) {
747
+ var rs = [];
748
+ for (var i = 0; i < types.length; i++) switch (types[i]) {
749
+ case 'recovery': case 'shadow':
750
+ rs.push(vaultKeys[i].getRecovery());
751
+ }
752
+ return Promise.all(rs);
610
753
  });
754
+ });
611
755
  });
612
756
  },
613
757
  getFriendsRecoveries: function() {
614
758
  var self = this;
615
759
  return self.me().then(function(me) {
616
- return me.getFriendsVaultKeys().then(function(vaultKeys) {
617
- return Promise.all(vaultKeys.map(function(e,i) {
618
- return e.getType();
760
+ return me.getFriendsKeyItems().then(function(vaultItems) {
761
+ var vaultKeys = [];
762
+ return Promise.all(vaultItems.map(function(e,i) {
763
+ return e.getVaultKey().then(function(vk) {
764
+ vaultKeys[i] = vk;
765
+ if (vk) return vk.getType();
766
+ });
619
767
  })).then(function(types) {
620
768
  var rs = [];
621
769
  for (var i = 0; i < types.length; i++) switch (types[i]) {
622
770
  case 'recovery': case 'shadow':
623
- rs.push(vaultKeys[i].getRecovery());
771
+ rs.push(vaultKeys[i].getRecovery([vaultItems[i]]));
624
772
  }
625
773
  return Promise.all(rs);
626
774
  });
627
775
  });
628
776
  });
777
+ },
778
+ getUnlockableKeys: function() {
779
+ var self = this;
780
+ return self.getVaultKey().then(function(vk) {
781
+ return vk.getField('unlockableVaultKeys').then(function(vks) {
782
+ var rs = {};
783
+ return Promise.all(vks.map(function(e, i) {
784
+ return e.getId().then(function(id) {
785
+ rs[id] = e;
786
+ });
787
+ })).then(function() {
788
+ return rs;
789
+ });
790
+ });
791
+ });
792
+ },
793
+ getAttn: function() {
794
+ var self = this;
795
+ var rs = {};
796
+ return self.get('attn').then(function(attn) {
797
+ return attn ? Promise.all([(attn.vaultKeys ? Promise.all(attn.vaultKeys.map(function(vk, i) {
798
+ return new libVES.VaultKey(vk, self);
799
+ })).then(function(vks) {
800
+ rs.vaultKeys = vks;
801
+ }) : null)]) : null;
802
+ }).then(function() {
803
+ return rs;
804
+ });
805
+ },
806
+ handleAttn: function(attn) {
807
+ var self = this;
808
+ return (attn ? Promise.resolve(attn) : self.getAttn()).then(function(attn) {
809
+ if (attn) return Promise.all([(attn.vaultKeys ? self.attnVaultKeys(attn.vaultKeys) : null)]);
810
+ });
811
+ },
812
+ attnVaultKeys: function(vkeys) {
813
+ var self = this;
814
+ return Promise.all(vkeys.map(function(vkey, i) {
815
+ console.log('attnVaultKeys:', vkey);
816
+ return vkey.getType().then(function(type) {
817
+ switch (type) {
818
+ case 'temp':
819
+ case 'lost':
820
+ return vkey.getUser().then(function(user) {
821
+ return self.me().then(function(me) {
822
+ return Promise.all([user.getId(), me.getId()]).then(function(ids) {
823
+ if (ids[0] == ids[1]) return type == 'temp' ? vkey.rekey()
824
+ : self.elevateAuth({authVaultKey: vkey}).then(function(optns) {
825
+ return vkey.rekey(optns);
826
+ });
827
+ else return vkey.getVaultItems().then(function(vis) {
828
+ return Promise.all(vis.map(function(vi, i) {
829
+ return vi.reshareWith([user]);
830
+ }));
831
+ });
832
+ });
833
+ });
834
+ });
835
+ case 'recovery':
836
+ return vkey.getRecovery().then(function(rcv) {
837
+ return rcv.recover();
838
+ });
839
+ }
840
+ });
841
+ }));
629
842
  }
630
843
  };
631
844
 
@@ -756,7 +969,7 @@ libVES.Util = {
756
969
  }
757
970
  },
758
971
  ASN1: {
759
- decode: function(der) {
972
+ decode: function(der,fStruct) {
760
973
  var p = 0;
761
974
  var data = function() {
762
975
  var l = der[p++];
@@ -771,10 +984,14 @@ libVES.Util = {
771
984
  };
772
985
  var rs = [];
773
986
  for (; p < der.length;) {
987
+ if (!fStruct && p) {
988
+ rs.push(der.slice(p));
989
+ break;
990
+ }
774
991
  var tag = der[p++];
775
992
  switch (tag) {
776
993
  case 48:
777
- rs.push(libVES.Util.ASN1.decode(data()));
994
+ rs.push(libVES.Util.ASN1.decode(data(),true));
778
995
  break;
779
996
  case 6:
780
997
  rs.push(new libVES.Util.OID(data()));
@@ -796,7 +1013,7 @@ libVES.Util = {
796
1013
  }
797
1014
  return rs;
798
1015
  },
799
- encode: function(data) {
1016
+ encode: function(data,fStruct) {
800
1017
  var i2a = function(v) {
801
1018
  if (v < 0) throw new libVES.Error('Internal',"Negative value for ASN.1 integer!");
802
1019
  var rs = [];
@@ -827,10 +1044,10 @@ libVES.Util = {
827
1044
  return rs;
828
1045
  };
829
1046
  var d;
830
- for (var i = 0; i < data.length; i++) switch (typeof(d = data[i])) {
1047
+ for (var i = 0; i < data.length; i++) if (fStruct || !i) switch (typeof(d = data[i])) {
831
1048
  case 'object':
832
1049
  if (d == null) buf(5,new Uint8Array(0));
833
- else if (d instanceof Array) buf(48,libVES.Util.ASN1.encode(d));
1050
+ else if (d instanceof Array) buf(48,libVES.Util.ASN1.encode(d,true));
834
1051
  else if (d instanceof libVES.Util.OID) buf(6,d.getBuffer());
835
1052
  else if (d instanceof Uint8Array || d instanceof ArrayBuffer) buf((d.ASN1type || 4),d);
836
1053
  else throw new libVES.Error('Internal',"ASN.1 encode - Unknown type");
@@ -839,7 +1056,7 @@ libVES.Util = {
839
1056
  buf(2,i2a(d));
840
1057
  break;
841
1058
  default: throw new libVES.Error('Internal',"ASN.1 encode - Unknown type");
842
- }
1059
+ } else bufs.push(d);
843
1060
  var l = 0;
844
1061
  for (var i = 0; i < bufs.length; i++) l += bufs[i].length;
845
1062
  var der = new Uint8Array(l);
@@ -848,7 +1065,7 @@ libVES.Util = {
848
1065
  return der;
849
1066
  },
850
1067
  import: function(der,optns) {
851
- var k = libVES.Util.ASN1.decode(der)[0];
1068
+ var k = optns && optns.decoded || libVES.Util.ASN1.decode(der)[0];
852
1069
  if (!k) throw new libVES.Error('Internal','Empty ASN.1 package?');
853
1070
  var i = 0;
854
1071
  if (typeof(k[i]) == 'number') (optns || (optns = {})).version = k[i++];
@@ -894,6 +1111,11 @@ libVES.Util = {
894
1111
  return crypto.subtle.importKey('pkcs8',der,{name:'RSA-OAEP', hash:'SHA-1'},true,['decrypt']);
895
1112
  });
896
1113
  });
1114
+ },
1115
+ encode: function(key,optns) {
1116
+ return crypto.subtle.exportKey('spki',key).then(function(der) {
1117
+ return libVES.Util.PEM.encode(der,'PUBLIC KEY');
1118
+ });
897
1119
  }
898
1120
  },
899
1121
  PKCS5: {
@@ -908,7 +1130,7 @@ libVES.Util = {
908
1130
  else return fp;
909
1131
  })(args[i],f);
910
1132
  return f('import',optns).then(function(der) {
911
- return crypto.subtle.importKey('pkcs8',der,{name:'RSA-OAEP', hash:'SHA-1'},true,['decrypt']);
1133
+ return libVES.Util.ASN1.import(new Uint8Array(der));
912
1134
  }).catch(function(e) {
913
1135
  throw new libVES.Error('InvalidKey',"Cannot import the private key (Invalid VESkey?)");
914
1136
  });
@@ -930,10 +1152,108 @@ libVES.Util = {
930
1152
  });
931
1153
  }
932
1154
  },
1155
+ PKCS8: {
1156
+ encode: function(key,optns) {
1157
+ var ops = {};
1158
+ for (var k in optns) ops[k] = optns[k];
1159
+ if (!ops.members) ops.members = [
1160
+ libVES.getModule(libVES.Util,'PBKDF2'),
1161
+ libVES.getModule(libVES.Cipher,'AES256CBC')
1162
+ ];
1163
+ return Promise.all(ops.members).then(function(ms) {
1164
+ ops.members = ms;
1165
+ return crypto.subtle.exportKey('pkcs8',key).catch(function(e) {
1166
+ console.log('PKCS8 failed, trying JWK...');
1167
+ return crypto.subtle.exportKey('jwk', key).then(function(jwk) {
1168
+ return libVES.Util.PKCS8.fromJWK(jwk);
1169
+ });
1170
+ }).then(function(der) {
1171
+ ops.content = der;
1172
+ var rec = [];
1173
+ if (ops.password) return libVES.Util.PKCS5.export(function(call,optns) {
1174
+ rec[1] = optns.content;
1175
+ return Promise.resolve();
1176
+ },ops).then(function(data) {
1177
+ rec[0] = data;
1178
+ return libVES.Util.PEM.encode(libVES.Util.ASN1.encode([rec]),'ENCRYPTED PRIVATE KEY');
1179
+ });
1180
+ else if (ops.opentext) return libVES.Util.PEM.encode(der,'PRIVATE KEY');
1181
+ else throw new libVES.Error('Internal','No password for key export (opentext=true to export without password?)');
1182
+ });
1183
+ });
1184
+ },
1185
+ fromJWK: function(jwk) {
1186
+ if (jwk.kty != 'EC') throw new libVES.Error('Internal', 'kty=="EC" expected (workaround for Firefox)');
1187
+ var pubx = libVES.Util.B64ToByteArray(jwk.x);
1188
+ var puby = libVES.Util.B64ToByteArray(jwk.y);
1189
+ var pub = new Uint8Array(pubx.byteLength + puby.byteLength + 2);
1190
+ pub[0] = 0;
1191
+ pub[1] = 4;
1192
+ pub.set(new Uint8Array(pubx), 2);
1193
+ pub.set(new Uint8Array(puby), pubx.byteLength + 2);
1194
+ pub.ASN1type = 3;
1195
+ pub = libVES.Util.ASN1.encode([pub]);
1196
+ pub.ASN1type = 0xa1;
1197
+ return libVES.Util.ASN1.encode([[
1198
+ 0,
1199
+ [
1200
+ new libVES.Util.OID('1.2.840.10045.2.1'),
1201
+ new libVES.Util.OID((function(crvs) {
1202
+ for (var k in crvs) if (crvs[k] == jwk.crv) return k;
1203
+ throw new libVES.Error('Internal', 'Unknown named curve: ' + jwk.crv);
1204
+ })(libVES.Util.EC.namedCurves))
1205
+ ],
1206
+ libVES.Util.ASN1.encode([[
1207
+ 1,
1208
+ libVES.Util.B64ToByteArray(jwk.d),
1209
+ pub
1210
+ ]])
1211
+ ]]).buffer;
1212
+ },
1213
+ toJWK: function(key) {
1214
+ var der = libVES.Util.ASN1.decode(key)[0];
1215
+ var idx = 0;
1216
+ if (!(der[0] instanceof Array)) idx++;
1217
+ switch (String(der[idx][0])) {
1218
+ case '1.2.840.10045.2.1':
1219
+ case '1.3.132.1.12':
1220
+ case '1.3.132.112': // Firefox on Mac - apparently a misspelling of the former
1221
+ break;
1222
+ default:
1223
+ console.log(der);
1224
+ throw new libVES.Error('Internal', 'EC PKCS8 expected');
1225
+ }
1226
+ var der2 = idx ? libVES.Util.ASN1.decode(der[idx + 1])[0] : null;
1227
+ var pub = idx ? (der2[2] ? libVES.Util.ASN1.decode(der2[2])[0] : null) : der[1];
1228
+ var publ = 0;
1229
+ if (pub && pub.byteLength > 2 && pub[1] == 4) publ = pub.byteLength / 2 - 1;
1230
+ return {
1231
+ crv: libVES.Util.EC.namedCurves[der[idx][1]],
1232
+ d: (idx ? libVES.Util.ByteArrayToB64W(der2[1]) : undefined),
1233
+ ext: true,
1234
+ key_ops: (idx ? ['deriveKey', 'deriveBits'] : []),
1235
+ kty: 'EC',
1236
+ x: (publ ? libVES.Util.ByteArrayToB64W(pub.slice(2, publ + 2)) : ''),
1237
+ y: (publ ? libVES.Util.ByteArrayToB64W(pub.slice(publ + 2, 2 * publ + 2)) : '')
1238
+ };
1239
+ }
1240
+ },
933
1241
  PBKDF2: {
934
- deriveKey: function(args,pwd,algo) {
935
- return crypto.subtle.importKey('raw',libVES.Util.StringToByteArray(pwd),'PBKDF2',false,['deriveKey']).then(function(k) {
936
- return crypto.subtle.deriveKey({name:'PBKDF2', salt:args[0], iterations:args[1], hash: 'SHA-1'},k,algo,true,['encrypt','decrypt']);
1242
+ deriveKey: function(args, pwd, algo) {
1243
+ return crypto.subtle.importKey('raw', libVES.Util.StringToByteArray(pwd), 'PBKDF2', false, ['deriveKey']).then(function(k) {
1244
+ var keyargs = {name:'PBKDF2', salt:args[0], iterations:args[1]};
1245
+ var ka;
1246
+ if (args[2] && (args[2][0] instanceof libVES.Util.OID)) ka = args[2][0].object().then(function(obj) {
1247
+ obj.setArgs(keyargs, args[2][1]);
1248
+ return keyargs;
1249
+ });
1250
+ else {
1251
+ keyargs.hash = 'SHA-1';
1252
+ ka = Promise.resolve(keyargs);
1253
+ }
1254
+ return ka.then(function(keyargs) {
1255
+ return crypto.subtle.deriveKey(keyargs, k, algo, true, ['encrypt', 'decrypt']);
1256
+ });
937
1257
  });
938
1258
  },
939
1259
  import: function(args,chain,optns) {
@@ -960,9 +1280,61 @@ libVES.Util = {
960
1280
  });
961
1281
  }
962
1282
  },
1283
+ EC: {
1284
+ import: function(args,chain,optns) {
1285
+ return chain('container',optns).then(function(der) {
1286
+ var a = {name:'ECDH',namedCurve:libVES.Util.EC.namedCurves[String(args)]};
1287
+ return crypto.subtle.importKey('spki',der,a,true,[]).catch(function() {;
1288
+ return crypto.subtle.importKey('pkcs8',der,a,true,['deriveKey','deriveBits']).catch(function(e) {
1289
+ console.log('PKCS8 failed, trying JWK...');
1290
+ var jwk = libVES.Util.PKCS8.toJWK(der);
1291
+ return crypto.subtle.importKey('jwk', jwk, a, true, jwk.key_ops);
1292
+ });
1293
+ });
1294
+ });
1295
+ },
1296
+ namedCurves: {
1297
+ "1.3.132.0.10": "P-256",
1298
+ "1.3.132.0.34": "P-384",
1299
+ "1.3.132.0.35": "P-521"
1300
+ }
1301
+ },
963
1302
  Hash: {
964
- SHA256: function(buf) {
965
- return crypto.subtle.digest('SHA-256',buf);
1303
+ SHA1: {
1304
+ setArgs: function(args, optns) {
1305
+ args.hash = 'SHA-1';
1306
+ return args;
1307
+ },
1308
+ hash: function(buf) {
1309
+ return crypto.subtle.digest('SHA-1',buf);
1310
+ }
1311
+ },
1312
+ SHA256: {
1313
+ setArgs: function(args, optns) {
1314
+ args.hash = 'SHA-256';
1315
+ return args;
1316
+ },
1317
+ hash: function(buf) {
1318
+ return crypto.subtle.digest('SHA-256',buf);
1319
+ }
1320
+ },
1321
+ SHA384: {
1322
+ setArgs: function(args, optns) {
1323
+ args.hash = 'SHA-384';
1324
+ return args;
1325
+ },
1326
+ hash: function(buf) {
1327
+ return crypto.subtle.digest('SHA-384',buf);
1328
+ }
1329
+ },
1330
+ SHA512: {
1331
+ setArgs: function(args, optns) {
1332
+ args.hash = 'SHA-512';
1333
+ return args;
1334
+ },
1335
+ hash: function(buf) {
1336
+ return crypto.subtle.digest('SHA-512',buf);
1337
+ }
966
1338
  }
967
1339
  }
968
1340
  };
@@ -993,10 +1365,15 @@ libVES.Util.OID.prototype = {
993
1365
  }
994
1366
  }
995
1367
 
996
- libVES.Util.OID['1.2.840.113549.1.5.13'] = libVES.getModuleFunc(libVES,['Util','PKCS5']);
997
- libVES.Util.OID['1.2.840.113549.1.5.12'] = libVES.getModuleFunc(libVES,['Util','PBKDF2']);
998
- libVES.Util.OID['2.16.840.1.101.3.4.1.42'] = libVES.getModuleFunc(libVES,['Cipher','AES256CBC']);
999
- libVES.Util.OID['1.2.840.113549.1.1.1'] = libVES.getModuleFunc(libVES,['Util','PKCS1']);
1368
+ libVES.Util.OID['1.2.840.113549.1.5.13'] = libVES.getModuleFunc(libVES, ['Util', 'PKCS5']);
1369
+ libVES.Util.OID['1.2.840.113549.1.5.12'] = libVES.getModuleFunc(libVES, ['Util', 'PBKDF2']);
1370
+ libVES.Util.OID['2.16.840.1.101.3.4.1.42'] = libVES.getModuleFunc(libVES, ['Cipher', 'AES256CBC']);
1371
+ libVES.Util.OID['1.2.840.113549.1.1.1'] = libVES.getModuleFunc(libVES, ['Util', 'PKCS1']);
1372
+ libVES.Util.OID['1.2.840.10045.2.1'] = libVES.Util.OID['1.3.132.1.12'] = libVES.Util.OID['1.3.132.112'] = libVES.getModuleFunc(libVES, ['Util', 'EC']);
1373
+ libVES.Util.OID['1.2.840.113549.2.7'] = libVES.getModuleFunc(libVES, ['Util', 'Hash', 'SHA1']);
1374
+ libVES.Util.OID['1.2.840.113549.2.9'] = libVES.getModuleFunc(libVES, ['Util', 'Hash', 'SHA256']);
1375
+ libVES.Util.OID['1.2.840.113549.2.10'] = libVES.getModuleFunc(libVES, ['Util', 'Hash', 'SHA384']);
1376
+ libVES.Util.OID['1.2.840.113549.2.11'] = libVES.getModuleFunc(libVES, ['Util', 'Hash', 'SHA512']);
1000
1377
 
1001
1378
  libVES.Math = {
1002
1379
  pad: function(a,n) {
@@ -1374,10 +1751,6 @@ libVES.External = function(data,VES,refs) {
1374
1751
  this.init(data,VES,refs);
1375
1752
  };
1376
1753
 
1377
- libVES.Lockbox = function(data,VES,refs) {
1378
- this.init(data,VES,refs);
1379
- };
1380
-
1381
1754
  libVES.File = function(data,VES,refs) {
1382
1755
  this.init(data,VES,refs);
1383
1756
  };
@@ -1386,7 +1759,7 @@ libVES.User.prototype = new libVES.Object({
1386
1759
  apiUri: 'users',
1387
1760
  fieldList: {id: true, email: true, type: true, firstName: true, lastName: true},
1388
1761
  fieldExtra: {vaultKeys: true, activeVaultKeys: true, currentVaultKey: true},
1389
- fieldClass: {vaultKeys: libVES.VaultKey, activeVaultKeys: libVES.VaultKey, currentVaultKey: libVES.VaultKey, shadowVaultKey: libVES.VaultKey, friendsVaultKeys: libVES.VaultKey},
1762
+ fieldClass: {vaultKeys: libVES.VaultKey, activeVaultKeys: libVES.VaultKey, currentVaultKey: libVES.VaultKey, shadowVaultKey: libVES.VaultKey, friendsKeyItems: libVES.VaultItem},
1390
1763
  getEmail: function() {
1391
1764
  return this.getField('email');
1392
1765
  },
@@ -1416,11 +1789,10 @@ libVES.User.prototype = new libVES.Object({
1416
1789
  });
1417
1790
  return this.getField('activeVaultKeys');
1418
1791
  },
1419
- getFriendsVaultKeys: function() {
1420
- return this.getField('friendsVaultKeys');
1792
+ getFriendsKeyItems: function() {
1793
+ return this.getField('friendsKeyItems');
1421
1794
  },
1422
1795
  getCurrentVaultKey: function() {
1423
-
1424
1796
  return this.getField('currentVaultKey');
1425
1797
  },
1426
1798
  getShadowVaultKey: function() {
@@ -1452,9 +1824,9 @@ libVES.User.prototype = new libVES.Object({
1452
1824
  libVES.VaultKey.prototype = new libVES.Object({
1453
1825
  apiUri: 'vaultKeys',
1454
1826
  fieldList: {id: true, algo: true, type: true, publicKey: true, privateKey: true},
1455
- fieldClass: {user: libVES.User, vaultItems: libVES.VaultItem, externals: libVES.External, sharedKeys: libVES.VaultKey},
1827
+ fieldClass: {user: libVES.User, vaultItems: libVES.VaultItem, externals: libVES.External, unlockableVaultKeys: libVES.VaultKey, creator: libVES.User},
1456
1828
  fieldExtra: {user: true, vaultItems: true},
1457
- fieldSets: [{vaultEntries: {id: true, encData: true, vaultItem: {id: true}}},{type: true, algo: true, publicKey: true}],
1829
+ fieldSets: [{vaultEntries: {id: true, encData: true, vaultItem: {id: true}}},{type: true, algo: true, publicKey: true}, {unlockableVaultKeys: {id: true, type: true}}],
1458
1830
  getAlgo: function() {
1459
1831
  return this.getField('algo');
1460
1832
  },
@@ -1478,18 +1850,12 @@ libVES.VaultKey.prototype = new libVES.Object({
1478
1850
  getVaultItems: function() {
1479
1851
  return this.getField('vaultItems');
1480
1852
  },
1481
- getSharedKeys: function() {
1482
- return this.getField('sharedKeys');
1483
- },
1484
1853
  getExternals: function() {
1485
1854
  return this.getField('externals');
1486
1855
  },
1487
1856
  getUser: function() {
1488
1857
  return this.getField('user');
1489
1858
  },
1490
- getVaultItems: function() {
1491
- return this.getField('vaultItems');
1492
- },
1493
1859
  resolveVESkey: function(veskey) {
1494
1860
  if (veskey) return Promise.resolve(veskey);
1495
1861
  var self = this;
@@ -1497,6 +1863,7 @@ libVES.VaultKey.prototype = new libVES.Object({
1497
1863
  switch (t) {
1498
1864
  case 'secondary':
1499
1865
  case 'temp':
1866
+ case 'lost':
1500
1867
  return self.getVaultItems().then(function(vis) {
1501
1868
  var f = function(vis) {
1502
1869
  if (!vis.length) throw new libVES.Error('InvalidKey','Cannot unlock the secondary key');
@@ -1564,10 +1931,12 @@ libVES.VaultKey.prototype = new libVES.Object({
1564
1931
  self.setField('algo',wc.engine.tag);
1565
1932
  if (!wc.privateKey) throw new libVES.Error('InvalidValue','Private key expected');
1566
1933
  return wc;
1567
- }) : this.engine().then(function(e) {
1568
- return e.generate(optns).then(function(ks) {
1569
- ks.engine = e;
1570
- return ks;
1934
+ }) : (optns && optns.algo ? self.setField('algo',optns.algo) : Promise.resolve()).then(function() {
1935
+ return self.engine().then(function(e) {
1936
+ return e.generate(optns).then(function(ks) {
1937
+ ks.engine = e;
1938
+ return ks;
1939
+ });
1571
1940
  });
1572
1941
  });
1573
1942
  return Promise.resolve(veskey).then(function(v) {
@@ -1611,16 +1980,14 @@ libVES.VaultKey.prototype = new libVES.Object({
1611
1980
  var self = this;
1612
1981
  var old_vis = {};
1613
1982
  return (self.vaultEntries ? self.vaultEntries.then(function(old_ves) {
1614
- return old_ves.map(function(ve,i) {
1983
+ if (old_ves) return old_ves.map(function(ve,i) {
1615
1984
  old_vis[ve.vaultItem.id] = true;
1616
1985
  });
1617
1986
  }) : Promise.resolve(null)).then(function() {
1618
1987
  return self.setField('vaultEntries',key.unlock(veskey).then(function() {
1619
1988
  return key.getVaultEntries().then(function(ves) {
1620
1989
  return Promise.all(ves.map(function(ve) {
1621
- return old_vis[ve.vaultItem.id] ? Promise.resolve({
1622
- vaultItem: {id: ve.vaultItem.id}
1623
- }) : key.decrypt(ve.encData).then(function(ptxt) {
1990
+ return old_vis[ve.vaultItem.id] ? null : key.decrypt(ve.encData).then(function(ptxt) {
1624
1991
  return self.encrypt(ptxt).then(function(ctxt) {
1625
1992
  return {
1626
1993
  vaultItem: {id: ve.vaultItem.id},
@@ -1633,6 +2000,8 @@ libVES.VaultKey.prototype = new libVES.Object({
1633
2000
  "$op": "ignore"
1634
2001
  };
1635
2002
  });
2003
+ }).filter(function(e, i) {
2004
+ return !!e;
1636
2005
  }));
1637
2006
  });
1638
2007
  }));
@@ -1640,7 +2009,7 @@ libVES.VaultKey.prototype = new libVES.Object({
1640
2009
  return self;
1641
2010
  });
1642
2011
  },
1643
- rekey: function() {
2012
+ rekey: function(optns) {
1644
2013
  var self = this;
1645
2014
  return self.getUser().then(function(user) {
1646
2015
  return self.getExternals().then(function(exts) {
@@ -1661,20 +2030,24 @@ libVES.VaultKey.prototype = new libVES.Object({
1661
2030
  }));
1662
2031
  }).then(function(keys) {
1663
2032
  return user.setField('vaultKeys',keys).then(function() {
1664
- return user.post(null,{vaultEntries: true},{refs: {'#/': user}}).then(function(data) {
2033
+ if (!optns) optns = {};
2034
+ optns.refs = {'#/': user};
2035
+ return user.post(null,{vaultEntries: true}, optns).then(function(data) {
1665
2036
  self.setFields(data,false);
1666
- return self;
2037
+ return self.VES.onRekey ? self.VES.onRekey(self).catch(function() {}).then(function() {
2038
+ return self;
2039
+ }) : self;
1667
2040
  });
1668
2041
  });
1669
2042
  });
1670
2043
  });
1671
2044
  },
1672
- getRecovery: function() {
2045
+ getRecovery: function(myItems) {
1673
2046
  var self = this;
1674
2047
  return self.getType().then(function(t) {
1675
2048
  switch (t) {
1676
2049
  case 'shadow': case 'recovery':
1677
- return new libVES.Recovery(self);
2050
+ return new libVES.Recovery(self, myItems);
1678
2051
  default: throw new libVES.Error('InvalidValue','Recovery is not applicable for VaultKey type ' + t);
1679
2052
  }
1680
2053
  });
@@ -1682,18 +2055,21 @@ libVES.VaultKey.prototype = new libVES.Object({
1682
2055
  getSessionToken: function() {
1683
2056
  var self = this;
1684
2057
  return this.getField('encSessionToken').then(function(tk) {
2058
+ if (!tk) return null;
1685
2059
  return self.decrypt(tk).then(function(b) {
1686
2060
  return libVES.Util.ByteArrayToString(b);
1687
2061
  });
1688
2062
  });
1689
2063
  },
1690
- reshareVESkey: function(veskey) {
2064
+ reshareVESkey: function(veskey, optns) {
1691
2065
  var self = this;
1692
2066
  return self.getVaultItems().then(function(vaultItems) {
1693
2067
  return self.getUser().then(function(user) {
1694
2068
  return Promise.all(vaultItems.map(function(vaultItem,i) {
1695
2069
  return vaultItem.getType().then(function(t) {
1696
- if (t == 'password') return vaultItem.reshareWith([user],veskey);
2070
+ if (t == 'password') return vaultItem.reshareWith([user], veskey, false).then(function(vi) {
2071
+ return vi.post(undefined, undefined, optns);
2072
+ });
1697
2073
  });
1698
2074
  }));
1699
2075
  });
@@ -1708,29 +2084,33 @@ libVES.VaultItem.prototype = new libVES.Object({
1708
2084
  apiUri: 'vaultItems',
1709
2085
  fieldList: {id: true},
1710
2086
  fieldClass: {vaultKey: libVES.VaultKey, file: libVES.File},
1711
- fieldSets: [{type: true, meta: true},{vaultEntries: {id: true, encData: true, vaultKey: {id: true}}},{vaultKey: true, file: true, lockbox: true}],
1712
- defaultCipher: 'AES256',
2087
+ fieldSets: [{type: true, meta: true},{vaultEntries: {id: true, encData: true, vaultKey: {id: true, type: true, user: {id: true}, algo: true}}},{vaultKey: true, file: true}],
2088
+ defaultCipher: 'AES256GCM',
1713
2089
  getRaw: function() {
1714
2090
  var self = this;
1715
- return self.VES.getVaultKeysById().then(function(vaultKeys) {
1716
- var f = function(vaultEntries) {
1717
- var i = 0;
1718
- var fn = function() {
1719
- for (; i < vaultEntries.length; i++) {
1720
- var k,d;
1721
- if ((d = vaultEntries[i].encData) != null && (k = vaultKeys[vaultEntries[i].vaultKey.id])) {
1722
- i++;
1723
- return k.decrypt(d).catch(fn);
1724
- }
2091
+ var f = function(vaultEntries, vaultKeys) {
2092
+ var i = 0;
2093
+ var fn = function() {
2094
+ if (vaultEntries) for (; i < vaultEntries.length; i++) {
2095
+ var k, d;
2096
+ if ((d = vaultEntries[i].encData) != null && vaultKeys[(k = vaultEntries[i].vaultKey).id]) {
2097
+ i++;
2098
+ return new libVES.VaultKey(k, self.VES).decrypt(d).catch(fn);
1725
2099
  }
1726
- return Promise.reject(new libVES.Error('Invalid Key',"No unlocked key to decrypt the item",{vaultItem: self}));
1727
- };
1728
- return fn();
2100
+ }
2101
+ return Promise.reject(new libVES.Error('InvalidKey',"No unlocked key to decrypt the item",{vaultItem: self}));
1729
2102
  };
1730
- var vaultEntries = [];
1731
- if (self.vaultEntryByKey) for (var k in self.vaultEntryByKey) vaultEntries.push(self.vaultEntryByKey[k]);
1732
- return f(vaultEntries).catch(function() {
1733
- return self.getVaultEntries().then(f);
2103
+ return fn();
2104
+ };
2105
+ var vaultEntries = [];
2106
+ if (self.vaultEntryByKey) for (var k in self.vaultEntryByKey) vaultEntries.push(self.vaultEntryByKey[k]);
2107
+ return f(vaultEntries, self.VES.unlockedKeys).catch(function() {
2108
+ return self.getVaultEntries().then(function(vaultEntries) {
2109
+ return f(vaultEntries, self.VES.unlockedKeys).catch(function() {
2110
+ return self.VES.getUnlockableKeys().then(function(vks) {
2111
+ return f(vaultEntries, vks);
2112
+ });
2113
+ });
1734
2114
  });
1735
2115
  });
1736
2116
  },
@@ -1749,7 +2129,7 @@ libVES.VaultItem.prototype = new libVES.Object({
1749
2129
  getVaultEntries: function() {
1750
2130
  var self = this;
1751
2131
  return this.getField('vaultEntries').then(function(ves) {
1752
- for (var i = 0; i < ves.length; i++) self.vaultEntryByKey[ves[i].vaultKey.id] = ves[i];
2132
+ if (ves) for (var i = 0; i < ves.length; i++) self.vaultEntryByKey[ves[i].vaultKey.id] = ves[i];
1753
2133
  return ves;
1754
2134
  });
1755
2135
  },
@@ -1759,9 +2139,6 @@ libVES.VaultItem.prototype = new libVES.Object({
1759
2139
  getFile: function() {
1760
2140
  return this.getField('file');
1761
2141
  },
1762
- getLockbox: function() {
1763
- return this.getField('lockbox');
1764
- },
1765
2142
  parse: function(buf) {
1766
2143
  var self = this;
1767
2144
  return this.getType().then(function(type) {
@@ -1780,42 +2157,49 @@ libVES.VaultItem.prototype = new libVES.Object({
1780
2157
  });
1781
2158
  });
1782
2159
  },
2160
+ resolveRaw: function(val) {
2161
+ return (val == null ? this.getRaw() : this.build(val));
2162
+ },
1783
2163
  shareWith: function(usrs,val,save) {
1784
2164
  var self = this;
1785
- return (val == null ? self.getRaw() : self.build(val)).then(function(v) {
1786
- return self.VES.usersToKeys(usrs).then(function(ks) {
1787
- return (val == null ? self.getVaultEntries().then(function(ves) {
1788
- var k_ves = {};
1789
- var k_used = {};
1790
- for (var j = 0; j < ves.length; j++) k_ves[ves[j].vaultKey.id] = ves[j];
1791
- return Promise.all(ks.map(function(k,j) {
1792
- return k.getId().then(function(k_id) {
1793
- k_used[k_id] = true;
1794
- return k_ves[k_id];
1795
- }).catch(function(){});
1796
- })).then(function(old_ves) {
1797
- for (var k_id in k_ves) if (!k_used[k_id]) old_ves.push(k_ves[k_id]);
1798
- return old_ves;
1799
- });
1800
- }) : Promise.resolve([])).then(function(old_ves) {
1801
- var new_ves = [];
1802
- var set_ves = [];
1803
- return Promise.all(ks.map(function(k,j) {
1804
- return new_ves[j] = (old_ves[j] || k.encrypt(v).then(function(ctext) {
1805
- return k.postData(null,libVES.Object._refs).then(function(pd) {
2165
+ return self.VES.usersToKeys(usrs).then(function(ks) {
2166
+ return (val == null || self.id ? self.getVaultEntries().then(function(ves) {
2167
+ var k_ves = {};
2168
+ var k_used = {};
2169
+ for (var j = 0; j < ves.length; j++) k_ves[ves[j].vaultKey.id] = ves[j];
2170
+ return Promise.all(ks.map(function(k,j) {
2171
+ return k.getId().then(function(k_id) {
2172
+ k_used[k_id] = true;
2173
+ return k_ves[k_id];
2174
+ }).catch(function(){});
2175
+ })).then(function(old_ves) {
2176
+ for (var k_id in k_ves) if (!k_used[k_id]) old_ves.push(k_ves[k_id]);
2177
+ return old_ves;
2178
+ });
2179
+ }) : Promise.resolve([])).then(function(old_ves) {
2180
+ var new_ves = [];
2181
+ var set_ves = [];
2182
+ var valr = null;
2183
+ return Promise.all(ks.map(function(k,j) {
2184
+ return new_ves[j] = (old_ves[j] || (valr != null ? valr : valr = self.resolveRaw(val)).then(function(v) {
2185
+ return k.encrypt(v).then(function(ctext) {
2186
+ return (function(refs) {
2187
+ if (refs) for (var i in refs) if (refs[i] === k) return Promise.resolve({'$ref':i});
2188
+ return k.postData(null,refs);
2189
+ })(libVES.Object._refs).then(function(pd) {
1806
2190
  return set_ves.push({vaultKey: pd, encData: ctext});
1807
2191
  });
1808
- }));
1809
- })).then(function() {
1810
- return Promise.all(old_ves.slice(ks.length).map(function(ve,j) {
1811
- return (new libVES.VaultKey(ve.vaultKey,self.VES)).matchVaults(ks).then(function(f) {
1812
- if (f === false) set_ves.push({vaultKey: ve.vaultKey, '$op': 'delete'});
1813
- });
1814
- }));
1815
- }).then(function() {
1816
- if (!set_ves.length) return save = false;
1817
- return self.setField('vaultEntries',set_ves);
1818
- });
2192
+ });
2193
+ }));
2194
+ })).then(function() {
2195
+ return Promise.all(old_ves.slice(ks.length).map(function(ve,j) {
2196
+ return (new libVES.VaultKey(ve.vaultKey,self.VES)).matchVaults(ks).then(function(f) {
2197
+ if (f === false) set_ves.push({vaultKey: {id: ve.vaultKey.id}, '$op': 'delete'});
2198
+ });
2199
+ }));
2200
+ }).then(function() {
2201
+ if (!set_ves.length) return save = false;
2202
+ return self.setField('vaultEntries',set_ves);
1819
2203
  });
1820
2204
  });
1821
2205
  }).then(function() {
@@ -1835,7 +2219,9 @@ libVES.VaultItem.prototype = new libVES.Object({
1835
2219
  var m_curr_ks = {};
1836
2220
  for (var i = 0; i < curr_ks.length; i++) m_curr_ks[curr_ids[i]] = curr_ks[i];
1837
2221
  return Promise.all(new_ks.map(function(k,i) {
1838
- return k.getId();
2222
+ return k.getId().catch(function(e) {
2223
+ if (e.code != 'NotFound') throw e;
2224
+ });
1839
2225
  })).then(function(new_ids) {
1840
2226
  for (var i = 0; i < new_ks.length; i++) if (!m_curr_ks[new_ids[i]]) curr_ks.push(m_curr_ks[new_ids[i]] = new_ks[i]);
1841
2227
  return self.shareWith(curr_ks,val,save);
@@ -1898,8 +2284,9 @@ libVES.VaultItem.Type = {
1898
2284
  return new ci(new Uint8Array(buf));
1899
2285
  });
1900
2286
  },
1901
- build: function(data) {
2287
+ build: function(data) {
1902
2288
  if (!(data instanceof libVES.Cipher)) throw new libVES.Error('InvalidData',"Content of a VaultItem type 'file' must be libVES.Cipher");
2289
+ this.setField('meta', data.getMeta());
1903
2290
  return data.getSecret();
1904
2291
  }
1905
2292
  },
@@ -1921,10 +2308,13 @@ libVES.VaultItem.Type.password = libVES.VaultItem.Type.string;
1921
2308
  libVES.File.prototype = new libVES.Object({
1922
2309
  apiUri: 'files',
1923
2310
  fieldList: {id: true},
1924
- fieldClass: {externals: libVES.External},
2311
+ fieldClass: {externals: libVES.External, creator: libVES.User},
1925
2312
  getExternals: function() {
1926
2313
  return this.getField('externals');
1927
- }
2314
+ },
2315
+ getCreator: function() {
2316
+ return this.getField('creator');
2317
+ },
1928
2318
  });
1929
2319
 
1930
2320
  libVES.External.prototype = new libVES.Object({
@@ -1944,49 +2334,171 @@ libVES.External.prototype = new libVES.Object({
1944
2334
  });
1945
2335
 
1946
2336
  libVES.Algo = {
1947
- RSA: {
1948
- tag: 'RSA',
2337
+ ECDH: {
2338
+ tag: 'ECDH',
1949
2339
  decrypt: function(k,buf) {
1950
- return crypto.subtle.decrypt('RSA-OAEP',k,buf);
2340
+ var b = new Uint8Array(buf);
2341
+ var data = libVES.Util.ASN1.decode(b);
2342
+ if (!data || data.length < 2) throw new libVES.Error('InvalidValue','Error parsing ECIES: Invalid ciphertext?');
2343
+ return libVES.Util.ASN1.import(b.slice(0,b.byteLength - data[1].byteLength),{decoded:data[0]}).then(function(eKey) {
2344
+ return libVES.Algo.ECDH.cipher(k,eKey).then(function(ci) {
2345
+ return ci.decrypt(data[1],true).catch(function(e) {
2346
+ if (e instanceof libVES.Error) throw e;
2347
+ throw new libVES.Error('InvalidValue','Error decrypting ECIES payload: Invalid ciphertext?',{error:e});
2348
+ });
2349
+ }).catch(function(e) {
2350
+ if (e instanceof libVES.Error) throw e;
2351
+ throw new libVES.Error('InvalidValue','Error negotiating DH cipher key: Invalid ciphertext?',{error:e});
2352
+ });
2353
+ }).catch(function(e) {
2354
+ if (e instanceof libVES.Error) throw e;
2355
+ throw new libVES.Error('InvalidValue','Error loading EC ephemeral: Invalid ciphertext?',{error:e});
2356
+ });
1951
2357
  },
1952
2358
  encrypt: function(k,buf) {
1953
- return crypto.subtle.encrypt('RSA-OAEP',k,buf);
2359
+ return libVES.Algo.ECDH.eKey(k).then(function(eKey) {
2360
+ return libVES.Algo.ECDH.cipher(eKey.privateKey,k).then(function(ci) {
2361
+ return Promise.all([
2362
+ crypto.subtle.exportKey('spki',eKey.publicKey),
2363
+ ci.encrypt(buf,true)
2364
+ ]).then(function(bufs) {
2365
+ var buf = new Uint8Array(bufs[0].byteLength + bufs[1].byteLength);
2366
+ buf.set(new Uint8Array(bufs[0]),0);
2367
+ buf.set(new Uint8Array(bufs[1]),bufs[0].byteLength);
2368
+ return buf;
2369
+ });
2370
+ }).catch(function(e) {
2371
+ if (e instanceof libVES.Error) throw e;
2372
+ throw new libVES.Error('InvalidValue','Error generating ECIES DH cipher',{error:e});
2373
+ });
2374
+ });
2375
+ },
2376
+ eKey: function(pub) {
2377
+ return crypto.subtle.exportKey('jwk',pub).then(function(kdata) {
2378
+ return crypto.subtle.generateKey({name:'ECDH',namedCurve: kdata.crv},true,['deriveKey','deriveBits']).catch(function(e) {
2379
+ throw new libVES.Error('InvalidValue','Error generating ECIES ephemeral',{error: e});
2380
+ });
2381
+ });
2382
+ },
2383
+ cipher: function(priv,pub) {
2384
+ return libVES.getModule(libVES.Cipher,'AES256GCMp').then(function(m) {
2385
+ return Promise.resolve(libVES.Algo.ECDH.curveBytes[pub.algorithm.namedCurve] || crypto.subtle.exportKey('jwk',pub).then(function(jwk) {
2386
+ return jwk.x.length * 3 / 4;
2387
+ })).then(function(len) {
2388
+ return crypto.subtle.deriveBits({name:'ECDH',public:pub},priv,8 * len).then(function(raw) {
2389
+ return crypto.subtle.digest({name:'SHA-384'},raw).then(function(buf) {
2390
+ return new m(new Uint8Array(buf).slice(0,m.prototype.keySize + m.prototype.ivSize));
2391
+ });
2392
+ });
2393
+ });
2394
+ });
2395
+ },
2396
+ curveBytes: {
2397
+ 'P-256': 32,
2398
+ 'P-384': 48,
2399
+ 'P-521': 66
1954
2400
  },
1955
2401
  import: function(data,optns) {
1956
2402
  return libVES.Util.PEM.import(data,optns);
1957
2403
  },
1958
2404
  export: function(data,optns) {
1959
2405
  if (data instanceof CryptoKey) switch (data.type) {
1960
- case 'private':
1961
- var ops = {};
1962
- for (var k in optns) ops[k] = optns[k];
1963
- if (!ops.members) ops.members = [
1964
- libVES.getModule(libVES.Util,'PBKDF2'),
1965
- libVES.getModule(libVES.Cipher,'AES256CBC')
1966
- ];
1967
- return Promise.all(ops.members).then(function(ms) {
1968
- ops.members = ms;
1969
- return crypto.subtle.exportKey('pkcs8',data).then(function(der) {
1970
- ops.content = der;
1971
- var rec = [];
1972
- if (ops.password) return libVES.Util.PKCS5.export(function(call,optns) {
1973
- rec[1] = optns.content;
1974
- return Promise.resolve();
1975
- },ops).then(function(data) {
1976
- rec[0] = data;
1977
- return libVES.Util.PEM.encode(libVES.Util.ASN1.encode([rec]),'ENCRYPTED PRIVATE KEY');
1978
- });
1979
- else if (ops.opentext) return crypto.subtle.exportKey('pkcs8',data).then(function(der) {
1980
- return libVES.Util.PEM.encode(der,'PRIVATE KEY');
1981
- });
1982
- else throw libVES.Error('Internal','No password for key export (opentext=true to export without password?)');
1983
- });
1984
- });
2406
+ case 'private': return libVES.Util.PKCS8.encode(data, optns);
1985
2407
  case 'public':
1986
- return crypto.subtle.exportKey('spki',data).then(function(der) {
1987
- return libVES.Util.PEM.encode(der,'PUBLIC KEY');
2408
+ return crypto.subtle.exportKey('spki', data).then(function(der) {
2409
+ var asn = libVES.Util.ASN1.decode(new Uint8Array(der))[0];
2410
+ console.log(asn);
2411
+ switch (String(asn[0][0])) {
2412
+ case '1.2.840.10045.2.1':
2413
+ return der;
2414
+ case '1.3.132.1.12':
2415
+ case '1.3.132.112': // Firefox on Mac - apparently a misspelling of the former
2416
+ asn[0][0] = new libVES.Util.OID('1.2.840.10045.2.1');
2417
+ asn[1].ASN1type = 3;
2418
+ return libVES.Util.ASN1.encode([asn]);
2419
+ default:
2420
+ console.log(asn);
2421
+ throw new libVES.Error('Internal', 'Unexpected EC pubkey format');
2422
+ }
2423
+ return der;
2424
+ }).then(function(der) {
2425
+ return libVES.Util.PEM.encode(der, 'PUBLIC KEY');
1988
2426
  });
1989
2427
  }
2428
+ throw new libVES.Error('Internal', "Unknown type of key object");
2429
+ },
2430
+ generate: function(optns) {
2431
+ var op = {name:'ECDH', namedCurve:'P-384'};
2432
+ if (optns) for (var k in optns) op[k] = optns[k];
2433
+ return crypto.subtle.generateKey(op,true,['deriveKey','deriveBits']);
2434
+ },
2435
+ getPublic: function(priv) {
2436
+ return crypto.subtle.exportKey('jwk',priv).then(function(k) {
2437
+ return crypto.subtle.importKey('jwk',{
2438
+ crv: k.crv,
2439
+ ext: true,
2440
+ key_ops: [],
2441
+ kty: 'EC',
2442
+ x: k.x,
2443
+ y: k.y
2444
+ },{name:'ECDH'},true,[]);
2445
+ });
2446
+ }
2447
+ },
2448
+ RSA: {
2449
+ tag: 'RSA',
2450
+ decrypt: function(k,buf) {
2451
+ var self = libVES.Algo.RSA;
2452
+ var maxl = (k.algorithm.modulusLength + 7) >> 3;
2453
+ if (buf.byteLength <= maxl) return self.decryptRSA(k,buf);
2454
+ var b = new Uint8Array(buf);
2455
+ return self.decryptRSA(k,b.slice(0,maxl)).then(function(ck) {
2456
+ return self.cipher(ck).then(function(ci) {
2457
+ return ci.decrypt(b.slice(maxl),true);
2458
+ });
2459
+ });
2460
+ },
2461
+ decryptRSA: function(k,buf) {
2462
+ return crypto.subtle.decrypt('RSA-OAEP',k,buf).catch(function(e) {
2463
+ throw new libVES.Error('InvalidValue','Error decrypting RSA OAEP: Invalid ciphertext?',{error:e});
2464
+ });
2465
+ },
2466
+ encrypt: function(k,buf) {
2467
+ var self = libVES.Algo.RSA;
2468
+ var maxl = ((k.algorithm.modulusLength + 7) >> 3) - 48;
2469
+ if (buf.byteLength <= maxl) return self.encryptRSA(k,buf);
2470
+ return self.cipher().then(function(ci) {
2471
+ return Promise.all([
2472
+ ci.getSecret().then(function(sk) {
2473
+ return self.encryptRSA(k,sk);
2474
+ }),
2475
+ ci.encrypt(buf,true)
2476
+ ]).then(function(bufs) {
2477
+ var rs = new Uint8Array(bufs[0].byteLength + bufs[1].byteLength);
2478
+ rs.set(new Uint8Array(bufs[0]),0);
2479
+ rs.set(new Uint8Array(bufs[1]),bufs[0].byteLength);
2480
+ return rs;
2481
+ });
2482
+ });
2483
+ },
2484
+ encryptRSA: function(k,buf) {
2485
+ return crypto.subtle.encrypt('RSA-OAEP',k,buf).catch(function(e) {
2486
+ throw new libVES.Error('InvalidValue','Error encrypting RSA OAEP: Buffer is too long?',{error:e});
2487
+ });;
2488
+ },
2489
+ cipher: function(secret) {
2490
+ return libVES.getModule(libVES.Cipher,'AES256GCMp').then(function(cls) {
2491
+ return new cls(secret);
2492
+ });
2493
+ },
2494
+ import: function(data,optns) {
2495
+ return libVES.Util.PEM.import(data,optns);
2496
+ },
2497
+ export: function(data,optns) {
2498
+ if (data instanceof CryptoKey) switch (data.type) {
2499
+ case 'private': return libVES.Util.PKCS8.encode(data,optns);
2500
+ case 'public': return libVES.Util.PKCS1.encode(data,optns);
2501
+ }
1990
2502
  throw new libVES.Error('Internal',"Unknown type of key object");
1991
2503
  },
1992
2504
  generate: function(optns) {
@@ -2006,7 +2518,6 @@ libVES.Algo = {
2006
2518
  },{name:'RSA-OAEP',hash:'SHA-1'},true,['encrypt']);
2007
2519
  });
2008
2520
  }
2009
-
2010
2521
  },
2011
2522
  RSA_PKCS1_15: {
2012
2523
  tag: 'RSA_PKCS1_15',
@@ -2066,26 +2577,31 @@ libVES.Cipher.prototype = {
2066
2577
  }
2067
2578
  },
2068
2579
  getSecret: function() {
2580
+ var self = this;
2069
2581
  var meta = null;
2070
2582
  if (this.meta) for (var k in this.meta) {
2071
2583
  meta = libVES.Util.StringToByteArray(JSON.stringify(this.meta));
2072
2584
  break;
2073
2585
  }
2074
2586
  var buf = new Uint8Array(this.keySize + this.ivSize + (meta ? meta.byteLength : 0));
2075
- return Promise.all([this.key,this.IV]).then(function(data) {
2076
- return crypto.subtle.exportKey("raw",data[0]).then(function(key) {
2077
- buf.set(new Uint8Array(key),0);
2078
- buf.set(new Uint8Array(data[1]),key.byteLength);
2079
- if (meta) buf.set(meta,key.byteLength + data[1].byteLength);
2080
- return buf;
2081
- });
2587
+ return Promise.all([(this.algo ? this.key.then(function(key) {
2588
+ return crypto.subtle.exportKey("raw", key);
2589
+ }) : this.key), this.IV]).then(function(data) {
2590
+ buf.set(new Uint8Array(data[0]), 0);
2591
+ buf.set(new Uint8Array(data[1]), data[0].byteLength);
2592
+ if (meta) buf.set(new Uint8Array(meta), data[0].byteLength + data[1].byteLength);
2593
+ return buf;
2082
2594
  });
2083
2595
  },
2596
+ getMeta: function() {
2597
+ return {a: this.a};
2598
+ },
2084
2599
  buildKey: function(key) {
2085
2600
  if (!this.algo) return Promise.resolve(key);
2086
2601
  return crypto.subtle.importKey('raw',key,this.algo,true,['encrypt','decrypt']);
2087
2602
  },
2088
- process: function(buf,final,callbk,chunkSize) {
2603
+ process: function(buf,final,callbk,sizefn) {
2604
+ var self = this;
2089
2605
  buf = new Uint8Array(buf);
2090
2606
  if (this.processBuf) {
2091
2607
  var b = new Uint8Array(buf.byteLength + this.processBuf.byteLength);
@@ -2093,9 +2609,24 @@ libVES.Cipher.prototype = {
2093
2609
  b.set(buf,this.processBuf.byteLength);
2094
2610
  buf = b;
2095
2611
  }
2096
- var p = final ? buf.byteLength : (chunkSize ? Math.floor(buf.byteLength / chunkSize) * chunkSize : 0);
2097
- this.processBuf = p < buf.byteLength ? buf.slice(p) : null;
2098
- return p > 0 ? callbk(buf.slice(0,p)) : Promise.resolve(new Uint8Array(0));
2612
+ var over = !final;
2613
+ var fn = function(cprev) {
2614
+ var chunkSize = sizefn();
2615
+ if ((!buf.byteLength && over) || (!final && (!chunkSize || buf.byteLength < chunkSize))) {
2616
+ self.processBuf = buf.byteLength ? buf : null;
2617
+ return Promise.resolve(cprev);
2618
+ }
2619
+ var cl = (chunkSize && chunkSize < buf.byteLength) ? chunkSize : buf.byteLength;
2620
+ if (!chunkSize || cl < chunkSize) over = true;
2621
+ return callbk(buf.slice(0, cl)).then(function(ctext) {
2622
+ buf = buf.slice(cl);
2623
+ var r = new Uint8Array(cprev.byteLength + ctext.byteLength);
2624
+ r.set(cprev, 0);
2625
+ r.set(new Uint8Array(ctext), cprev.byteLength);
2626
+ return r;
2627
+ }).then(fn);
2628
+ };
2629
+ return fn(new Uint8Array(0));
2099
2630
  },
2100
2631
  encryptChunk: function(buf) {
2101
2632
  return Promise.all([this.key,this.algoInfo()]).then(function(info) {
@@ -2111,37 +2642,60 @@ libVES.Cipher.prototype = {
2111
2642
  return Promise.resolve(this.algo);
2112
2643
  },
2113
2644
  encrypt: function(buf,final) {
2114
- return this.process(buf,final,this.encryptChunk.bind(this),this.chunkSizeP);
2645
+ return this.process(buf,final,this.encryptChunk.bind(this),(function() { return this.chunkSizeP; }).bind(this));
2115
2646
  },
2116
2647
  decrypt: function(buf,final) {
2117
- return this.process(buf,final,this.decryptChunk.bind(this),this.chunkSizeC);
2648
+ return this.process(buf,final,this.decryptChunk.bind(this),(function() { return this.chunkSizeC; }).bind(this));
2118
2649
  }
2119
2650
  };
2120
2651
 
2121
- libVES.Cipher.AES256 = function(rec) {
2652
+ libVES.Cipher.NULL = function(rec) {
2122
2653
  this.init(rec);
2654
+ }
2655
+
2656
+ libVES.Cipher.AES = function(data) {
2657
+ for (var k in data) this[k] = data[k];
2123
2658
  };
2124
2659
 
2125
2660
  libVES.Cipher.AES256CBC = function(rec) {
2126
2661
  this.init(rec);
2127
2662
  };
2128
2663
 
2129
- libVES.Cipher.AES256.prototype = new libVES.Cipher({
2130
- keySize: 32,
2131
- ivSize: 32
2664
+ libVES.Cipher.AES256GCM = function(rec) {
2665
+ this.init(rec);
2666
+ };
2667
+
2668
+ libVES.Cipher.AES256GCMp = function(rec) {
2669
+ this.init(rec);
2670
+ };
2671
+
2672
+ libVES.Cipher.AES256GCM1K = function(rec) {
2673
+ this.init(rec);
2674
+ };
2675
+
2676
+ libVES.Cipher.NULL.prototype = new libVES.Cipher({
2677
+ a: 'NULL',
2678
+ keySize: 0,
2679
+ ivSize: 0
2132
2680
  });
2133
2681
 
2134
- libVES.Cipher.AES256CBC.prototype = new libVES.Cipher({
2135
- algo: 'AES-CBC',
2682
+ libVES.Cipher.AES.prototype = new libVES.Cipher({
2136
2683
  keySize: 32,
2137
- ivSize: 16,
2684
+ ivSize: 32,
2138
2685
  algoInfo: function() {
2686
+ var self = this;
2139
2687
  return this.IV.then(function(iv) {
2140
- return {name: 'AES-CBC', iv: iv};
2688
+ return {name: self.algo, iv: iv};
2141
2689
  });
2142
2690
  }
2143
2691
  });
2144
2692
 
2693
+ libVES.Cipher.AES256CBC.prototype = new libVES.Cipher.AES({
2694
+ algo: 'AES-CBC',
2695
+ keySize: 32,
2696
+ ivSize: 16
2697
+ });
2698
+
2145
2699
  libVES.Cipher.AES256CBC.import = function(args,chain,optns) {
2146
2700
  return chain('import').then(function(buf) {
2147
2701
  return crypto.subtle.decrypt({name: 'AES-CBC', iv: args},optns.key,buf);
@@ -2160,6 +2714,88 @@ libVES.Cipher.AES256CBC.info = function(chain,optns) {
2160
2714
  return Promise.resolve({algorithm: {name: 'AES-CBC', length: 256}});
2161
2715
  };
2162
2716
 
2717
+ libVES.Cipher.AES256GCM.prototype = new libVES.Cipher.AES({
2718
+ algo: 'AES-GCM',
2719
+ keySize: 32,
2720
+ ivSize: 12
2721
+ });
2722
+
2723
+ libVES.Cipher.AES256GCMp.prototype = new libVES.Cipher.AES({
2724
+ algo: 'AES-GCM',
2725
+ keySize: 32,
2726
+ ivSize: 12,
2727
+ padSize: 32,
2728
+ encryptChunk: function(buf0) {
2729
+ var buf = new Uint8Array(buf0);
2730
+ var pad = this.padSize - (buf.byteLength % this.padSize) - 1;
2731
+ var bufp = new Uint8Array(buf.byteLength + pad + 1);
2732
+ bufp[0] = pad;
2733
+ bufp.set(buf,1);
2734
+ return libVES.Cipher.prototype.encryptChunk.call(this,bufp);
2735
+ },
2736
+ decryptChunk: function(buf) {
2737
+ return libVES.Cipher.prototype.decryptChunk.call(this,buf).then(function(bufp0) {
2738
+ var bufp = new Uint8Array(bufp0);
2739
+ return bufp.slice(1,bufp.byteLength - bufp[0]);
2740
+ });
2741
+ }
2742
+ });
2743
+
2744
+ libVES.Cipher.AES256GCM1K.prototype = new libVES.Cipher.AES({
2745
+ algo: 'AES-GCM',
2746
+ keySize: 32,
2747
+ ivSize: 12,
2748
+ chunkSizeP: 1024,
2749
+ chunkSizeC: 16,
2750
+ algoInfo: function() {
2751
+ var self = this;
2752
+ if (!this.lastGCM) throw new libVES.Error('Internal', 'GCM HMAC is not buffered');
2753
+ return this.IV.then(function(iv) {
2754
+ var buf = new Uint8Array(iv.byteLength + self.lastGCM.byteLength);
2755
+ buf.set(new Uint8Array(iv), 0);
2756
+ buf.set(new Uint8Array(self.lastGCM), iv.byteLength);
2757
+ return libVES.Util.Hash.SHA256.hash(buf).then(function(sha) {
2758
+ return { name: self.algo, iv: sha.slice(0, self.ivSize) };
2759
+ });
2760
+ });
2761
+ },
2762
+ encryptChunk: function(buf) {
2763
+ var self = this;
2764
+ var seed = null;
2765
+ if (!this.lastGCM) {
2766
+ seed = new Uint8Array(16);
2767
+ crypto.getRandomValues(seed);
2768
+ this.lastGCM = seed;
2769
+ }
2770
+ return Promise.all([this.key, this.algoInfo()]).then(function(info) {
2771
+ return crypto.subtle.encrypt(info[1], info[0], buf).then(function(ctext) {
2772
+ self.lastGCM = new Uint8Array(ctext.slice(-16));
2773
+ if (!seed) return ctext;
2774
+ var rs = new Uint8Array(seed.byteLength + ctext.byteLength);
2775
+ rs.set(seed, 0);
2776
+ rs.set(new Uint8Array(ctext), seed.byteLength);
2777
+ return rs;
2778
+ });
2779
+ });
2780
+ },
2781
+ decryptChunk: function(buf) {
2782
+ var self = this;
2783
+ if (buf.byteLength < 16) throw new libVES.Error('Crypto', 'Invalid ciphertext block size');
2784
+ var rs;
2785
+ if (this.lastGCM) {
2786
+ rs = Promise.all([this.key, this.algoInfo()]).then(function(info) {
2787
+ self.lastGCM = buf.slice(-16);
2788
+ return crypto.subtle.decrypt(info[1], info[0], buf);
2789
+ });
2790
+ } else {
2791
+ rs = Promise.resolve(new Uint8Array(0));
2792
+ this.chunkSizeC = 1040;
2793
+ this.lastGCM = buf;
2794
+ }
2795
+ return rs;
2796
+ }
2797
+ });
2798
+
2163
2799
  libVES.Scramble = {
2164
2800
  RDX: function(x) {
2165
2801
  this.size = x;
@@ -2230,17 +2866,23 @@ libVES.Scramble.RDX.prototype = {
2230
2866
  scramble: function(vec,b) {
2231
2867
  return libVES.Math.mulv(vec,this.getCv(b));
2232
2868
  },
2233
- explode: function(sc,ct) {
2869
+ explode: function(sc,ct,optns) {
2234
2870
  var self = this;
2235
2871
  return this.toVector(sc).then(function(v) {
2236
2872
  var bs = self.getBases(ct);
2237
2873
  var rs = [];
2238
2874
  for (i = 0; i < bs.length; i++) rs[i] = {
2239
- meta: {
2875
+ meta: (function(m) {
2876
+ if (optns) {
2877
+ if (optns.t != null) m.t = optns.t;
2878
+ if (optns.shadow) for (var k in optns.shadow) m[k] = optns.shadow[k];
2879
+ }
2880
+ return m;
2881
+ })({
2240
2882
  v: self.tag,
2241
2883
  n: self.size,
2242
2884
  b: bs[i]
2243
- },
2885
+ }),
2244
2886
  value: self.scramble(v,bs[i])
2245
2887
  }
2246
2888
  return rs;
@@ -2305,8 +2947,9 @@ libVES.Scramble.algo = {
2305
2947
  'RDX1.2': libVES.Scramble.RDX
2306
2948
  };
2307
2949
 
2308
- libVES.Recovery = function(vaultKey) {
2950
+ libVES.Recovery = function(vaultKey, myItems) {
2309
2951
  this.vaultKey = vaultKey;
2952
+ if (myItems) this.myItems = Promise.resolve(myItems);
2310
2953
  };
2311
2954
 
2312
2955
  libVES.Recovery.prototype = {
@@ -2315,7 +2958,7 @@ libVES.Recovery.prototype = {
2315
2958
  var self = this;
2316
2959
  return this.tokens = self.vaultKey.getType().then(function(t) {
2317
2960
  switch (t) {
2318
- case 'shadow': case 'recovery': return self.vaultKey.getField('vaultItems',{
2961
+ case 'shadow': case 'recovery': return (self.myItems ? self.myItems : self.vaultKey.getField('vaultItems',{
2319
2962
  id: true,
2320
2963
  meta: true,
2321
2964
  type: true,
@@ -2326,7 +2969,7 @@ libVES.Recovery.prototype = {
2326
2969
  type: true
2327
2970
  }
2328
2971
  }
2329
- },true).then(function(vis) {
2972
+ },true)).then(function(vis) {
2330
2973
  var frnds = {};
2331
2974
  var fn = function() {
2332
2975
  return self.vaultKey.getUser().then(function(my_u) {
@@ -2336,10 +2979,15 @@ libVES.Recovery.prototype = {
2336
2979
  return Promise.all([
2337
2980
  vi.getVaultEntries().then(function(ves) {
2338
2981
  return Promise.all(ves.map(function(ve) {
2339
- return new libVES.VaultKey(ve.vaultKey,self.vaultKey.VES).getUser().then(function(u) {
2982
+ var vk = new libVES.VaultKey(ve.vaultKey,self.vaultKey.VES);
2983
+ return vk.getUser().then(function(u) {
2340
2984
  return u.getId().then(function(uid) {
2341
- if (uid == my_uid) frnd.assisted = true;
2342
- else {
2985
+ if (uid == my_uid) {
2986
+ frnd.assisted = true;
2987
+ return vk.getType().then(function(type) {
2988
+ if (type == 'current') frnd.current = true;
2989
+ });
2990
+ } else {
2343
2991
  frnd.user = u;
2344
2992
  frnds[uid] = frnd;
2345
2993
  }
@@ -2398,10 +3046,10 @@ libVES.Recovery.prototype = {
2398
3046
  return Promise.all(tkns.map(function(v,i) {
2399
3047
  return v.user.getId();
2400
3048
  })).then(function(uids) {
2401
- return user.getId().then(function(uid) {
2402
- for (var i = 0; i < uids.length; i++) if (uids[i] == uid) return tkns[i];
2403
- throw new libVES.Error('InvalidValue','Not a friend: ' + uid);
2404
- });
3049
+ return user.getId().then(function(uid) {
3050
+ for (var i = 0; i < uids.length; i++) if (uids[i] == uid) return tkns[i];
3051
+ throw new libVES.Error('InvalidValue','Not a friend: ' + uid);
3052
+ });
2405
3053
  });
2406
3054
  });
2407
3055
  },
@@ -2424,22 +3072,11 @@ libVES.Recovery.prototype = {
2424
3072
  getFriendsAssisted: function() {
2425
3073
  var self = this;
2426
3074
  return this.getTokens().then(function(tkns) {
2427
- return Promise.all(tkns.map(function(v,i) {
2428
- return v.vaultItem.getVaultEntries().then(function() {
2429
- return v.vaultEntryByKey;
2430
- });
2431
- })).then(function(ves) {
2432
- return self.vaultKey.getUser().then(function(user) {
2433
- return user.getCurrentVaultKey().then(function(vk) {
2434
- return vk.getId().then(function(vkid) {
2435
- var rs = 0;
2436
- for (var i = 0; i < ves.length; i++) if (ves[i]) if(ves[i][vkid]) rs++;
2437
- return rs;
2438
- });
2439
- });
2440
- });
3075
+ var a = 0;
3076
+ tkns.map(function(t, i) {
3077
+ if (t.assisted) a++;
2441
3078
  });
2442
- return tkns[0].meta.n;
3079
+ return a;
2443
3080
  });
2444
3081
  },
2445
3082
  getFriendsToGo: function() {
@@ -2464,7 +3101,10 @@ libVES.Recovery.prototype = {
2464
3101
  });
2465
3102
  },
2466
3103
  assist: function() {
2467
- return this._assist(true);
3104
+ var self = this;
3105
+ return self.vaultKey.getType().then(function(t) {
3106
+ return t == 'recovery' ? self._assist(true) : null;
3107
+ });
2468
3108
  },
2469
3109
  revoke: function() {
2470
3110
  return this._assist(false);
@@ -2484,7 +3124,9 @@ libVES.Recovery.prototype = {
2484
3124
  _recover: function() {
2485
3125
  var self = this;
2486
3126
  return self.unlock().then(function() {
2487
- return self.vaultKey.rekey();
3127
+ return self.vaultKey.VES.elevateAuth({authVaultKey: self.vaultKey}).then(function(optns) {
3128
+ return self.vaultKey.rekey(optns);
3129
+ });
2488
3130
  });
2489
3131
  },
2490
3132
  recover: function() {
@@ -2500,11 +3142,14 @@ libVES.Delegate = {
2500
3142
  html: '<div style="position:fixed;top:0;left:0;width:100%;height:100%;background:rgba(0,0,0,0.5);display:table;z-index:200000;">'
2501
3143
  + '<div style="display:table-row;"><div style="display:table-cell;vertical-align:middle;text-align:center;">'
2502
3144
  + '<div style="min-width:320px;max-width:640px;;background-color:white;margin: auto;padding: 30px;">'
2503
- + '<p>Use the VESvault popup window to grant access to the App Vault</p>'
2504
- + '<p class="VESvaultDelegateBlockerMsg" style="color: #bf7f00; font-style:italic;">&nbsp;</p>'
2505
- + '<p><a class="VESvaultDelegateRetryLnk" href="{$url}" target="VESvaultDelegate" onclick="return !libVES.Delegate.retryPopup(this.href,this)">Click here</a> if you can\'t see VESvault popup window</p>'
2506
- + '<p><a class="VESvaultDelegateCancelLnk" href="#" onclick="libVES.Delegate.cancel(); return false;">Cancel</a></p>'
3145
+ + '{$content}'
2507
3146
  + '</div></div></div></div>',
3147
+ htmlDlg: '<p>Use the VESvault popup window to grant access to the App Vault</p>'
3148
+ + '<p class="VESvaultDelegateBlockerMsg" style="color: #bf7f00; font-style:italic;">&nbsp;</p>'
3149
+ + '<p><a class="VESvaultDelegateRetryLnk" href="{$url}" target="VESvaultDelegate" onclick="return !libVES.Delegate.retryPopup(this.href,this)">Click here</a> if you can\'t see VESvault popup window</p>'
3150
+ + '<p><a class="VESvaultDelegateCancelLnk" href="#" onclick="libVES.Delegate.cancel(); return false;">Cancel</a></p>',
3151
+ htmlFlw: '<p>Authorizing VES Access</p>'
3152
+ + '<iframe style="width: 100%; height: 48px; border: none;" src="{$url}"></iframe>',
2508
3153
  htmlBlockerMsg: 'Looks like your browser is using a popup blocker...',
2509
3154
  name: 'VESvaultDelegate',
2510
3155
  login: function(VES,challenge,optns) {
@@ -2518,11 +3163,10 @@ libVES.Delegate = {
2518
3163
  return this.response = new Promise(function(resolve,reject) {
2519
3164
  self.reject = reject;
2520
3165
  self.resolve = resolve;
2521
- var url = VES.wwwUrl + 'session/delegate/' + escape(VES.app) + '/' + escape(VES.domain);
2522
- self.matchOrigin = (function(m) { return m ? m[0] : document.location.protocol + '//' + document.location.host; })(url.match(/^(https\:\/\/[^\/\?\#]+)/));
2523
- self.popup = document.createElement('DIV');
2524
- self.popup.innerHTML = self.html.replace('{$url}',url);
2525
- document.getElementsByTagName('BODY')[0].appendChild(self.popup);
3166
+ var url = VES.wwwUrl + 'vv/unlock?via=delegate&url=' + encodeURIComponent(document.location.href) + '&domain=' + encodeURIComponent(VES.domain);
3167
+ if (optns) for (var k in optns) if (optns[k] != null) url += '&' + encodeURIComponent(k) + '=' + encodeURIComponent(optns[k]);
3168
+ self.setOrigin(url);
3169
+ self.showOverlay(self.htmlDlg.replace('{$url}',url));
2526
3170
  self.retryPopupCalled = 0;
2527
3171
  try {
2528
3172
  document.getElementsByClassName('VESvaultDelegateRetryLnk')[0].onclick = function() {
@@ -2545,6 +3189,35 @@ libVES.Delegate = {
2545
3189
  self.popupInterval = window.setInterval(self.chkCancel.bind(self),1000);
2546
3190
  });
2547
3191
  },
3192
+ flow: function(VES, start, optns) {
3193
+ if (this.response) return this.response;
3194
+ this.VES = VES;
3195
+ var self = this;
3196
+ var unlk = VES.wwwUrl + 'vv/unlock';
3197
+ if (start || (!sessionStorage['libVES.flowStarted'] && document.referrer.substr(0, unlk.length).toLowerCase() == unlk.toLowerCase())) {
3198
+ return self.response = new Promise(function(resolve, reject) {
3199
+ self.reject = reject;
3200
+ self.resolve = resolve;
3201
+ var ifrm = VES.wwwUrl + 'vv/flowin?url=' + encodeURIComponent(document.location.href) + '&domain=' + encodeURIComponent(VES.domain);
3202
+ if (optns) for (var k in optns) if (optns[k] != null) ifrm += '&' + encodeURIComponent(k) + '=' + encodeURIComponent(optns[k]);
3203
+ self.setOrigin(ifrm);
3204
+ window.addEventListener('message',self.listener.bind(self));
3205
+ self.showOverlay(self.htmlFlw.replace('{$url}', ifrm));
3206
+ if (start) delete(sessionStorage['libVES.flowStarted'])
3207
+ else sessionStorage['libVES.flowStarted'] = true;
3208
+ });
3209
+ } else {
3210
+ return new Promise(function(){});
3211
+ }
3212
+ },
3213
+ showOverlay: function(cont) {
3214
+ this.popup = document.createElement('DIV');
3215
+ this.popup.innerHTML = this.html.replace('{$content}', cont);
3216
+ document.getElementsByTagName('BODY')[0].appendChild(this.popup);
3217
+ },
3218
+ setOrigin: function(url) {
3219
+ this.matchOrigin = (function(m) { return m ? m[0] : document.location.protocol + '//' + document.location.host; })(url.match(/^(https\:\/\/[^\/\?\#]+)/));
3220
+ },
2548
3221
  openPopup: function(url) {
2549
3222
  return this.popupWindow = window.open(url,this.name,"width=600,height=600,top=100,left=100");
2550
3223
  },
@@ -2558,30 +3231,13 @@ libVES.Delegate = {
2558
3231
  return !f && this.openPopup(url);
2559
3232
  },
2560
3233
  listener: function(evnt) {
2561
- if (this.popupWindow && evnt.origin == this.matchOrigin) try {
3234
+ if (this.popup && evnt.origin == this.matchOrigin) try {
2562
3235
  var msg = JSON.parse(evnt.data);
2563
- var VES = this.VES;
2564
- if (msg.externalId) {
2565
- VES.externalId = msg.externalId;
2566
- this.resolve(VES.unlock(msg.VESkey).then(function() {
2567
- return VES;
2568
- }));
2569
- } else if (msg.token) {
2570
- VES.token = msg.token;
2571
- this.resolve(VES.getSecondaryKey({domain:VES.domain},true).then(function(vaultKey) {
2572
- return vaultKey.getExternals().then(function(externals) {
2573
- return Promise.all(externals.map(function(ext,i) {
2574
- return ext.getDomain();
2575
- })).then(function(domains) {
2576
- for (var i = 0; i < domains.length; i++) if (domains[i] == VES.domain) return externals[i].getExternalId();
2577
- throw new libVES.Error('Internal','No external id found for newly created secondary key');
2578
- }).then(function(extId) {
2579
- VES.externalId = extId;
2580
- return VES;
2581
- });
2582
- });
2583
- }));
2584
- } else return;
3236
+ if (msg.redirect) {
3237
+ document.location.replace(msg.redirect);
3238
+ } else {
3239
+ this.resolve(this.VES.authorize(msg));
3240
+ }
2585
3241
  this.close();
2586
3242
  if (!evnt.source.closed) evnt.source.close();
2587
3243
  } catch(e) {}
@@ -2598,7 +3254,7 @@ libVES.Delegate = {
2598
3254
  this.popupInterval = null;
2599
3255
  this.popup.parentNode.removeChild(this.popup);
2600
3256
  this.popup = null;
2601
- return self.response;
3257
+ return this.response;
2602
3258
  }
2603
3259
  },
2604
3260
  cancel: function() {
@@ -2611,4 +3267,255 @@ libVES.Delegate = {
2611
3267
  }
2612
3268
  };
2613
3269
 
3270
+
3271
+ libVES.Flow = function(name, optns) {
3272
+ this.name = name;
3273
+ this.origin = document.location.origin;
3274
+ this.url = this.origin + '/VESflow';
3275
+ if (optns) for (var k in optns) this[k] = optns[k];
3276
+ return this;
3277
+ };
3278
+
3279
+ libVES.Flow.algo = {name: 'ECDH', namedCurve: 'P-256'};
3280
+
3281
+ libVES.Flow.keyAlgo = function(jwk) {
3282
+ if (jwk && jwk.kty == 'EC') return {name: 'ECDH', namedCurve: jwk.crv};
3283
+ return null;
3284
+ };
3285
+
3286
+ libVES.Flow.toOrigin = function(url) {
3287
+ var a = document.createElement('a');
3288
+ a.href = url;
3289
+ return a.origin;
3290
+ };
3291
+
3292
+ libVES.Flow.jwkPub = function(jwk) {
3293
+ delete(jwk.d);
3294
+ delete(jwk.key_ops);
3295
+ delete(jwk.ext);
3296
+ return jwk;
3297
+ };
3298
+
3299
+ libVES.Flow.parseToken = function(src, callbk) {
3300
+ src = src.replace(/\#VESflow\.(\w+)=([^\#\&\=]+)/g, function(s, k, v) {
3301
+ var tk;
3302
+ try {
3303
+ tk = JSON.parse(decodeURIComponent(v));
3304
+ if (callbk(decodeURIComponent(k), tk)) return '';
3305
+ } catch(e) {
3306
+ console.log(e);
3307
+ }
3308
+ return s;
3309
+ });
3310
+ return src;
3311
+ };
3312
+
3313
+ libVES.Flow.logout = function() {
3314
+ for (var k in sessionStorage) if (k.match(/^VESflow\b/)) delete(sessionStorage[k]);
3315
+ };
3316
+
3317
+ libVES.Flow.prototype.source = function(src) {
3318
+ if (src != null) history.replaceState(null, '', src);
3319
+ return document.location.href;
3320
+ };
3321
+
3322
+ libVES.Flow.prototype.privKey = function(algo) {
3323
+ if (!algo) algo = libVES.Flow.algo;
3324
+ var a = JSON.stringify(algo);
3325
+ return Promise.resolve(sessionStorage['VESflow' + a]).then(function(k) {
3326
+ if (!k) throw null;
3327
+ return crypto.subtle.importKey('jwk', JSON.parse(k), algo, true, ['deriveBits', 'deriveKey']);
3328
+ }).catch(function(e) {
3329
+ return crypto.subtle.generateKey(algo, true, ['deriveBits', 'deriveKey']).then(function(pair) {
3330
+ return crypto.subtle.exportKey('jwk', pair.privateKey).then(function(jwk) {
3331
+ sessionStorage['VESflow' + a] = JSON.stringify(jwk);
3332
+ return pair.privateKey;
3333
+ });
3334
+ });
3335
+ });
3336
+ };
3337
+
3338
+ libVES.Flow.prototype.pubJWK = function(org) {
3339
+ return ((!org || org == this.origin) ? this.privKey().then(function(priv) {
3340
+ return crypto.subtle.exportKey('jwk', priv).then(function(jwk) {
3341
+ return libVES.Flow.jwkPub(jwk);
3342
+ });
3343
+ }) : Promise.resolve(sessionStorage['VESflow|' + org]).then(function(k) {
3344
+ if (!k) throw {code: 'NotFound', message: 'No public key stored for ' + org};
3345
+ return JSON.parse(k);
3346
+ }));
3347
+ };
3348
+
3349
+ libVES.Flow.prototype.pubKey = function(org) {
3350
+ return this.pubJWK(org).then(function(jwk) {
3351
+ return crypto.subtle.importKey('jwk', jwk, libVES.Flow.keyAlgo(jwk), true, []);
3352
+ });
3353
+ };
3354
+
3355
+ libVES.Flow.prototype.cipher = function(url) {
3356
+ var self = this;
3357
+ return self.pubKey(libVES.Flow.toOrigin(url)).then(function(pub) {
3358
+ return self.privKey(pub.algorithm).then(function(priv) {
3359
+ return crypto.subtle.deriveBits({name: 'ECDH', public: pub}, priv, 8 * libVES.Algo.ECDH.curveBytes[pub.algorithm.namedCurve]);
3360
+ });
3361
+ }).then(function(raw) {
3362
+ return crypto.subtle.digest({name: 'SHA-256'}, raw).then(function(buf) {
3363
+ return crypto.subtle.importKey('raw', buf, 'AES-GCM', false, ['encrypt', 'decrypt']);
3364
+ });
3365
+ });
3366
+ };
3367
+
3368
+ libVES.Flow.prototype.encrypt = function(url, val) {
3369
+ var self = this;
3370
+ return Promise.resolve(val || this.value()).then(function(val) {
3371
+ if (val == null) throw {code: 'NoData', message: 'Empty value'};
3372
+ return self.cipher(url).then(function(ci) {
3373
+ var iv = new Uint8Array(12);
3374
+ crypto.getRandomValues(iv);
3375
+ return crypto.subtle.encrypt({name: 'AES-GCM', iv: iv.buffer}, ci, libVES.Util.StringToByteArray(JSON.stringify(val))).then(function(ctext) {
3376
+ return libVES.Util.ByteArrayToB64W(iv) + '.' + libVES.Util.ByteArrayToB64W(ctext);
3377
+ });
3378
+ });
3379
+ });
3380
+ };
3381
+
3382
+ libVES.Flow.prototype.token = function(url) {
3383
+ var self = this;
3384
+ return this.pubJWK().then(function(jwk) {
3385
+ return {url: self.url, key: jwk};
3386
+ }).then(function(tk) {
3387
+ return self.encrypt(url).then(function(ctext) {
3388
+ tk.enc = ctext;
3389
+ self.erase(url);
3390
+ self.sent = true;
3391
+ return tk;
3392
+ }).catch(function(e) {
3393
+ self.store(url).catch(function(e) {});
3394
+ return tk;
3395
+ });
3396
+ });
3397
+ };
3398
+
3399
+ libVES.Flow.prototype.addToken = function(url) {
3400
+ var self = this;
3401
+ return self.token(url).then(function(tk) {
3402
+ return url + '#VESflow.' + self.name + '=' + encodeURIComponent(JSON.stringify(tk));
3403
+ });
3404
+ };
3405
+
3406
+ libVES.Flow.prototype.decrypt = function(ctext, url) {
3407
+ var self = this;
3408
+ var c = ctext.split('.');
3409
+ return self.cipher(url).then(function(ci) {
3410
+ return crypto.subtle.decrypt({name: 'AES-GCM', iv: libVES.Util.B64ToByteArray(c[0])}, ci, libVES.Util.B64ToByteArray(c[1])).then(function(ptext) {
3411
+ return JSON.parse(libVES.Util.ByteArrayToString(ptext));
3412
+ });
3413
+ });
3414
+ };
3415
+
3416
+ libVES.Flow.prototype.value = function() {
3417
+ var self = this;
3418
+ return Promise.resolve(history.state && history.state['VESflow.' + self.name]).then(function(ctext) {
3419
+ return self.decrypt(ctext);
3420
+ });
3421
+ };
3422
+
3423
+ libVES.Flow.prototype.setValue = function(val) {
3424
+ var self = this;
3425
+ return Promise.resolve(val? self.encrypt(null, val) : null).then(function(ctext) {
3426
+ var s = {};
3427
+ if (history.state) for (var k in history.state) s[k] = history.state[k];
3428
+ if (ctext) s['VESflow.' + self.name] = ctext;
3429
+ else delete(s['VESflow.' + self.name]);
3430
+ history.replaceState(s, '');
3431
+ return val;
3432
+ });
3433
+ };
3434
+
3435
+ libVES.Flow.prototype.click = function(a) {
3436
+ var self = this;
3437
+ (a.origin == this.origin ? self.store() : self.addToken(a.href).then(function(url) {
3438
+ a.href = url;
3439
+ })).then(function() {
3440
+ document.location.href = a.href;
3441
+ });
3442
+ return false;
3443
+ };
3444
+
3445
+ libVES.Flow.prototype.reload = function(e) {
3446
+ if (!this.sent) this.store().catch(function(e) {});
3447
+ };
3448
+
3449
+ libVES.Flow.prototype.recv = function(dval) {
3450
+ var self = this;
3451
+ return Promise.resolve(self.source()).then(function(src) {
3452
+ var tk = null;
3453
+ src = libVES.Flow.parseToken(src, function(k, t) {
3454
+ if (k != self.name) return false;
3455
+ tk = t;
3456
+ return true;
3457
+ });
3458
+ if (tk == null) throw {code: 'NotFound', message: 'No valid token in the source url'};
3459
+ return Promise.resolve(self.source(src)).then(function() {
3460
+ if (dval) for (var k in dval) if (tk[k] === undefined) tk[k] = dval[k];
3461
+ return self.recvToken(tk);
3462
+ });
3463
+ });
3464
+ };
3465
+
3466
+ libVES.Flow.prototype.recvToken = function(tk) {
3467
+ var org = libVES.Flow.toOrigin(tk.url);
3468
+ if (org != this.origin && tk.key) sessionStorage['VESflow|' + org] = JSON.stringify(tk.key);
3469
+ if (!tk.enc) return Promise.reject({url: tk.url, code: 'Incomplete', message: 'Missing encrypted data'});
3470
+ return this.decrypt(tk.enc, org);
3471
+ };
3472
+
3473
+ libVES.Flow.prototype.get = function() {
3474
+ var self = this;
3475
+ return self.fetch().catch(function(e) {
3476
+ if (!e || e.code != 'NotFound') console.log(e);
3477
+ return null;
3478
+ }).then(function(data) {
3479
+ return self.recv().catch(function(e) {
3480
+ if (e && e.code == 'Incomplete') {
3481
+ self.url = document.location.href;
3482
+ return self.addToken(e.url).then(function(url) {
3483
+ document.location.replace(url);
3484
+ throw {code: 'Reload', message: 'Completing the key exchange'};
3485
+ });
3486
+ }
3487
+ if (!data) throw e;
3488
+ return data;
3489
+ });
3490
+ });
3491
+ };
3492
+
3493
+ libVES.Flow.prototype.store = function(url) {
3494
+ if (history.state && history.state['VESflow.' + this.name]) {
3495
+ this.sent = 1;
3496
+ return Promise.resolve(sessionStorage['VESflow.' + this.name + '>' + libVES.Flow.toOrigin(url)] = history.state['VESflow.' + this.name]);
3497
+ }
3498
+ return Promise.reject({code: 'NotFound', message: 'Value is not set'});
3499
+ };
3500
+
3501
+ libVES.Flow.prototype.fetch = function(url) {
3502
+ var self = this;
3503
+ return Promise.resolve(sessionStorage['VESflow.' + self.name + '>' + libVES.Flow.toOrigin(url)]).then(function(ctext) {
3504
+ if (!ctext) throw {code: 'NotFound', message: 'Not in the session storage'};
3505
+ return self.decrypt(ctext).then(function(data) {
3506
+ self.erase(url);
3507
+ return data;
3508
+ });
3509
+ });
3510
+ };
3511
+
3512
+ libVES.Flow.prototype.erase = function(url) {
3513
+ delete(sessionStorage['VESflow.' + this.name + '>' + libVES.Flow.toOrigin(url)]);
3514
+ };
3515
+
3516
+ libVES.Flow.prototype.logout = function() {
3517
+ this.setValue(undefined);
3518
+ return libVES.Flow.logout();
3519
+ };
3520
+
2614
3521
  module.exports = libVES;