libves 1.0.4 → 2.0.1

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 - 2022 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 2.00
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,12 +140,83 @@ 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
+ });
109
149
  });
110
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;
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
+ }
219
+ },
111
220
  me: function() {
112
221
  var self = this;
113
222
  if (!this.userMe) this.userMe = this.get('me').then((function(data) {
@@ -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,23 @@ 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
+ var email = ext.externalId ? ext.externalId.replace(/\!.*/, '') : '';
353
+ if (!email.match(/\@/)) throw {code: 'NotFound', message: 'externalId is not an email for non-existing vault'};
354
+ return new libVES.User({email: email}, self);
355
+ }
356
+ };
357
+ });
236
358
  };
237
359
  return (ext.externalId ? self.prepareExternals(ext) : m().then(function(dom) {
238
360
  return Promise.resolve(user || self.me()).then(function(u) {
@@ -241,7 +363,7 @@ libVES.prototype = {
241
363
  return self.prepareExternals([ex]);
242
364
  });
243
365
  }).catch(function(e) {
244
- throw new libVES.Error('NotFound','Cannot match externalId for domain:' + ext.domain + ', user:' + user + '. Define libVES.Domain.' + ext.domain + '.userToVaultRef(user) to return a valid reference.',{error: e});
366
+ throw new libVES.Error('NotFound', 'Cannot match externalId for domain:' + ext.domain + ', user:' + user + '. Define libVES.Domain.' + ext.domain + '.userToVaultRef(user) to return a valid reference.',{error: e});
245
367
  })).then(function(exts) {
246
368
  var vkey = new libVES.VaultKey({externals: exts, creator: self.me()},self);
247
369
  return vkey.getId().then(function() {
@@ -251,16 +373,17 @@ libVES.prototype = {
251
373
  return Promise.resolve(user || m().then(function(dom) {
252
374
  return dom.vaultRefToUser(exts[0]);
253
375
  })).catch(function(e) {
254
- throw new libVES.Error('NotFound','No matching secondary key (domain:' + ext.domain + ', externalId:' + ext.externalId + '). Supply "user" to create the temp key, or define libVES.Domain.' + ext.domain + '.vaultRefToUser(vaultRef) to return matching libVES.User',{error: e});
376
+ throw new libVES.Error('NotFound', 'No matching secondary vault key',{error: e});
255
377
  }).then(function(u) {
256
378
  return self.me().then(function(me) {
257
379
  return Promise.all([me.getId(),u.getId()]).then(function(ids) {
258
380
  if (ids[0] == ids[1]) return self.getSecondaryKey(exts,true);
259
381
  }).catch(function(e) {
260
- if (e != 'NotFound') throw e;
382
+ if (e.code != 'NotFound') throw e;
261
383
  }).then(function(rs) {
262
384
  return rs || self.createTempKey(self._matchUser(u)).then(function(vkey) {
263
385
  return vkey.setField('externals',exts).then(function() {
386
+ if (appUrl) vkey.setField('appUrl', appUrl);
264
387
  return vkey;
265
388
  });
266
389
  });
@@ -292,21 +415,21 @@ libVES.prototype = {
292
415
  return keys;
293
416
  });
294
417
  },
295
- createTempKey: function(usr,optns) {
418
+ createTempKey: function(usr, optns) {
296
419
  var self = this;
297
- var key = new libVES.VaultKey({type: 'temp', algo: this.keyAlgo, user: usr},self);
420
+ var key = new libVES.VaultKey({type: 'temp', algo: this.keyAlgo, user: usr}, self);
298
421
  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() {
422
+ return key.generate(veskey, optns).then(function(k) {
423
+ if (self.e2e && self.e2e.length) usr.e2e = self.getVESkeyE2E(veskey, usr);
424
+ key.setField('vaultItems', veskey.then(function(v) {
425
+ var vi = new libVES.VaultItem({type: 'password'}, self);
426
+ return Promise.all(self.type == 'secondary' ? [self.me(), self.getVaultKey()] : [self.me()]).then(function(sh) {
427
+ return vi.shareWith(sh, v, false).then(function() {
305
428
  return [vi];
306
429
  });
307
430
  });
308
431
  }));
309
- key.setField('creator',self.me());
432
+ key.setField('creator', self.me());
310
433
  return key;
311
434
  });
312
435
  },
@@ -331,22 +454,31 @@ libVES.prototype = {
331
454
  var self = this;
332
455
  return this.me().then(function(me) {
333
456
  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() {
457
+ return me.getCurrentVaultKey().then(function(cur) {
458
+ return (cur ? cur.unlock().then(function() {
459
+ return k.rekeyFrom(cur);
460
+ }).catch(function(e) {
461
+ if (!lost) throw e;
462
+ }) : Promise.resolve(null)).then(function() {
463
+ var r;
464
+ if (cur && lost) r = cur.setField('type','lost').then(function() {
338
465
  k.user = undefined;
339
466
  return me.setField('vaultKeys',[cur,k]).then(function() {
340
467
  return me;
341
468
  });
342
469
  });
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;
470
+ else r = k;
471
+ me.currentVaultKey = me.activeVaultKeys = me.shadowVaultKey = undefined;
472
+ if (!cur || !lost) me.vaultKeys = undefined;
473
+ return r;
474
+ });
348
475
  }).then(function(r) {
349
- return r.post(undefined,undefined,options);
476
+ return self.elevateAuth(options).then(function(optns) {
477
+ return r.post(undefined, undefined, optns);
478
+ });
479
+ }).catch(function(e) {
480
+ self.reset();
481
+ throw e;
350
482
  }).then(function(post) {
351
483
  return self.reset(post);
352
484
  }).then(function() {
@@ -370,21 +502,21 @@ libVES.prototype = {
370
502
  return rs;
371
503
  });
372
504
  },
373
- getSecondaryKey: function(ext,force) {
505
+ getSecondaryKey: function(ext, force) {
374
506
  var self = this;
375
507
  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) {
508
+ if (e.domain && !e.externalId) return libVES.getModule(libVES.Domain, e.domain).then(function(mod) {
377
509
  return self.me().then(function(me) {
378
- return mod.userToVaultRef(me,self);
510
+ return mod.userToVaultRef(me, self);
379
511
  });
380
512
  });
381
513
  return e;
382
514
  })).then(function(ext) {
383
- var vkey = new libVES.VaultKey({externals: ext},self);
515
+ var vkey = new libVES.VaultKey({externals: ext}, self);
384
516
  return vkey.getId().then(function(id) {
385
517
  return vkey;
386
518
  }).catch(function(e) {
387
- if (!force) throw e;
519
+ if (!force || e.code != 'NotFound') throw e;
388
520
  return self.setSecondaryKey(ext);
389
521
  });
390
522
  });
@@ -395,14 +527,25 @@ libVES.prototype = {
395
527
  if (!veskey) veskey = self.generateVESkey();
396
528
  return self.me().then(function(me) {
397
529
  return (new libVES.VaultKey({type: 'secondary', algo: self.keyAlgo, user: me, externals: ext},self)).generate(veskey,optns).then(function(k) {
530
+ return self.getSecondaryKey(ext).then(function(k2) {
531
+ return k.rekeyFrom(k2);
532
+ }).catch(function(e) {
533
+ delete(k.vaultEntries);
534
+ return k;
535
+ });
536
+ }).then(function(k) {
398
537
  var vi = new libVES.VaultItem({type: "password"},self);
399
538
  k.setField('vaultItems',[vi]);
400
539
  return Promise.resolve(veskey).then(function(v) {
401
540
  if (!v) throw new libVES.Error('InvalidValue','VESkey cannot be empty');
402
541
  return vi.shareWith([me],v,false).then(function() {
403
- return k.post().then(function(post) {
404
- self.reset(post);
405
- return k;
542
+ return self.elevateAuth(optns);
543
+ }).then(function(optns) {
544
+ return k.post(undefined, undefined, optns).then(function(post) {
545
+ return k.setField('id', post.id, false).then(function() {
546
+ k.fieldUpdate = {id: true};
547
+ return k;
548
+ });
406
549
  });
407
550
  });
408
551
  });
@@ -410,15 +553,44 @@ libVES.prototype = {
410
553
  });
411
554
  });
412
555
  },
556
+ setAnonymousKey: function(veskey, optns) {
557
+ var self = this;
558
+ return this.prepareExternals({externalId: self.externalId}).then(function(ext) {
559
+ if (!veskey) throw new libVES.Error('InvalidValue','VESkey cannot be empty');
560
+ return (new libVES.VaultKey({type: 'secondary', algo: (optns && optns.algo ? optns.algo : self.keyAlgo), externals: ext},self)).generate(veskey, optns).then(function(k) {
561
+ return k.post(undefined, ['encSessionToken'], optns).then(function(post) {
562
+ return k.setField('id', post.id, false).then(function() {
563
+ k.fieldUpdate = {id: true};
564
+ });
565
+ }).catch(function(e) {
566
+ if (!e || e.code != 'Unauthorized') throw e;
567
+ }).then(function() {
568
+ return self.unlock(veskey);
569
+ });
570
+ });
571
+ });
572
+ },
413
573
  setShadow: function(usrs,optns) {
414
- if (!optns || !optns.n) return Promise.reject(new libVES.Error('InvalidValue','optns.n must be an integer'));
415
574
  var self = this;
575
+ if (!(usrs instanceof Array)) return Promise.reject(new libVES.Error('InvalidValue', 'usrs must be an array'));
576
+ if (!usrs.length) {
577
+ return self.getShadowKey().then(function(sh) {
578
+ return (sh ? sh.getId().then(function(id) {
579
+ return self.elevateAuth(optns).then(function(optns) {
580
+ return self.post('vaultKeys/' + id, {'$op': 'delete'}, undefined, optns). then(function() {
581
+ return null;
582
+ });
583
+ });
584
+ }) : null);
585
+ });
586
+ }
587
+ if (!optns || !optns.n) return Promise.reject(new libVES.Error('InvalidValue','optns.n must be an integer'));
416
588
  var rkey = new Uint8Array(32);
417
589
  window.crypto.getRandomValues(rkey);
418
590
  var algo = optns.v ? libVES.Scramble.algo[optns.v] : libVES.Scramble.RDX;
419
591
  if (!algo) return Promise.reject(new libVES.Error('InvalidValue','Unknown scramble algorithm: ' + optns.v));
420
592
  var s = new algo(optns.n);
421
- return s.explode(rkey,usrs.length).then(function(tkns) {
593
+ return s.explode(rkey,usrs.length,optns).then(function(tkns) {
422
594
  return self.me().then(function(me) {
423
595
  me.activeVaultKeys = undefined;
424
596
  return me.setField('shadowVaultKey',new libVES.VaultKey({type: 'shadow', user: me, algo: self.keyAlgo},self).generate(rkey,optns),false).then(function(k) {
@@ -434,14 +606,16 @@ libVES.prototype = {
434
606
  delete(libVES.Object._refs);
435
607
  return vis;
436
608
  }));
437
- return k.post();
609
+ return self.elevateAuth(optns).then(function(optns) {
610
+ return k.post(undefined, undefined, optns);
611
+ });
438
612
  });
439
613
  });
440
614
  }).catch(function(e) {
441
615
  me.shadowVaultKey = undefined;
442
616
  throw e;
443
617
  }).then(function() {
444
- me.currentVaultKey = me.activeVaultKeys = undefined;
618
+ me.currentVaultKey = me.shadowVaultKey = me.activeVaultKeys = undefined;
445
619
  return me.getShadowVaultKey();
446
620
  });
447
621
  });
@@ -467,30 +641,11 @@ libVES.prototype = {
467
641
  putValue: function(fileRef,value,shareWith) {
468
642
  var self = this;
469
643
  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() {
644
+ return vaultItem.setField('type',libVES.VaultItem.Type._detect(value)).then(function() {
490
645
  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}]);
646
+ return vi.getShareList();
647
+ }).catch(function(e) {
648
+ return self.usersToKeys([{domain: (fileRef.domain || VES.domain)}]);
494
649
  })).then(function(shareWith) {
495
650
  return vaultItem.shareWith(shareWith,value);
496
651
  });
@@ -502,6 +657,16 @@ libVES.prototype = {
502
657
  return vaultItem.shareWith(shareWith);
503
658
  });
504
659
  },
660
+ fileExists: function(fileRef) {
661
+ return this.getFileItem(fileRef).then(function(vaultItem) {
662
+ return vaultItem.getId().then(function(id) {
663
+ return true;
664
+ }).catch(function(e) {
665
+ if (e.code == 'NotFound') return false;
666
+ throw e;
667
+ });
668
+ });
669
+ },
505
670
  deleteFile: function(fileRef) {
506
671
  return this.getFile(fileRef).then(function(file) {
507
672
  return file.delete();
@@ -539,8 +704,8 @@ libVES.prototype = {
539
704
  hashText: function(text,cls) {
540
705
  if (cls) cls = cls.split('.')[0];
541
706
  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) {
707
+ return libVES.getModule(libVES.Util,['Hash',cls]).then(function(mod) {
708
+ return mod.hash(libVES.Util.StringToByteArray(text)).then(function(buf) {
544
709
  return cls + '.' + libVES.Util.ByteArrayToB64W(buf);
545
710
  });
546
711
  });
@@ -579,8 +744,6 @@ libVES.prototype = {
579
744
  }).then(function() {
580
745
  chain = chain.then(function(ct) {
581
746
  return vaultKey.rekey().then(function() {
582
- if (self.onRekey) return self.onRekey(vaultKey);
583
- }).then(function() {
584
747
  return ct + 1;
585
748
  });
586
749
  }).catch(function(e) {console.log(e);});;
@@ -597,35 +760,104 @@ libVES.prototype = {
597
760
  var self = this;
598
761
  return self.me().then(function(me) {
599
762
  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
- });
763
+ return Promise.all(vaultKeys.map(function(e,i) {
764
+ return e.getType();
765
+ })).then(function(types) {
766
+ var rs = [];
767
+ for (var i = 0; i < types.length; i++) switch (types[i]) {
768
+ case 'recovery': case 'shadow':
769
+ rs.push(vaultKeys[i].getRecovery());
770
+ }
771
+ return Promise.all(rs);
610
772
  });
773
+ });
611
774
  });
612
775
  },
613
776
  getFriendsRecoveries: function() {
614
777
  var self = this;
615
778
  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();
779
+ return me.getFriendsKeyItems().then(function(vaultItems) {
780
+ var vaultKeys = [];
781
+ return Promise.all(vaultItems.map(function(e,i) {
782
+ return e.getVaultKey().then(function(vk) {
783
+ vaultKeys[i] = vk;
784
+ if (vk) return vk.getType();
785
+ });
619
786
  })).then(function(types) {
620
787
  var rs = [];
621
788
  for (var i = 0; i < types.length; i++) switch (types[i]) {
622
789
  case 'recovery': case 'shadow':
623
- rs.push(vaultKeys[i].getRecovery());
790
+ rs.push(vaultKeys[i].getRecovery([vaultItems[i]]));
624
791
  }
625
792
  return Promise.all(rs);
626
793
  });
627
794
  });
628
795
  });
796
+ },
797
+ getUnlockableKeys: function() {
798
+ var self = this;
799
+ return self.getVaultKey().then(function(vk) {
800
+ return vk.getField('unlockableVaultKeys').then(function(vks) {
801
+ var rs = {};
802
+ return Promise.all(vks.map(function(e, i) {
803
+ return e.getId().then(function(id) {
804
+ rs[id] = e;
805
+ });
806
+ })).then(function() {
807
+ return rs;
808
+ });
809
+ });
810
+ });
811
+ },
812
+ getAttn: function() {
813
+ var self = this;
814
+ var rs = {};
815
+ return self.get('attn').then(function(attn) {
816
+ return attn ? Promise.all([(attn.vaultKeys ? Promise.all(attn.vaultKeys.map(function(vk, i) {
817
+ return new libVES.VaultKey(vk, self);
818
+ })).then(function(vks) {
819
+ rs.vaultKeys = vks;
820
+ }) : null)]) : null;
821
+ }).then(function() {
822
+ return rs;
823
+ });
824
+ },
825
+ handleAttn: function(attn) {
826
+ var self = this;
827
+ return (attn ? Promise.resolve(attn) : self.getAttn()).then(function(attn) {
828
+ if (attn) return Promise.all([(attn.vaultKeys ? self.attnVaultKeys(attn.vaultKeys) : null)]);
829
+ });
830
+ },
831
+ attnVaultKeys: function(vkeys) {
832
+ var self = this;
833
+ return Promise.all(vkeys.map(function(vkey, i) {
834
+ console.log('attnVaultKeys:', vkey);
835
+ return vkey.getType().then(function(type) {
836
+ switch (type) {
837
+ case 'temp':
838
+ case 'lost':
839
+ return vkey.getUser().then(function(user) {
840
+ return self.me().then(function(me) {
841
+ return Promise.all([user.getId(), me.getId()]).then(function(ids) {
842
+ if (ids[0] == ids[1]) return type == 'temp' ? vkey.rekey()
843
+ : self.elevateAuth({authVaultKey: vkey}).then(function(optns) {
844
+ return vkey.rekey(optns);
845
+ });
846
+ else return vkey.getVaultItems().then(function(vis) {
847
+ return Promise.all(vis.map(function(vi, i) {
848
+ return vi.reshareWith([user]);
849
+ }));
850
+ });
851
+ });
852
+ });
853
+ });
854
+ case 'recovery':
855
+ return vkey.getRecovery().then(function(rcv) {
856
+ return rcv.recover();
857
+ });
858
+ }
859
+ });
860
+ }));
629
861
  }
630
862
  };
631
863
 
@@ -756,7 +988,7 @@ libVES.Util = {
756
988
  }
757
989
  },
758
990
  ASN1: {
759
- decode: function(der) {
991
+ decode: function(der,fStruct) {
760
992
  var p = 0;
761
993
  var data = function() {
762
994
  var l = der[p++];
@@ -771,10 +1003,14 @@ libVES.Util = {
771
1003
  };
772
1004
  var rs = [];
773
1005
  for (; p < der.length;) {
1006
+ if (!fStruct && p) {
1007
+ rs.push(der.slice(p));
1008
+ break;
1009
+ }
774
1010
  var tag = der[p++];
775
1011
  switch (tag) {
776
1012
  case 48:
777
- rs.push(libVES.Util.ASN1.decode(data()));
1013
+ rs.push(libVES.Util.ASN1.decode(data(),true));
778
1014
  break;
779
1015
  case 6:
780
1016
  rs.push(new libVES.Util.OID(data()));
@@ -796,7 +1032,7 @@ libVES.Util = {
796
1032
  }
797
1033
  return rs;
798
1034
  },
799
- encode: function(data) {
1035
+ encode: function(data,fStruct) {
800
1036
  var i2a = function(v) {
801
1037
  if (v < 0) throw new libVES.Error('Internal',"Negative value for ASN.1 integer!");
802
1038
  var rs = [];
@@ -827,10 +1063,10 @@ libVES.Util = {
827
1063
  return rs;
828
1064
  };
829
1065
  var d;
830
- for (var i = 0; i < data.length; i++) switch (typeof(d = data[i])) {
1066
+ for (var i = 0; i < data.length; i++) if (fStruct || !i) switch (typeof(d = data[i])) {
831
1067
  case 'object':
832
1068
  if (d == null) buf(5,new Uint8Array(0));
833
- else if (d instanceof Array) buf(48,libVES.Util.ASN1.encode(d));
1069
+ else if (d instanceof Array) buf(48,libVES.Util.ASN1.encode(d,true));
834
1070
  else if (d instanceof libVES.Util.OID) buf(6,d.getBuffer());
835
1071
  else if (d instanceof Uint8Array || d instanceof ArrayBuffer) buf((d.ASN1type || 4),d);
836
1072
  else throw new libVES.Error('Internal',"ASN.1 encode - Unknown type");
@@ -839,7 +1075,7 @@ libVES.Util = {
839
1075
  buf(2,i2a(d));
840
1076
  break;
841
1077
  default: throw new libVES.Error('Internal',"ASN.1 encode - Unknown type");
842
- }
1078
+ } else bufs.push(d);
843
1079
  var l = 0;
844
1080
  for (var i = 0; i < bufs.length; i++) l += bufs[i].length;
845
1081
  var der = new Uint8Array(l);
@@ -848,7 +1084,7 @@ libVES.Util = {
848
1084
  return der;
849
1085
  },
850
1086
  import: function(der,optns) {
851
- var k = libVES.Util.ASN1.decode(der)[0];
1087
+ var k = optns && optns.decoded || libVES.Util.ASN1.decode(der)[0];
852
1088
  if (!k) throw new libVES.Error('Internal','Empty ASN.1 package?');
853
1089
  var i = 0;
854
1090
  if (typeof(k[i]) == 'number') (optns || (optns = {})).version = k[i++];
@@ -894,6 +1130,11 @@ libVES.Util = {
894
1130
  return crypto.subtle.importKey('pkcs8',der,{name:'RSA-OAEP', hash:'SHA-1'},true,['decrypt']);
895
1131
  });
896
1132
  });
1133
+ },
1134
+ encode: function(key,optns) {
1135
+ return crypto.subtle.exportKey('spki',key).then(function(der) {
1136
+ return libVES.Util.PEM.encode(der,'PUBLIC KEY');
1137
+ });
897
1138
  }
898
1139
  },
899
1140
  PKCS5: {
@@ -908,7 +1149,7 @@ libVES.Util = {
908
1149
  else return fp;
909
1150
  })(args[i],f);
910
1151
  return f('import',optns).then(function(der) {
911
- return crypto.subtle.importKey('pkcs8',der,{name:'RSA-OAEP', hash:'SHA-1'},true,['decrypt']);
1152
+ return libVES.Util.ASN1.import(new Uint8Array(der));
912
1153
  }).catch(function(e) {
913
1154
  throw new libVES.Error('InvalidKey',"Cannot import the private key (Invalid VESkey?)");
914
1155
  });
@@ -930,10 +1171,108 @@ libVES.Util = {
930
1171
  });
931
1172
  }
932
1173
  },
1174
+ PKCS8: {
1175
+ encode: function(key,optns) {
1176
+ var ops = {};
1177
+ for (var k in optns) ops[k] = optns[k];
1178
+ if (!ops.members) ops.members = [
1179
+ libVES.getModule(libVES.Util,'PBKDF2'),
1180
+ libVES.getModule(libVES.Cipher,'AES256CBC')
1181
+ ];
1182
+ return Promise.all(ops.members).then(function(ms) {
1183
+ ops.members = ms;
1184
+ return crypto.subtle.exportKey('pkcs8',key).catch(function(e) {
1185
+ console.log('PKCS8 failed, trying JWK...');
1186
+ return crypto.subtle.exportKey('jwk', key).then(function(jwk) {
1187
+ return libVES.Util.PKCS8.fromJWK(jwk);
1188
+ });
1189
+ }).then(function(der) {
1190
+ ops.content = der;
1191
+ var rec = [];
1192
+ if (ops.password) return libVES.Util.PKCS5.export(function(call,optns) {
1193
+ rec[1] = optns.content;
1194
+ return Promise.resolve();
1195
+ },ops).then(function(data) {
1196
+ rec[0] = data;
1197
+ return libVES.Util.PEM.encode(libVES.Util.ASN1.encode([rec]),'ENCRYPTED PRIVATE KEY');
1198
+ });
1199
+ else if (ops.opentext) return libVES.Util.PEM.encode(der,'PRIVATE KEY');
1200
+ else throw new libVES.Error('Internal','No password for key export (opentext=true to export without password?)');
1201
+ });
1202
+ });
1203
+ },
1204
+ fromJWK: function(jwk) {
1205
+ if (jwk.kty != 'EC') throw new libVES.Error('Internal', 'kty=="EC" expected (workaround for Firefox)');
1206
+ var pubx = libVES.Util.B64ToByteArray(jwk.x);
1207
+ var puby = libVES.Util.B64ToByteArray(jwk.y);
1208
+ var pub = new Uint8Array(pubx.byteLength + puby.byteLength + 2);
1209
+ pub[0] = 0;
1210
+ pub[1] = 4;
1211
+ pub.set(new Uint8Array(pubx), 2);
1212
+ pub.set(new Uint8Array(puby), pubx.byteLength + 2);
1213
+ pub.ASN1type = 3;
1214
+ pub = libVES.Util.ASN1.encode([pub]);
1215
+ pub.ASN1type = 0xa1;
1216
+ return libVES.Util.ASN1.encode([[
1217
+ 0,
1218
+ [
1219
+ new libVES.Util.OID('1.2.840.10045.2.1'),
1220
+ new libVES.Util.OID((function(crvs) {
1221
+ for (var k in crvs) if (crvs[k] == jwk.crv) return k;
1222
+ throw new libVES.Error('Internal', 'Unknown named curve: ' + jwk.crv);
1223
+ })(libVES.Util.EC.namedCurves))
1224
+ ],
1225
+ libVES.Util.ASN1.encode([[
1226
+ 1,
1227
+ libVES.Util.B64ToByteArray(jwk.d),
1228
+ pub
1229
+ ]])
1230
+ ]]).buffer;
1231
+ },
1232
+ toJWK: function(key) {
1233
+ var der = libVES.Util.ASN1.decode(key)[0];
1234
+ var idx = 0;
1235
+ if (!(der[0] instanceof Array)) idx++;
1236
+ switch (String(der[idx][0])) {
1237
+ case '1.2.840.10045.2.1':
1238
+ case '1.3.132.1.12':
1239
+ case '1.3.132.112': // Firefox on Mac - apparently a misspelling of the former
1240
+ break;
1241
+ default:
1242
+ console.log(der);
1243
+ throw new libVES.Error('Internal', 'EC PKCS8 expected');
1244
+ }
1245
+ var der2 = idx ? libVES.Util.ASN1.decode(der[idx + 1])[0] : null;
1246
+ var pub = idx ? (der2[2] ? libVES.Util.ASN1.decode(der2[2])[0] : null) : der[1];
1247
+ var publ = 0;
1248
+ if (pub && pub.byteLength > 2 && pub[1] == 4) publ = pub.byteLength / 2 - 1;
1249
+ return {
1250
+ crv: libVES.Util.EC.namedCurves[der[idx][1]],
1251
+ d: (idx ? libVES.Util.ByteArrayToB64W(der2[1]) : undefined),
1252
+ ext: true,
1253
+ key_ops: (idx ? ['deriveKey', 'deriveBits'] : []),
1254
+ kty: 'EC',
1255
+ x: (publ ? libVES.Util.ByteArrayToB64W(pub.slice(2, publ + 2)) : ''),
1256
+ y: (publ ? libVES.Util.ByteArrayToB64W(pub.slice(publ + 2, 2 * publ + 2)) : '')
1257
+ };
1258
+ }
1259
+ },
933
1260
  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']);
1261
+ deriveKey: function(args, pwd, algo) {
1262
+ return crypto.subtle.importKey('raw', libVES.Util.StringToByteArray(pwd), 'PBKDF2', false, ['deriveKey']).then(function(k) {
1263
+ var keyargs = {name:'PBKDF2', salt:args[0], iterations:args[1]};
1264
+ var ka;
1265
+ if (args[2] && (args[2][0] instanceof libVES.Util.OID)) ka = args[2][0].object().then(function(obj) {
1266
+ obj.setArgs(keyargs, args[2][1]);
1267
+ return keyargs;
1268
+ });
1269
+ else {
1270
+ keyargs.hash = 'SHA-1';
1271
+ ka = Promise.resolve(keyargs);
1272
+ }
1273
+ return ka.then(function(keyargs) {
1274
+ return crypto.subtle.deriveKey(keyargs, k, algo, true, ['encrypt', 'decrypt']);
1275
+ });
937
1276
  });
938
1277
  },
939
1278
  import: function(args,chain,optns) {
@@ -960,9 +1299,61 @@ libVES.Util = {
960
1299
  });
961
1300
  }
962
1301
  },
1302
+ EC: {
1303
+ import: function(args,chain,optns) {
1304
+ return chain('container',optns).then(function(der) {
1305
+ var a = {name:'ECDH',namedCurve:libVES.Util.EC.namedCurves[String(args)]};
1306
+ return crypto.subtle.importKey('spki',der,a,true,[]).catch(function() {;
1307
+ return crypto.subtle.importKey('pkcs8',der,a,true,['deriveKey','deriveBits']).catch(function(e) {
1308
+ console.log('PKCS8 failed, trying JWK...');
1309
+ var jwk = libVES.Util.PKCS8.toJWK(der);
1310
+ return crypto.subtle.importKey('jwk', jwk, a, true, jwk.key_ops);
1311
+ });
1312
+ });
1313
+ });
1314
+ },
1315
+ namedCurves: {
1316
+ "1.3.132.0.10": "P-256",
1317
+ "1.3.132.0.34": "P-384",
1318
+ "1.3.132.0.35": "P-521"
1319
+ }
1320
+ },
963
1321
  Hash: {
964
- SHA256: function(buf) {
965
- return crypto.subtle.digest('SHA-256',buf);
1322
+ SHA1: {
1323
+ setArgs: function(args, optns) {
1324
+ args.hash = 'SHA-1';
1325
+ return args;
1326
+ },
1327
+ hash: function(buf) {
1328
+ return crypto.subtle.digest('SHA-1',buf);
1329
+ }
1330
+ },
1331
+ SHA256: {
1332
+ setArgs: function(args, optns) {
1333
+ args.hash = 'SHA-256';
1334
+ return args;
1335
+ },
1336
+ hash: function(buf) {
1337
+ return crypto.subtle.digest('SHA-256',buf);
1338
+ }
1339
+ },
1340
+ SHA384: {
1341
+ setArgs: function(args, optns) {
1342
+ args.hash = 'SHA-384';
1343
+ return args;
1344
+ },
1345
+ hash: function(buf) {
1346
+ return crypto.subtle.digest('SHA-384',buf);
1347
+ }
1348
+ },
1349
+ SHA512: {
1350
+ setArgs: function(args, optns) {
1351
+ args.hash = 'SHA-512';
1352
+ return args;
1353
+ },
1354
+ hash: function(buf) {
1355
+ return crypto.subtle.digest('SHA-512',buf);
1356
+ }
966
1357
  }
967
1358
  }
968
1359
  };
@@ -993,10 +1384,15 @@ libVES.Util.OID.prototype = {
993
1384
  }
994
1385
  }
995
1386
 
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']);
1387
+ libVES.Util.OID['1.2.840.113549.1.5.13'] = libVES.getModuleFunc(libVES, ['Util', 'PKCS5']);
1388
+ libVES.Util.OID['1.2.840.113549.1.5.12'] = libVES.getModuleFunc(libVES, ['Util', 'PBKDF2']);
1389
+ libVES.Util.OID['2.16.840.1.101.3.4.1.42'] = libVES.getModuleFunc(libVES, ['Cipher', 'AES256CBC']);
1390
+ libVES.Util.OID['1.2.840.113549.1.1.1'] = libVES.getModuleFunc(libVES, ['Util', 'PKCS1']);
1391
+ 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']);
1392
+ libVES.Util.OID['1.2.840.113549.2.7'] = libVES.getModuleFunc(libVES, ['Util', 'Hash', 'SHA1']);
1393
+ libVES.Util.OID['1.2.840.113549.2.9'] = libVES.getModuleFunc(libVES, ['Util', 'Hash', 'SHA256']);
1394
+ libVES.Util.OID['1.2.840.113549.2.10'] = libVES.getModuleFunc(libVES, ['Util', 'Hash', 'SHA384']);
1395
+ libVES.Util.OID['1.2.840.113549.2.11'] = libVES.getModuleFunc(libVES, ['Util', 'Hash', 'SHA512']);
1000
1396
 
1001
1397
  libVES.Math = {
1002
1398
  pad: function(a,n) {
@@ -1374,10 +1770,6 @@ libVES.External = function(data,VES,refs) {
1374
1770
  this.init(data,VES,refs);
1375
1771
  };
1376
1772
 
1377
- libVES.Lockbox = function(data,VES,refs) {
1378
- this.init(data,VES,refs);
1379
- };
1380
-
1381
1773
  libVES.File = function(data,VES,refs) {
1382
1774
  this.init(data,VES,refs);
1383
1775
  };
@@ -1386,7 +1778,7 @@ libVES.User.prototype = new libVES.Object({
1386
1778
  apiUri: 'users',
1387
1779
  fieldList: {id: true, email: true, type: true, firstName: true, lastName: true},
1388
1780
  fieldExtra: {vaultKeys: true, activeVaultKeys: true, currentVaultKey: true},
1389
- fieldClass: {vaultKeys: libVES.VaultKey, activeVaultKeys: libVES.VaultKey, currentVaultKey: libVES.VaultKey, shadowVaultKey: libVES.VaultKey, friendsVaultKeys: libVES.VaultKey},
1781
+ fieldClass: {vaultKeys: libVES.VaultKey, activeVaultKeys: libVES.VaultKey, currentVaultKey: libVES.VaultKey, shadowVaultKey: libVES.VaultKey, friendsKeyItems: libVES.VaultItem},
1390
1782
  getEmail: function() {
1391
1783
  return this.getField('email');
1392
1784
  },
@@ -1416,11 +1808,10 @@ libVES.User.prototype = new libVES.Object({
1416
1808
  });
1417
1809
  return this.getField('activeVaultKeys');
1418
1810
  },
1419
- getFriendsVaultKeys: function() {
1420
- return this.getField('friendsVaultKeys');
1811
+ getFriendsKeyItems: function() {
1812
+ return this.getField('friendsKeyItems');
1421
1813
  },
1422
1814
  getCurrentVaultKey: function() {
1423
-
1424
1815
  return this.getField('currentVaultKey');
1425
1816
  },
1426
1817
  getShadowVaultKey: function() {
@@ -1452,9 +1843,9 @@ libVES.User.prototype = new libVES.Object({
1452
1843
  libVES.VaultKey.prototype = new libVES.Object({
1453
1844
  apiUri: 'vaultKeys',
1454
1845
  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},
1846
+ fieldClass: {user: libVES.User, vaultItems: libVES.VaultItem, externals: libVES.External, unlockableVaultKeys: libVES.VaultKey, creator: libVES.User},
1456
1847
  fieldExtra: {user: true, vaultItems: true},
1457
- fieldSets: [{vaultEntries: {id: true, encData: true, vaultItem: {id: true}}},{type: true, algo: true, publicKey: true}],
1848
+ fieldSets: [{vaultEntries: {id: true, encData: true, vaultItem: {id: true}}},{type: true, algo: true, publicKey: true}, {unlockableVaultKeys: {id: true, type: true}}],
1458
1849
  getAlgo: function() {
1459
1850
  return this.getField('algo');
1460
1851
  },
@@ -1478,18 +1869,12 @@ libVES.VaultKey.prototype = new libVES.Object({
1478
1869
  getVaultItems: function() {
1479
1870
  return this.getField('vaultItems');
1480
1871
  },
1481
- getSharedKeys: function() {
1482
- return this.getField('sharedKeys');
1483
- },
1484
1872
  getExternals: function() {
1485
1873
  return this.getField('externals');
1486
1874
  },
1487
1875
  getUser: function() {
1488
1876
  return this.getField('user');
1489
1877
  },
1490
- getVaultItems: function() {
1491
- return this.getField('vaultItems');
1492
- },
1493
1878
  resolveVESkey: function(veskey) {
1494
1879
  if (veskey) return Promise.resolve(veskey);
1495
1880
  var self = this;
@@ -1497,6 +1882,7 @@ libVES.VaultKey.prototype = new libVES.Object({
1497
1882
  switch (t) {
1498
1883
  case 'secondary':
1499
1884
  case 'temp':
1885
+ case 'lost':
1500
1886
  return self.getVaultItems().then(function(vis) {
1501
1887
  var f = function(vis) {
1502
1888
  if (!vis.length) throw new libVES.Error('InvalidKey','Cannot unlock the secondary key');
@@ -1564,10 +1950,12 @@ libVES.VaultKey.prototype = new libVES.Object({
1564
1950
  self.setField('algo',wc.engine.tag);
1565
1951
  if (!wc.privateKey) throw new libVES.Error('InvalidValue','Private key expected');
1566
1952
  return wc;
1567
- }) : this.engine().then(function(e) {
1568
- return e.generate(optns).then(function(ks) {
1569
- ks.engine = e;
1570
- return ks;
1953
+ }) : (optns && optns.algo ? self.setField('algo',optns.algo) : Promise.resolve()).then(function() {
1954
+ return self.engine().then(function(e) {
1955
+ return e.generate(optns).then(function(ks) {
1956
+ ks.engine = e;
1957
+ return ks;
1958
+ });
1571
1959
  });
1572
1960
  });
1573
1961
  return Promise.resolve(veskey).then(function(v) {
@@ -1611,16 +1999,14 @@ libVES.VaultKey.prototype = new libVES.Object({
1611
1999
  var self = this;
1612
2000
  var old_vis = {};
1613
2001
  return (self.vaultEntries ? self.vaultEntries.then(function(old_ves) {
1614
- return old_ves.map(function(ve,i) {
2002
+ if (old_ves) return old_ves.map(function(ve,i) {
1615
2003
  old_vis[ve.vaultItem.id] = true;
1616
2004
  });
1617
2005
  }) : Promise.resolve(null)).then(function() {
1618
2006
  return self.setField('vaultEntries',key.unlock(veskey).then(function() {
1619
2007
  return key.getVaultEntries().then(function(ves) {
1620
2008
  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) {
2009
+ return old_vis[ve.vaultItem.id] ? null : key.decrypt(ve.encData).then(function(ptxt) {
1624
2010
  return self.encrypt(ptxt).then(function(ctxt) {
1625
2011
  return {
1626
2012
  vaultItem: {id: ve.vaultItem.id},
@@ -1633,6 +2019,8 @@ libVES.VaultKey.prototype = new libVES.Object({
1633
2019
  "$op": "ignore"
1634
2020
  };
1635
2021
  });
2022
+ }).filter(function(e, i) {
2023
+ return !!e;
1636
2024
  }));
1637
2025
  });
1638
2026
  }));
@@ -1640,7 +2028,7 @@ libVES.VaultKey.prototype = new libVES.Object({
1640
2028
  return self;
1641
2029
  });
1642
2030
  },
1643
- rekey: function() {
2031
+ rekey: function(optns) {
1644
2032
  var self = this;
1645
2033
  return self.getUser().then(function(user) {
1646
2034
  return self.getExternals().then(function(exts) {
@@ -1661,20 +2049,24 @@ libVES.VaultKey.prototype = new libVES.Object({
1661
2049
  }));
1662
2050
  }).then(function(keys) {
1663
2051
  return user.setField('vaultKeys',keys).then(function() {
1664
- return user.post(null,{vaultEntries: true},{refs: {'#/': user}}).then(function(data) {
2052
+ if (!optns) optns = {};
2053
+ optns.refs = {'#/': user};
2054
+ return user.post(null,{vaultEntries: true}, optns).then(function(data) {
1665
2055
  self.setFields(data,false);
1666
- return self;
2056
+ return self.VES.onRekey ? self.VES.onRekey(self).catch(function() {}).then(function() {
2057
+ return self;
2058
+ }) : self;
1667
2059
  });
1668
2060
  });
1669
2061
  });
1670
2062
  });
1671
2063
  },
1672
- getRecovery: function() {
2064
+ getRecovery: function(myItems) {
1673
2065
  var self = this;
1674
2066
  return self.getType().then(function(t) {
1675
2067
  switch (t) {
1676
2068
  case 'shadow': case 'recovery':
1677
- return new libVES.Recovery(self);
2069
+ return new libVES.Recovery(self, myItems);
1678
2070
  default: throw new libVES.Error('InvalidValue','Recovery is not applicable for VaultKey type ' + t);
1679
2071
  }
1680
2072
  });
@@ -1682,18 +2074,21 @@ libVES.VaultKey.prototype = new libVES.Object({
1682
2074
  getSessionToken: function() {
1683
2075
  var self = this;
1684
2076
  return this.getField('encSessionToken').then(function(tk) {
2077
+ if (!tk) return null;
1685
2078
  return self.decrypt(tk).then(function(b) {
1686
2079
  return libVES.Util.ByteArrayToString(b);
1687
2080
  });
1688
2081
  });
1689
2082
  },
1690
- reshareVESkey: function(veskey) {
2083
+ reshareVESkey: function(veskey, optns) {
1691
2084
  var self = this;
1692
2085
  return self.getVaultItems().then(function(vaultItems) {
1693
2086
  return self.getUser().then(function(user) {
1694
2087
  return Promise.all(vaultItems.map(function(vaultItem,i) {
1695
2088
  return vaultItem.getType().then(function(t) {
1696
- if (t == 'password') return vaultItem.reshareWith([user],veskey);
2089
+ if (t == 'password') return vaultItem.reshareWith([user], veskey, false).then(function(vi) {
2090
+ return vi.post(undefined, undefined, optns);
2091
+ });
1697
2092
  });
1698
2093
  }));
1699
2094
  });
@@ -1708,29 +2103,33 @@ libVES.VaultItem.prototype = new libVES.Object({
1708
2103
  apiUri: 'vaultItems',
1709
2104
  fieldList: {id: true},
1710
2105
  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',
2106
+ fieldSets: [{type: true, meta: true},{vaultEntries: {id: true, encData: true, vaultKey: {id: true, type: true, user: {id: true}, algo: true, externals: {id: true}}}},{vaultKey: true, file: true}],
2107
+ defaultCipher: 'AES256GCM',
1713
2108
  getRaw: function() {
1714
2109
  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
- }
2110
+ var f = function(vaultEntries, vaultKeys) {
2111
+ var i = 0;
2112
+ var fn = function() {
2113
+ if (vaultEntries) for (; i < vaultEntries.length; i++) {
2114
+ var k, d;
2115
+ if ((d = vaultEntries[i].encData) != null && vaultKeys[(k = vaultEntries[i].vaultKey).id]) {
2116
+ i++;
2117
+ return new libVES.VaultKey(k, self.VES).decrypt(d).catch(fn);
1725
2118
  }
1726
- return Promise.reject(new libVES.Error('Invalid Key',"No unlocked key to decrypt the item",{vaultItem: self}));
1727
- };
1728
- return fn();
2119
+ }
2120
+ return Promise.reject(new libVES.Error('InvalidKey',"No unlocked key to decrypt the item",{vaultItem: self}));
1729
2121
  };
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);
2122
+ return fn();
2123
+ };
2124
+ var vaultEntries = [];
2125
+ if (self.vaultEntryByKey) for (var k in self.vaultEntryByKey) vaultEntries.push(self.vaultEntryByKey[k]);
2126
+ return f(vaultEntries, self.VES.unlockedKeys).catch(function() {
2127
+ return self.getVaultEntries().then(function(vaultEntries) {
2128
+ return f(vaultEntries, self.VES.unlockedKeys).catch(function() {
2129
+ return self.VES.getUnlockableKeys().then(function(vks) {
2130
+ return f(vaultEntries, vks);
2131
+ });
2132
+ });
1734
2133
  });
1735
2134
  });
1736
2135
  },
@@ -1749,7 +2148,7 @@ libVES.VaultItem.prototype = new libVES.Object({
1749
2148
  getVaultEntries: function() {
1750
2149
  var self = this;
1751
2150
  return this.getField('vaultEntries').then(function(ves) {
1752
- for (var i = 0; i < ves.length; i++) self.vaultEntryByKey[ves[i].vaultKey.id] = ves[i];
2151
+ if (ves) for (var i = 0; i < ves.length; i++) self.vaultEntryByKey[ves[i].vaultKey.id] = ves[i];
1753
2152
  return ves;
1754
2153
  });
1755
2154
  },
@@ -1759,9 +2158,6 @@ libVES.VaultItem.prototype = new libVES.Object({
1759
2158
  getFile: function() {
1760
2159
  return this.getField('file');
1761
2160
  },
1762
- getLockbox: function() {
1763
- return this.getField('lockbox');
1764
- },
1765
2161
  parse: function(buf) {
1766
2162
  var self = this;
1767
2163
  return this.getType().then(function(type) {
@@ -1780,42 +2176,49 @@ libVES.VaultItem.prototype = new libVES.Object({
1780
2176
  });
1781
2177
  });
1782
2178
  },
2179
+ resolveRaw: function(val) {
2180
+ return (val == null ? this.getRaw() : this.build(val));
2181
+ },
1783
2182
  shareWith: function(usrs,val,save) {
1784
2183
  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) {
2184
+ return self.VES.usersToKeys(usrs).then(function(ks) {
2185
+ return (val == null || self.id ? self.getVaultEntries().then(function(ves) {
2186
+ var k_ves = {};
2187
+ var k_used = {};
2188
+ for (var j = 0; j < ves.length; j++) k_ves[ves[j].vaultKey.id] = ves[j];
2189
+ return Promise.all(ks.map(function(k,j) {
2190
+ return k.getId().then(function(k_id) {
2191
+ k_used[k_id] = true;
2192
+ return k_ves[k_id];
2193
+ }).catch(function(){});
2194
+ })).then(function(old_ves) {
2195
+ for (var k_id in k_ves) if (!k_used[k_id]) old_ves.push(k_ves[k_id]);
2196
+ return old_ves;
2197
+ });
2198
+ }) : Promise.resolve([])).then(function(old_ves) {
2199
+ var new_ves = [];
2200
+ var set_ves = [];
2201
+ var valr = null;
2202
+ return Promise.all(ks.map(function(k,j) {
2203
+ return new_ves[j] = (old_ves[j] || (valr != null ? valr : valr = self.resolveRaw(val)).then(function(v) {
2204
+ return k.encrypt(v).then(function(ctext) {
2205
+ return (function(refs) {
2206
+ if (refs) for (var i in refs) if (refs[i] === k) return Promise.resolve({'$ref':i});
2207
+ return k.postData(null,refs);
2208
+ })(libVES.Object._refs).then(function(pd) {
1806
2209
  return set_ves.push({vaultKey: pd, encData: ctext});
1807
2210
  });
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
- });
2211
+ });
2212
+ }));
2213
+ })).then(function() {
2214
+ return Promise.all(old_ves.slice(ks.length).map(function(ve,j) {
2215
+ return (new libVES.VaultKey(ve.vaultKey,self.VES)).matchVaults(ks).then(function(f) {
2216
+ if (f === false) set_ves.push({vaultKey: {id: ve.vaultKey.id}, '$op': 'delete'});
2217
+ });
2218
+ }));
2219
+ }).then(function() {
2220
+ if (!set_ves.length) return save = false;
2221
+ return self.setField('vaultEntries',set_ves);
1819
2222
  });
1820
2223
  });
1821
2224
  }).then(function() {
@@ -1835,7 +2238,9 @@ libVES.VaultItem.prototype = new libVES.Object({
1835
2238
  var m_curr_ks = {};
1836
2239
  for (var i = 0; i < curr_ks.length; i++) m_curr_ks[curr_ids[i]] = curr_ks[i];
1837
2240
  return Promise.all(new_ks.map(function(k,i) {
1838
- return k.getId();
2241
+ return k.getId().catch(function(e) {
2242
+ if (e.code != 'NotFound') throw e;
2243
+ });
1839
2244
  })).then(function(new_ids) {
1840
2245
  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
2246
  return self.shareWith(curr_ks,val,save);
@@ -1898,8 +2303,9 @@ libVES.VaultItem.Type = {
1898
2303
  return new ci(new Uint8Array(buf));
1899
2304
  });
1900
2305
  },
1901
- build: function(data) {
2306
+ build: function(data) {
1902
2307
  if (!(data instanceof libVES.Cipher)) throw new libVES.Error('InvalidData',"Content of a VaultItem type 'file' must be libVES.Cipher");
2308
+ this.setField('meta', data.getMeta());
1903
2309
  return data.getSecret();
1904
2310
  }
1905
2311
  },
@@ -1921,10 +2327,13 @@ libVES.VaultItem.Type.password = libVES.VaultItem.Type.string;
1921
2327
  libVES.File.prototype = new libVES.Object({
1922
2328
  apiUri: 'files',
1923
2329
  fieldList: {id: true},
1924
- fieldClass: {externals: libVES.External},
2330
+ fieldClass: {externals: libVES.External, creator: libVES.User},
1925
2331
  getExternals: function() {
1926
2332
  return this.getField('externals');
1927
- }
2333
+ },
2334
+ getCreator: function() {
2335
+ return this.getField('creator');
2336
+ },
1928
2337
  });
1929
2338
 
1930
2339
  libVES.External.prototype = new libVES.Object({
@@ -1944,49 +2353,171 @@ libVES.External.prototype = new libVES.Object({
1944
2353
  });
1945
2354
 
1946
2355
  libVES.Algo = {
1947
- RSA: {
1948
- tag: 'RSA',
2356
+ ECDH: {
2357
+ tag: 'ECDH',
1949
2358
  decrypt: function(k,buf) {
1950
- return crypto.subtle.decrypt('RSA-OAEP',k,buf);
2359
+ var b = new Uint8Array(buf);
2360
+ var data = libVES.Util.ASN1.decode(b);
2361
+ if (!data || data.length < 2) throw new libVES.Error('InvalidValue','Error parsing ECIES: Invalid ciphertext?');
2362
+ return libVES.Util.ASN1.import(b.slice(0,b.byteLength - data[1].byteLength),{decoded:data[0]}).then(function(eKey) {
2363
+ return libVES.Algo.ECDH.cipher(k,eKey).then(function(ci) {
2364
+ return ci.decrypt(data[1],true).catch(function(e) {
2365
+ if (e instanceof libVES.Error) throw e;
2366
+ throw new libVES.Error('InvalidValue','Error decrypting ECIES payload: Invalid ciphertext?',{error:e});
2367
+ });
2368
+ }).catch(function(e) {
2369
+ if (e instanceof libVES.Error) throw e;
2370
+ throw new libVES.Error('InvalidValue','Error negotiating DH cipher key: Invalid ciphertext?',{error:e});
2371
+ });
2372
+ }).catch(function(e) {
2373
+ if (e instanceof libVES.Error) throw e;
2374
+ throw new libVES.Error('InvalidValue','Error loading EC ephemeral: Invalid ciphertext?',{error:e});
2375
+ });
1951
2376
  },
1952
2377
  encrypt: function(k,buf) {
1953
- return crypto.subtle.encrypt('RSA-OAEP',k,buf);
2378
+ return libVES.Algo.ECDH.eKey(k).then(function(eKey) {
2379
+ return libVES.Algo.ECDH.cipher(eKey.privateKey,k).then(function(ci) {
2380
+ return Promise.all([
2381
+ crypto.subtle.exportKey('spki',eKey.publicKey),
2382
+ ci.encrypt(buf,true)
2383
+ ]).then(function(bufs) {
2384
+ var buf = new Uint8Array(bufs[0].byteLength + bufs[1].byteLength);
2385
+ buf.set(new Uint8Array(bufs[0]),0);
2386
+ buf.set(new Uint8Array(bufs[1]),bufs[0].byteLength);
2387
+ return buf;
2388
+ });
2389
+ }).catch(function(e) {
2390
+ if (e instanceof libVES.Error) throw e;
2391
+ throw new libVES.Error('InvalidValue','Error generating ECIES DH cipher',{error:e});
2392
+ });
2393
+ });
2394
+ },
2395
+ eKey: function(pub) {
2396
+ return crypto.subtle.exportKey('jwk',pub).then(function(kdata) {
2397
+ return crypto.subtle.generateKey({name:'ECDH',namedCurve: kdata.crv},true,['deriveKey','deriveBits']).catch(function(e) {
2398
+ throw new libVES.Error('InvalidValue','Error generating ECIES ephemeral',{error: e});
2399
+ });
2400
+ });
2401
+ },
2402
+ cipher: function(priv,pub) {
2403
+ return libVES.getModule(libVES.Cipher,'AES256GCMp').then(function(m) {
2404
+ return Promise.resolve(libVES.Algo.ECDH.curveBytes[pub.algorithm.namedCurve] || crypto.subtle.exportKey('jwk',pub).then(function(jwk) {
2405
+ return jwk.x.length * 3 / 4;
2406
+ })).then(function(len) {
2407
+ return crypto.subtle.deriveBits({name:'ECDH',public:pub},priv,8 * len).then(function(raw) {
2408
+ return crypto.subtle.digest({name:'SHA-384'},raw).then(function(buf) {
2409
+ return new m(new Uint8Array(buf).slice(0,m.prototype.keySize + m.prototype.ivSize));
2410
+ });
2411
+ });
2412
+ });
2413
+ });
2414
+ },
2415
+ curveBytes: {
2416
+ 'P-256': 32,
2417
+ 'P-384': 48,
2418
+ 'P-521': 66
1954
2419
  },
1955
2420
  import: function(data,optns) {
1956
2421
  return libVES.Util.PEM.import(data,optns);
1957
2422
  },
1958
2423
  export: function(data,optns) {
1959
2424
  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
- });
2425
+ case 'private': return libVES.Util.PKCS8.encode(data, optns);
1985
2426
  case 'public':
1986
- return crypto.subtle.exportKey('spki',data).then(function(der) {
1987
- return libVES.Util.PEM.encode(der,'PUBLIC KEY');
2427
+ return crypto.subtle.exportKey('spki', data).then(function(der) {
2428
+ var asn = libVES.Util.ASN1.decode(new Uint8Array(der))[0];
2429
+ console.log(asn);
2430
+ switch (String(asn[0][0])) {
2431
+ case '1.2.840.10045.2.1':
2432
+ return der;
2433
+ case '1.3.132.1.12':
2434
+ case '1.3.132.112': // Firefox on Mac - apparently a misspelling of the former
2435
+ asn[0][0] = new libVES.Util.OID('1.2.840.10045.2.1');
2436
+ asn[1].ASN1type = 3;
2437
+ return libVES.Util.ASN1.encode([asn]);
2438
+ default:
2439
+ console.log(asn);
2440
+ throw new libVES.Error('Internal', 'Unexpected EC pubkey format');
2441
+ }
2442
+ return der;
2443
+ }).then(function(der) {
2444
+ return libVES.Util.PEM.encode(der, 'PUBLIC KEY');
1988
2445
  });
1989
2446
  }
2447
+ throw new libVES.Error('Internal', "Unknown type of key object");
2448
+ },
2449
+ generate: function(optns) {
2450
+ var op = {name:'ECDH', namedCurve:'P-384'};
2451
+ if (optns) for (var k in optns) op[k] = optns[k];
2452
+ return crypto.subtle.generateKey(op,true,['deriveKey','deriveBits']);
2453
+ },
2454
+ getPublic: function(priv) {
2455
+ return crypto.subtle.exportKey('jwk',priv).then(function(k) {
2456
+ return crypto.subtle.importKey('jwk',{
2457
+ crv: k.crv,
2458
+ ext: true,
2459
+ key_ops: [],
2460
+ kty: 'EC',
2461
+ x: k.x,
2462
+ y: k.y
2463
+ },{name:'ECDH'},true,[]);
2464
+ });
2465
+ }
2466
+ },
2467
+ RSA: {
2468
+ tag: 'RSA',
2469
+ decrypt: function(k,buf) {
2470
+ var self = libVES.Algo.RSA;
2471
+ var maxl = (k.algorithm.modulusLength + 7) >> 3;
2472
+ if (buf.byteLength <= maxl) return self.decryptRSA(k,buf);
2473
+ var b = new Uint8Array(buf);
2474
+ return self.decryptRSA(k,b.slice(0,maxl)).then(function(ck) {
2475
+ return self.cipher(ck).then(function(ci) {
2476
+ return ci.decrypt(b.slice(maxl),true);
2477
+ });
2478
+ });
2479
+ },
2480
+ decryptRSA: function(k,buf) {
2481
+ return crypto.subtle.decrypt('RSA-OAEP',k,buf).catch(function(e) {
2482
+ throw new libVES.Error('InvalidValue','Error decrypting RSA OAEP: Invalid ciphertext?',{error:e});
2483
+ });
2484
+ },
2485
+ encrypt: function(k,buf) {
2486
+ var self = libVES.Algo.RSA;
2487
+ var maxl = ((k.algorithm.modulusLength + 7) >> 3) - 48;
2488
+ if (buf.byteLength <= maxl) return self.encryptRSA(k,buf);
2489
+ return self.cipher().then(function(ci) {
2490
+ return Promise.all([
2491
+ ci.getSecret().then(function(sk) {
2492
+ return self.encryptRSA(k,sk);
2493
+ }),
2494
+ ci.encrypt(buf,true)
2495
+ ]).then(function(bufs) {
2496
+ var rs = new Uint8Array(bufs[0].byteLength + bufs[1].byteLength);
2497
+ rs.set(new Uint8Array(bufs[0]),0);
2498
+ rs.set(new Uint8Array(bufs[1]),bufs[0].byteLength);
2499
+ return rs;
2500
+ });
2501
+ });
2502
+ },
2503
+ encryptRSA: function(k,buf) {
2504
+ return crypto.subtle.encrypt('RSA-OAEP',k,buf).catch(function(e) {
2505
+ throw new libVES.Error('InvalidValue','Error encrypting RSA OAEP: Buffer is too long?',{error:e});
2506
+ });;
2507
+ },
2508
+ cipher: function(secret) {
2509
+ return libVES.getModule(libVES.Cipher,'AES256GCMp').then(function(cls) {
2510
+ return new cls(secret);
2511
+ });
2512
+ },
2513
+ import: function(data,optns) {
2514
+ return libVES.Util.PEM.import(data,optns);
2515
+ },
2516
+ export: function(data,optns) {
2517
+ if (data instanceof CryptoKey) switch (data.type) {
2518
+ case 'private': return libVES.Util.PKCS8.encode(data,optns);
2519
+ case 'public': return libVES.Util.PKCS1.encode(data,optns);
2520
+ }
1990
2521
  throw new libVES.Error('Internal',"Unknown type of key object");
1991
2522
  },
1992
2523
  generate: function(optns) {
@@ -2006,7 +2537,6 @@ libVES.Algo = {
2006
2537
  },{name:'RSA-OAEP',hash:'SHA-1'},true,['encrypt']);
2007
2538
  });
2008
2539
  }
2009
-
2010
2540
  },
2011
2541
  RSA_PKCS1_15: {
2012
2542
  tag: 'RSA_PKCS1_15',
@@ -2066,26 +2596,31 @@ libVES.Cipher.prototype = {
2066
2596
  }
2067
2597
  },
2068
2598
  getSecret: function() {
2599
+ var self = this;
2069
2600
  var meta = null;
2070
2601
  if (this.meta) for (var k in this.meta) {
2071
2602
  meta = libVES.Util.StringToByteArray(JSON.stringify(this.meta));
2072
2603
  break;
2073
2604
  }
2074
2605
  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
- });
2606
+ return Promise.all([(this.algo ? this.key.then(function(key) {
2607
+ return crypto.subtle.exportKey("raw", key);
2608
+ }) : this.key), this.IV]).then(function(data) {
2609
+ buf.set(new Uint8Array(data[0]), 0);
2610
+ buf.set(new Uint8Array(data[1]), data[0].byteLength);
2611
+ if (meta) buf.set(new Uint8Array(meta), data[0].byteLength + data[1].byteLength);
2612
+ return buf;
2082
2613
  });
2083
2614
  },
2615
+ getMeta: function() {
2616
+ return {a: this.a};
2617
+ },
2084
2618
  buildKey: function(key) {
2085
2619
  if (!this.algo) return Promise.resolve(key);
2086
2620
  return crypto.subtle.importKey('raw',key,this.algo,true,['encrypt','decrypt']);
2087
2621
  },
2088
- process: function(buf,final,callbk,chunkSize) {
2622
+ process: function(buf,final,callbk,sizefn) {
2623
+ var self = this;
2089
2624
  buf = new Uint8Array(buf);
2090
2625
  if (this.processBuf) {
2091
2626
  var b = new Uint8Array(buf.byteLength + this.processBuf.byteLength);
@@ -2093,9 +2628,24 @@ libVES.Cipher.prototype = {
2093
2628
  b.set(buf,this.processBuf.byteLength);
2094
2629
  buf = b;
2095
2630
  }
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));
2631
+ var over = !final;
2632
+ var fn = function(cprev) {
2633
+ var chunkSize = sizefn();
2634
+ if ((!buf.byteLength && over) || (!final && (!chunkSize || buf.byteLength < chunkSize))) {
2635
+ self.processBuf = buf.byteLength ? buf : null;
2636
+ return Promise.resolve(cprev);
2637
+ }
2638
+ var cl = (chunkSize && chunkSize < buf.byteLength) ? chunkSize : buf.byteLength;
2639
+ if (!chunkSize || cl < chunkSize) over = true;
2640
+ return callbk(buf.slice(0, cl)).then(function(ctext) {
2641
+ buf = buf.slice(cl);
2642
+ var r = new Uint8Array(cprev.byteLength + ctext.byteLength);
2643
+ r.set(cprev, 0);
2644
+ r.set(new Uint8Array(ctext), cprev.byteLength);
2645
+ return r;
2646
+ }).then(fn);
2647
+ };
2648
+ return fn(new Uint8Array(0));
2099
2649
  },
2100
2650
  encryptChunk: function(buf) {
2101
2651
  return Promise.all([this.key,this.algoInfo()]).then(function(info) {
@@ -2111,37 +2661,60 @@ libVES.Cipher.prototype = {
2111
2661
  return Promise.resolve(this.algo);
2112
2662
  },
2113
2663
  encrypt: function(buf,final) {
2114
- return this.process(buf,final,this.encryptChunk.bind(this),this.chunkSizeP);
2664
+ return this.process(buf,final,this.encryptChunk.bind(this),(function() { return this.chunkSizeP; }).bind(this));
2115
2665
  },
2116
2666
  decrypt: function(buf,final) {
2117
- return this.process(buf,final,this.decryptChunk.bind(this),this.chunkSizeC);
2667
+ return this.process(buf,final,this.decryptChunk.bind(this),(function() { return this.chunkSizeC; }).bind(this));
2118
2668
  }
2119
2669
  };
2120
2670
 
2121
- libVES.Cipher.AES256 = function(rec) {
2671
+ libVES.Cipher.NULL = function(rec) {
2122
2672
  this.init(rec);
2673
+ }
2674
+
2675
+ libVES.Cipher.AES = function(data) {
2676
+ for (var k in data) this[k] = data[k];
2123
2677
  };
2124
2678
 
2125
2679
  libVES.Cipher.AES256CBC = function(rec) {
2126
2680
  this.init(rec);
2127
2681
  };
2128
2682
 
2129
- libVES.Cipher.AES256.prototype = new libVES.Cipher({
2130
- keySize: 32,
2131
- ivSize: 32
2683
+ libVES.Cipher.AES256GCM = function(rec) {
2684
+ this.init(rec);
2685
+ };
2686
+
2687
+ libVES.Cipher.AES256GCMp = function(rec) {
2688
+ this.init(rec);
2689
+ };
2690
+
2691
+ libVES.Cipher.AES256GCM1K = function(rec) {
2692
+ this.init(rec);
2693
+ };
2694
+
2695
+ libVES.Cipher.NULL.prototype = new libVES.Cipher({
2696
+ a: 'NULL',
2697
+ keySize: 0,
2698
+ ivSize: 0
2132
2699
  });
2133
2700
 
2134
- libVES.Cipher.AES256CBC.prototype = new libVES.Cipher({
2135
- algo: 'AES-CBC',
2701
+ libVES.Cipher.AES.prototype = new libVES.Cipher({
2136
2702
  keySize: 32,
2137
- ivSize: 16,
2703
+ ivSize: 32,
2138
2704
  algoInfo: function() {
2705
+ var self = this;
2139
2706
  return this.IV.then(function(iv) {
2140
- return {name: 'AES-CBC', iv: iv};
2707
+ return {name: self.algo, iv: iv};
2141
2708
  });
2142
2709
  }
2143
2710
  });
2144
2711
 
2712
+ libVES.Cipher.AES256CBC.prototype = new libVES.Cipher.AES({
2713
+ algo: 'AES-CBC',
2714
+ keySize: 32,
2715
+ ivSize: 16
2716
+ });
2717
+
2145
2718
  libVES.Cipher.AES256CBC.import = function(args,chain,optns) {
2146
2719
  return chain('import').then(function(buf) {
2147
2720
  return crypto.subtle.decrypt({name: 'AES-CBC', iv: args},optns.key,buf);
@@ -2160,6 +2733,88 @@ libVES.Cipher.AES256CBC.info = function(chain,optns) {
2160
2733
  return Promise.resolve({algorithm: {name: 'AES-CBC', length: 256}});
2161
2734
  };
2162
2735
 
2736
+ libVES.Cipher.AES256GCM.prototype = new libVES.Cipher.AES({
2737
+ algo: 'AES-GCM',
2738
+ keySize: 32,
2739
+ ivSize: 12
2740
+ });
2741
+
2742
+ libVES.Cipher.AES256GCMp.prototype = new libVES.Cipher.AES({
2743
+ algo: 'AES-GCM',
2744
+ keySize: 32,
2745
+ ivSize: 12,
2746
+ padSize: 32,
2747
+ encryptChunk: function(buf0) {
2748
+ var buf = new Uint8Array(buf0);
2749
+ var pad = this.padSize - (buf.byteLength % this.padSize) - 1;
2750
+ var bufp = new Uint8Array(buf.byteLength + pad + 1);
2751
+ bufp[0] = pad;
2752
+ bufp.set(buf,1);
2753
+ return libVES.Cipher.prototype.encryptChunk.call(this,bufp);
2754
+ },
2755
+ decryptChunk: function(buf) {
2756
+ return libVES.Cipher.prototype.decryptChunk.call(this,buf).then(function(bufp0) {
2757
+ var bufp = new Uint8Array(bufp0);
2758
+ return bufp.slice(1,bufp.byteLength - bufp[0]);
2759
+ });
2760
+ }
2761
+ });
2762
+
2763
+ libVES.Cipher.AES256GCM1K.prototype = new libVES.Cipher.AES({
2764
+ algo: 'AES-GCM',
2765
+ keySize: 32,
2766
+ ivSize: 12,
2767
+ chunkSizeP: 1024,
2768
+ chunkSizeC: 16,
2769
+ algoInfo: function() {
2770
+ var self = this;
2771
+ if (!this.lastGCM) throw new libVES.Error('Internal', 'GCM HMAC is not buffered');
2772
+ return this.IV.then(function(iv) {
2773
+ var buf = new Uint8Array(iv.byteLength + self.lastGCM.byteLength);
2774
+ buf.set(new Uint8Array(iv), 0);
2775
+ buf.set(new Uint8Array(self.lastGCM), iv.byteLength);
2776
+ return libVES.Util.Hash.SHA256.hash(buf).then(function(sha) {
2777
+ return { name: self.algo, iv: sha.slice(0, self.ivSize) };
2778
+ });
2779
+ });
2780
+ },
2781
+ encryptChunk: function(buf) {
2782
+ var self = this;
2783
+ var seed = null;
2784
+ if (!this.lastGCM) {
2785
+ seed = new Uint8Array(16);
2786
+ crypto.getRandomValues(seed);
2787
+ this.lastGCM = seed;
2788
+ }
2789
+ return Promise.all([this.key, this.algoInfo()]).then(function(info) {
2790
+ return crypto.subtle.encrypt(info[1], info[0], buf).then(function(ctext) {
2791
+ self.lastGCM = new Uint8Array(ctext.slice(-16));
2792
+ if (!seed) return ctext;
2793
+ var rs = new Uint8Array(seed.byteLength + ctext.byteLength);
2794
+ rs.set(seed, 0);
2795
+ rs.set(new Uint8Array(ctext), seed.byteLength);
2796
+ return rs;
2797
+ });
2798
+ });
2799
+ },
2800
+ decryptChunk: function(buf) {
2801
+ var self = this;
2802
+ if (buf.byteLength < 16) throw new libVES.Error('Crypto', 'Invalid ciphertext block size');
2803
+ var rs;
2804
+ if (this.lastGCM) {
2805
+ rs = Promise.all([this.key, this.algoInfo()]).then(function(info) {
2806
+ self.lastGCM = buf.slice(-16);
2807
+ return crypto.subtle.decrypt(info[1], info[0], buf);
2808
+ });
2809
+ } else {
2810
+ rs = Promise.resolve(new Uint8Array(0));
2811
+ this.chunkSizeC = 1040;
2812
+ this.lastGCM = buf;
2813
+ }
2814
+ return rs;
2815
+ }
2816
+ });
2817
+
2163
2818
  libVES.Scramble = {
2164
2819
  RDX: function(x) {
2165
2820
  this.size = x;
@@ -2230,17 +2885,23 @@ libVES.Scramble.RDX.prototype = {
2230
2885
  scramble: function(vec,b) {
2231
2886
  return libVES.Math.mulv(vec,this.getCv(b));
2232
2887
  },
2233
- explode: function(sc,ct) {
2888
+ explode: function(sc,ct,optns) {
2234
2889
  var self = this;
2235
2890
  return this.toVector(sc).then(function(v) {
2236
2891
  var bs = self.getBases(ct);
2237
2892
  var rs = [];
2238
2893
  for (i = 0; i < bs.length; i++) rs[i] = {
2239
- meta: {
2894
+ meta: (function(m) {
2895
+ if (optns) {
2896
+ if (optns.t != null) m.t = optns.t;
2897
+ if (optns.shadow) for (var k in optns.shadow) m[k] = optns.shadow[k];
2898
+ }
2899
+ return m;
2900
+ })({
2240
2901
  v: self.tag,
2241
2902
  n: self.size,
2242
2903
  b: bs[i]
2243
- },
2904
+ }),
2244
2905
  value: self.scramble(v,bs[i])
2245
2906
  }
2246
2907
  return rs;
@@ -2305,8 +2966,9 @@ libVES.Scramble.algo = {
2305
2966
  'RDX1.2': libVES.Scramble.RDX
2306
2967
  };
2307
2968
 
2308
- libVES.Recovery = function(vaultKey) {
2969
+ libVES.Recovery = function(vaultKey, myItems) {
2309
2970
  this.vaultKey = vaultKey;
2971
+ if (myItems) this.myItems = Promise.resolve(myItems);
2310
2972
  };
2311
2973
 
2312
2974
  libVES.Recovery.prototype = {
@@ -2315,7 +2977,7 @@ libVES.Recovery.prototype = {
2315
2977
  var self = this;
2316
2978
  return this.tokens = self.vaultKey.getType().then(function(t) {
2317
2979
  switch (t) {
2318
- case 'shadow': case 'recovery': return self.vaultKey.getField('vaultItems',{
2980
+ case 'shadow': case 'recovery': return (self.myItems ? self.myItems : self.vaultKey.getField('vaultItems',{
2319
2981
  id: true,
2320
2982
  meta: true,
2321
2983
  type: true,
@@ -2326,7 +2988,7 @@ libVES.Recovery.prototype = {
2326
2988
  type: true
2327
2989
  }
2328
2990
  }
2329
- },true).then(function(vis) {
2991
+ },true)).then(function(vis) {
2330
2992
  var frnds = {};
2331
2993
  var fn = function() {
2332
2994
  return self.vaultKey.getUser().then(function(my_u) {
@@ -2336,10 +2998,15 @@ libVES.Recovery.prototype = {
2336
2998
  return Promise.all([
2337
2999
  vi.getVaultEntries().then(function(ves) {
2338
3000
  return Promise.all(ves.map(function(ve) {
2339
- return new libVES.VaultKey(ve.vaultKey,self.vaultKey.VES).getUser().then(function(u) {
3001
+ var vk = new libVES.VaultKey(ve.vaultKey,self.vaultKey.VES);
3002
+ return vk.getUser().then(function(u) {
2340
3003
  return u.getId().then(function(uid) {
2341
- if (uid == my_uid) frnd.assisted = true;
2342
- else {
3004
+ if (uid == my_uid) {
3005
+ frnd.assisted = true;
3006
+ return vk.getType().then(function(type) {
3007
+ if (type == 'current') frnd.current = true;
3008
+ });
3009
+ } else {
2343
3010
  frnd.user = u;
2344
3011
  frnds[uid] = frnd;
2345
3012
  }
@@ -2398,10 +3065,10 @@ libVES.Recovery.prototype = {
2398
3065
  return Promise.all(tkns.map(function(v,i) {
2399
3066
  return v.user.getId();
2400
3067
  })).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
- });
3068
+ return user.getId().then(function(uid) {
3069
+ for (var i = 0; i < uids.length; i++) if (uids[i] == uid) return tkns[i];
3070
+ throw new libVES.Error('InvalidValue','Not a friend: ' + uid);
3071
+ });
2405
3072
  });
2406
3073
  });
2407
3074
  },
@@ -2424,22 +3091,11 @@ libVES.Recovery.prototype = {
2424
3091
  getFriendsAssisted: function() {
2425
3092
  var self = this;
2426
3093
  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
- });
3094
+ var a = 0;
3095
+ tkns.map(function(t, i) {
3096
+ if (t.assisted) a++;
2441
3097
  });
2442
- return tkns[0].meta.n;
3098
+ return a;
2443
3099
  });
2444
3100
  },
2445
3101
  getFriendsToGo: function() {
@@ -2464,7 +3120,10 @@ libVES.Recovery.prototype = {
2464
3120
  });
2465
3121
  },
2466
3122
  assist: function() {
2467
- return this._assist(true);
3123
+ var self = this;
3124
+ return self.vaultKey.getType().then(function(t) {
3125
+ return t == 'recovery' ? self._assist(true) : null;
3126
+ });
2468
3127
  },
2469
3128
  revoke: function() {
2470
3129
  return this._assist(false);
@@ -2484,7 +3143,9 @@ libVES.Recovery.prototype = {
2484
3143
  _recover: function() {
2485
3144
  var self = this;
2486
3145
  return self.unlock().then(function() {
2487
- return self.vaultKey.rekey();
3146
+ return self.vaultKey.VES.elevateAuth({authVaultKey: self.vaultKey}).then(function(optns) {
3147
+ return self.vaultKey.rekey(optns);
3148
+ });
2488
3149
  });
2489
3150
  },
2490
3151
  recover: function() {
@@ -2500,11 +3161,14 @@ libVES.Delegate = {
2500
3161
  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
3162
  + '<div style="display:table-row;"><div style="display:table-cell;vertical-align:middle;text-align:center;">'
2502
3163
  + '<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>'
3164
+ + '{$content}'
2507
3165
  + '</div></div></div></div>',
3166
+ htmlDlg: '<p>Use the VESvault popup window to grant access to the App Vault</p>'
3167
+ + '<p class="VESvaultDelegateBlockerMsg" style="color: #bf7f00; font-style:italic;">&nbsp;</p>'
3168
+ + '<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>'
3169
+ + '<p><a class="VESvaultDelegateCancelLnk" href="#" onclick="libVES.Delegate.cancel(); return false;">Cancel</a></p>',
3170
+ htmlFlw: '<p>Authorizing VES Access</p>'
3171
+ + '<iframe style="width: 100%; height: 48px; border: none;" src="{$url}"></iframe>',
2508
3172
  htmlBlockerMsg: 'Looks like your browser is using a popup blocker...',
2509
3173
  name: 'VESvaultDelegate',
2510
3174
  login: function(VES,challenge,optns) {
@@ -2518,11 +3182,10 @@ libVES.Delegate = {
2518
3182
  return this.response = new Promise(function(resolve,reject) {
2519
3183
  self.reject = reject;
2520
3184
  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);
3185
+ var url = VES.wwwUrl + 'vv/unlock?via=delegate&url=' + encodeURIComponent(document.location.href) + '&domain=' + encodeURIComponent(VES.domain);
3186
+ if (optns) for (var k in optns) if (optns[k] != null) url += '&' + encodeURIComponent(k) + '=' + encodeURIComponent(optns[k]);
3187
+ self.setOrigin(url);
3188
+ self.showOverlay(self.htmlDlg.replace('{$url}',url));
2526
3189
  self.retryPopupCalled = 0;
2527
3190
  try {
2528
3191
  document.getElementsByClassName('VESvaultDelegateRetryLnk')[0].onclick = function() {
@@ -2545,43 +3208,55 @@ libVES.Delegate = {
2545
3208
  self.popupInterval = window.setInterval(self.chkCancel.bind(self),1000);
2546
3209
  });
2547
3210
  },
3211
+ flow: function(VES, start, optns) {
3212
+ if (this.response) return this.response;
3213
+ this.VES = VES;
3214
+ var self = this;
3215
+ var unlk = VES.wwwUrl + 'vv/unlock';
3216
+ if (start || (!sessionStorage['libVES.flowStarted'] && document.referrer.substr(0, unlk.length).toLowerCase() == unlk.toLowerCase())) {
3217
+ return self.response = new Promise(function(resolve, reject) {
3218
+ self.reject = reject;
3219
+ self.resolve = resolve;
3220
+ var ifrm = VES.wwwUrl + 'vv/flowin?url=' + encodeURIComponent(document.location.href) + '&domain=' + encodeURIComponent(VES.domain);
3221
+ if (optns) for (var k in optns) if (optns[k] != null) ifrm += '&' + encodeURIComponent(k) + '=' + encodeURIComponent(optns[k]);
3222
+ self.setOrigin(ifrm);
3223
+ window.addEventListener('message',self.listener.bind(self));
3224
+ self.showOverlay(self.htmlFlw.replace('{$url}', ifrm));
3225
+ if (start) delete(sessionStorage['libVES.flowStarted'])
3226
+ else sessionStorage['libVES.flowStarted'] = true;
3227
+ });
3228
+ } else {
3229
+ return new Promise(function(){});
3230
+ }
3231
+ },
3232
+ showOverlay: function(cont) {
3233
+ this.popup = document.createElement('DIV');
3234
+ this.popup.innerHTML = this.html.replace('{$content}', cont);
3235
+ document.getElementsByTagName('BODY')[0].appendChild(this.popup);
3236
+ },
3237
+ setOrigin: function(url) {
3238
+ this.matchOrigin = (function(m) { return m ? m[0] : document.location.protocol + '//' + document.location.host; })(url.match(/^(https\:\/\/[^\/\?\#]+)/));
3239
+ },
2548
3240
  openPopup: function(url) {
2549
3241
  return this.popupWindow = window.open(url,this.name,"width=600,height=600,top=100,left=100");
2550
3242
  },
2551
3243
  retryPopup: function(url,href) {
2552
- var f = this.retryPopupCalled;
2553
3244
  this.retryPopupCalled++;
2554
- if (href && f > 1) href.target = '_blank';
2555
- else if (this.popupWindow) try {
3245
+ if (this.popupWindow) try {
2556
3246
  this.popupWindow.focus();
3247
+ return true;
2557
3248
  } catch(e) {}
2558
- return !f && this.openPopup(url);
3249
+ this.openPopup(url);
3250
+ return true;
2559
3251
  },
2560
3252
  listener: function(evnt) {
2561
- if (this.popupWindow && evnt.origin == this.matchOrigin) try {
3253
+ if (this.popup && evnt.origin == this.matchOrigin) try {
2562
3254
  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;
3255
+ if (msg.redirect) {
3256
+ document.location.replace(msg.redirect);
3257
+ } else {
3258
+ this.resolve(this.VES.authorize(msg));
3259
+ }
2585
3260
  this.close();
2586
3261
  if (!evnt.source.closed) evnt.source.close();
2587
3262
  } catch(e) {}
@@ -2598,7 +3273,7 @@ libVES.Delegate = {
2598
3273
  this.popupInterval = null;
2599
3274
  this.popup.parentNode.removeChild(this.popup);
2600
3275
  this.popup = null;
2601
- return self.response;
3276
+ return this.response;
2602
3277
  }
2603
3278
  },
2604
3279
  cancel: function() {
@@ -2611,4 +3286,255 @@ libVES.Delegate = {
2611
3286
  }
2612
3287
  };
2613
3288
 
3289
+
3290
+ libVES.Flow = function(name, optns) {
3291
+ this.name = name;
3292
+ this.origin = document.location.origin;
3293
+ this.url = this.origin + '/VESflow';
3294
+ if (optns) for (var k in optns) this[k] = optns[k];
3295
+ return this;
3296
+ };
3297
+
3298
+ libVES.Flow.algo = {name: 'ECDH', namedCurve: 'P-256'};
3299
+
3300
+ libVES.Flow.keyAlgo = function(jwk) {
3301
+ if (jwk && jwk.kty == 'EC') return {name: 'ECDH', namedCurve: jwk.crv};
3302
+ return null;
3303
+ };
3304
+
3305
+ libVES.Flow.toOrigin = function(url) {
3306
+ var a = document.createElement('a');
3307
+ a.href = url;
3308
+ return a.origin;
3309
+ };
3310
+
3311
+ libVES.Flow.jwkPub = function(jwk) {
3312
+ delete(jwk.d);
3313
+ delete(jwk.key_ops);
3314
+ delete(jwk.ext);
3315
+ return jwk;
3316
+ };
3317
+
3318
+ libVES.Flow.parseToken = function(src, callbk) {
3319
+ src = src.replace(/\#VESflow\.(\w+)=([^\#\&\=]+)/g, function(s, k, v) {
3320
+ var tk;
3321
+ try {
3322
+ tk = JSON.parse(decodeURIComponent(v));
3323
+ if (callbk(decodeURIComponent(k), tk)) return '';
3324
+ } catch(e) {
3325
+ console.log(e);
3326
+ }
3327
+ return s;
3328
+ });
3329
+ return src;
3330
+ };
3331
+
3332
+ libVES.Flow.logout = function() {
3333
+ for (var k in sessionStorage) if (k.match(/^VESflow\b/)) delete(sessionStorage[k]);
3334
+ };
3335
+
3336
+ libVES.Flow.prototype.source = function(src) {
3337
+ if (src != null) history.replaceState(null, '', src);
3338
+ return document.location.href;
3339
+ };
3340
+
3341
+ libVES.Flow.prototype.privKey = function(algo) {
3342
+ if (!algo) algo = libVES.Flow.algo;
3343
+ var a = JSON.stringify(algo);
3344
+ return Promise.resolve(sessionStorage['VESflow' + a]).then(function(k) {
3345
+ if (!k) throw null;
3346
+ return crypto.subtle.importKey('jwk', JSON.parse(k), algo, true, ['deriveBits', 'deriveKey']);
3347
+ }).catch(function(e) {
3348
+ return crypto.subtle.generateKey(algo, true, ['deriveBits', 'deriveKey']).then(function(pair) {
3349
+ return crypto.subtle.exportKey('jwk', pair.privateKey).then(function(jwk) {
3350
+ sessionStorage['VESflow' + a] = JSON.stringify(jwk);
3351
+ return pair.privateKey;
3352
+ });
3353
+ });
3354
+ });
3355
+ };
3356
+
3357
+ libVES.Flow.prototype.pubJWK = function(org) {
3358
+ return ((!org || org == this.origin) ? this.privKey().then(function(priv) {
3359
+ return crypto.subtle.exportKey('jwk', priv).then(function(jwk) {
3360
+ return libVES.Flow.jwkPub(jwk);
3361
+ });
3362
+ }) : Promise.resolve(sessionStorage['VESflow|' + org]).then(function(k) {
3363
+ if (!k) throw {code: 'NotFound', message: 'No public key stored for ' + org};
3364
+ return JSON.parse(k);
3365
+ }));
3366
+ };
3367
+
3368
+ libVES.Flow.prototype.pubKey = function(org) {
3369
+ return this.pubJWK(org).then(function(jwk) {
3370
+ return crypto.subtle.importKey('jwk', jwk, libVES.Flow.keyAlgo(jwk), true, []);
3371
+ });
3372
+ };
3373
+
3374
+ libVES.Flow.prototype.cipher = function(url) {
3375
+ var self = this;
3376
+ return self.pubKey(libVES.Flow.toOrigin(url)).then(function(pub) {
3377
+ return self.privKey(pub.algorithm).then(function(priv) {
3378
+ return crypto.subtle.deriveBits({name: 'ECDH', public: pub}, priv, 8 * libVES.Algo.ECDH.curveBytes[pub.algorithm.namedCurve]);
3379
+ });
3380
+ }).then(function(raw) {
3381
+ return crypto.subtle.digest({name: 'SHA-256'}, raw).then(function(buf) {
3382
+ return crypto.subtle.importKey('raw', buf, 'AES-GCM', false, ['encrypt', 'decrypt']);
3383
+ });
3384
+ });
3385
+ };
3386
+
3387
+ libVES.Flow.prototype.encrypt = function(url, val) {
3388
+ var self = this;
3389
+ return Promise.resolve(val || this.value()).then(function(val) {
3390
+ if (val == null) throw {code: 'NoData', message: 'Empty value'};
3391
+ return self.cipher(url).then(function(ci) {
3392
+ var iv = new Uint8Array(12);
3393
+ crypto.getRandomValues(iv);
3394
+ return crypto.subtle.encrypt({name: 'AES-GCM', iv: iv.buffer}, ci, libVES.Util.StringToByteArray(JSON.stringify(val))).then(function(ctext) {
3395
+ return libVES.Util.ByteArrayToB64W(iv) + '.' + libVES.Util.ByteArrayToB64W(ctext);
3396
+ });
3397
+ });
3398
+ });
3399
+ };
3400
+
3401
+ libVES.Flow.prototype.token = function(url) {
3402
+ var self = this;
3403
+ return this.pubJWK().then(function(jwk) {
3404
+ return {url: self.url, key: jwk};
3405
+ }).then(function(tk) {
3406
+ return self.encrypt(url).then(function(ctext) {
3407
+ tk.enc = ctext;
3408
+ self.erase(url);
3409
+ self.sent = true;
3410
+ return tk;
3411
+ }).catch(function(e) {
3412
+ self.store(url).catch(function(e) {});
3413
+ return tk;
3414
+ });
3415
+ });
3416
+ };
3417
+
3418
+ libVES.Flow.prototype.addToken = function(url) {
3419
+ var self = this;
3420
+ return self.token(url).then(function(tk) {
3421
+ return url + '#VESflow.' + self.name + '=' + encodeURIComponent(JSON.stringify(tk));
3422
+ });
3423
+ };
3424
+
3425
+ libVES.Flow.prototype.decrypt = function(ctext, url) {
3426
+ var self = this;
3427
+ var c = ctext.split('.');
3428
+ return self.cipher(url).then(function(ci) {
3429
+ return crypto.subtle.decrypt({name: 'AES-GCM', iv: libVES.Util.B64ToByteArray(c[0])}, ci, libVES.Util.B64ToByteArray(c[1])).then(function(ptext) {
3430
+ return JSON.parse(libVES.Util.ByteArrayToString(ptext));
3431
+ });
3432
+ });
3433
+ };
3434
+
3435
+ libVES.Flow.prototype.value = function() {
3436
+ var self = this;
3437
+ return Promise.resolve(history.state && history.state['VESflow.' + self.name]).then(function(ctext) {
3438
+ return self.decrypt(ctext);
3439
+ });
3440
+ };
3441
+
3442
+ libVES.Flow.prototype.setValue = function(val) {
3443
+ var self = this;
3444
+ return Promise.resolve(val? self.encrypt(null, val) : null).then(function(ctext) {
3445
+ var s = {};
3446
+ if (history.state) for (var k in history.state) s[k] = history.state[k];
3447
+ if (ctext) s['VESflow.' + self.name] = ctext;
3448
+ else delete(s['VESflow.' + self.name]);
3449
+ history.replaceState(s, '');
3450
+ return val;
3451
+ });
3452
+ };
3453
+
3454
+ libVES.Flow.prototype.click = function(a) {
3455
+ var self = this;
3456
+ (a.origin == this.origin ? self.store() : self.addToken(a.href).then(function(url) {
3457
+ a.href = url;
3458
+ })).then(function() {
3459
+ document.location.href = a.href;
3460
+ });
3461
+ return false;
3462
+ };
3463
+
3464
+ libVES.Flow.prototype.reload = function(e) {
3465
+ if (!this.sent) this.store().catch(function(e) {});
3466
+ };
3467
+
3468
+ libVES.Flow.prototype.recv = function(dval) {
3469
+ var self = this;
3470
+ return Promise.resolve(self.source()).then(function(src) {
3471
+ var tk = null;
3472
+ src = libVES.Flow.parseToken(src, function(k, t) {
3473
+ if (k != self.name) return false;
3474
+ tk = t;
3475
+ return true;
3476
+ });
3477
+ if (tk == null) throw {code: 'NotFound', message: 'No valid token in the source url'};
3478
+ return Promise.resolve(self.source(src)).then(function() {
3479
+ if (dval) for (var k in dval) if (tk[k] === undefined) tk[k] = dval[k];
3480
+ return self.recvToken(tk);
3481
+ });
3482
+ });
3483
+ };
3484
+
3485
+ libVES.Flow.prototype.recvToken = function(tk) {
3486
+ var org = libVES.Flow.toOrigin(tk.url);
3487
+ if (org != this.origin && tk.key) sessionStorage['VESflow|' + org] = JSON.stringify(tk.key);
3488
+ if (!tk.enc) return Promise.reject({url: tk.url, code: 'Incomplete', message: 'Missing encrypted data'});
3489
+ return this.decrypt(tk.enc, org);
3490
+ };
3491
+
3492
+ libVES.Flow.prototype.get = function() {
3493
+ var self = this;
3494
+ return self.fetch().catch(function(e) {
3495
+ if (!e || e.code != 'NotFound') console.log(e);
3496
+ return null;
3497
+ }).then(function(data) {
3498
+ return self.recv().catch(function(e) {
3499
+ if (e && e.code == 'Incomplete') {
3500
+ self.url = document.location.href;
3501
+ return self.addToken(e.url).then(function(url) {
3502
+ document.location.replace(url);
3503
+ throw {code: 'Reload', message: 'Completing the key exchange'};
3504
+ });
3505
+ }
3506
+ if (!data) throw e;
3507
+ return data;
3508
+ });
3509
+ });
3510
+ };
3511
+
3512
+ libVES.Flow.prototype.store = function(url) {
3513
+ if (history.state && history.state['VESflow.' + this.name]) {
3514
+ this.sent = 1;
3515
+ return Promise.resolve(sessionStorage['VESflow.' + this.name + '>' + libVES.Flow.toOrigin(url)] = history.state['VESflow.' + this.name]);
3516
+ }
3517
+ return Promise.reject({code: 'NotFound', message: 'Value is not set'});
3518
+ };
3519
+
3520
+ libVES.Flow.prototype.fetch = function(url) {
3521
+ var self = this;
3522
+ return Promise.resolve(sessionStorage['VESflow.' + self.name + '>' + libVES.Flow.toOrigin(url)]).then(function(ctext) {
3523
+ if (!ctext) throw {code: 'NotFound', message: 'Not in the session storage'};
3524
+ return self.decrypt(ctext).then(function(data) {
3525
+ self.erase(url);
3526
+ return data;
3527
+ });
3528
+ });
3529
+ };
3530
+
3531
+ libVES.Flow.prototype.erase = function(url) {
3532
+ delete(sessionStorage['VESflow.' + this.name + '>' + libVES.Flow.toOrigin(url)]);
3533
+ };
3534
+
3535
+ libVES.Flow.prototype.logout = function() {
3536
+ this.setValue(undefined);
3537
+ return libVES.Flow.logout();
3538
+ };
3539
+
2614
3540
  module.exports = libVES;